diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9e16544..b9cf60c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 11 + - name: Set up JDK 16 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 16 - name: Cache SonarCloud packages uses: actions/cache@v1 with: diff --git a/pom.xml b/pom.xml index 9e27c11..dc1f977 100644 --- a/pom.xml +++ b/pom.xml @@ -32,17 +32,19 @@ UTF-8 UTF-8 - 1.8 - 2.0.4 + 16 + 2.0.9 - 1.15.2-R0.1-SNAPSHOT - 1.15.4 - 2.5.0 + 1.17.1-R0.1-SNAPSHOT + 1.2.3-SNAPSHOT + 1.20.0 + 2.6.3 1.7 + 1.0.0 ${build.version}-SNAPSHOT - 0.8.4 + 1.0.0 -LOCAL BentoBoxWorld_Challenges @@ -92,35 +94,33 @@ + + + apache.snapshots + https://repository.apache.org/snapshots/ + + + + spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots - - spigotmc-public - https://hub.spigotmc.org/nexus/content/groups/public/ - + codemc-repo - https://repo.codemc.org/repository/maven-public/ + https://repo.codemc.io/repository/maven-public + - codemc-nms - https://repo.codemc.org/repository/nms/ - - - - vault-repo - http://nexus.hc.to/content/repositories/pub_releases - - - jitpack.io - https://jitpack.io + minecraft-repo + https://libraries.minecraft.net/ + org.spigotmc spigot-api @@ -129,15 +129,49 @@ org.spigotmc - spigot - ${spigot.version} + plugin-annotations + 1.2.3-SNAPSHOT + provided + + + + net.milkbowl.vault + VaultAPI + ${vault.version} + provided + + + + world.bentobox + bentobox + ${bentobox.version} + provided + + + world.bentobox + level + ${level.version} + provided + + + + lv.id.bonne + panelutils + ${panelutils.version} + compile + + + + org.jetbrains + annotations + 22.0.0 provided org.mockito mockito-core - 3.1.0 + 3.11.2 test @@ -152,22 +186,11 @@ ${powermock.version} test + - world.bentobox - bentobox - ${bentobox.version} - provided - - - world.bentobox - level - ${level.version} - provided - - - net.milkbowl.vault - VaultAPI - ${vault.version} + com.mojang + authlib + 1.5.21 provided @@ -213,16 +236,49 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.7.0 ${java.version} ${java.version} + ${java.version} org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.0.0-M5 + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.math=ALL-UNNAMED + --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens + java.base/java.util.stream=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED + --add-opens + java.base/java.util.regex=ALL-UNNAMED + --add-opens + java.base/java.nio.channels.spi=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED + --add-opens java.base/java.net=ALL-UNNAMED + --add-opens + java.base/java.util.concurrent=ALL-UNNAMED + --add-opens java.base/sun.nio.fs=ALL-UNNAMED + --add-opens java.base/sun.nio.cs=ALL-UNNAMED + --add-opens java.base/java.nio.file=ALL-UNNAMED + --add-opens + java.base/java.nio.charset=ALL-UNNAMED + --add-opens + java.base/java.lang.reflect=ALL-UNNAMED + --add-opens + java.logging/java.util.logging=ALL-UNNAMED + --add-opens java.base/java.lang.ref=ALL-UNNAMED + --add-opens java.base/java.util.jar=ALL-UNNAMED + --add-opens java.base/java.util.zip=ALL-UNNAMED + + + org.apache.maven.plugins @@ -234,12 +290,10 @@ maven-javadoc-plugin 3.1.1 - 8 public false -Xdoclint:none - - + ${java.home}/bin/javadoc @@ -263,28 +317,6 @@ - - org.apache.maven.plugins - maven-shade-plugin - 3.2.1 - - true - - - io.github.TheBusyBiscuit.GitHubWebAPI4Java - world.bentobox.bentobox.api.github - - - - - - package - - shade - - - - org.apache.maven.plugins maven-install-plugin @@ -322,6 +354,38 @@ + + org.apache.maven.plugins + maven-shade-plugin + 3.3.1-SNAPSHOT + + true + + + lv.id.bonne:panelutils:* + + + + + + MANIFEST.MF + + + + META-INF/MANIFEST.MF + src/main/resources/META-INF/MANIFEST.MF + + + + + + package + + shade + + + + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..9bb88d3 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +/.DS_Store diff --git a/src/main/java/world/bentobox/challenges/ChallengesAddon.java b/src/main/java/world/bentobox/challenges/ChallengesAddon.java index da875a8..1882d57 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesAddon.java +++ b/src/main/java/world/bentobox/challenges/ChallengesAddon.java @@ -3,7 +3,6 @@ package world.bentobox.challenges; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -16,10 +15,10 @@ import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; import world.bentobox.bentobox.hooks.VaultHook; import world.bentobox.bentobox.managers.RanksManager; -import world.bentobox.challenges.commands.ChallengesCommand; -import world.bentobox.challenges.commands.ChallengesUserCommand; -import world.bentobox.challenges.commands.admin.Challenges; +import world.bentobox.challenges.commands.ChallengesPlayerCommand; +import world.bentobox.challenges.commands.ChallengesGlobalPlayerCommand; import world.bentobox.challenges.commands.admin.ChallengesAdminCommand; +import world.bentobox.challenges.commands.admin.ChallengesGlobalAdminCommand; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.database.object.ChallengeLevel; import world.bentobox.challenges.handlers.ChallengeDataRequestHandler; @@ -29,6 +28,8 @@ import world.bentobox.challenges.handlers.LevelDataRequestHandler; import world.bentobox.challenges.handlers.LevelListRequestHandler; import world.bentobox.challenges.listeners.ResetListener; import world.bentobox.challenges.listeners.SaveListener; +import world.bentobox.challenges.managers.ChallengesImportManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.web.WebManager; import world.bentobox.level.Level; @@ -72,6 +73,12 @@ public class ChallengesAddon extends Addon { */ private boolean levelProvided; + /** + * List of hooked gamemode addons. + */ + private final List hookedGameModes = new ArrayList<>(); + + // --------------------------------------------------------------------- // Section: Constants // --------------------------------------------------------------------- @@ -112,6 +119,12 @@ public class ChallengesAddon extends Addon { this.saveDefaultConfig(); // Load the plugin's config this.loadSettings(); + + if (this.settings.isUseCommonGUI()) + { + new ChallengesGlobalPlayerCommand(this, this.hookedGameModes); + new ChallengesGlobalAdminCommand(this, this.hookedGameModes); + } } @@ -150,77 +163,28 @@ public class ChallengesAddon extends Addon { // Web content loading this.webManager = new WebManager(this); - List hookedGameModes = new ArrayList<>(); + this.hookedGameModes.clear(); 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; + gameModeAddon.getPlayerCommand().ifPresent(command -> + new ChallengesPlayerCommand(this, command)); + gameModeAddon.getAdminCommand().ifPresent(command -> + new ChallengesAdminCommand(this, command)); - hookedGameModes.add(gameModeAddon); - } - - if (gameModeAddon.getAdminCommand().isPresent()) - { - new Challenges(this, gameModeAddon.getAdminCommand().get()); - this.hooked = true; - } + this.hooked = true; + this.hookedGameModes.add(gameModeAddon); CHALLENGES_WORLD_PROTECTION.addGameModeAddon(gameModeAddon); CHALLENGES_ISLAND_PROTECTION.addGameModeAddon(gameModeAddon); this.registerPlaceholders(gameModeAddon); - - // TODO: this is old placeholders. Remove when backward compatibility ends. - this.registerPlaceholdersOld(gameModeAddon); } }); if (this.hooked) { - - // Create general challenge commands - - if (this.settings.isUseCommonGUI()) - { - new ChallengesUserCommand(this, - this.settings.getUserCommand(), - hookedGameModes); - new ChallengesAdminCommand(this, - this.settings.getAdminCommand(), - hookedGameModes); - } - - // 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; - } - else - { - this.levelProvided = true; - this.levelAddon = (Level) level.get(); - } - - Optional vault = this.getPlugin().getVault(); - - if (!vault.isPresent() || !vault.get().hook()) - { - this.vaultHook = null; - this.logWarning("Vault plugin not found. Economy will not work!"); - } - else - { - this.vaultHook = vault.get(); - } - // Register the reset listener this.registerListener(new ResetListener(this)); // Register the autosave listener. @@ -255,6 +219,44 @@ public class ChallengesAddon extends Addon { } + /** + * Process Level addon and Vault Hook when everything is loaded. + */ + @Override + public void allLoaded() + { + super.allLoaded(); + + // Try to find Level addon and if it does not exist, display a warning + this.getAddonByName("Level").ifPresentOrElse(addon -> { + this.levelAddon = (Level) addon; + this.levelProvided = true; + this.log("Challenges Addon hooked into Level addon."); + }, () -> { + this.levelAddon = null; + this.logWarning("Level add-on not found so level challenges will not work!"); + }); + + + // Try to find Vault Plugin and if it does not exist, display a warning + this.getPlugin().getVault().ifPresentOrElse(hook -> { + this.vaultHook = hook; + + if (this.vaultHook.hook()) + { + this.log("Challenges Addon hooked into Economy."); + } + else + { + this.logWarning("Challenges Addon could not hook into valid Economy."); + } + }, () -> { + this.vaultHook = null; + this.logWarning("Vault plugin not found. Economy will not work!"); + }); + } + + /** * {@inheritDoc} */ @@ -306,6 +308,15 @@ public class ChallengesAddon extends Addon { this.logError("Challenges settings could not load! Addon disabled."); this.setState(State.DISABLED); } + + // Save existing panels. + this.saveResource("panels/main_panel.yml", false); + this.saveResource("panels/multiple_panel.yml",false); + this.saveResource("panels/gamemode_panel.yml",false); + + // Save template + this.saveResource("template.yml",false); + this.saveResource("default.json",false); } @@ -403,75 +414,6 @@ public class ChallengesAddon extends Addon { } - /** - * This method registers placeholders into GameMode addon. - * @param gameModeAddon GameMode addon where placeholders must be hooked in. - * @since 0.8.1 - * @deprecated remove after 0.9.0 - */ - @Deprecated - private void registerPlaceholdersOld(GameModeAddon gameModeAddon) - { - final String gameMode = gameModeAddon.getDescription().getName().toLowerCase(); - final World world = gameModeAddon.getOverWorld(); - - // Number of completions for all challenges placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_total_completion_count", - user -> String.valueOf(this.challengesManager.getTotalChallengeCompletionCount(user, world))); - - // Completed challenge count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_completed_count", - user -> String.valueOf(this.challengesManager.getCompletedChallengeCount(user, world))); - - // Uncompleted challenge count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_uncompleted_count", - user -> String.valueOf(this.challengesManager.getChallengeCount(world) - - this.challengesManager.getCompletedChallengeCount(user, world))); - - // Completed challenge level count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_completed_level_count", - user -> String.valueOf(this.challengesManager.getCompletedLevelCount(user, world))); - - // Uncompleted challenge level count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_uncompleted_level_count", - user -> String.valueOf(this.challengesManager.getLevelCount(world) - - this.challengesManager.getCompletedLevelCount(user, world))); - - // Unlocked challenge level count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_unlocked_level_count", - user -> String.valueOf(this.challengesManager.getLevelCount(world) - - this.challengesManager.getUnlockedLevelCount(user, world))); - - // Locked challenge level count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_locked_level_count", - user -> String.valueOf(this.challengesManager.getLevelCount(world) - - this.challengesManager.getUnlockedLevelCount(user, world))); - - // Latest challenge level name placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_latest_level_name", - user -> { - ChallengeLevel level = this.challengesManager.getLatestUnlockedLevel(user, world); - return level != null ? level.getFriendlyName() : ""; - }); - - // Latest challenge level id placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_latest_level_id", - user -> { - ChallengeLevel level = this.challengesManager.getLatestUnlockedLevel(user, world); - return level != null ? level.getUniqueId() : ""; - }); - } - - // --------------------------------------------------------------------- // Section: Getters // --------------------------------------------------------------------- diff --git a/src/main/java/world/bentobox/challenges/ChallengesImportManager.java b/src/main/java/world/bentobox/challenges/ChallengesImportManager.java deleted file mode 100644 index 47e0fca..0000000 --- a/src/main/java/world/bentobox/challenges/ChallengesImportManager.java +++ /dev/null @@ -1,572 +0,0 @@ -package world.bentobox.challenges; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.bukkit.World; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.Expose; - -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory; -import world.bentobox.bentobox.database.objects.DataObject; -import world.bentobox.bentobox.util.Util; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.ChallengeLevel; -import world.bentobox.challenges.utils.Utils; - - -/** - * Imports challenges - * @author BONNe1704 - * - */ -public class ChallengesImportManager -{ - /** - * Import challenges from default.json - * @param challengesAddon - */ - public ChallengesImportManager(ChallengesAddon challengesAddon) - { - this.addon = challengesAddon; - } - - - // --------------------------------------------------------------------- - // Section: Default Challenge Loader - // --------------------------------------------------------------------- - - - /** - * This method loads default challenges into memory. - * @param user User who calls default challenge loading - * @param world Target world. - * @return true if everything was successful, otherwise false. - */ - public boolean loadDefaultChallenges(User user, World world) - { - ChallengesManager manager = this.addon.getChallengesManager(); - - // If exist any challenge or level that is bound to current world, then do not load default challenges. - if (manager.hasAnyChallengeData(world.getName())) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.exist-challenges-or-levels"); - } - else - { - this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); - } - - return false; - } - - // default configuration should be removed. - // user made configuration should not!. - boolean removeAtEnd = - !Files.exists(Paths.get(this.addon.getDataFolder().getPath() + "/default.json")); - - // Safe json configuration to Challenges folder. - this.addon.saveResource("default.json", false); - - try - { - // This prefix will be used to all challenges. That is a unique way how to separate challenged for - // each game mode. - String uniqueIDPrefix = Utils.getGameMode(world) + "_"; - DefaultDataHolder defaultChallenges = new DefaultJSONHandler(this.addon).loadObject(); - if (defaultChallenges != null) { - // All new challenges should get correct ID. So we need to map it to loaded challenges. - defaultChallenges.getChallengeList().forEach(challenge -> { - // Set correct challenge ID - challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); - // Set up correct level ID if it is necessary - if (!challenge.getLevel().isEmpty()) - { - challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); - } - // Load challenge in memory - manager.loadChallenge(challenge, false, user, user == null); - }); - - defaultChallenges.getLevelList().forEach(challengeLevel -> { - // Set correct level ID - challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); - // Set correct world name - challengeLevel.setWorld(Util.getWorld(world).getName()); - // Reset names for all challenges. - challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> uniqueIDPrefix + challenge). - collect(Collectors.toSet())); - // Load level in memory - manager.loadLevel(challengeLevel, false, user, user == null); - }); - } - } - catch (Exception e) - { - e.printStackTrace(); - return false; - } - - this.addon.getChallengesManager().saveChallenges(); - this.addon.getChallengesManager().saveLevels(); - - if (removeAtEnd) - { - // Remove default.yml file from resources to avoid interacting with it. - return new File(this.addon.getDataFolder(), "default.json").delete(); - } - - return true; - } - - - /** - * This method loads downloaded challenges into memory. - * @param user User who calls downloaded challenge loading - * @param world Target world. - * @param downloadString String that need to be loaded via DefaultDataHolder. - * @return true if everything was successful, otherwise false. - */ - public boolean loadDownloadedChallenges(User user, World world, String downloadString) - { - ChallengesManager manager = this.addon.getChallengesManager(); - - // If exist any challenge or level that is bound to current world, then do not load default challenges. - if (manager.hasAnyChallengeData(world.getName())) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.exist-challenges-or-levels"); - } - else - { - this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); - } - - return false; - } - - try - { - // This prefix will be used to all challenges. That is a unique way how to separate challenged for - // each game mode. - String uniqueIDPrefix = Utils.getGameMode(world) + "_"; - DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadWebObject(downloadString); - - // All new challenges should get correct ID. So we need to map it to loaded challenges. - downloadedChallenges.getChallengeList().forEach(challenge -> { - // Set correct challenge ID - challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); - // Set up correct level ID if it is necessary - if (!challenge.getLevel().isEmpty()) - { - challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); - } - // Load challenge in memory - manager.loadChallenge(challenge, false, user, user == null); - }); - - downloadedChallenges.getLevelList().forEach(challengeLevel -> { - // Set correct level ID - challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); - // Set correct world name - challengeLevel.setWorld(Util.getWorld(world).getName()); - // Reset names for all challenges. - challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> uniqueIDPrefix + challenge). - collect(Collectors.toSet())); - // Load level in memory - manager.loadLevel(challengeLevel, false, user, user == null); - }); - } - catch (Exception e) - { - addon.getPlugin().logStacktrace(e); - return false; - } - - this.addon.getChallengesManager().saveChallenges(); - this.addon.getChallengesManager().saveLevels(); - - return true; - } - - - // --------------------------------------------------------------------- - // Section: Default generation - // --------------------------------------------------------------------- - - - /** - * Create method that can generate default challenge file from existing challenges in given world. - * This method will create default.json file in Challenges folder. - * @param user User who calls this method. - * @param world from which challenges must be stored. - * @param overwrite indicates if existing default.json file can be overwritten. - * @return true if everything was successful, otherwise false - */ - public boolean generateDefaultChallengeFile(User user, World world, boolean overwrite) - { - File defaultFile = new File(this.addon.getDataFolder(), "default.json"); - - if (defaultFile.exists()) - { - if (overwrite) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.messages.defaults-file-overwrite"); - } - else - { - this.addon.logWarning("challenges.messages.defaults-file-overwrite"); - } - - if (!defaultFile.delete()) { - this.addon.logError("Could not delete file: " + defaultFile.getAbsolutePath()); - } - } - else - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.defaults-file-exist"); - } - else - { - this.addon.logWarning("challenges.errors.defaults-file-exist"); - } - - return false; - } - } - - try - { - if (defaultFile.createNewFile()) - { - String replacementString = Utils.getGameMode(world) + "_"; - ChallengesManager manager = this.addon.getChallengesManager(); - - List challengeList = manager.getAllChallenges(world). - stream(). - map(challenge -> { - // Use clone to avoid any changes in existing challenges. - Challenge clone = challenge.clone(); - // Remove world name from challenge id. - clone.setUniqueId(challenge.getUniqueId().replaceFirst(replacementString, "")); - // Remove world name from level id. - clone.setLevel(challenge.getLevel().replaceFirst(replacementString, "")); - - return clone; - }). - collect(Collectors.toList()); - - List levelList = manager.getLevels(world). - stream(). - map(challengeLevel -> { - // Use clone to avoid any changes in existing levels. - ChallengeLevel clone = challengeLevel.clone(); - // Remove world name from level ID. - clone.setUniqueId(challengeLevel.getUniqueId().replaceFirst(replacementString, "")); - // Remove world name. - clone.setWorld(""); - // Challenges must be reassign, as they also contains world name. - clone.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> challenge.replaceFirst(replacementString, "")). - collect(Collectors.toSet())); - - return clone; - }). - collect(Collectors.toList()); - - DefaultDataHolder defaultChallenges = new DefaultDataHolder(); - defaultChallenges.setChallengeList(challengeList); - defaultChallenges.setLevelList(levelList); - defaultChallenges.setVersion(this.addon.getDescription().getVersion()); - - try (BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(defaultFile), StandardCharsets.UTF_8))) { - writer.write(Objects.requireNonNull( - new DefaultJSONHandler(this.addon).toJsonString(defaultChallenges))); - } - } - } - catch (IOException e) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.defaults-file-error"); - } - - this.addon.logError("Could not save json file: " + e.getMessage()); - return false; - } - finally - { - if (user.isPlayer()) - { - user.sendMessage("challenges.messages.defaults-file-completed", "[world]", world.getName()); - } - else - { - this.addon.logWarning("challenges.messages.defaults-file-completed"); - } - } - - return true; - } - - - // --------------------------------------------------------------------- - // Section: Private classes for default challenges - // --------------------------------------------------------------------- - - - /** - * This Class allows to load default challenges and their levels as objects much easier. - */ - private static final class DefaultJSONHandler - { - /** - * This constructor inits JSON builder that will be used to parse challenges. - * @param addon Challenges Adddon - */ - DefaultJSONHandler(ChallengesAddon addon) - { - GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); - // Register adapters - builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(addon.getPlugin())); - // Keep null in the database - builder.serializeNulls(); - // Allow characters like < or > without escaping them - builder.disableHtmlEscaping(); - - this.addon = addon; - this.gson = builder.setPrettyPrinting().create(); - } - - - /** - * This method returns json object that is parsed to string. Json object is made from given instance. - * @param instance Instance that must be parsed to json string. - * @return String that contains JSON information from instance object. - */ - String toJsonString(DefaultDataHolder instance) - { - // Null check - if (instance == null) - { - this.addon.logError("JSON database request to store a null. "); - return null; - } - - return this.gson.toJson(instance); - } - - - /** - * This method creates and adds to list all objects from default.json file. - * @return List of all objects from default.json that is with T instance. - */ - DefaultDataHolder loadObject() - { - File defaultFile = new File(this.addon.getDataFolder(), "default.json"); - - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(defaultFile), StandardCharsets.UTF_8)) - { - DefaultDataHolder object = this.gson.fromJson(reader, DefaultDataHolder.class); - - reader.close(); // NOSONAR Required to keep OS file handlers low and not rely on GC - - return object; - } - catch (FileNotFoundException e) - { - this.addon.logError("Could not load file '" + defaultFile.getName() + "': File not found."); - } - catch (Exception e) - { - this.addon.logError("Could not load objects " + defaultFile.getName() + " " + e.getMessage()); - } - - return null; - } - - - /** - * This method creates and adds to list all objects from default.json file. - * @return List of all objects from default.json that is with T instance. - */ - DefaultDataHolder loadWebObject(String downloadedObject) - { - return this.gson.fromJson(downloadedObject, DefaultDataHolder.class); - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * Holds JSON builder object. - */ - private Gson gson; - - /** - * Holds ChallengesAddon object. - */ - private ChallengesAddon addon; - } - - - /** - * This is simple object that will allow to store all current challenges and levels - * in single file. - */ - private static final class DefaultDataHolder implements DataObject - { - /** - * Default constructor. Creates object with empty lists. - */ - DefaultDataHolder() - { - this.challengeList = Collections.emptyList(); - this.challengeLevelList = Collections.emptyList(); - this.version = ""; - } - - - /** - * This method returns stored challenge list. - * @return list that contains default challenges. - */ - List getChallengeList() - { - return challengeList; - } - - - /** - * This method sets given list as default challenge list. - * @param challengeList new default challenge list. - */ - void setChallengeList(List challengeList) - { - this.challengeList = challengeList; - } - - - /** - * This method returns list of default challenge levels. - * @return List that contains default challenge levels. - */ - List getLevelList() - { - return challengeLevelList; - } - - - /** - * This method sets given list as default challenge level list. - * @param levelList new default challenge level list. - */ - void setLevelList(List levelList) - { - this.challengeLevelList = levelList; - } - - - /** - * This method returns the version value. - * @return the value of version. - */ - public String getVersion() - { - return version; - } - - - /** - * This method sets the version value. - * @param version the version new value. - * - */ - public void setVersion(String version) - { - this.version = version; - } - - - /** - * @return default.json - */ - @Override - public String getUniqueId() - { - return "default.json"; - } - - - /** - * @param uniqueId - unique ID the uniqueId to set - */ - @Override - public void setUniqueId(String uniqueId) - { - // method not used. - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * Holds a list with default challenges. - */ - @Expose - private List challengeList; - - /** - * Holds a list with default levels. - */ - @Expose - private List challengeLevelList; - - /** - * Holds a variable that stores in which addon version file was made. - */ - @Expose - private String version; - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - private ChallengesAddon addon; -} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/ChallengesPladdon.java b/src/main/java/world/bentobox/challenges/ChallengesPladdon.java new file mode 100644 index 0000000..b636f7a --- /dev/null +++ b/src/main/java/world/bentobox/challenges/ChallengesPladdon.java @@ -0,0 +1,30 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges; + + +import org.bukkit.plugin.java.annotation.dependency.Dependency; +import org.bukkit.plugin.java.annotation.plugin.ApiVersion; +import org.bukkit.plugin.java.annotation.plugin.Plugin; + +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.addons.Pladdon; + +/** + * @author tastybento + */ +@Plugin(name="Pladdon", version="1.0") +@ApiVersion(ApiVersion.Target.v1_17) +@Dependency(value = "BentoBox") +public class ChallengesPladdon extends Pladdon +{ + @Override + public Addon getAddon() + { + return new ChallengesAddon(); + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java deleted file mode 100644 index 7012c9b..0000000 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java +++ /dev/null @@ -1,96 +0,0 @@ -package world.bentobox.challenges.commands; - -import java.util.List; - -import world.bentobox.bentobox.api.addons.GameModeAddon; -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.user.ChallengesGUI; - - -public class ChallengesCommand extends CompositeCommand -{ - public static final String CHALLENGE_COMMAND = "challenges"; - - - public ChallengesCommand(ChallengesAddon addon, CompositeCommand cmd) - { - super(addon, cmd, CHALLENGE_COMMAND); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean canExecute(User user, String label, List args) - { - if (!getIWM().inWorld(getWorld())) { - // Not a GameMode world. - user.sendMessage("general.errors.wrong-world"); - return false; - } - - if (!((ChallengesAddon) this.getAddon()).getChallengesManager().hasAnyChallengeData(this.getWorld())) - { - // Do not open gui if there is no challenges. - this.getAddon().logError("There are no challenges set up in " + this.getWorld() + "!"); - - // Show admin better explanation. - if (user.isOp() || user.hasPermission(this.getPermissionPrefix() + "admin.challenges")) - { - String topLabel = getIWM().getAddon(this.getWorld()) - .map(GameModeAddon::getAdminCommand) - .map(optionalAdminCommand -> optionalAdminCommand.map(ac -> ac.getTopLabel()).orElse(this.getTopLabel())).orElse(this.getTopLabel()); - user.sendMessage("challenges.errors.no-challenges-admin", "[command]", topLabel + " challenges"); - } - else - { - user.sendMessage("challenges.errors.no-challenges"); - } - - return false; - } - - if (this.getIslands().getIsland(this.getWorld(), user) == null) - { - // Do not open gui if there is no island for this player. - user.sendMessage("general.errors.no-island"); - return false; - } - - return true; - } - - - @Override - public boolean execute(User user, String label, List args) - { - // Open up the challenges GUI - if (user.isPlayer()) - { - new ChallengesGUI((ChallengesAddon) this.getAddon(), - this.getWorld(), - user, - this.getTopLabel(), - this.getPermissionPrefix()).build(); - return true; - } - // Show help - showHelp(this, user); - return false; - } - - - @Override - public void setup() - { - this.setPermission(CHALLENGE_COMMAND); - this.setParametersHelp("challenges.commands.user.main.parameters"); - this.setDescription("challenges.commands.user.main.description"); - - new CompleteChallengeCommand(this.getAddon(), this); - this.setOnlyPlayer(true); - } -} diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java similarity index 52% rename from src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java rename to src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java index 4c90d22..6583866 100644 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java @@ -8,27 +8,27 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.config.SettingsUtils.GuiMode; -import world.bentobox.challenges.panel.GameModesGUI; +import world.bentobox.challenges.panel.user.GameModePanel; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; /** * This class provides all necessary thing to implement challenges user command */ -public class ChallengesUserCommand extends CompositeCommand +public class ChallengesGlobalPlayerCommand extends CompositeCommand { /** - * Constructor that inits command with given string. + * Constructor that init command with given string. * @param addon Challenges Addon - * @param commands String that contains main command and its alias separated via whitespace. * @param gameModeAddons List with GameModes where challenges addon operates. */ - public ChallengesUserCommand(ChallengesAddon addon, String commands, List gameModeAddons) + public ChallengesGlobalPlayerCommand(ChallengesAddon addon, List gameModeAddons) { super(addon, - commands.split(" ")[0], - commands.split(" ")); + addon.getChallengesSettings().getPlayerGlobalCommand().split(" ")[0], + addon.getChallengesSettings().getPlayerGlobalCommand().split(" ")); this.gameModeAddons = gameModeAddons; - this.addon = addon; } @@ -39,7 +39,7 @@ public class ChallengesUserCommand extends CompositeCommand public void setup() { this.setOnlyPlayer(true); - this.setPermission("challenges"); + this.setPermission("addon.challenges"); this.setParametersHelp("challenges.commands.user.main.parameters"); this.setDescription("challenges.commands.user.main.description"); } @@ -53,13 +53,19 @@ public class ChallengesUserCommand extends CompositeCommand { // It is not necessary to check 0, as in that case addon will not be hooked. - if (this.gameModeAddons.size() == 1) + if (this.gameModeAddons.isEmpty()) + { + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "not-hooked")); + return false; + } + else if (this.gameModeAddons.size() == 1) { this.gameModeAddons.get(0).getPlayerCommand().ifPresent(compositeCommand -> - user.performCommand(compositeCommand.getTopLabel() + " challenges")); + user.performCommand(compositeCommand.getTopLabel() + " " + + this.getAddon().getChallengesSettings().getPlayerMainCommand().split(" ")[0])); return true; } - else if (this.addon.getChallengesSettings().getUserGuiMode() == GuiMode.CURRENT_WORLD) + else if (this.getAddon().getChallengesSettings().getUserGuiMode() == GuiMode.CURRENT_WORLD) { // Find GameMode and run command for (GameModeAddon addon : this.gameModeAddons) @@ -67,21 +73,22 @@ public class ChallengesUserCommand extends CompositeCommand if (addon.inWorld(user.getWorld())) { addon.getPlayerCommand().ifPresent(compositeCommand -> - user.performCommand(compositeCommand.getTopLabel() + " challenges")); + user.performCommand(compositeCommand.getTopLabel() + " " + + this.getAddon().getChallengesSettings().getPlayerMainCommand().split(" ")[0])); return true; } } + + Utils.sendMessage(user, user.getTranslation("general.errors.wrong-world")); } - else if (this.addon.getChallengesSettings().getUserGuiMode() == GuiMode.GAMEMODE_LIST) + else if (this.getAddon().getChallengesSettings().getUserGuiMode() == GuiMode.GAMEMODE_LIST) { - new GameModesGUI(this.addon, + GameModePanel.open(this.getAddon(), this.getWorld(), user, - this.getTopLabel(), - this.getPermissionPrefix(), - false, - this.gameModeAddons).build(); + this.gameModeAddons, + false); return true; } @@ -97,10 +104,5 @@ public class ChallengesUserCommand extends CompositeCommand /** * List with hooked GameMode addons. */ - private List gameModeAddons; - - /** - * Challenges addon for easier operations. - */ - private ChallengesAddon addon; + private final List gameModeAddons; } diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java new file mode 100644 index 0000000..fe2fd6e --- /dev/null +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java @@ -0,0 +1,110 @@ +package world.bentobox.challenges.commands; + +import java.util.List; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.user.ChallengesPanel; +import world.bentobox.challenges.utils.Utils; + + +public class ChallengesPlayerCommand extends CompositeCommand +{ + public ChallengesPlayerCommand(ChallengesAddon addon, CompositeCommand cmd) + { + super(addon, + cmd, + addon.getChallengesSettings().getPlayerMainCommand().split(" ")[0], + addon.getChallengesSettings().getPlayerMainCommand().split(" ")); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean canExecute(User user, String label, List args) + { + if (!this.getIWM().inWorld(user.getWorld()) || + !Util.sameWorld(this.getWorld(), user.getWorld())) { + // Not a GameMode world. + Utils.sendMessage(user, user.getTranslation("general.errors.wrong-world")); + return false; + } + + if (!((ChallengesAddon) this.getAddon()).getChallengesManager().hasAnyChallengeData(this.getWorld())) + { + // Do not open gui if there is no challenges. + this.getAddon().logError("There are no challenges set up in " + this.getWorld() + "!"); + + // Show admin better explanation. + if (user.isOp() || user.hasPermission(this.getPermissionPrefix() + "admin.challenges")) + { + String topLabel = this.getIWM().getAddon(this.getWorld()). + map(GameModeAddon::getAdminCommand). + map(optionalAdminCommand -> optionalAdminCommand.map(CompositeCommand::getTopLabel).orElse(this.getTopLabel())). + orElse(this.getTopLabel()); + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-challenges-admin", + "[command]", + topLabel + " " + this.getAddon().getChallengesSettings().getAdminMainCommand().split(" ")[0])); + } + else + { + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-challenges")); + } + + return false; + } + + if (this.getIslands().getIsland(this.getWorld(), user) == null) + { + // Do not open gui if there is no island for this player. + Utils.sendMessage(user, user.getTranslation("general.errors.no-island")); + return false; + } else if (ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.getWorld()) && + !this.getIslands().locationIsOnIsland(user.getPlayer(), user.getLocation())) + { + // Do not open gui if player is not on the island, but challenges requires island for + // completion. + Utils.sendMessage(user, user.getTranslation("challenges.errors.not-on-island")); + return false; + } + + return true; + } + + + @Override + public boolean execute(User user, String label, List args) + { + // Open up the challenges GUI + if (user.isPlayer()) + { + ChallengesPanel.open(this.getAddon(), + this.getWorld(), + user, + this.getTopLabel(), + this.getPermissionPrefix()); + + return true; + } + // Show help + showHelp(this, user); + return false; + } + + + @Override + public void setup() + { + this.setPermission("challenges"); + this.setParametersHelp("challenges.commands.user.main.parameters"); + this.setDescription("challenges.commands.user.main.description"); + + new CompleteChallengeCommand(this.getAddon(), this); + this.setOnlyPlayer(true); + } +} diff --git a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java index 840759f..f59a1d5 100644 --- a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java @@ -4,9 +4,7 @@ package world.bentobox.challenges.commands; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; -import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; @@ -17,7 +15,7 @@ import world.bentobox.challenges.utils.Utils; /** - * This command allows to complete challenges without a gui. + * This command allows completing challenges without a gui. */ public class CompleteChallengeCommand extends CompositeCommand { @@ -26,10 +24,12 @@ public class CompleteChallengeCommand extends CompositeCommand * @param addon Challenges addon. * @param cmd Parent Command. */ - public CompleteChallengeCommand(Addon addon, CompositeCommand cmd) + public CompleteChallengeCommand(ChallengesAddon addon, CompositeCommand cmd) { - super(addon, cmd, "complete"); - this.addon = (ChallengesAddon) addon; + super(addon, + cmd, + addon.getChallengesSettings().getPlayerCompleteCommand().split(" ")[0], + addon.getChallengesSettings().getPlayerCompleteCommand().split(" ")); } @@ -40,7 +40,7 @@ public class CompleteChallengeCommand extends CompositeCommand public void setup() { this.setOnlyPlayer(true); - this.setPermission("complete"); + this.setPermission("challenges"); this.setParametersHelp("challenges.commands.user.complete.parameters"); this.setDescription("challenges.commands.user.complete.description"); } @@ -54,7 +54,7 @@ public class CompleteChallengeCommand extends CompositeCommand { if (args.isEmpty()) { - user.sendMessage("challenges.errors.no-name"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); this.showHelp(this, user); return false; } @@ -62,22 +62,22 @@ public class CompleteChallengeCommand extends CompositeCommand { // Add world name back at the start String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(0); - Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName); + Challenge challenge = this.getAddon().getChallengesManager().getChallenge(challengeName); if (challenge != null) { - int count = args.size() == 2 ? Integer.valueOf(args.get(1)) : 1; + int count = args.size() == 2 ? Integer.parseInt(args.get(1)) : 1; boolean canMultipleTimes = user.hasPermission(this.getPermission() + ".multiple"); if (!canMultipleTimes && count > 1) { - user.sendMessage("challenges.error.no-multiple-permission"); + Utils.sendMessage(user, user.getTranslation("challenges.error.no-multiple-permission")); count = 1; } - return TryToComplete.complete(this.addon, + return TryToComplete.complete(this.getAddon(), user, challenge, this.getWorld(), @@ -87,7 +87,7 @@ public class CompleteChallengeCommand extends CompositeCommand } else { - user.sendMessage("challenges.errors.unknown-challenge"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); this.showHelp(this, user); return false; } @@ -113,10 +113,11 @@ public class CompleteChallengeCommand extends CompositeCommand case 3: // Create suggestions with all challenges that is available for users. - returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream(). - filter(challenge -> challenge.startsWith(Utils.getGameMode(this.getWorld()) + "_")). - map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)). - collect(Collectors.toList())); + returnList.addAll(this.getAddon().getChallengesManager().getAllChallengesNames(this.getWorld()). + stream(). + filter(challenge -> challenge.startsWith(Utils.getGameMode(this.getWorld()) + "_") || + challenge.startsWith(Utils.getGameMode(this.getWorld()).toLowerCase() + "_")). + map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).toList()); break; case 4: // Suggest a number of completions. @@ -135,14 +136,4 @@ public class CompleteChallengeCommand extends CompositeCommand return Optional.of(Util.tabLimit(returnList, lastString)); } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * Variable that holds challenge addon. Single casting. - */ - private ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java b/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java deleted file mode 100644 index 1567503..0000000 --- a/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java +++ /dev/null @@ -1,68 +0,0 @@ -package world.bentobox.challenges.commands.admin; - -import java.util.List; - -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.admin.AdminGUI; - - -public class Challenges extends CompositeCommand -{ - - /** - * Admin command for challenges - * - * @param parent - */ - public Challenges(ChallengesAddon addon, CompositeCommand parent) - { - super(addon, parent, "challenges"); - } - - - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.main.parameters"); - this.setDescription("challenges.commands.admin.main.description"); - - // Register sub commands - - // This method reloads challenges addon - new ReloadChallenges(getAddon(), this); - - // Defaults processing command - new DefaultsCommand(this.getAddon(), this); - - // Complete challenge command - new CompleteCommand(this.getAddon(), this); - - // Reset challenge command - new ResetCommand(this.getAddon(), this); - - new ShowChallenges(this.getAddon(), this); - - new MigrateCommand(this.getAddon(), this); - } - - - @Override - public boolean execute(User user, String label, List args) - { - // Open up the admin challenges GUI - if (user.isPlayer()) - { - 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/ChallengesAdminCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesAdminCommand.java index 3137d8c..57f9efc 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ChallengesAdminCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesAdminCommand.java @@ -1,87 +1,68 @@ package world.bentobox.challenges.commands.admin; - import java.util.List; -import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.GameModesGUI; +import world.bentobox.challenges.panel.admin.AdminPanel; -/** - * This class provides all necessary thing to implement challenges admin command - */ public class ChallengesAdminCommand extends CompositeCommand { - /** - * Constructor that inits command with given string. - * @param addon Challenges Addon - * @param commands String that contains main command and its alias separated via whitespace. - * @param gameModeAddons List with GameModes where challenges addon operates. - */ - public ChallengesAdminCommand(ChallengesAddon addon, String commands, List gameModeAddons) - { - super(addon, - commands.split(" ")[0], - commands.split(" ")); - this.gameModeAddons = gameModeAddons; - this.addon = addon; - } + /** + * Instantiates a new Challenges' admin command. + * + * @param addon the addon + * @param parent the parent + */ + public ChallengesAdminCommand(ChallengesAddon addon, CompositeCommand parent) + { + super(addon, + parent, + addon.getChallengesSettings().getAdminMainCommand().split(" ")[0], + addon.getChallengesSettings().getAdminMainCommand().split(" ")); + } - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.main.parameters"); - this.setDescription("challenges.commands.admin.main.description"); - } + @Override + public void setup() + { + this.setPermission("admin.challenges"); + this.setParametersHelp("challenges.commands.admin.main.parameters"); + this.setDescription("challenges.commands.admin.main.description"); + + // Register sub commands + + // This method reloads challenges addon + new ReloadChallenges(getAddon(), this); + + // Complete challenge command + new CompleteCommand(this.getAddon(), this); + + // Reset challenge command + new ResetCommand(this.getAddon(), this); + + new ShowChallenges(this.getAddon(), this); + + new MigrateCommand(this.getAddon(), this); + } - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - // For single game mode just open correct gui. + @Override + public boolean execute(User user, String label, List args) + { + // Open up the admin challenges GUI + if (user.isPlayer()) + { + AdminPanel.open(this.getAddon(), + this.getWorld(), + user, + this.getTopLabel(), + this.getPermissionPrefix()); - if (this.gameModeAddons.size() == 1) - { - this.gameModeAddons.get(0).getAdminCommand().ifPresent(compositeCommand -> - user.performCommand(compositeCommand.getTopLabel() + " challenges")); - } - else - { - new GameModesGUI(this.addon, - this.getWorld(), - user, - this.getTopLabel(), - this.getPermissionPrefix(), - true, - this.gameModeAddons).build(); - } - - return true; - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * This variable stores challenges addon. - */ - private ChallengesAddon addon; - - /** - * This variable stores List with game modes where challenges addon are hooked in. - */ - private List gameModeAddons; + return true; + } + return false; + } } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java new file mode 100644 index 0000000..0e09c2b --- /dev/null +++ b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java @@ -0,0 +1,87 @@ +package world.bentobox.challenges.commands.admin; + + +import java.util.List; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.user.GameModePanel; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class provides all necessary thing to implement challenges admin command + */ +public class ChallengesGlobalAdminCommand extends CompositeCommand +{ + /** + * Constructor that init command with given string. + * @param addon Challenges Addon + * @param gameModeAddons List with GameModes where challenges addon operates. + */ + public ChallengesGlobalAdminCommand(ChallengesAddon addon, List gameModeAddons) + { + super(addon, + addon.getChallengesSettings().getAdminGlobalCommand().split(" ")[0], + addon.getChallengesSettings().getAdminGlobalCommand().split(" ")); + this.gameModeAddons = gameModeAddons; + } + + + /** + * {@inheritDoc} + */ + @Override + public void setup() + { + this.setPermission("addon.admin.challenges"); + this.setParametersHelp("challenges.commands.admin.main.parameters"); + this.setDescription("challenges.commands.admin.main.description"); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean execute(User user, String label, List args) + { + // For single game mode just open correct gui. + + if (this.gameModeAddons.isEmpty()) + { + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "not-hooked")); + return false; + } + else if (this.gameModeAddons.size() == 1) + { + this.gameModeAddons.get(0).getAdminCommand().ifPresent(compositeCommand -> + user.performCommand(compositeCommand.getTopLabel() + " " + + this.getAddon().getChallengesSettings().getAdminMainCommand().split(" ")[0])); + } + else + { + GameModePanel.open(this.getAddon(), + this.getWorld(), + user, + this.gameModeAddons, + true); + } + + return true; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * This variable stores List with game modes where challenges addon are hooked in. + */ + private final List gameModeAddons; +} diff --git a/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java index d0d3f67..24f8913 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java @@ -2,11 +2,9 @@ package world.bentobox.challenges.commands.admin; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; -import java.util.stream.Collectors; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.commands.CompositeCommand; @@ -15,11 +13,12 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; /** - * This command allows to complete challenges without a gui. + * This command allows completing challenges without a gui. */ public class CompleteCommand extends CompositeCommand { @@ -57,7 +56,7 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.no-name"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); } else { @@ -68,7 +67,7 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.missing-arguments"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.missing-arguments")); } else { @@ -83,9 +82,9 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("general.errors.unknown-player", + Utils.sendMessage(user, user.getTranslation("general.errors.unknown-player", TextVariables.NAME, - args.get(0)); + args.get(0))); } else { @@ -98,6 +97,7 @@ public class CompleteCommand extends CompositeCommand // Add world name back at the start String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(1); Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName); + User target = User.getInstance(targetUUID); if (challenge != null) { @@ -106,23 +106,24 @@ public class CompleteCommand extends CompositeCommand this.addon.getChallengesManager().setChallengeComplete( targetUUID, this.getWorld(), challenge, user.getUniqueId()); + if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.completed", - "[name]", challenge.getFriendlyName(), - "[player]", User.getInstance(targetUUID).getName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.completed", + Constants.PARAMETER_NAME, challenge.getFriendlyName(), + Constants.PARAMETER_PLAYER, target.getName())); } else { this.addon.log("Challenge " + challenge.getFriendlyName() + " completed for player " + - User.getInstance(targetUUID).getName()); + target.getName()); } } else { if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.already-completed"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.already-completed")); } else { @@ -136,7 +137,7 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.unknown-challenge"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); } else { @@ -165,23 +166,15 @@ public class CompleteCommand extends CompositeCommand switch (size) { - case 3: + case 3 -> // Create suggestions with all challenges that is available for users. - returnList.addAll(Util.getOnlinePlayerList(user)); - break; - case 4: + case 4 -> // Create suggestions with all challenges that is available for users. returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream(). - map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)). - collect(Collectors.toList())); - - break; - default: - { - returnList.addAll(Collections.singletonList("help")); - break; - } + map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).toList()); + default -> + returnList.add("help"); } return Optional.of(Util.tabLimit(returnList, lastString)); @@ -195,5 +188,5 @@ public class CompleteCommand extends CompositeCommand /** * Variable that holds challenge addon. Single casting. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/DefaultsCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/DefaultsCommand.java deleted file mode 100644 index df9d7a9..0000000 --- a/src/main/java/world/bentobox/challenges/commands/admin/DefaultsCommand.java +++ /dev/null @@ -1,165 +0,0 @@ -package world.bentobox.challenges.commands.admin; - - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import world.bentobox.bentobox.api.addons.Addon; -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.util.Util; -import world.bentobox.challenges.ChallengesAddon; - - -/** - * This method generates default challenges file. - */ -public class DefaultsCommand extends CompositeCommand -{ - - /** - * Constructor that inits generate defaults command. - * - * @param addon Addon that inits this command - * @param cmd Parent command - */ - public DefaultsCommand(Addon addon, CompositeCommand cmd) - { - super(addon, cmd, "defaults"); - this.addon = (ChallengesAddon) addon; - } - - - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.defaults.parameters"); - this.setDescription("challenges.commands.admin.defaults.description"); - - // Register sub commands - // This method reloads challenges addon - new ImportCommand(this); - // Import ASkyBlock Challenges - new GenerateCommand(this); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - return this.showHelp(this, user); - } - - -// --------------------------------------------------------------------- -// Section: Private Classes -// --------------------------------------------------------------------- - - - /** - * This class allows to process import command. - */ - private class ImportCommand extends CompositeCommand - { - /** - * Default constructor for import method. - * @param parent composite command - */ - private ImportCommand(CompositeCommand parent) - { - super(DefaultsCommand.this.addon, parent, "import"); - } - - - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.defaults-import.parameters"); - this.setDescription("challenges.commands.admin.defaults-import.description"); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - return DefaultsCommand.this.addon.getImportManager().loadDefaultChallenges(user, this.getWorld()); - } - } - - - /** - * This class allows to process generate command. - */ - private class GenerateCommand extends CompositeCommand - { - /** - * Default constructor for generate method. - * @param parent composite command - */ - private GenerateCommand(CompositeCommand parent) - { - super(DefaultsCommand.this.addon, parent, "generate"); - } - - - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.defaults-generate.parameters"); - this.setDescription("challenges.commands.admin.defaults-generate.description"); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - return DefaultsCommand.this.addon.getImportManager().generateDefaultChallengeFile( - user, - this.getWorld(), - !args.isEmpty() && args.get(0).equalsIgnoreCase("overwrite")); - } - - - /** - * {@inheritDoc} - */ - @Override - public Optional> tabComplete(User user, String alias, List args) - { - String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : ""; - return Optional.of(Util.tabLimit(Collections.singletonList("overwrite"), lastArg)); - } - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * Holds challenges addon as variable. - */ - private ChallengesAddon addon; -} diff --git a/src/main/java/world/bentobox/challenges/commands/admin/MigrateCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/MigrateCommand.java index cfcadfd..17ed6d5 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/MigrateCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/MigrateCommand.java @@ -9,27 +9,32 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; -public class MigrateCommand extends CompositeCommand { - +public class MigrateCommand extends CompositeCommand +{ /** - * Migrates challenges - * @param addon - * @param cmd + * Instantiates a new Migrate command command. + * + * @param addon the addon + * @param cmd the cmd */ - public MigrateCommand(Addon addon, CompositeCommand cmd) { + public MigrateCommand(Addon addon, CompositeCommand cmd) + { super(addon, cmd, "migrate"); } + @Override - public boolean execute(User user, String label, List args) { - ((ChallengesAddon)getAddon()).getChallengesManager().migrateDatabase(user, getWorld()); + public boolean execute(User user, String label, List args) + { + ((ChallengesAddon) getAddon()).getChallengesManager().migrateDatabase(user, getWorld()); return true; } @Override - public void setup() { + public void setup() + { this.setPermission("challenges.admin"); this.setParametersHelp("challenges.commands.admin.migrate.parameters"); this.setDescription("challenges.commands.admin.migrate.description"); diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java b/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java index 6022895..0dd7ea9 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java @@ -6,17 +6,20 @@ import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; +import world.bentobox.challenges.utils.Utils; /** - * This class allows to reload challenges addon. + * This class allows reloading challenges addon. */ public class ReloadChallenges extends CompositeCommand { /** - * Admin command to reloads challenges addon. - * @param parent + * Instantiates a new Reload challenges command. + * + * @param addon the addon + * @param parent the parent */ public ReloadChallenges(Addon addon, CompositeCommand parent) { @@ -46,13 +49,13 @@ public class ReloadChallenges extends CompositeCommand if (args.isEmpty()) { this.manager.load(); - user.sendMessage("general.success"); + Utils.sendMessage(user, user.getTranslation("general.success")); return true; } else if (args.get(0).equalsIgnoreCase("hard")) { this.manager.reload(); - user.sendMessage("general.success"); + Utils.sendMessage(user, user.getTranslation("general.success")); return true; } else @@ -68,5 +71,8 @@ public class ReloadChallenges extends CompositeCommand // --------------------------------------------------------------------- - private ChallengesManager manager; + /** + * Addon Manager instance. + */ + private final ChallengesManager manager; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java index 649b728..8955da5 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java @@ -2,11 +2,9 @@ package world.bentobox.challenges.commands.admin; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; -import java.util.stream.Collectors; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.commands.CompositeCommand; @@ -15,11 +13,12 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; /** - * This command allows to reset challenges without a gui. + * This command allows resetting challenges without a gui. */ public class ResetCommand extends CompositeCommand { @@ -57,7 +56,7 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.no-name"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); } else { @@ -68,7 +67,7 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.missing-arguments"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.missing-arguments")); } else { @@ -83,7 +82,8 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); + Utils.sendMessage(user, user.getTranslation("general.errors.unknown-player", + TextVariables.NAME, args.get(0))); } else { @@ -92,7 +92,8 @@ public class ResetCommand extends CompositeCommand return false; } - + + User target = User.getInstance(targetUUID); // Add world name back at the start if (args.get(1).equals("all")) @@ -101,13 +102,12 @@ public class ResetCommand extends CompositeCommand if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.reset-all", - "[player]", User.getInstance(targetUUID).getName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.reset-all", + Constants.PARAMETER_PLAYER, target.getName())); } else { - this.addon.log("All challenges for user " + - User.getInstance(targetUUID).getName() + " was reset!"); + this.addon.log("All challenges for user " + target.getName() + " was reset!"); } return true; @@ -125,21 +125,21 @@ public class ResetCommand extends CompositeCommand if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.reset", - "[name]", challenge.getFriendlyName(), - "[player]", User.getInstance(targetUUID).getName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.reset", + Constants.PARAMETER_NAME, challenge.getFriendlyName(), + Constants.PARAMETER_PLAYER, target.getName())); } else { this.addon.log("Challenge " + challenge.getFriendlyName() + " was reset for player " + - User.getInstance(targetUUID).getName()); + target.getName()); } } else { if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.not-completed"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.not-completed")); } else { @@ -153,7 +153,7 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.unknown-challenge"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); } else { @@ -183,25 +183,17 @@ public class ResetCommand extends CompositeCommand switch (size) { - case 3: + case 3 -> // Create suggestions with all challenges that is available for users. - returnList.addAll(Util.getOnlinePlayerList(user)); - break; - case 4: + case 4 -> { // Create suggestions with all challenges that is available for users. returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream(). - map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)). - collect(Collectors.toList())); - + map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).toList()); returnList.add("all"); - - break; - default: - { - returnList.addAll(Collections.singletonList("help")); - break; } + default -> + returnList.add("help"); } return Optional.of(Util.tabLimit(returnList, lastString)); @@ -215,5 +207,5 @@ public class ResetCommand extends CompositeCommand /** * Variable that holds challenge addon. Single casting. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } 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 a735dc3..3171d63 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java @@ -7,27 +7,32 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; -public class ShowChallenges extends CompositeCommand { - - +public class ShowChallenges extends CompositeCommand +{ /** - * Admin command to show challenges and manage them - * @param parent + * Instantiates a new Show challenges command. + * + * @param addon the addon + * @param parent the parent */ - public ShowChallenges(Addon addon, CompositeCommand parent) { + public ShowChallenges(Addon addon, CompositeCommand parent) + { super(addon, parent, "show"); } + @Override - public void setup() { + public void setup() + { this.setPermission("admin.challenges"); this.setParametersHelp("challenges.commands.admin.show.parameters"); this.setDescription("challenges.commands.admin.show.description"); - } + @Override - public boolean execute(User user, String label, List args) { + public boolean execute(User user, String label, List args) + { if (user.isPlayer()) { ((ChallengesAddon) getAddon()).getChallengesManager(). @@ -40,7 +45,5 @@ public class ShowChallenges extends CompositeCommand { } return true; - } - } diff --git a/src/main/java/world/bentobox/challenges/config/Settings.java b/src/main/java/world/bentobox/challenges/config/Settings.java index 2332b46..237fe12 100644 --- a/src/main/java/world/bentobox/challenges/config/Settings.java +++ b/src/main/java/world/bentobox/challenges/config/Settings.java @@ -1,9 +1,7 @@ package world.bentobox.challenges.config; -import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.bukkit.Material; @@ -13,15 +11,13 @@ import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.api.configuration.ConfigEntry; import world.bentobox.bentobox.api.configuration.ConfigObject; import world.bentobox.bentobox.api.configuration.StoreAt; -import world.bentobox.bentobox.database.objects.adapters.Adapter; -import world.bentobox.challenges.config.SettingsUtils.ChallengeLore; import world.bentobox.challenges.config.SettingsUtils.GuiMode; -import world.bentobox.challenges.config.SettingsUtils.LevelLore; import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; -import world.bentobox.challenges.database.object.adapters.ChallengeLoreAdapter; -import world.bentobox.challenges.database.object.adapters.LevelLoreAdapter; +/** + * The type Settings. + */ @StoreAt(filename="config.yml", path="addons/Challenges") @ConfigComment("Challenges [version] Configuration") @ConfigComment("This config file is dynamic and saved when the server is shutdown.") @@ -30,37 +26,60 @@ import world.bentobox.challenges.database.object.adapters.LevelLoreAdapter; @ConfigComment("") public class Settings implements ConfigObject { - @ConfigComment("") - @ConfigComment("Allows to define common challenges command that will open User GUI") - @ConfigComment("with all GameMode selection or Challenges from user world.") - @ConfigComment("This will not affect /{gamemode_user} challenges command.") - @ConfigEntry(path = "commands.user", needsRestart = true) - private String userCommand = "challenges c"; - - @ConfigComment("") - @ConfigComment("Allows to define common challenges command that will open Admin GUI") - @ConfigComment("with all GameMode selection.") - @ConfigComment("This will not affect /{gamemode_admin} challenges command.") - @ConfigEntry(path = "commands.admin", needsRestart = true) - private String adminCommand = "challengesadmin chadmin"; - @ConfigComment("") @ConfigComment("This enables/disables common command that will be independent from") @ConfigComment("all GameModes. For admins it will open selection with all GameModes") @ConfigComment("(unless there is one), but for users it will open GUI that corresponds") @ConfigComment("to their world (unless it is specified other way in Admin GUI).") - @ConfigEntry(path = "commands.single-gui", needsRestart = true) + @ConfigComment("This means that writing `/[user_global]` will open Challenges GUI's") + @ConfigComment("and `/[admin_global]` will open Admin GUI's") + @ConfigEntry(path = "commands.global-command", needsRestart = true) private boolean useCommonGUI = false; @ConfigComment("") - @ConfigComment("This allows for admins to define which GUI will be opened for admins") - @ConfigComment("when users calls single-gui command.") + @ConfigComment("This allows to define which GUI will be opened when `single-gui` is enabled.") + @ConfigComment("This option is ignored if `single-gui` is disabled.") @ConfigComment("Acceptable values:") @ConfigComment(" - CURRENT_WORLD - will open GUI that corresponds to user location.") @ConfigComment(" - GAMEMODE_LIST - will open GUI with all installed game modes.") - @ConfigEntry(path = "commands.single-gamemode") + @ConfigEntry(path = "commands.global-view-mode") private GuiMode userGuiMode = GuiMode.CURRENT_WORLD; + @ConfigComment("") + @ConfigComment("Allows to define a global challenges user command. This command will work") + @ConfigComment("only if `global-commands` is enabled. This allows to execute `/challenges`") + @ConfigComment("without referring to the gamemode.") + @ConfigEntry(path = "commands.player.global", needsRestart = true) + private String playerGlobalCommand = "challenges c"; + + @ConfigComment("") + @ConfigComment("Allows to define user command for opening challenges GUI's.") + @ConfigComment("Unlike `global` command, this requires to have gamemode player command before it.") + @ConfigComment("This will look like: `/[player_cmd] challenges`") + @ConfigEntry(path = "commands.player.main", needsRestart = true) + private String playerMainCommand = "challenges"; + + @ConfigComment("") + @ConfigComment("Allows to define complete command.") + @ConfigComment("This will look like: `/[player_cmd] challenges complete`") + @ConfigEntry(path = "commands.player.complete", needsRestart = true) + private String playerCompleteCommand = "complete"; + + @ConfigComment("") + @ConfigComment("Allows to define a global challenges admin command. This command will work") + @ConfigComment("only if `global-commands` is enabled. This allows to execute `/chadmin`") + @ConfigComment("without referring to the gamemode.") + @ConfigComment("Note, this must not be the same as user global command.") + @ConfigEntry(path = "commands.admin.global", needsRestart = true) + private String adminGlobalCommand = "challengesadmin chadmin"; + + @ConfigComment("") + @ConfigComment("Allows to define admin command for opening challenges GUI's.") + @ConfigComment("Unlike `global` command, this requires to have gamemode admin command before it.") + @ConfigComment("This will look like: `/[admin_cmd] challenges`") + @ConfigEntry(path = "commands.admin.main", needsRestart = true) + private String adminMainCommand = "challenges"; + @ConfigComment("") @ConfigComment("This indicate if player challenges data history will be stored or not.") @ConfigEntry(path = "history.store-history-data") @@ -99,62 +118,10 @@ public class Settings implements ConfigObject @ConfigEntry(path = "gui-settings.locked-level-icon") private ItemStack lockedLevelIcon = new ItemStack(Material.BOOK); - @ConfigComment("") - @ConfigComment("This indicate if free challenges must be at the start (true) or at the end (false) of list.") - @ConfigEntry(path = "gui-settings.free-challenges-first") - private boolean freeChallengesFirst = true; - - @ConfigComment("") - @ConfigComment("This allows to change lore description line length. By default it is 25, but some server") - @ConfigComment("owners may like it to be larger.") - @ConfigEntry(path = "gui-settings.lore-length") - private int loreLineLength = 25; - - @ConfigComment("") - @ConfigComment("This string allows to change element order in Challenge description. Each letter represents") - @ConfigComment("one object from challenge description. If letter is not used, then its represented part") - @ConfigComment("will not be in description. If use any letter that is not recognized, then it will be") - @ConfigComment("ignored. Some strings can be customized via lang file under 'challenges.gui.challenge-description'.") - @ConfigComment("List of values and their meaning: ") - @ConfigComment(" - LEVEL - Level String: '*.level'") - @ConfigComment(" - STATUS - Status String: '*.completed'") - @ConfigComment(" - COUNT - Times String: '*.completed-times', '*.completed-times-of' or '*.maxed-reached'") - @ConfigComment(" - DESCRIPTION - Description String: defined in challenge object - challenge.description") - @ConfigComment(" - WARNINGS - Warning String: '*.warning-items-take', '*.objects-close-by', '*.warning-entities-kill', '*.warning-blocks-remove'") - @ConfigComment(" - ENVIRONMENT - Environment String: defined in challenge object - challenge.environment") - @ConfigComment(" - REQUIREMENTS - Requirement String: '*.required-level', '*.required-money', '*.required-experience' and items, blocks or entities") - @ConfigComment(" - REWARD_TEXT - Reward String: message that is defined in challenge.rewardTest and challenge.repeatRewardText") - @ConfigComment(" - REWARD_OTHER - Reward extra String: '*.experience-reward', '*.money-reward', '*.not-repeatable'") - @ConfigComment(" - REWARD_ITEMS - Reward Items: List of items that will be rewarded.") - @ConfigComment(" - REWARD_COMMANDS - Reward Commands: List of commands that will be rewarded.") - @ConfigComment("Requirement and reward items, blocks and entities that are defined in challenge and can be customized under 'challenges.gui.item-description.*'") - @ConfigEntry(path = "gui-settings.challenge-lore") - @Adapter(ChallengeLoreAdapter.class) - private List challengeLoreMessage = Arrays.asList(ChallengeLore.values()); - - @ConfigComment("") - @ConfigComment("This string allows to change element order in Level description. Each letter represents") - @ConfigComment("one object from level description. If letter is not used, then its represented part") - @ConfigComment("will not be in description. If use any letter that is not recognized, then it will be") - @ConfigComment("ignored. Some strings can be customized via lang file under 'challenges.gui.level-description'.") - @ConfigComment("List of values and their meaning: ") - @ConfigComment(" - LEVEL_STATUS - Status String: '*.completed'") - @ConfigComment(" - CHALLENGE_COUNT - Count of completed challenges String: '*.completed-challenges-of'") - @ConfigComment(" - UNLOCK_MESSAGE - Description String: defined in level object - challengeLevel.unlockMessage") - @ConfigComment(" - WAIVER_AMOUNT - WaiverAmount String: '*.waver-amount'") - @ConfigComment(" - LEVEL_REWARD_TEXT - Reward String: message that is defined in challengeLevel.rewardText.") - @ConfigComment(" - LEVEL_REWARD_OTHER - Reward extra String: '*.experience-reward', '*.money-reward'") - @ConfigComment(" - LEVEL_REWARD_ITEMS - Reward Items: List of items that will be rewarded.") - @ConfigComment(" - LEVEL_REWARD_COMMANDS - Reward Commands: List of commands that will be rewarded.") - @ConfigComment("Reward items that are defined in challenge level and can be customized under 'challenges.gui.item-description.*'") - @ConfigEntry(path = "gui-settings.level-lore") - @Adapter(LevelLoreAdapter.class) - private List levelLoreMessage = Arrays.asList(LevelLore.values()); - @ConfigComment("") @ConfigComment("This indicate if challenges data will be stored per island (true) or per player (false).") @ConfigEntry(path = "store-island-data") - private boolean storeAsIslandData = false; + private boolean storeAsIslandData = true; @ConfigComment("") @ConfigComment("Reset Challenges - if this is true, player's challenges will reset when users") @@ -205,17 +172,6 @@ public class Settings implements ConfigObject // Section: Getters // --------------------------------------------------------------------- - - /** - * This method returns the challengeLoreMessage object. - * @return the challengeLoreMessage object. - */ - public List getChallengeLoreMessage() - { - return challengeLoreMessage; - } - - /** * This method returns the configVersion object. * @return the configVersion object. @@ -271,35 +227,6 @@ public class Settings implements ConfigObject } - /** - * @return freeChallengesFirst value. - */ - public boolean isFreeChallengesFirst() - { - return this.freeChallengesFirst; - } - - - /** - * This method returns the loreLineLength object. - * @return the loreLineLength object. - */ - public int getLoreLineLength() - { - return loreLineLength; - } - - - /** - * This method returns the levelLoreMessage object. - * @return the levelLoreMessage object. - */ - public List getLevelLoreMessage() - { - return levelLoreMessage; - } - - /** * This method returns the storeAsIslandData object. * @return the storeAsIslandData object. @@ -324,9 +251,42 @@ public class Settings implements ConfigObject * This method returns the userCommand value. * @return the value of userCommand. */ - public String getUserCommand() + public String getPlayerGlobalCommand() { - return userCommand; + return playerGlobalCommand; + } + + + /** + * Gets main user command. + * + * @return the main user command + */ + public String getPlayerMainCommand() + { + return playerMainCommand; + } + + + /** + * Gets complete user command. + * + * @return the complete user command + */ + public String getPlayerCompleteCommand() + { + return playerCompleteCommand; + } + + + /** + * Gets main admin command. + * + * @return the main admin command + */ + public String getAdminMainCommand() + { + return adminMainCommand; } @@ -334,9 +294,9 @@ public class Settings implements ConfigObject * This method returns the adminCommand value. * @return the value of adminCommand. */ - public String getAdminCommand() + public String getAdminGlobalCommand() { - return adminCommand; + return adminGlobalCommand; } @@ -489,26 +449,6 @@ public class Settings implements ConfigObject } - /** - * This method sets the challengeLoreMessage object value. - * @param challengeLoreMessage the challengeLoreMessage object new value. - */ - public void setChallengeLoreMessage(List challengeLoreMessage) - { - this.challengeLoreMessage = challengeLoreMessage; - } - - - /** - * This method sets the levelLoreMessage object value. - * @param levelLoreMessage the levelLoreMessage object new value. - */ - public void setLevelLoreMessage(List levelLoreMessage) - { - this.levelLoreMessage = levelLoreMessage; - } - - /** * @param resetChallenges new resetChallenges value. */ @@ -554,25 +494,6 @@ public class Settings implements ConfigObject } - /** - * @param freeChallengesFirst new freeChallengesFirst value. - */ - public void setFreeChallengesFirst(boolean freeChallengesFirst) - { - this.freeChallengesFirst = freeChallengesFirst; - } - - - /** - * This method sets the loreLineLength object value. - * @param loreLineLength the loreLineLength object new value. - */ - public void setLoreLineLength(int loreLineLength) - { - this.loreLineLength = loreLineLength; - } - - /** * This method sets the storeAsIslandData object value. * @param storeAsIslandData the storeAsIslandData object new value. @@ -595,21 +516,54 @@ public class Settings implements ConfigObject /** * This method sets the userCommand value. - * @param userCommand the userCommand new value. + * @param playerGlobalCommand the userCommand new value. */ - public void setUserCommand(String userCommand) + public void setPlayerGlobalCommand(String playerGlobalCommand) { - this.userCommand = userCommand; + this.playerGlobalCommand = playerGlobalCommand; + } + + + /** + * Sets main user command. + * + * @param playerMainCommand the main user command + */ + public void setPlayerMainCommand(String playerMainCommand) + { + this.playerMainCommand = playerMainCommand; + } + + + /** + * Sets complete user command. + * + * @param playerCompleteCommand the complete user command + */ + public void setPlayerCompleteCommand(String playerCompleteCommand) + { + this.playerCompleteCommand = playerCompleteCommand; + } + + + /** + * Sets main admin command. + * + * @param adminMainCommand the main admin command + */ + public void setAdminMainCommand(String adminMainCommand) + { + this.adminMainCommand = adminMainCommand; } /** * This method sets the adminCommand value. - * @param adminCommand the adminCommand new value. + * @param adminGlobalCommand the adminCommand new value. */ - public void setAdminCommand(String adminCommand) + public void setAdminGlobalCommand(String adminGlobalCommand) { - this.adminCommand = adminCommand; + this.adminGlobalCommand = adminGlobalCommand; } diff --git a/src/main/java/world/bentobox/challenges/database/object/Challenge.java b/src/main/java/world/bentobox/challenges/database/object/Challenge.java index fb14d8a..c595a0a 100644 --- a/src/main/java/world/bentobox/challenges/database/object/Challenge.java +++ b/src/main/java/world/bentobox/challenges/database/object/Challenge.java @@ -6,7 +6,6 @@ import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.NonNull; @@ -16,8 +15,8 @@ import com.google.gson.annotations.JsonAdapter; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.Table; -import world.bentobox.challenges.database.object.adapters.EntityCompatibilityAdapter; import world.bentobox.challenges.database.object.adapters.RequirementsAdapter; +import world.bentobox.challenges.database.object.adapters.TypeMigrationAdapter; import world.bentobox.challenges.database.object.requirements.Requirements; @@ -45,18 +44,23 @@ public class Challenge implements DataObject /** * The player must have the items on them. */ - INVENTORY, + INVENTORY_TYPE, /** * Items or required entities have to be within x blocks of the player. */ - ISLAND, + ISLAND_TYPE, /** * Other type, like required money / experience or island level. This my request * other plugins to be setup before it could work. */ - OTHER, + OTHER_TYPE, + + /** + * Challenge based on player statistic data. + */ + STATISTIC_TYPE } @@ -106,7 +110,8 @@ public class Challenge implements DataObject * Challenge type can be INVENTORY, OTHER or ISLAND. */ @Expose - private ChallengeType challengeType = ChallengeType.INVENTORY; + @JsonAdapter(TypeMigrationAdapter.class) + private ChallengeType challengeType = ChallengeType.INVENTORY_TYPE; /** * List of environments where this challenge will occur: NETHER, NORMAL, THE_END. Leave blank for all. @@ -137,63 +142,6 @@ public class Challenge implements DataObject @JsonAdapter(RequirementsAdapter.class) private Requirements requirements; - // --------------------------------------------------------------------- - // Section: Deprecated Requirements - // --------------------------------------------------------------------- - - @Deprecated - @Expose - private Set requiredPermissions = new HashSet<>(); - - @Deprecated - @Expose - private Map requiredBlocks = new EnumMap<>(Material.class); - - @Deprecated - @Expose - private boolean removeBlocks; - - @Deprecated - @Expose - @JsonAdapter(EntityCompatibilityAdapter.class) - private Map requiredEntities = new EnumMap<>(EntityType.class); - - @Deprecated - @Expose - private boolean removeEntities; - - @Deprecated - @Expose - private List requiredItems = new ArrayList<>(); - - @Deprecated - @Expose - private boolean takeItems = true; - - @Deprecated - @Expose - private int requiredExperience = 0; - - @Deprecated - @Expose - private boolean takeExperience; - - @Deprecated - @Expose - private int requiredMoney = 0; - - @Deprecated - @Expose - private boolean takeMoney; - - @Deprecated - @Expose - private long requiredIslandLevel; - - @Deprecated - @Expose - private int searchRadius = 10; - // --------------------------------------------------------------------- // Section: Rewards @@ -221,7 +169,7 @@ public class Challenge implements DataObject * Money reward. Economy plugin or addon required for this option. */ @Expose - private int rewardMoney = 0; + private double rewardMoney = 0; /** * Commands to run when the player completes the challenge for the first time. String List @@ -229,6 +177,11 @@ public class Challenge implements DataObject @Expose private List rewardCommands = new ArrayList<>(); + /** + * Set of item stacks that should ignore metadata. + */ + @Expose + private Set ignoreRewardMetaData = new HashSet<>(); // --------------------------------------------------------------------- // Section: Repeat Rewards @@ -240,6 +193,12 @@ public class Challenge implements DataObject @Expose private boolean repeatable; + /** + * Timeout for repeatable challenge before it can be completed again. + */ + @Expose + private long timeout; + /** * Description of the repeat rewards. If blank, it will be autogenerated */ @@ -268,7 +227,7 @@ public class Challenge implements DataObject * Repeat money reward. Economy plugin or addon required for this option. */ @Expose - private int repeatMoneyReward; + private double repeatMoneyReward; /** * Commands to run when challenge is repeated. String List. @@ -384,136 +343,6 @@ public class Challenge implements DataObject } - /** - * @return the requiredPermissions - */ - @Deprecated - public Set getRequiredPermissions() - { - return requiredPermissions; - } - - - /** - * @return the requiredBlocks - */ - @Deprecated - public Map getRequiredBlocks() - { - return requiredBlocks; - } - - - /** - * @return the removeBlocks - */ - @Deprecated - public boolean isRemoveBlocks() - { - return removeBlocks; - } - - - /** - * @return the requiredEntities - */ - @Deprecated - public Map getRequiredEntities() - { - return requiredEntities; - } - - - /** - * @return the removeEntities - */ - @Deprecated - public boolean isRemoveEntities() - { - return removeEntities; - } - - - /** - * @return the requiredItems - */ - @Deprecated - public List getRequiredItems() - { - return requiredItems; - } - - - /** - * @return the takeItems - */ - @Deprecated - public boolean isTakeItems() - { - return takeItems; - } - - - /** - * @return the requiredExperience - */ - @Deprecated - public int getRequiredExperience() - { - return requiredExperience; - } - - - /** - * @return the takeExperience - */ - @Deprecated - public boolean isTakeExperience() - { - return takeExperience; - } - - - /** - * @return the requiredMoney - */ - @Deprecated - public int getRequiredMoney() - { - return requiredMoney; - } - - - /** - * @return the takeMoney - */ - @Deprecated - public boolean isTakeMoney() - { - return takeMoney; - } - - - /** - * @return the requiredIslandLevel - */ - @Deprecated - public long getRequiredIslandLevel() - { - return requiredIslandLevel; - } - - - /** - * @return the searchRadius - */ - @Deprecated - public int getSearchRadius() - { - return searchRadius; - } - - /** * @return the rewardText */ @@ -544,7 +373,7 @@ public class Challenge implements DataObject /** * @return the rewardMoney */ - public int getRewardMoney() + public double getRewardMoney() { return rewardMoney; } @@ -607,7 +436,7 @@ public class Challenge implements DataObject /** * @return the repeatMoneyReward */ - public int getRepeatMoneyReward() + public double getRepeatMoneyReward() { return repeatMoneyReward; } @@ -622,6 +451,28 @@ public class Challenge implements DataObject } + /** + * Gets timeout. + * + * @return the timeout + */ + public long getTimeout() + { + return timeout; + } + + + /** + * Gets ignore reward meta data. + * + * @return the ignore reward meta data + */ + public Set getIgnoreRewardMetaData() + { + return ignoreRewardMetaData; + } + + // --------------------------------------------------------------------- // Section: Setters // --------------------------------------------------------------------- @@ -735,162 +586,6 @@ public class Challenge implements DataObject } - /** - * This method sets the requiredPermissions value. - * @param requiredPermissions the requiredPermissions new value. - * - */ - @Deprecated - public void setRequiredPermissions(Set requiredPermissions) - { - this.requiredPermissions = requiredPermissions; - } - - - /** - * This method sets the requiredBlocks value. - * @param requiredBlocks the requiredBlocks new value. - * - */ - @Deprecated - public void setRequiredBlocks(Map requiredBlocks) - { - this.requiredBlocks = requiredBlocks; - } - - - /** - * This method sets the removeBlocks value. - * @param removeBlocks the removeBlocks new value. - * - */ - @Deprecated - public void setRemoveBlocks(boolean removeBlocks) - { - this.removeBlocks = removeBlocks; - } - - - /** - * This method sets the requiredEntities value. - * @param requiredEntities the requiredEntities new value. - * - */ - @Deprecated - public void setRequiredEntities(Map requiredEntities) - { - this.requiredEntities = requiredEntities; - } - - - /** - * This method sets the removeEntities value. - * @param removeEntities the removeEntities new value. - * - */ - @Deprecated - public void setRemoveEntities(boolean removeEntities) - { - this.removeEntities = removeEntities; - } - - - /** - * This method sets the requiredItems value. - * @param requiredItems the requiredItems new value. - * - */ - @Deprecated - public void setRequiredItems(List requiredItems) - { - this.requiredItems = requiredItems; - } - - - /** - * This method sets the takeItems value. - * @param takeItems the takeItems new value. - * - */ - @Deprecated - public void setTakeItems(boolean takeItems) - { - this.takeItems = takeItems; - } - - - /** - * This method sets the requiredExperience value. - * @param requiredExperience the requiredExperience new value. - * - */ - @Deprecated - public void setRequiredExperience(int requiredExperience) - { - this.requiredExperience = requiredExperience; - } - - - /** - * This method sets the takeExperience value. - * @param takeExperience the takeExperience new value. - * - */ - @Deprecated - public void setTakeExperience(boolean takeExperience) - { - this.takeExperience = takeExperience; - } - - - /** - * This method sets the requiredMoney value. - * @param requiredMoney the requiredMoney new value. - * - */ - @Deprecated - public void setRequiredMoney(int requiredMoney) - { - this.requiredMoney = requiredMoney; - } - - - /** - * This method sets the takeMoney value. - * @param takeMoney the takeMoney new value. - * - */ - @Deprecated - public void setTakeMoney(boolean takeMoney) - { - this.takeMoney = takeMoney; - } - - - /** - * This method sets the requiredIslandLevel value. - * @param requiredIslandLevel the requiredIslandLevel new value. - * - */ - @Deprecated - public void setRequiredIslandLevel(long requiredIslandLevel) - { - this.requiredIslandLevel = requiredIslandLevel; - } - - - /** - * This method sets the searchRadius value. - * @param searchRadius the searchRadius new value. - * - */ - @Deprecated - public void setSearchRadius(int searchRadius) - { - this.searchRadius = searchRadius; - } - - /** * This method sets the rewardText value. * @param rewardText the rewardText new value. @@ -929,7 +624,7 @@ public class Challenge implements DataObject * @param rewardMoney the rewardMoney new value. * */ - public void setRewardMoney(int rewardMoney) + public void setRewardMoney(double rewardMoney) { this.rewardMoney = rewardMoney; } @@ -1006,7 +701,7 @@ public class Challenge implements DataObject * @param repeatMoneyReward the repeatMoneyReward new value. * */ - public void setRepeatMoneyReward(int repeatMoneyReward) + public void setRepeatMoneyReward(double repeatMoneyReward) { this.repeatMoneyReward = repeatMoneyReward; } @@ -1034,6 +729,28 @@ public class Challenge implements DataObject } + /** + * Sets timeout. + * + * @param timeout the timeout + */ + public void setTimeout(long timeout) + { + this.timeout = timeout; + } + + + /** + * Sets ignore reward meta data. + * + * @param ignoreRewardMetaData the ignore reward meta data + */ + public void setIgnoreRewardMetaData(Set ignoreRewardMetaData) + { + this.ignoreRewardMetaData = ignoreRewardMetaData; + } + + // --------------------------------------------------------------------- // Section: Other methods // --------------------------------------------------------------------- @@ -1051,7 +768,7 @@ public class Challenge implements DataObject public boolean matchGameMode(String gameMode) { return gameMode != null && - this.uniqueId.regionMatches(true, 0, gameMode, 0, gameMode.length()); + this.uniqueId.regionMatches(true, 0, gameMode, 0, gameMode.length()); } @@ -1082,13 +799,11 @@ public class Challenge implements DataObject return true; } - if (!(obj instanceof Challenge)) + if (!(obj instanceof Challenge other)) { return false; } - Challenge other = (Challenge) obj; - if (uniqueId == null) { return other.uniqueId == null; @@ -1148,7 +863,7 @@ public class Challenge implements DataObject clone.setEnvironment(new HashSet<>(this.environment)); clone.setLevel(this.level); clone.setRemoveWhenCompleted(this.removeWhenCompleted); - clone.setRequirements(this.requirements.clone()); + clone.setRequirements(this.requirements.copy()); clone.setRewardText(this.rewardText); clone.setRewardItems( this.rewardItems.stream(). @@ -1167,6 +882,8 @@ public class Challenge implements DataObject collect(Collectors.toCollection(() -> new ArrayList<>(this.repeatItemReward.size())))); clone.setRepeatMoneyReward(this.repeatMoneyReward); clone.setRepeatRewardCommands(new ArrayList<>(this.repeatRewardCommands)); + clone.setTimeout(this.timeout); + clone.setIgnoreRewardMetaData(new HashSet<>(this.ignoreRewardMetaData)); } catch (Exception e) { diff --git a/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java b/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java index d5baf35..f885632 100644 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import com.google.gson.annotations.Expose; @@ -13,7 +14,7 @@ import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.Table; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; /** @@ -100,7 +101,7 @@ public class ChallengeLevel implements DataObject, Comparable @ConfigComment("") @ConfigComment("Money reward. Economy plugin or addon required for this option.") @Expose - private int rewardMoney = 0; + private double rewardMoney = 0; @ConfigComment("") @ConfigComment("Commands to run when the player completes all challenges in current") @@ -114,6 +115,10 @@ public class ChallengeLevel implements DataObject, Comparable @Expose private Set challenges = new HashSet<>(); + @ConfigComment("") + @ConfigComment("Set of materials which metadata can be ignored.") + @Expose + private Set ignoreRewardMetaData = new HashSet<>(); // --------------------------------------------------------------------- // Section: Getters @@ -236,7 +241,7 @@ public class ChallengeLevel implements DataObject, Comparable * This method returns the rewardMoney value. * @return the value of rewardMoney. */ - public int getRewardMoney() + public double getRewardMoney() { return rewardMoney; } @@ -262,6 +267,17 @@ public class ChallengeLevel implements DataObject, Comparable } + /** + * Gets ignore reward meta data. + * + * @return the ignore reward meta data + */ + public Set getIgnoreRewardMetaData() + { + return ignoreRewardMetaData; + } + + // --------------------------------------------------------------------- // Section: Setters // --------------------------------------------------------------------- @@ -395,7 +411,7 @@ public class ChallengeLevel implements DataObject, Comparable * @param rewardMoney the rewardMoney new value. * */ - public void setRewardMoney(int rewardMoney) + public void setRewardMoney(double rewardMoney) { this.rewardMoney = rewardMoney; } @@ -423,6 +439,17 @@ public class ChallengeLevel implements DataObject, Comparable } + /** + * Sets ignore reward meta data. + * + * @param ignoreRewardMetaData the ignore reward meta data + */ + public void setIgnoreRewardMetaData(Set ignoreRewardMetaData) + { + this.ignoreRewardMetaData = ignoreRewardMetaData; + } + + // --------------------------------------------------------------------- // Section: Other methods // --------------------------------------------------------------------- @@ -449,7 +476,7 @@ public class ChallengeLevel implements DataObject, Comparable * {@inheritDoc} */ @Override - public int compareTo(ChallengeLevel o) + public int compareTo(@NotNull ChallengeLevel o) { if (this.equals(o)) { @@ -501,13 +528,11 @@ public class ChallengeLevel implements DataObject, Comparable return true; } - if (!(obj instanceof ChallengeLevel)) + if (!(obj instanceof ChallengeLevel other)) { return false; } - ChallengeLevel other = (ChallengeLevel) obj; - if (uniqueId == null) { return other.uniqueId == null; @@ -542,8 +567,7 @@ public class ChallengeLevel implements DataObject, Comparable * Clone method that returns clone of current challengeLevel. * @return ChallengeLevel that is cloned from current object. */ - @Override - public ChallengeLevel clone() + public ChallengeLevel copy() { ChallengeLevel clone = new ChallengeLevel(); @@ -566,6 +590,7 @@ public class ChallengeLevel implements DataObject, Comparable clone.setRewardMoney(this.rewardMoney); clone.setRewardCommands(new ArrayList<>(this.rewardCommands)); clone.setChallenges(new HashSet<>(this.challenges)); + clone.setIgnoreRewardMetaData(new HashSet<>(this.ignoreRewardMetaData)); } catch (Exception e) { 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 c1de447..8a8106e 100644 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java @@ -65,7 +65,7 @@ public class ChallengesPlayerData implements DataObject private Map challengeStatus = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); /** - * Map of challenges completion time where key is challenges unique id and value is + * Map of challenges completion time when key is challenges unique id and value is * timestamp when challenge was completed last time. */ @Expose @@ -251,13 +251,25 @@ public class ChallengesPlayerData implements DataObject * @param challengeName - unique challenge name * @param times - the number of times to set */ - public void setChallengeTimes(@NonNull String challengeName, @NonNull int times) + public void setChallengeTimes(@NonNull String challengeName, int times) { challengeStatus.put(challengeName, times); challengesTimestamp.put(challengeName, System.currentTimeMillis()); } + /** + * Gets last completion time. + * + * @param challengeName the unique id + * @return the last completion time + */ + public long getLastCompletionTime(@NonNull String challengeName) + { + return this.challengesTimestamp.getOrDefault(challengeName, 0L); + } + + /** * Check if a challenge has been done * @@ -341,13 +353,11 @@ public class ChallengesPlayerData implements DataObject return true; } - if (!(obj instanceof ChallengesPlayerData)) + if (!(obj instanceof ChallengesPlayerData other)) { return false; } - ChallengesPlayerData other = (ChallengesPlayerData) obj; - if (uniqueId == null) { return other.uniqueId == null; diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/ChallengeLoreAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/ChallengeLoreAdapter.java deleted file mode 100644 index 450ca72..0000000 --- a/src/main/java/world/bentobox/challenges/database/object/adapters/ChallengeLoreAdapter.java +++ /dev/null @@ -1,63 +0,0 @@ -// -// Created by BONNe -// Copyright - 2019 -// - - -package world.bentobox.challenges.database.object.adapters; - - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import world.bentobox.bentobox.database.objects.adapters.AdapterInterface; -import world.bentobox.challenges.config.SettingsUtils.ChallengeLore; - - -/** - * This adapter allows to serialize and deserialize ChallengeLore object. - */ -public class ChallengeLoreAdapter implements AdapterInterface, List> -{ - @SuppressWarnings("unchecked") - @Override - public List deserialize(Object from) - { - List result; - - if (from instanceof List) - { - result = ((List) from).stream(). - map(ChallengeLore::valueOf). - collect(Collectors.toCollection(ArrayList::new)); - } - else - { - result = new ArrayList<>(0); - } - - return result; - } - - - @SuppressWarnings("unchecked") - @Override - public List serialize(Object to) - { - List result; - - if (to instanceof List) - { - result = ((List) to).stream(). - map(ChallengeLore::name). - collect(Collectors.toCollection(ArrayList::new)); - } - else - { - result = new ArrayList<>(0); - } - - return result; - } -} diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/EntityCompatibilityAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/EntityCompatibilityAdapter.java index b64a32f..8abcf5f 100644 --- a/src/main/java/world/bentobox/challenges/database/object/adapters/EntityCompatibilityAdapter.java +++ b/src/main/java/world/bentobox/challenges/database/object/adapters/EntityCompatibilityAdapter.java @@ -33,10 +33,7 @@ public class EntityCompatibilityAdapter implements { JsonObject jsonArray = new JsonObject(); - src.forEach((entity, number) -> - { - jsonArray.addProperty(entity.name(), number); - }); + src.forEach((entity, number) -> jsonArray.addProperty(entity.name(), number)); return jsonArray; } @@ -46,7 +43,6 @@ public class EntityCompatibilityAdapter implements * This method deserializes json object that stores Entity Name and amount as integer. * @param json Json element that must be parsed. * @return EnumMap that contains EntityType as key and Integer as value. - * @throws JsonParseException */ @Override public Map deserialize(JsonElement json, diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/LevelLoreAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/LevelLoreAdapter.java deleted file mode 100644 index 6c60245..0000000 --- a/src/main/java/world/bentobox/challenges/database/object/adapters/LevelLoreAdapter.java +++ /dev/null @@ -1,63 +0,0 @@ -// -// Created by BONNe -// Copyright - 2019 -// - - -package world.bentobox.challenges.database.object.adapters; - - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import world.bentobox.bentobox.database.objects.adapters.AdapterInterface; -import world.bentobox.challenges.config.SettingsUtils.LevelLore; - - -/** - * This adapter allows to serialize and deserialize LevelLore object. - */ -public class LevelLoreAdapter implements AdapterInterface, List> -{ - @SuppressWarnings("unchecked") - @Override - public List deserialize(Object from) - { - List result; - - if (from instanceof List) - { - result = ((List) from).stream(). - map(LevelLore::valueOf). - collect(Collectors.toCollection(ArrayList::new)); - } - else - { - result = new ArrayList<>(0); - } - - return result; - } - - - @SuppressWarnings("unchecked") - @Override - public List serialize(Object to) - { - List result; - - if (to instanceof List) - { - result = ((List) to).stream(). - map(LevelLore::name). - collect(Collectors.toCollection(ArrayList::new)); - } - else - { - result = new ArrayList<>(0); - } - - return result; - } -} diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/TypeMigrationAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/TypeMigrationAdapter.java new file mode 100644 index 0000000..2c8c434 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/database/object/adapters/TypeMigrationAdapter.java @@ -0,0 +1,51 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.challenges.database.object.adapters; + + +import com.google.gson.*; +import java.lang.reflect.Type; + +import world.bentobox.challenges.database.object.Challenge; + + +/** + * This is a generic JSON serializer and deserializer for abstract classes. + * It store target class in class object, and instance variables in variables object. + */ +public class TypeMigrationAdapter implements JsonSerializer, JsonDeserializer +{ + /** + * Use default enum name serialization. + */ + @Override + public JsonElement serialize(Challenge.ChallengeType src, Type typeOfSrc, JsonSerializationContext context) + { + return new JsonPrimitive(src.name()); + } + + + /** + * Deserialize enum with old type format. + */ + @Override + public Challenge.ChallengeType deserialize(JsonElement json, + Type typeOfT, + JsonDeserializationContext context) + throws JsonParseException + { + JsonPrimitive primitive = json.getAsJsonPrimitive(); + + return switch (primitive.getAsString()) + { + case "INVENTORY", "INVENTORY_TYPE" -> Challenge.ChallengeType.INVENTORY_TYPE; + case "OTHER", "OTHER_TYPE" -> Challenge.ChallengeType.OTHER_TYPE; + case "STATISTIC", "STATISTIC_TYPE" -> Challenge.ChallengeType.STATISTIC_TYPE; + default -> Challenge.ChallengeType.ISLAND_TYPE; + }; + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java index 64741b4..1f3686e 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java @@ -7,12 +7,10 @@ package world.bentobox.challenges.database.object.requirements; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; +import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import com.google.gson.annotations.Expose; @@ -81,6 +79,34 @@ public class InventoryRequirements extends Requirements } + /** + * Gets ignore meta data. + * + * @return the ignore meta data + */ + public Set getIgnoreMetaData() + { + if (this.ignoreMetaData == null) + { + // Fixes null-pointer, that should not be possible, but may be. + this.ignoreMetaData = new HashSet<>(); + } + + return this.ignoreMetaData; + } + + + /** + * Sets ignore meta data. + * + * @param ignoreMetaData the ignore meta data + */ + public void setIgnoreMetaData(Set ignoreMetaData) + { + this.ignoreMetaData = ignoreMetaData; + } + + // --------------------------------------------------------------------- // Section: Other methods // --------------------------------------------------------------------- @@ -100,12 +126,12 @@ public class InventoryRequirements extends Requirements /** - * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary + * Method Requirements#copy allows copies Requirements object, to avoid changing content when it is necessary * to use it. - * @return InventoryRequirements clone + * @return InventoryRequirements copy */ @Override - public Requirements clone() + public Requirements copy() { InventoryRequirements clone = new InventoryRequirements(); clone.setRequiredPermissions(new HashSet<>(this.getRequiredPermissions())); @@ -114,6 +140,7 @@ public class InventoryRequirements extends Requirements map(ItemStack::clone). collect(Collectors.toCollection(() -> new ArrayList<>(this.requiredItems.size())))); clone.setTakeItems(this.takeItems); + clone.setIgnoreMetaData(new HashSet<>(this.ignoreMetaData)); return clone; } @@ -129,6 +156,12 @@ public class InventoryRequirements extends Requirements @Expose private List requiredItems = new ArrayList<>(); + /** + * Set of item stacks that should ignore metadata. + */ + @Expose + private Set ignoreMetaData = new HashSet<>(); + /** * Boolean that indicate if challenge completion should remove items from inventory. */ diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/IslandRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/IslandRequirements.java index 1de6737..407c28b 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/IslandRequirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/IslandRequirements.java @@ -39,7 +39,7 @@ public class IslandRequirements extends Requirements /** * Method IslandRequirements#getRequiredBlocks returns the requiredBlocks of this object. * - * @return the requiredBlocks (type Map) of this object. + * @return the requiredBlocks (type {@code Map}) of this object. */ public Map getRequiredBlocks() { @@ -83,7 +83,7 @@ public class IslandRequirements extends Requirements /** * Method IslandRequirements#getRequiredEntities returns the requiredEntities of this object. * - * @return the requiredEntities (type Map) of this object. + * @return the requiredEntities (type {@code Map}) of this object. */ public Map getRequiredEntities() { @@ -165,12 +165,12 @@ public class IslandRequirements extends Requirements /** - * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary + * Method Requirements#copy allows copies Requirements object, to avoid changing content when it is necessary * to use it. - * @return IslandRequirements clone + * @return IslandRequirements copy */ @Override - public Requirements clone() + public Requirements copy() { IslandRequirements clone = new IslandRequirements(); clone.setRequiredPermissions(new HashSet<>(this.getRequiredPermissions())); diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/OtherRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/OtherRequirements.java index fb1bf44..5f59e7d 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/OtherRequirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/OtherRequirements.java @@ -147,12 +147,12 @@ public class OtherRequirements extends Requirements /** - * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary + * Method Requirements#copy allows copies Requirements object, to avoid changing content when it is necessary * to use it. - * @return OtherRequirements clone + * @return OtherRequirements copy */ @Override - public Requirements clone() + public Requirements copy() { OtherRequirements clone = new OtherRequirements(); clone.setRequiredPermissions(new HashSet<>(this.getRequiredPermissions())); diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/Requirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/Requirements.java index 1b292b7..ce8279a 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/Requirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/Requirements.java @@ -70,12 +70,11 @@ public abstract class Requirements /** - * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary + * Method Requirements#copy allows to copy Requirements object, to avoid changing content when it is necessary * to use it. - * @return Requirements clone + * @return Requirements copy */ - @Override - public abstract Requirements clone(); + public abstract Requirements copy(); // --------------------------------------------------------------------- diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java new file mode 100644 index 0000000..b90ec70 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java @@ -0,0 +1,228 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.database.object.requirements; + + +import com.google.gson.annotations.Expose; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.entity.EntityType; +import org.eclipse.jdt.annotation.Nullable; + + +public class StatisticRequirements extends Requirements +{ + /** + * Constructor Requirements creates a new Requirements instance. + */ + public StatisticRequirements() + { + // Empty constructor + } + + + /** + * This method copies given statistic object. + * @return Copy of this object. + */ + @Override + public Requirements copy() + { + StatisticRequirements requirements = new StatisticRequirements(); + requirements.setStatistic(this.statistic); + requirements.setEntity(this.entity); + requirements.setMaterial(this.material); + requirements.setAmount(this.amount); + requirements.setReduceStatistic(this.reduceStatistic); + + return requirements; + } + + + @Override + public boolean isValid() + { + if (!super.isValid()) + { + return false; + } + + if (this.statistic == null) + { + return false; + } + + return switch (this.statistic.getType()) + { + case ITEM -> this.material != null && this.material.isItem(); + + case BLOCK -> this.material != null && this.material.isBlock(); + + case ENTITY -> this.entity != null; + + case UNTYPED -> true; + + }; + + } + + + // --------------------------------------------------------------------- +// Section: Getters and setters +// --------------------------------------------------------------------- + + + /** + * Gets statistic. + * + * @return the statistic + */ + @Nullable + public Statistic getStatistic() + { + return statistic; + } + + + /** + * Sets statistic. + * + * @param statistic the statistic + */ + public void setStatistic(@Nullable Statistic statistic) + { + this.statistic = statistic; + } + + + /** + * Gets entity. + * + * @return the entity + */ + @Nullable + public EntityType getEntity() + { + return entity; + } + + + /** + * Sets entity. + * + * @param entity the entity + */ + public void setEntity(@Nullable EntityType entity) + { + this.entity = entity; + } + + + /** + * Gets material. + * + * @return the material + */ + @Nullable + public Material getMaterial() + { + return material; + } + + + /** + * Sets material. + * + * @param material the material + */ + public void setMaterial(@Nullable Material material) + { + this.material = material; + } + + + /** + * Gets amount. + * + * @return the amount + */ + public int getAmount() + { + return amount; + } + + + /** + * Sets amount. + * + * @param amount the amount + */ + public void setAmount(int amount) + { + this.amount = amount; + } + + + /** + * Is reduce statistic boolean. + * + * @return the boolean + */ + public boolean isReduceStatistic() + { + return reduceStatistic; + } + + + /** + * Sets reduce statistic. + * + * @param reduceStatistic the reduce statistic + */ + public void setReduceStatistic(boolean reduceStatistic) + { + this.reduceStatistic = reduceStatistic; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Type of the statistic field. + */ + @Expose + @Nullable + private Statistic statistic; + + /** + * Type of entity for entity related statistics. + */ + @Expose + @Nullable + private EntityType entity; + + /** + * Type of material for block and item related statistics. + */ + @Expose + @Nullable + private Material material; + + /** + * Amount of the stats. + */ + @Expose + private int amount; + + /** + * Indicate that player statistic fields must be adjusted after completing challenges. + */ + @Expose + private boolean reduceStatistic; +} diff --git a/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java index c099961..58af0c0 100644 --- a/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java @@ -101,5 +101,5 @@ public class ChallengeDataRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java index 5c067cf..9222c0b 100644 --- a/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.Map; import org.bukkit.Bukkit; +import org.bukkit.World; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; @@ -43,15 +44,21 @@ public class ChallengeListRequestHandler extends AddonRequestHandler */ if (metaData == null || - metaData.isEmpty() || - metaData.get("world-name") == null || - !(metaData.get("world-name") instanceof String) || - Bukkit.getWorld((String) metaData.get("world-name")) == null) + metaData.isEmpty() || + metaData.get("world-name") == null || + !(metaData.get("world-name") instanceof String)) { return Collections.emptyList(); } - return this.addon.getChallengesManager().getAllChallengesNames(Bukkit.getWorld((String) metaData.get("world-name"))); + World world = Bukkit.getWorld((String) metaData.get("world-name")); + + if (world == null) + { + return Collections.emptyList(); + } + + return this.addon.getChallengesManager().getAllChallengesNames(world); } @@ -63,5 +70,5 @@ public class ChallengeListRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java index 3ea05bb..dc0f9f0 100644 --- a/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java @@ -11,7 +11,7 @@ import org.bukkit.World; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; /** @@ -50,24 +50,27 @@ public class CompletedChallengesRequestHandler extends AddonRequestHandler */ if (metaData == null || - metaData.isEmpty() || - metaData.get("world-name") == null || - !(metaData.get("world-name") instanceof String) || - metaData.get("player") == null || - !(metaData.get("player") instanceof UUID) || - Bukkit.getWorld((String) metaData.get("world-name")) == null) + metaData.isEmpty() || + metaData.get("world-name") == null || + !(metaData.get("world-name") instanceof String) || + metaData.get("player") == null || + !(metaData.get("player") instanceof UUID player)) { return Collections.emptySet(); } World world = Bukkit.getWorld((String) metaData.get("world-name")); - UUID player = (UUID) metaData.get("player"); + + if (world == null) + { + return Collections.emptySet(); + } ChallengesManager manager = this.addon.getChallengesManager(); return manager.getAllChallengesNames(world).stream(). - filter(challenge -> manager.isChallengeComplete(player, world, challenge)). - collect(Collectors.toSet()); + filter(challenge -> manager.isChallengeComplete(player, world, challenge)). + collect(Collectors.toSet()); } @@ -79,5 +82,5 @@ public class CompletedChallengesRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/handlers/LevelDataRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/LevelDataRequestHandler.java index 10bf01d..492148d 100644 --- a/src/main/java/world/bentobox/challenges/handlers/LevelDataRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/LevelDataRequestHandler.java @@ -94,5 +94,5 @@ public class LevelDataRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java index f449297..290f1d9 100644 --- a/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.Map; import org.bukkit.Bukkit; +import org.bukkit.World; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; @@ -44,16 +45,21 @@ public class LevelListRequestHandler extends AddonRequestHandler */ if (metaData == null || - metaData.isEmpty() || - metaData.get("world-name") == null || - !(metaData.get("world-name") instanceof String) || - Bukkit.getWorld((String) metaData.get("world-name")) == null) + metaData.isEmpty() || + metaData.get("world-name") == null || + !(metaData.get("world-name") instanceof String)) { return Collections.emptyList(); } - return this.addon.getChallengesManager().getLevelNames( - Bukkit.getWorld((String) metaData.get("world-name"))); + World world = Bukkit.getWorld((String) metaData.get("world-name")); + + if (world == null) + { + return Collections.emptyList(); + } + + return this.addon.getChallengesManager().getLevelNames(world); } @@ -65,5 +71,5 @@ public class LevelListRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/listeners/ResetListener.java b/src/main/java/world/bentobox/challenges/listeners/ResetListener.java index 2106e03..1f961bf 100644 --- a/src/main/java/world/bentobox/challenges/listeners/ResetListener.java +++ b/src/main/java/world/bentobox/challenges/listeners/ResetListener.java @@ -1,6 +1,3 @@ -/** - * - */ package world.bentobox.challenges.listeners; import org.bukkit.event.EventHandler; @@ -10,22 +7,18 @@ import org.bukkit.event.Listener; import world.bentobox.bentobox.api.events.island.IslandCreatedEvent; import world.bentobox.bentobox.api.events.island.IslandRegisteredEvent; import world.bentobox.bentobox.api.events.island.IslandResettedEvent; +import world.bentobox.bentobox.api.events.team.TeamKickEvent; +import world.bentobox.bentobox.api.events.team.TeamLeaveEvent; import world.bentobox.challenges.ChallengesAddon; + /** * Resets challenges when the island is reset - * @author tastybento * + * @author tastybento */ -public class ResetListener implements Listener { - - private ChallengesAddon addon; - - public ResetListener(ChallengesAddon addon) { - this.addon = addon; - } - - +public record ResetListener(ChallengesAddon addon) implements Listener +{ /** * This method handles Island Created event. * @@ -34,7 +27,13 @@ public class ResetListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onIslandCreated(IslandCreatedEvent e) { - addon.getChallengesManager().resetAllChallenges(e.getOwner(), e.getLocation().getWorld(), e.getOwner()); + // Reset any challenges that can be assigned to the island or its owner. + if (this.addon.getChallengesSettings().isResetChallenges()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getOwner(), + e.getLocation().getWorld(), + e.getOwner()); + } } @@ -44,9 +43,16 @@ public class ResetListener implements Listener { * @param e Event that must be handled. */ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onIslandCreated(IslandResettedEvent e) + public void onIslandResetted(IslandResettedEvent e) { - addon.getChallengesManager().resetAllChallenges(e.getOwner(), e.getLocation().getWorld(), e.getOwner()); + // Reset owner challenges only if data is stored per player. + if (this.addon.getChallengesSettings().isResetChallenges() && + !this.addon.getChallengesSettings().isStoreAsIslandData()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getOwner(), + e.getLocation().getWorld(), + e.getOwner()); + } } @@ -56,8 +62,53 @@ public class ResetListener implements Listener { * @param e Event that must be handled. */ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onIslandCreated(IslandRegisteredEvent e) + public void onIslandRegistered(IslandRegisteredEvent e) { - addon.getChallengesManager().resetAllChallenges(e.getOwner(), e.getLocation().getWorld(), e.getOwner()); + // Reset owner challenges only if data is stored per player. + if (this.addon.getChallengesSettings().isResetChallenges() && + !this.addon.getChallengesSettings().isStoreAsIslandData()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getOwner(), + e.getLocation().getWorld(), + e.getOwner()); + } + } + + + /** + * This method handles Island Registered event. + * + * @param e Event that must be handled. + */ + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onTeamLeave(TeamLeaveEvent e) + { + // Reset player challenges only if data is stored per player. + if (this.addon.getChallengesSettings().isResetChallenges() && + !this.addon.getChallengesSettings().isStoreAsIslandData()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getPlayerUUID(), + e.getLocation().getWorld(), + e.getOwner()); + } + } + + + /** + * This method handles Island Registered event. + * + * @param e Event that must be handled. + */ + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onTeamKick(TeamKickEvent e) + { + // Reset player challenges only if data is stored per player. + if (this.addon.getChallengesSettings().isResetChallenges() && + !this.addon.getChallengesSettings().isStoreAsIslandData()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getPlayerUUID(), + e.getLocation().getWorld(), + e.getOwner()); + } } } diff --git a/src/main/java/world/bentobox/challenges/listeners/SaveListener.java b/src/main/java/world/bentobox/challenges/listeners/SaveListener.java index f20e588..a718772 100644 --- a/src/main/java/world/bentobox/challenges/listeners/SaveListener.java +++ b/src/main/java/world/bentobox/challenges/listeners/SaveListener.java @@ -50,5 +50,5 @@ public class SaveListener implements Listener // --------------------------------------------------------------------- - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java b/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java new file mode 100644 index 0000000..aa76502 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java @@ -0,0 +1,1348 @@ +package world.bentobox.challenges.managers; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory; +import world.bentobox.bentobox.database.objects.DataObject; +import world.bentobox.bentobox.util.ItemParser; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.database.object.requirements.InventoryRequirements; +import world.bentobox.challenges.database.object.requirements.IslandRequirements; +import world.bentobox.challenges.database.object.requirements.OtherRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * Imports challenges + * @author BONNe1704 + * + */ +public class ChallengesImportManager +{ + /** + * Import challenges from file or link. + * @param challengesAddon Challenges addon. + */ + public ChallengesImportManager(ChallengesAddon challengesAddon) + { + this.addon = challengesAddon; + } + + + // --------------------------------------------------------------------- + // Section: YAML Importers + // --------------------------------------------------------------------- + + + /** + * This method imports generator tiers from template + * + * @param user - user + * @param world - world to import into + * @param file - file that must be imported + */ + public void importFile(@Nullable User user, World world, String file) + { + File generatorFile = new File(this.addon.getDataFolder(), file.endsWith(".yml") ? file : file + ".yml"); + + if (!generatorFile.exists()) + { + if (user != null) + { + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-file", Constants.PARAMETER_FILE, file)); + } + + return; + } + + YamlConfiguration config = new YamlConfiguration(); + + try + { + config.load(generatorFile); + } + catch (IOException | InvalidConfigurationException e) + { + if (user != null) + { + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-load", + Constants.PARAMETER_FILE, file, TextVariables.DESCRIPTION, e.getMessage())); + } + else + { + this.addon.logError("Exception when loading file. " + e.getMessage()); + } + + return; + } + + Optional optional = this.addon.getPlugin().getIWM().getAddon(world); + + if (optional.isEmpty()) + { + if (user != null) + { + Utils.sendMessage(user, + user.getTranslation(Constants.ERRORS + "not-a-gamemode-world", + Constants.PARAMETER_WORLD, world.getName())); + } + else + { + this.addon.logWarning("Given world is not a gamemode world."); + } + + return; + } + + this.addon.getChallengesManager().wipeDatabase(optional.get().getDescription().getName().toLowerCase()); + this.createChallenges(config, user, optional.get(), world); + } + + + /** + * This method creates generator tier object from config file. + * + * @param config YamlConfiguration that contains all generators. + * @param user User who calls reading. + * @param gameMode GameMode in which generator tiers must be imported + */ + private void createChallenges(YamlConfiguration config, @Nullable User user, GameModeAddon gameMode, World world) + { + final String prefix = gameMode.getDescription().getName().toLowerCase() + "_"; + + long challengeCount = 0; + long levelCount = 0; + + if (config.contains("challenges")) + { + ConfigurationSection reader = config.getConfigurationSection("challenges"); + + if (reader != null) + { + challengeCount = reader.getKeys(false).stream(). + mapToInt(challengeId -> this.createChallenge(challengeId, + prefix, + reader.getConfigurationSection(challengeId))). + sum(); + } + } + + if (config.contains("levels")) + { + ConfigurationSection reader = config.getConfigurationSection("levels"); + + if (reader != null) + { + levelCount = reader.getKeys(false).stream(). + mapToInt(levelId -> this.createLevel(levelId, + prefix, + world, + reader.getConfigurationSection(levelId))). + sum(); + } + } + + if (user != null) + { + Utils.sendMessage(user, + user.getTranslation(Constants.MESSAGES + "import-count", + "[levels]", String.valueOf(levelCount), + "[challenges]", String.valueOf(challengeCount))); + } + + this.addon.log("Imported " + challengeCount + " challenges and " + + levelCount + " levels into database."); + } + + + /** + * This method creates challenge from given config section. + * @param challengeId Challenge ID. + * @param prefix GameMode prefix. + * @param section Configuration Section that contains information. + * @return 1 if challenge is created, otherwise 0. + */ + private int createChallenge(String challengeId, + String prefix, + @Nullable ConfigurationSection section) + { + if (section == null) + { + return 0; + } + + try + { + Challenge challenge = new Challenge(); + challenge.setUniqueId(prefix + challengeId); + + challenge.setFriendlyName(section.getString("name", challengeId)); + challenge.setIcon(matchIcon(section.getString("icon"), new ItemStack(Material.PAPER))); + + // Read description + if (section.isList("description")) + { + challenge.setDescription(section.getStringList("description")); + } + else if (section.isString("description")) + { + String description = section.getString("description"); + + if (description != null) + { + // Define as list. + challenge.setDescription(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + + challenge.setDeployed(section.getBoolean("deployed", true)); + challenge.setOrder(section.getInt("order", 0)); + challenge.setChallengeType(matchChallengeType(section.getString("type"), + Challenge.ChallengeType.ISLAND_TYPE)); + + // Read environment + Set environments = new HashSet<>(); + challenge.setEnvironment(environments); + + if (section.isList("environments")) + { + section.getStringList("environments"). + forEach(text -> environments.add(matchEnvironment(text, + World.Environment.NORMAL))); + } + else if (section.isString("environments")) + { + environments.add(matchEnvironment(section.getString("environments"), + World.Environment.NORMAL)); + } + + challenge.setRemoveWhenCompleted(section.getBoolean("remove-completed", false)); + + // Read Requirements + this.populateRequirements(challenge, section.getConfigurationSection("requirements")); + // Read Rewards + this.populateRewards(challenge, section.getConfigurationSection("rewards")); + + // Check Repeating status + challenge.setRepeatable(section.getBoolean("repeatable", false)); + challenge.setMaxTimes(section.getInt("repeat-times", -1)); + + if (challenge.isRepeatable()) + { + // Read Repeat Rewards + this.populateRepeatRewards(challenge, + section.getConfigurationSection("repeat-rewards")); + } + + this.addon.getChallengesManager().saveChallenge(challenge); + this.addon.getChallengesManager().loadChallenge(challenge, true, null, true); + } + catch (Exception e) + { + return 0; + } + + return 1; + } + + + /** + * Populates requirements for the given challenge. + * + * @param challenge the challenge + * @param section the section + */ + private void populateRequirements(Challenge challenge, ConfigurationSection section) + { + switch (challenge.getChallengeType()) + { + case INVENTORY_TYPE -> { + InventoryRequirements requirements = new InventoryRequirements(); + challenge.setRequirements(requirements); + + requirements.setTakeItems(section.getBoolean("take-items", false)); + List requiredItems = new ArrayList<>(); + requirements.setRequiredItems(requiredItems); + + if (section.isList("items")) + { + section.getStringList("items"). + forEach(text -> { + ItemStack itemStack = ItemParser.parse(text); + + if (itemStack != null) + { + requiredItems.add(itemStack); + } + }); + } + } + case ISLAND_TYPE -> { + IslandRequirements requirements = new IslandRequirements(); + challenge.setRequirements(requirements); + + requirements.setRemoveBlocks(section.getBoolean("remove-blocks", false)); + requirements.setRequiredBlocks(this.createMaterialMap(section.getConfigurationSection("blocks"))); + + requirements.setRemoveEntities(section.getBoolean("remove-entities", false)); + requirements.setRequiredEntities(this.createEntityMap(section.getConfigurationSection("entities"))); + + requirements.setSearchRadius(section.getInt("search-distance", 10)); + } + case OTHER_TYPE -> { + OtherRequirements requirements = new OtherRequirements(); + challenge.setRequirements(requirements); + + requirements.setTakeMoney(section.getBoolean("take-money", false)); + requirements.setRequiredMoney(section.getDouble("money", 0)); + + requirements.setTakeExperience(section.getBoolean("take-experience", false)); + requirements.setRequiredExperience(section.getInt("experience", 0)); + + requirements.setRequiredIslandLevel(section.getInt("level", 0)); + } + case STATISTIC_TYPE -> { + StatisticRequirements requirements = new StatisticRequirements(); + challenge.setRequirements(requirements); + + requirements.setAmount(section.getInt("amount", 0)); + requirements.setReduceStatistic(section.getBoolean("reduce", false)); + + requirements.setStatistic(matchStatistic(section.getString("statistic"))); + requirements.setEntity(matchEntity(section.getString("entity"))); + requirements.setMaterial(matchMaterial(section.getString("material"))); + } + } + + // Read permissions + if (challenge.getRequirements() != null) + { + Set permissions = new HashSet<>(); + challenge.getRequirements().setRequiredPermissions(permissions); + + if (section.isList("permissions")) + { + permissions.addAll(section.getStringList("permissions")); + } + else if (section.isString("permissions")) + { + String description = section.getString("permissions"); + + if (description != null) + { + // Define as list. + permissions.addAll(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + } + } + + + /** + * This method populates material map from given section field. + * @param section Section that contains material. + * @return Map that links material and number. + */ + private Map createMaterialMap(ConfigurationSection section) + { + Map materialMaps = new HashMap<>(); + + if (section != null) + { + for (String materialKey : section.getKeys(false)) + { + Material material = matchMaterial(materialKey); + + if (material != null) + { + materialMaps.put(material, section.getInt(materialKey, 0)); + } + } + } + + return materialMaps; + } + + + /** + * This method populates entity map from given section field. + * @param section Section that contains material. + * @return Map that links entity and number. + */ + private Map createEntityMap(ConfigurationSection section) + { + Map entityMap = new HashMap<>(); + + if (section != null) + { + for (String EntityType : section.getKeys(false)) + { + EntityType entity = matchEntity(EntityType); + + if (entity != null) + { + entityMap.put(entity, section.getInt(EntityType, 0)); + } + } + } + + return entityMap; + } + + + /** + * This method populates rewards for a challenge. + * @param challenge Challenge + * @param section Section that contains rewards + */ + private void populateRewards(Challenge challenge, @Nullable ConfigurationSection section) + { + List rewardItems = new ArrayList<>(); + challenge.setRewardItems(rewardItems); + + if (section == null) + { + return; + } + + challenge.setRewardText(section.getString("text", "")); + + if (section.isList("items")) + { + section.getStringList("items"). + forEach(text -> { + ItemStack itemStack = ItemParser.parse(text); + + if (itemStack != null) + { + rewardItems.add(itemStack); + } + }); + } + + challenge.setRewardExperience(section.getInt("experience", 0)); + challenge.setRewardMoney(section.getDouble("money", 0)); + + if (section.isList("commands")) + { + challenge.setRewardCommands(section.getStringList("commands")); + } + else if (section.isString("commands")) + { + String description = section.getString("commands"); + + if (description != null) + { + // Define as list. + challenge.setRewardCommands(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + } + + + /** + * This method populates repeat rewards for a challenge. + * @param challenge Challenge + * @param section Section that contains rewards + */ + private void populateRepeatRewards(Challenge challenge, @Nullable ConfigurationSection section) + { + List rewardItems = new ArrayList<>(); + challenge.setRepeatItemReward(rewardItems); + + if (section == null) + { + return; + } + + challenge.setRepeatRewardText(section.getString("text", "")); + + if (section.isList("items")) + { + section.getStringList("items"). + forEach(text -> { + ItemStack itemStack = ItemParser.parse(text); + + if (itemStack != null) + { + rewardItems.add(itemStack); + } + }); + } + + challenge.setRepeatExperienceReward(section.getInt("experience", 0)); + challenge.setRepeatMoneyReward(section.getDouble("money", 0)); + + if (section.isList("commands")) + { + challenge.setRepeatRewardCommands(section.getStringList("commands")); + } + else if (section.isString("commands")) + { + String description = section.getString("commands"); + + if (description != null) + { + // Define as list. + challenge.setRepeatRewardCommands(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + } + + + /** + * This method populates rewards for a level. + * @param level level + * @param section Section that contains rewards + */ + private void populateRewards(ChallengeLevel level, @Nullable ConfigurationSection section) + { + List rewardItems = new ArrayList<>(); + level.setRewardItems(rewardItems); + + if (section == null) + { + return; + } + + level.setRewardText(section.getString("text", "")); + + if (section.isList("items")) + { + section.getStringList("items"). + forEach(text -> { + ItemStack itemStack = ItemParser.parse(text); + + if (itemStack != null) + { + rewardItems.add(itemStack); + } + }); + } + + level.setRewardExperience(section.getInt("experience", 0)); + level.setRewardMoney(section.getDouble("money", 0)); + + if (section.isList("commands")) + { + level.setRewardCommands(section.getStringList("commands")); + } + else if (section.isString("commands")) + { + String description = section.getString("commands"); + + if (description != null) + { + // Define as list. + level.setRewardCommands(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + } + + + /** + * This method creates Level + * @param levelId Level Id + * @param prefix Gamemode prefix + * @param world World where level operates. + * @param section Section that contains level info. + * @return 1 if level created, 0 otherwise. + */ + private int createLevel(String levelId, + String prefix, + World world, + @Nullable ConfigurationSection section) + { + if (section == null) + { + return 0; + } + + try + { + ChallengeLevel level = new ChallengeLevel(); + level.setUniqueId(prefix + levelId); + + level.setFriendlyName(section.getString("name", levelId)); + level.setIcon(matchIcon(section.getString("icon"), new ItemStack(Material.PAPER))); + level.setLockedIcon(matchIcon(section.getString("icon"))); + + level.setWorld(world.getName()); + + level.setOrder(section.getInt("order", 0)); + level.setWaiverAmount(section.getInt("waiver", 0)); + + level.setUnlockMessage(section.getString("description", "")); + + this.populateRewards(level, section.getConfigurationSection("rewards")); + + Set challenges = new HashSet<>(); + level.setChallenges(challenges); + + if (section.isList("challenges")) + { + section.getStringList("challenges").forEach(text -> { + Challenge challenge = this.addon.getChallengesManager().getChallenge(prefix + text); + + if (challenge != null) + { + challenges.add(challenge.getUniqueId()); + this.addon.getChallengesManager().addChallengeToLevel(challenge, level); + } + }); + } + + this.addon.getChallengesManager().saveLevel(level); + this.addon.getChallengesManager().loadLevel(level, true, null, true); + } + catch (Exception ignored) + { + return 0; + } + + return 1; + } + + + // --------------------------------------------------------------------- + // Section: JSON Importers + // --------------------------------------------------------------------- + + + /** + * Import database file from local storage. + * + * @param user the user + * @param world the world + * @param fileName the file name + */ + public void importDatabaseFile(User user, World world, String fileName) + { + World correctWorld = Util.getWorld(world); + + if (correctWorld == null) + { + this.addon.logError("Given world is not part of BentoBox"); + return; + } + + ChallengesManager manager = this.addon.getChallengesManager(); + + // If exist any generator that is bound to current world, then do not load generators. + if (manager.hasAnyChallengeData(world.getName())) + { + this.addon.getPlugin().getIWM().getAddon(world).ifPresent(gameModeAddon -> + manager.wipeDatabase(gameModeAddon.getDescription().getName().toLowerCase())); + } + + try + { + // This prefix will be used to all generators. That is a unique way how to separate generators for + // each game mode. + String uniqueIDPrefix = Utils.getGameMode(world).toLowerCase() + "_"; + DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadObject(fileName); + + if (downloadedChallenges == null) + { + return; + } + + // All new challenges should get correct ID. So we need to map it to loaded challenges. + downloadedChallenges.getChallengeList().forEach(challenge -> { + // Set correct challenge ID + challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); + // Set up correct level ID if it is necessary + if (!challenge.getLevel().isEmpty()) + { + challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); + } + // Load challenge in memory + manager.loadChallenge(challenge, false, user, user == null); + }); + + downloadedChallenges.getLevelList().forEach(challengeLevel -> { + // Set correct level ID + challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); + // Set correct world name + challengeLevel.setWorld(correctWorld.getName()); + // Reset names for all challenges. + challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> uniqueIDPrefix + challenge). + collect(Collectors.toSet())); + // Load level in memory + manager.loadLevel(challengeLevel, false, user, user == null); + }); + } + catch (Exception e) + { + this.addon.getPlugin().logStacktrace(e); + return; + } + + manager.saveChallenges(); + manager.saveLevels(); + } + + + /** + * This method loads downloaded challenges into memory. + * @param user User who calls downloaded challenge loading + * @param world Target world. + * @param downloadString String that need to be loaded via DefaultDataHolder. + */ + public void loadDownloadedChallenges(User user, World world, String downloadString) + { + World correctWorld = Util.getWorld(world); + + if (correctWorld == null) + { + this.addon.logError("Given world is not part of BentoBox"); + return; + } + + ChallengesManager manager = this.addon.getChallengesManager(); + + // If exist any challenge or level that is bound to current world, then do not load default challenges. + if (manager.hasAnyChallengeData(world.getName())) + { + if (user.isPlayer()) + { + Utils.sendMessage(user, user.getTranslation("challenges.errors.exist-challenges-or-levels")); + } + else + { + this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); + } + + return; + } + + try + { + // This prefix will be used to all challenges. That is a unique way how to separate challenged for + // each game mode. + String uniqueIDPrefix = Utils.getGameMode(world).toLowerCase() + "_"; + DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadWebObject(downloadString); + + // All new challenges should get correct ID. So we need to map it to loaded challenges. + downloadedChallenges.getChallengeList().forEach(challenge -> { + // Set correct challenge ID + challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); + // Set up correct level ID if it is necessary + if (!challenge.getLevel().isEmpty()) + { + challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); + } + // Load challenge in memory + manager.loadChallenge(challenge, false, user, user == null); + }); + + downloadedChallenges.getLevelList().forEach(challengeLevel -> { + // Set correct level ID + challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); + // Set correct world name + challengeLevel.setWorld(correctWorld.getName()); + // Reset names for all challenges. + challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> uniqueIDPrefix + challenge). + collect(Collectors.toSet())); + // Load level in memory + manager.loadLevel(challengeLevel, false, user, user == null); + }); + } + catch (Exception e) + { + this.addon.getPlugin().logStacktrace(e); + return; + } + + this.addon.getChallengesManager().saveChallenges(); + this.addon.getChallengesManager().saveLevels(); + } + + + // --------------------------------------------------------------------- + // Section: Default generation + // --------------------------------------------------------------------- + + + public void generateDatabaseFile(User user, World world, String fileName) + { + File defaultFile = new File(this.addon.getDataFolder(), + fileName.endsWith(".json") ? fileName : fileName + ".json"); + + if (defaultFile.exists()) + { + if (user.isPlayer()) + { + Utils.sendMessage(user, + user.getTranslation(Constants.ERRORS + "file-exist", + Constants.PARAMETER_FILE, fileName)); + } + else + { + this.addon.logWarning(Constants.ERRORS + "file-exist"); + } + + return; + } + + try + { + if (defaultFile.createNewFile()) + { + String replacementString = Utils.getGameMode(world).toLowerCase() + "_"; + ChallengesManager manager = this.addon.getChallengesManager(); + + List challengeList = manager.getAllChallenges(world). + stream(). + map(challenge -> { + // Use clone to avoid any changes in existing challenges. + Challenge clone = challenge.clone(); + // Remove world name from challenge id. + clone.setUniqueId(challenge.getUniqueId().replaceFirst(replacementString, "")); + // Remove world name from level id. + clone.setLevel(challenge.getLevel().replaceFirst(replacementString, "")); + + return clone; + }). + collect(Collectors.toList()); + + List levelList = manager.getLevels(world). + stream(). + map(challengeLevel -> { + // Use clone to avoid any changes in existing levels. + ChallengeLevel clone = challengeLevel.copy(); + // Remove world name from level ID. + clone.setUniqueId(challengeLevel.getUniqueId().replaceFirst(replacementString, "")); + // Remove world name. + clone.setWorld(""); + // Challenges must be reassign, as they also contains world name. + clone.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> challenge.replaceFirst(replacementString, "")). + collect(Collectors.toSet())); + + return clone; + }). + collect(Collectors.toList()); + + DefaultDataHolder defaultChallenges = new DefaultDataHolder(); + defaultChallenges.setChallengeList(challengeList); + defaultChallenges.setLevelList(levelList); + defaultChallenges.setVersion(this.addon.getDescription().getVersion()); + + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(defaultFile), StandardCharsets.UTF_8))) { + writer.write(Objects.requireNonNull( + new DefaultJSONHandler(this.addon).toJsonString(defaultChallenges))); + } + } + } + catch (IOException e) + { + if (user.isPlayer()) + { + Utils.sendMessage(user, + user.getTranslation(Constants.ERRORS + "no-load", + Constants.PARAMETER_FILE, fileName, + TextVariables.DESCRIPTION, e.getMessage())); + } + + this.addon.logError("Could not save json file: " + e.getMessage()); + } + finally + { + if (user.isPlayer()) + { + Utils.sendMessage(user, + user.getTranslation(Constants.CONVERSATIONS + "database-export-completed", + Constants.PARAMETER_WORLD, world.getName(), + Constants.PARAMETER_FILE, fileName)); + } + else + { + this.addon.logWarning("Database Export Completed"); + } + } + } + + + // --------------------------------------------------------------------- + // Section: Static Methods + // --------------------------------------------------------------------- + + + /** + * Match item stack. + * + * @param text the text + * @return the item stack + */ + @Nullable + private static ItemStack matchIcon(@Nullable String text) + { + if (text == null || text.isBlank()) + { + return new ItemStack(Material.PAPER); + } + else + { + return ItemParser.parse(text, new ItemStack(Material.PAPER)); + } + } + + + /** + * Match item stack. + * + * @param text the text + * @param defaultItem the default item + * @return the item stack + */ + @NonNull + private static ItemStack matchIcon(@Nullable String text, ItemStack defaultItem) + { + ItemStack item = matchIcon(text); + return item == null ? defaultItem : item; + } + + + /** + * Match material. + * + * @param text the text + * @return the material + */ + @Nullable + private static Material matchMaterial(@Nullable String text) + { + if (text == null || text.isBlank()) + { + return null; + } + else + { + return Material.getMaterial(text.toUpperCase()); + } + } + + + /** + * Match material. + * + * @param text the text + * @param defaultItem the default item + * @return the material + */ + @NonNull + private static Material matchMaterial(@Nullable String text, Material defaultItem) + { + Material item = matchMaterial(text); + return item == null ? defaultItem : item; + } + + + /** + * Match entity type. + * + * @param text the text + * @return the entity type + */ + @Nullable + private static EntityType matchEntity(@Nullable String text) + { + if (text == null || text.isBlank()) + { + return null; + } + else + { + try + { + return EntityType.valueOf(text.toUpperCase()); + } + catch (Exception e) + { + return null; + } + } + } + + + /** + * Match entity type. + * + * @param text the text + * @param defaultItem the default item + * @return the entity type + */ + @NonNull + private static EntityType matchEntity(@Nullable String text, EntityType defaultItem) + { + EntityType item = matchEntity(text); + return item == null ? defaultItem : item; + } + + + /** + * Match statistic value. + * + * @param text the text + * @return the statistic + */ + @Nullable + private static Statistic matchStatistic(@Nullable String text) + { + if (text == null || text.isBlank()) + { + return null; + } + else + { + try + { + return Statistic.valueOf(text.toUpperCase()); + } + catch (Exception e) + { + return null; + } + } + } + + + /** + * Match challenge type + * + * @param text the text + * @param defaultType default type + * @return the challenge type + */ + private static Challenge.ChallengeType matchChallengeType(@Nullable String text, Challenge.ChallengeType defaultType) + { + if (text == null || text.isBlank()) + { + return defaultType; + } + else + { + try + { + return Challenge.ChallengeType.valueOf(text.toUpperCase()); + } + catch (Exception e) + { + return defaultType; + } + } + } + + + /** + * Match world environment. + * + * @param text the text + * @param defaultType the default type + * @return the world environment + */ + private static World.Environment matchEnvironment(@Nullable String text, World.Environment defaultType) + { + if (text == null || text.isBlank()) + { + return defaultType; + } + else + { + try + { + return World.Environment.valueOf(text.toUpperCase()); + } + catch (Exception e) + { + return defaultType; + } + } + } + + + // --------------------------------------------------------------------- + // Section: Private classes for default challenges + // --------------------------------------------------------------------- + + + /** + * This Class allows to load default challenges and their levels as objects much easier. + */ + private static final class DefaultJSONHandler + { + /** + * This constructor inits JSON builder that will be used to parse challenges. + * @param addon Challenges Adddon + */ + DefaultJSONHandler(ChallengesAddon addon) + { + GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); + // Register adapters + builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(addon.getPlugin())); + // Keep null in the database + builder.serializeNulls(); + // Allow characters like < or > without escaping them + builder.disableHtmlEscaping(); + + this.addon = addon; + this.gson = builder.setPrettyPrinting().create(); + } + + + /** + * This method returns json object that is parsed to string. Json object is made from given instance. + * @param instance Instance that must be parsed to json string. + * @return String that contains JSON information from instance object. + */ + String toJsonString(DefaultDataHolder instance) + { + // Null check + if (instance == null) + { + this.addon.logError("JSON database request to store a null. "); + return null; + } + + return this.gson.toJson(instance); + } + + + /** + * This method creates and adds to list all objects from default.json file. + * @return List of all objects from default.json that is with T instance. + */ + DefaultDataHolder loadObject(String fileName) + { + if (!fileName.endsWith(".json")) + { + fileName = fileName + ".json"; + } + + File defaultFile = new File(this.addon.getDataFolder(), fileName); + + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(defaultFile), StandardCharsets.UTF_8)) + { + DefaultDataHolder object = this.gson.fromJson(reader, DefaultDataHolder.class); + + reader.close(); // NOSONAR Required to keep OS file handlers low and not rely on GC + + return object; + } + catch (FileNotFoundException e) + { + this.addon.logError("Could not load file '" + defaultFile.getName() + "': File not found."); + } + catch (Exception e) + { + this.addon.logError("Could not load objects " + defaultFile.getName() + " " + e.getMessage()); + } + + return null; + } + + + /** + * This method creates and adds to list all objects from default.json file. + * @return List of all objects from default.json that is with T instance. + */ + DefaultDataHolder loadWebObject(String downloadedObject) + { + return this.gson.fromJson(downloadedObject, DefaultDataHolder.class); + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * Holds JSON builder object. + */ + private final Gson gson; + + /** + * Holds ChallengesAddon object. + */ + private final ChallengesAddon addon; + } + + + /** + * This is simple object that will allow to store all current challenges and levels + * in single file. + */ + private static final class DefaultDataHolder implements DataObject + { + /** + * Default constructor. Creates object with empty lists. + */ + DefaultDataHolder() + { + this.challengeList = Collections.emptyList(); + this.challengeLevelList = Collections.emptyList(); + this.version = ""; + } + + + /** + * This method returns stored challenge list. + * @return list that contains default challenges. + */ + List getChallengeList() + { + return challengeList; + } + + + /** + * This method sets given list as default challenge list. + * @param challengeList new default challenge list. + */ + void setChallengeList(List challengeList) + { + this.challengeList = challengeList; + } + + + /** + * This method returns list of default challenge levels. + * @return List that contains default challenge levels. + */ + List getLevelList() + { + return challengeLevelList; + } + + + /** + * This method sets given list as default challenge level list. + * @param levelList new default challenge level list. + */ + void setLevelList(List levelList) + { + this.challengeLevelList = levelList; + } + + + /** + * This method returns the version value. + * @return the value of version. + */ + public String getVersion() + { + return version; + } + + + /** + * This method sets the version value. + * @param version the version new value. + * + */ + public void setVersion(String version) + { + this.version = version; + } + + + /** + * @return default.json + */ + @Override + public String getUniqueId() + { + return "default.json"; + } + + + /** + * @param uniqueId - unique ID the uniqueId to set + */ + @Override + public void setUniqueId(String uniqueId) + { + // method not used. + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * Holds a list with default challenges. + */ + @Expose + private List challengeList; + + /** + * Holds a list with default levels. + */ + @Expose + private List challengeLevelList; + + /** + * Holds a variable that stores in which addon version file was made. + */ + @Expose + private String version; + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + private final ChallengesAddon addon; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/ChallengesManager.java b/src/main/java/world/bentobox/challenges/managers/ChallengesManager.java similarity index 87% rename from src/main/java/world/bentobox/challenges/ChallengesManager.java rename to src/main/java/world/bentobox/challenges/managers/ChallengesManager.java index 6624dc5..6e19df9 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesManager.java +++ b/src/main/java/world/bentobox/challenges/managers/ChallengesManager.java @@ -1,23 +1,14 @@ -package world.bentobox.challenges; +package world.bentobox.challenges.managers; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Statistic; import org.bukkit.World; +import org.bukkit.entity.EntityType; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; @@ -27,13 +18,11 @@ import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.config.Settings; 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.database.object.requirements.InventoryRequirements; -import world.bentobox.challenges.database.object.requirements.IslandRequirements; -import world.bentobox.challenges.database.object.requirements.OtherRequirements; import world.bentobox.challenges.database.object.requirements.Requirements; import world.bentobox.challenges.events.ChallengeCompletedEvent; import world.bentobox.challenges.events.ChallengeResetAllEvent; @@ -56,47 +45,47 @@ public class ChallengesManager /** * This config object stores structures for challenge objects. */ - private Database challengeDatabase; + private final Database challengeDatabase; /** * This config object stores structures for challenge level objects. */ - private Database levelDatabase; + private final Database levelDatabase; /** - * This database allows to access player challenge data. + * This database allows accessing player challenge data. */ - private Database playersDatabase; + private final Database playersDatabase; /** * This is local cache that links challenge unique id with challenge object. */ - private Map challengeCacheData; + private final Map challengeCacheData; /** * This is local cache that links level unique id with level object. */ - private Map levelCacheData; + private final Map levelCacheData; /** * This is local cache that links UUID with corresponding player challenge data. */ - private Map playerCacheData; + private final Map playerCacheData; /** - * This variable allows to access ChallengesAddon. + * This variable allows accessing ChallengesAddon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; /** - * This variable allows to access ChallengesAddon settings. + * This variable allows accessing ChallengesAddon settings. */ - private Settings settings; + private final Settings settings; /** - * Island world manager allows to detect which world refferes to which gamemode addon. + * Island world manager allows detecting which world refers to which gamemode addon. */ - private IslandWorldManager islandWorldManager; + private final IslandWorldManager islandWorldManager; // --------------------------------------------------------------------- @@ -128,7 +117,7 @@ public class ChallengesManager { if (o1.getOrder() == o2.getOrder()) { - // If orders are equal, sort by unique Id + // If orders are equal, sort by unique id return o1.getUniqueId().compareToIgnoreCase(o2.getUniqueId()); } else @@ -146,9 +135,18 @@ public class ChallengesManager } else { - // Sort by challenges level order numbers - return Integer.compare(this.getLevel(o1.getLevel()).getOrder(), - this.getLevel(o2.getLevel()).getOrder()); + ChallengeLevel o1Level = this.getLevel(o1.getLevel()); + ChallengeLevel o2Level = this.getLevel(o2.getLevel()); + + if (o1Level == null || o2Level == null) + { + return Boolean.compare(o1Level == null, o2Level == null); + } + else + { + // Sort by challenges level order numbers + return Integer.compare(o1Level.getOrder(), o2Level.getOrder()); + } } } }; @@ -173,7 +171,7 @@ public class ChallengesManager // 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 + // playersDatabase is where all the player history will be stored this.playersDatabase = new Database<>(addon, ChallengesPlayerData.class); // Init all cache objects. @@ -233,9 +231,6 @@ public class ChallengesManager // store player data before cleaning. this.savePlayersData(); } - //this.challengeDatabase = new Database<>(addon, Challenge.class); - //this.levelDatabase = new Database<>(addon, ChallengeLevel.class); - //this.playersDatabase = new Database<>(addon, ChallengesPlayerData.class); this.loadAndValidate(); } @@ -245,7 +240,6 @@ public class ChallengesManager * Load challenge silently. Used when loading. * * @param challenge Challenge that must be loaded. - * @return true if successful */ private void loadChallenge(@NonNull Challenge challenge) { @@ -273,7 +267,7 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("load-error", "[value]", "NULL"); + Utils.sendMessage(user, user.getTranslation("load-error", "[value]", "NULL")); } return false; @@ -283,11 +277,14 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.errors.invalid-challenge", "[challenge]", challenge.getUniqueId()); + Utils.sendMessage(user, user.getTranslation("challenges.errors.invalid-challenge", + "[challenge]", challenge.getUniqueId())); } this.addon.logWarning("Data for challenge `" + challenge.getUniqueId() + "` is not valid. It could be NULL element in item-stack!"); - return false; + + // Load the challenge but set it as "undeployed" + challenge.setDeployed(false); } if (this.challengeCacheData.containsKey(challenge.getUniqueId())) @@ -296,8 +293,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-skipping", - VALUE, challenge.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-skipping", + VALUE, challenge.getFriendlyName())); } return false; @@ -306,8 +303,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-overwriting", - VALUE, challenge.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-overwriting", + VALUE, challenge.getFriendlyName())); } } } @@ -315,8 +312,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-add", - VALUE, challenge.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-add", + VALUE, challenge.getFriendlyName())); } } @@ -357,7 +354,7 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("load-error", "[value]", "NULL"); + Utils.sendMessage(user, user.getTranslation("load-error", "[value]", "NULL")); } return false; @@ -367,7 +364,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.errors.invalid-level", "[level]", level.getUniqueId()); + Utils.sendMessage(user, user.getTranslation("challenges.errors.invalid-level", + "[level]", level.getUniqueId())); } this.addon.logWarning("Data for level `" + level.getUniqueId() + "` is not valid. It could be NULL element in item-stack!"); @@ -378,13 +376,12 @@ public class ChallengesManager { if (user != null) { - user.sendMessage("challenges.errors.load-error", - VALUE, level.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.errors.load-error", + VALUE, level.getFriendlyName())); } else { - this.addon.logError( - "Challenge Level '" + level.getUniqueId() + "' is not valid and skipped"); + this.addon.logError("Challenge Level '" + level.getUniqueId() + "' is not valid and skipped"); } return false; @@ -396,8 +393,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-skipping", - VALUE, level.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-skipping", + VALUE, level.getFriendlyName())); } return false; @@ -406,8 +403,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-overwriting", - VALUE, level.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-overwriting", + VALUE, level.getFriendlyName())); } } } @@ -415,8 +412,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-add", - VALUE, level.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-add", + VALUE, level.getFriendlyName())); } } @@ -425,26 +422,6 @@ public class ChallengesManager } - /** - * This method stores PlayerData into local cache. - * - * @param playerData ChallengesPlayerData that must be loaded. - * - * TODO: Remove this unused method? - */ - private void loadPlayerData(@NonNull ChallengesPlayerData playerData) - { - try - { - this.playerCacheData.put(playerData.getUniqueId(), playerData); - } - catch (Exception e) - { - this.addon.getLogger().severe("UUID for player in challenge data file is invalid!"); - } - } - - /** * This method removes given player from cache data. * @@ -453,12 +430,6 @@ public class ChallengesManager public void removeFromCache(UUID playerID) { // Remove due possible issues with saving... (#246) -// if (!this.settings.isStoreAsIslandData() && this.playerCacheData.containsKey(playerID.toString())) -// { -// // save before remove -// this.savePlayerData(playerID.toString()); -// this.playerCacheData.remove(playerID.toString()); -// } this.savePlayerData(playerID.toString()); @@ -588,29 +559,45 @@ public class ChallengesManager /** * This method removes all challenges addon data from Database. * @param complete Remove also user data. + * @param name Name of the GameMode. */ - public void wipeDatabase(boolean complete) + public void wipeDatabase(boolean complete, String name) { - this.wipeLevels(); - this.wipeChallenges(); + this.wipeLevels(name); + this.wipeChallenges(name); if (complete) { - this.wipePlayers(); + this.wipePlayers(name); } } + /** + * This method removes all challenges addon data from Database. + * @param name Name of the GameMode. + */ + public void wipeDatabase(String name) + { + this.wipeDatabase(false, name); + } + + /** * This method collects all data from levels database and removes them. * Also clears levels cache data. */ - private void wipeLevels() + private void wipeLevels(String gamemode) { List levelList = this.levelDatabase.loadObjects(); - levelList.forEach(level -> this.levelDatabase.deleteID(level.getUniqueId())); - this.levelCacheData.clear(); + levelList.stream(). + filter(level -> level.getUniqueId().startsWith(gamemode.toLowerCase()) || + level.getUniqueId().startsWith(gamemode)). + forEach(level -> { + this.levelDatabase.deleteID(level.getUniqueId()); + this.levelCacheData.remove(level.getUniqueId()); + }); } @@ -618,12 +605,17 @@ public class ChallengesManager * This method collects all data from challenges database and removes them. * Also clears challenges cache data. */ - private void wipeChallenges() + private void wipeChallenges(String gamemode) { List challengeList = this.challengeDatabase.loadObjects(); - challengeList.forEach(challenge -> this.challengeDatabase.deleteID(challenge.getUniqueId())); - this.challengeCacheData.clear(); + challengeList.stream(). + filter(challenge -> challenge.getUniqueId().startsWith(gamemode.toLowerCase()) || + challenge.getUniqueId().startsWith(gamemode)). + forEach(challenge -> { + this.challengeDatabase.deleteID(challenge.getUniqueId()); + this.challengeCacheData.remove(challenge.getUniqueId()); + }); } @@ -631,12 +623,15 @@ public class ChallengesManager * This method collects all data from players database and removes them. * Also clears players cache data. */ - public void wipePlayers() + public void wipePlayers(String gamemode) { - List playerDataList = this.playersDatabase.loadObjects(); - - playerDataList.forEach(playerData -> this.playersDatabase.deleteID(playerData.getUniqueId())); this.playerCacheData.clear(); + + List playerDataList = this.playersDatabase.loadObjects(); + playerDataList.forEach(playerData -> { + playerData.reset(gamemode); + this.playersDatabase.saveObjectAsync(playerData); + }); } @@ -651,10 +646,14 @@ public class ChallengesManager public void migrateDatabase(User user, World world) { world = Util.getWorld(world); - + if (world == null) { + this.addon.logError("No such world!"); + return; + } + if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.migrate-start"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-start")); } else { @@ -670,7 +669,7 @@ public class ChallengesManager if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.migrate-end"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-end")); } else { @@ -681,7 +680,7 @@ public class ChallengesManager { if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.migrate-not"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-not")); } else { @@ -735,7 +734,6 @@ public class ChallengesManager /** * This method collects all data from challenges database and migrates them. */ - @SuppressWarnings("deprecation") private boolean migrateChallenges(World world) { String addonName = Utils.getGameMode(world); @@ -768,49 +766,6 @@ public class ChallengesManager this.challengeDatabase.saveObjectAsync(challenge); this.challengeCacheData.put(challenge.getUniqueId(), challenge); } - - // Migrate Requirements. - if (challenge.getRequirements() == null) - { - switch (challenge.getChallengeType()) - { - case INVENTORY: - InventoryRequirements inventoryRequirements = new InventoryRequirements(); - inventoryRequirements.setRequiredItems(challenge.getRequiredItems()); - inventoryRequirements.setTakeItems(challenge.isTakeItems()); - - inventoryRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(inventoryRequirements); - break; - case ISLAND: - IslandRequirements islandRequirements = new IslandRequirements(); - islandRequirements.setRemoveBlocks(challenge.isRemoveBlocks()); - islandRequirements.setRemoveEntities(challenge.isRemoveEntities()); - islandRequirements.setRequiredBlocks(challenge.getRequiredBlocks()); - islandRequirements.setRequiredEntities(challenge.getRequiredEntities()); - islandRequirements.setSearchRadius(challenge.getSearchRadius()); - - islandRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(islandRequirements); - break; - case OTHER: - OtherRequirements otherRequirements = new OtherRequirements(); - otherRequirements.setRequiredExperience(challenge.getRequiredExperience()); - otherRequirements.setRequiredIslandLevel(challenge.getRequiredIslandLevel()); - otherRequirements.setRequiredMoney(challenge.getRequiredMoney()); - otherRequirements.setTakeExperience(challenge.isTakeExperience()); - otherRequirements.setTakeMoney(challenge.isTakeMoney()); - - otherRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(otherRequirements); - break; - } - - // This save should not involve any upgrades in other parts. - - this.challengeDatabase.saveObjectAsync(challenge); - this.challengeCacheData.put(challenge.getUniqueId(), challenge); - } } return updated; @@ -876,8 +831,6 @@ public class ChallengesManager { // Challenges and Levels are saved on modifications only to avoid issues with // NULL's in data after interrupting server while in saving stage. - // this.saveChallenges(); - // this.saveLevels(); this.savePlayersData(); } @@ -1287,6 +1240,101 @@ public class ChallengesManager // --------------------------------------------------------------------- + /** + * Gets statistic data. + * + * @param user the user + * @param world the world + * @param statistic the statistic + * @return the statistic data + */ + public int getStatisticData(User user, World world, Statistic statistic) + { + if (this.settings.isStoreAsIslandData()) + { + Island island = this.addon.getIslands().getIsland(world, user); + + if (island == null) + { + return 0; + } + + return island.getMemberSet().stream().map(Bukkit::getPlayer). + filter(Objects::nonNull). + mapToInt(player -> player.getStatistic(statistic)). + sum(); + } + else + { + return user.getPlayer().getStatistic(statistic); + } + } + + + /** + * Gets statistic data. + * + * @param user the user + * @param world the world + * @param statistic the statistic + * @param material the material + * @return the statistic data + */ + public int getStatisticData(User user, World world, Statistic statistic, Material material) + { + if (this.settings.isStoreAsIslandData()) + { + Island island = this.addon.getIslands().getIsland(world, user); + + if (island == null) + { + return 0; + } + + return island.getMemberSet().stream().map(Bukkit::getPlayer). + filter(Objects::nonNull). + mapToInt(player -> player.getStatistic(statistic, material)). + sum(); + } + else + { + return user.getPlayer().getStatistic(statistic, material); + } + } + + + /** + * Gets statistic data. + * + * @param user the user + * @param world the world + * @param statistic the statistic + * @param entity the entity + * @return the statistic data + */ + public int getStatisticData(User user, World world, Statistic statistic, EntityType entity) + { + if (this.settings.isStoreAsIslandData()) + { + Island island = this.addon.getIslands().getIsland(world, user); + + if (island == null) + { + return 0; + } + + return island.getMemberSet().stream().map(Bukkit::getPlayer). + filter(Objects::nonNull). + mapToInt(player -> player.getStatistic(statistic, entity)). + sum(); + } + else + { + return user.getPlayer().getStatistic(statistic, entity); + } + } + + /** * This method returns if given user has completed given challenge in world. * @param user - User that must be checked. @@ -1327,6 +1375,43 @@ public class ChallengesManager } + /** + * This method returns if given user breached timeout for given challenge. + * @param user - User that must be checked. + * @param world - World where challenge operates. + * @param challenge - Challenge that must be checked. + * @return True, if challenge is breached timeout, otherwise - false. + */ + public boolean isBreachingTimeOut(User user, World world, Challenge challenge) + { + if (challenge.getTimeout() <= 0) + { + // Challenge does not have a timeout. + return false; + } + + return System.currentTimeMillis() < + this.getLastCompletionDate(user, world, challenge) + challenge.getTimeout(); + } + + + /** + * Gets last completion date for given challenge. + * + * @param user the user + * @param world the world + * @param challenge the challenge + * @return the last completion date + */ + public long getLastCompletionDate(User user, World world, Challenge challenge) + { + String userId = this.getDataUniqueID(user, Util.getWorld(world)); + this.addPlayerData(userId); + + return this.playerCacheData.get(userId).getLastCompletionTime(challenge.getUniqueId()); + } + + /** * This method sets given challenge as completed. * @param user - Targeted user. @@ -1607,6 +1692,21 @@ public class ChallengesManager } + /** + * Returns if the given level is last leve in given world. + * + * @param level the level + * @param world the world + * @return the boolean + */ + public boolean isLastLevel(ChallengeLevel level, World world) + { + List levels = this.getLevels(world); + + return levels.get(levels.size() - 1) == level; + } + + // --------------------------------------------------------------------- // Section: Challenges related methods // --------------------------------------------------------------------- @@ -1735,12 +1835,13 @@ public class ChallengesManager * @return Challenge that is currently created. */ @Nullable - public Challenge createChallenge(String uniqueID, Challenge.ChallengeType type, Requirements requirements) + public Challenge createChallenge(String uniqueID, String name, Challenge.ChallengeType type, Requirements requirements) { if (!this.containsChallenge(uniqueID)) { Challenge challenge = new Challenge(); challenge.setUniqueId(uniqueID); + challenge.setFriendlyName(name); challenge.setRequirements(requirements); challenge.setChallengeType(type); @@ -2030,15 +2131,18 @@ public class ChallengesManager /** * This method creates and returns new challenges level with given uniqueID. * @param uniqueID - new ID for challenge level. + * @param name Name - name of the level. + * @param world World where level is created. * @return ChallengeLevel that is currently created. */ @Nullable - public ChallengeLevel createLevel(String uniqueID, World world) + public ChallengeLevel createLevel(String uniqueID, String name, World world) { if (!this.containsLevel(uniqueID)) { ChallengeLevel level = new ChallengeLevel(); level.setUniqueId(uniqueID); + level.setFriendlyName(name); level.setWorld(world.getName()); this.saveLevel(level); diff --git a/src/main/java/world/bentobox/challenges/panel/CommonGUI.java b/src/main/java/world/bentobox/challenges/panel/CommonGUI.java deleted file mode 100644 index e3d69af..0000000 --- a/src/main/java/world/bentobox/challenges/panel/CommonGUI.java +++ /dev/null @@ -1,1106 +0,0 @@ -package world.bentobox.challenges.panel; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.conversations.Conversation; -import org.bukkit.conversations.ConversationContext; -import org.bukkit.conversations.ConversationFactory; -import org.bukkit.conversations.Prompt; -import org.bukkit.conversations.StringPrompt; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BookMeta; -import org.bukkit.inventory.meta.EnchantmentStorageMeta; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.KnowledgeBookMeta; -import org.bukkit.inventory.meta.LeatherArmorMeta; -import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.inventory.meta.SkullMeta; -import org.bukkit.inventory.meta.SpawnEggMeta; -import org.bukkit.inventory.meta.TropicalFishBucketMeta; -import org.bukkit.potion.PotionData; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; - -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.TextComponent; -import world.bentobox.bentobox.BentoBox; -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.bentobox.util.Util; -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.database.object.requirements.InventoryRequirements; -import world.bentobox.challenges.database.object.requirements.IslandRequirements; -import world.bentobox.challenges.database.object.requirements.OtherRequirements; -import world.bentobox.challenges.utils.LevelStatus; -import world.bentobox.challenges.utils.Utils; - - -/** - * 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. - */ - protected 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 DEFAULT = "defaults"; - - protected static final String GENERATE = "generate"; - - protected static final String SETTINGS = "settings"; - - protected static final String DELETE = "delete"; - - protected static final String WIPE = "wipe"; - - protected static final String EDIT = "edit"; - - protected static final String ADD = "add"; - - protected static final String RESET = "reset"; - - protected static final String COMPLETE = "complete"; - - protected static final String DOWNLOAD = "download"; - - // --------------------------------------------------------------------- - // 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.return")). - icon(Material.OAK_DOOR). - clickHandler((panel, user1, clickType, i) -> { - if (this.parentGUI == null) - { - this.user.closeInventory(); - return true; - } - - this.parentGUI.build(); - return true; - }).build(); - } - - - /** - * Default constructor that inits panels with minimal requirements. - * @param parentGUI Parent panel for current panel. - */ - public CommonGUI(CommonGUI parentGUI) - { - this.addon = parentGUI.addon; - this.world = parentGUI.world; - this.user = parentGUI.user; - - this.topLabel = parentGUI.topLabel; - this.permissionPrefix = parentGUI.permissionPrefix; - - this.parentGUI = parentGUI; - - this.pageIndex = 0; - - this.returnButton = new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.return")). - 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.OAK_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.OAK_SIGN); - clickHandler = (panel, user, clickType, slot) -> { - this.pageIndex--; - this.build(); - return true; - }; - - break; - } - case RETURN: - return this.returnButton; - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(description). - glow(false). - clickHandler(clickHandler). - build(); - } - - - /** - * This method sets new value to ValueObject variable. - * @param value new Value of valueObject. - */ - public void setValue(Object value) - { - this.valueObject = value; - } - - - // --------------------------------------------------------------------- - // Section: Generate Challenge Description - // --------------------------------------------------------------------- - - - /** - * This method generates and returns given challenge description. It is used here to avoid multiple - * duplicates, as it would be nice to have single place where challenge could be generated. - * @param challenge Challenge which description must be generated. - * @return List of strings that will be used in challenges description. - */ - protected List generateChallengeDescription(Challenge challenge, Player user) - { - List result = new ArrayList<>(); - - // Some values to avoid overchecking. - ChallengesManager manager = this.addon.getChallengesManager(); - - final boolean isCompletedOnce = - manager.isChallengeComplete(user.getUniqueId(), world, challenge); - final long doneTimes = challenge.isRepeatable() ? - manager.getChallengeTimes(this.user, this.world, challenge) : isCompletedOnce ? 0 : 1; - - boolean isCompletedAll = isCompletedOnce && challenge.isRepeatable() && - challenge.getMaxTimes() > 0 && - doneTimes >= challenge.getMaxTimes(); - - this.addon.getChallengesSettings().getChallengeLoreMessage().forEach(messagePart -> { - switch (messagePart) - { - case LEVEL: - { - ChallengeLevel level = manager.getLevel(challenge); - - if (level == null) - { - result.add(this.user.getTranslation("challenges.errors.missing-level", - "[level]", challenge.getLevel())); - } - else - { - result.add(this.user - .getTranslation("challenges.gui.challenge-description.level", - "[level]", level.getFriendlyName())); - } - break; - } - case STATUS: - { - if (isCompletedOnce) - { - result.add(this.user - .getTranslation("challenges.gui.challenge-description.completed")); - } - break; - } - case COUNT: - { - if (challenge.isRepeatable()) - { - if (challenge.getMaxTimes() > 0) - { - if (isCompletedAll) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.maxed-reached", - "[donetimes]", - String.valueOf(doneTimes), - "[maxtimes]", - String.valueOf(challenge.getMaxTimes()))); - } - else - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.completed-times-of", - "[donetimes]", - String.valueOf(doneTimes), - "[maxtimes]", - String.valueOf(challenge.getMaxTimes()))); - } - } - else - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.completed-times", - "[donetimes]", - String.valueOf(doneTimes))); - } - } - break; - } - case DESCRIPTION: - { - result.addAll(challenge.getDescription()); - break; - } - case WARNINGS: - { - if (!isCompletedAll) - { - if (challenge.getChallengeType().equals(Challenge.ChallengeType.INVENTORY)) - { - if (challenge.getRequirements().isTakeItems()) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.warning-items-take")); - } - } - else if (challenge.getChallengeType().equals(Challenge.ChallengeType.ISLAND)) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.objects-close-by")); - - IslandRequirements requirements = challenge.getRequirements(); - - if (requirements.isRemoveEntities() && !requirements.getRequiredEntities().isEmpty()) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.warning-entities-kill")); - } - - if (requirements.isRemoveBlocks() && !requirements.getRequiredBlocks().isEmpty()) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.warning-blocks-remove")); - } - } - } - break; - } - case ENVIRONMENT: - { - // Display only if there are limited environments - - if (!isCompletedAll && - !challenge.getEnvironment().isEmpty() && - challenge.getEnvironment().size() != 3) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.environment")); - - if (challenge.getEnvironment().contains(World.Environment.NORMAL)) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.normal")); - } - - if (challenge.getEnvironment().contains(World.Environment.NETHER)) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.nether")); - } - - if (challenge.getEnvironment().contains(World.Environment.THE_END)) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.the-end")); - } - } - break; - } - case REQUIREMENTS: - { - if (!isCompletedAll) - { - switch (challenge.getChallengeType()) - { - case INVENTORY: - result.addAll(this.getInventoryRequirements(challenge.getRequirements())); - break; - case ISLAND: - result.addAll(this.getIslandRequirements(challenge.getRequirements())); - break; - case OTHER: - result.addAll(this.getOtherRequirements(challenge.getRequirements())); - break; - } - } - - break; - } - case REWARD_TEXT: - { - if (isCompletedAll) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.not-repeatable")); - } - else - { - // Show a title to the rewards - result.add(this.user.getTranslation("challenges.gui.challenge-description.rewards-title")); - if (isCompletedOnce) - { - result.add(challenge.getRepeatRewardText()); - } - else - { - result.add(challenge.getRewardText()); - } - } - break; - } - case REWARD_OTHER: - { - if (!isCompletedAll) - { - result.addAll(this.getChallengeRewardOthers(challenge, isCompletedOnce)); - } - break; - } - case REWARD_ITEMS: - { - if (!isCompletedAll) - { - result.addAll(this.getChallengeRewardItems(challenge, isCompletedOnce)); - } - break; - } - case REWARD_COMMANDS: - { - if (!isCompletedAll) - { - result.addAll(this.getChallengeRewardCommands(challenge, isCompletedOnce, user)); - } - break; - } - } - }); - - 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. - * @param isCompletedOnce indicate if must use repeat rewards - * @return list of strings that contains rewards message. - */ - private List getChallengeRewardOthers(Challenge challenge, boolean isCompletedOnce) - { - double rewardMoney; - int rewardExperience; - - - if (!isCompletedOnce) - { - rewardMoney = challenge.getRewardMoney(); - rewardExperience = challenge.getRewardExperience(); - } - else - { - rewardMoney = challenge.getRepeatMoneyReward(); - rewardExperience = challenge.getRepeatExperienceReward(); - } - - List result = new ArrayList<>(); - - // Add message about reward XP - if (rewardExperience > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.experience-reward", - "[value]", Integer.toString(rewardExperience))); - } - - // Add message about reward money - if (this.addon.getPlugin().getSettings().isUseEconomy() && rewardMoney > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.money-reward", - "[value]", Double.toString(rewardMoney))); - } - - return result; - } - - - /** - * This method returns list of strings that contains reward items from given challenge. - * @param challenge Challenge which reward items and commands must be returned. - * @param isCompletedOnce Boolean that indicate if must use repeat rewards. - * @return List of strings that contains message from challenges. - */ - private List getChallengeRewardItems(Challenge challenge, boolean isCompletedOnce) - { - List result = new ArrayList<>(); - - List rewardItems; - - if (isCompletedOnce) - { - rewardItems = challenge.getRepeatItemReward(); - } - else - { - rewardItems = challenge.getRewardItems(); - } - - // Add message about reward items - if (!rewardItems.isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.reward-items")); - - Utils.groupEqualItems(rewardItems).forEach(itemStack -> - result.addAll(this.generateItemStackDescription(itemStack))); - } - - return result; - } - - - /** - * This method returns list of strings that contains reward commands from given challenge. - * @param challenge Challenge which reward items and commands must be returned. - * @param isCompletedOnce Boolean that indicate if must use repeat rewards. - * @param user Target user for command string. - * @return List of strings that contains message from challenges. - */ - private List getChallengeRewardCommands(Challenge challenge, boolean isCompletedOnce, Player user) - { - List result = new ArrayList<>(); - - List rewardCommands; - - if (isCompletedOnce) - { - rewardCommands = challenge.getRepeatRewardCommands(); - } - else - { - rewardCommands = challenge.getRewardCommands(); - } - - // Add message about reward commands - if (!rewardCommands.isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.reward-commands")); - - for (String command : rewardCommands) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command.replace("[player]", user.getName()).replace("[SELF]", ""))); - } - } - - return result; - } - - - /** - * This method returns list of strings that contains basic information about requirements. - * @param requirements which requirements message must be created. - * @return list of strings that contains requirements message. - */ - private List getOtherRequirements(OtherRequirements requirements) - { - List result = new ArrayList<>(); - - // Add message about required exp - if (requirements.getRequiredExperience() > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-experience", - "[value]", Integer.toString(requirements.getRequiredExperience()))); - } - - // Add message about required money - if (this.addon.isEconomyProvided() && requirements.getRequiredMoney() > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-money", - "[value]", Double.toString(requirements.getRequiredMoney()))); - } - - // Add message about required island level - if (this.addon.isLevelProvided() && requirements.getRequiredIslandLevel() > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-island-level", - "[value]", Long.toString(requirements.getRequiredIslandLevel()))); - } - - return result; - } - - - /** - * This method returns list of strings that contains basic information about requirements. - * @param requirements which requirements message must be created. - * @return list of strings that contains requirements message. - */ - private List getInventoryRequirements(InventoryRequirements requirements) - { - List result = new ArrayList<>(); - - // Add message about required items - if (!requirements.getRequiredItems().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-items")); - - Utils.groupEqualItems(requirements.getRequiredItems()).forEach(itemStack -> - result.addAll(this.generateItemStackDescription(itemStack))); - } - - return result; - } - - - /** - * This method returns list of strings that contains required items, entities and blocks from given challenge. - * @param challenge Challenge which requirement items, entities and blocks must be returned. - * @return List of strings that contains message from challenges. - */ - private List getIslandRequirements(IslandRequirements challenge) - { - List result = new ArrayList<>(); - - if (!challenge.getRequiredBlocks().isEmpty() || !challenge.getRequiredEntities().isEmpty()) - { - // Add required blocks - if (!challenge.getRequiredBlocks().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-blocks")); - - for (Map.Entry entry : challenge.getRequiredBlocks().entrySet()) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.block", - "[block]", Util.prettifyText(entry.getKey().name()), - "[count]", Integer.toString(entry.getValue()))); - } - } - - // Add required entities - if (!challenge.getRequiredEntities().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-entities")); - - for (Map.Entry entry : challenge.getRequiredEntities().entrySet()) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.entity", - "[entity]", Util.prettifyText(entry.getKey().name()), - "[count]", Integer.toString(entry.getValue()))); - } - } - } - - return result; - } - - - // --------------------------------------------------------------------- - // Section: Generate Level Description - // --------------------------------------------------------------------- - - - /** - * This method generates level description string. - * @param level Level which string must be generated. - * @param user User who calls generation. - * @return List with generated description. - */ - protected List generateLevelDescription(ChallengeLevel level, Player user) - { - List result = new ArrayList<>(); - - ChallengesManager manager = this.addon.getChallengesManager(); - LevelStatus status = manager.getChallengeLevelStatus(user.getUniqueId(), this.world, level); - - // Check if unlock message should appear. - boolean hasCompletedOne = status.isComplete() || status.isUnlocked() && - level.getChallenges().stream().anyMatch(challenge -> - this.addon.getChallengesManager().isChallengeComplete(user.getUniqueId(), world, challenge)); - - this.addon.getChallengesSettings().getLevelLoreMessage().forEach(messagePart -> { - switch (messagePart) - { - case LEVEL_STATUS: - { - if (status.isComplete()) - { - result.add(this.user.getTranslation("challenges.gui.level-description.completed")); - } - break; - } - case CHALLENGE_COUNT: - { - if (!status.isComplete() && status.isUnlocked()) - { - int doneChallengeCount = (int) level.getChallenges().stream(). - filter(challenge -> this.addon.getChallengesManager().isChallengeComplete(user.getUniqueId(), world, challenge)). - count(); - - result.add(this.user.getTranslation("challenges.gui.level-description.completed-challenges-of", - "[number]", Integer.toString(doneChallengeCount), - "[max]", Integer.toString(level.getChallenges().size()))); - } - - break; - } - case UNLOCK_MESSAGE: - { - if (!hasCompletedOne) - { - result.add(level.getUnlockMessage()); - } - - break; - } - case WAIVER_AMOUNT: - { - if (status.isUnlocked() && !status.isComplete()) - { - result.add(this.user.getTranslation("challenges.gui.level-description.waver-amount", - "[value]", Integer.toString(level.getWaiverAmount()))); - } - - break; - } - case LEVEL_REWARD_TEXT: - { - if (status.isUnlocked() && !status.isComplete()) - { - result.add(level.getRewardText()); - } - break; - } - case LEVEL_REWARD_OTHER: - { - if (status.isUnlocked() && !status.isComplete()) - { - if (level.getRewardExperience() > 0) - { - result.add(this.user.getTranslation("challenges.gui.level-description.experience-reward", - "[value]", Integer.toString(level.getRewardExperience()))); - } - - if (this.addon.isEconomyProvided() && level.getRewardMoney() > 0) - { - result.add(this.user.getTranslation("challenges.gui.level-description.money-reward", - "[value]", Integer.toString(level.getRewardMoney()))); - } - } - break; - } - case LEVEL_REWARD_ITEMS: - { - if (status.isUnlocked() && !status.isComplete() && !level.getRewardItems().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.level-description.reward-items")); - - Utils.groupEqualItems(level.getRewardItems()).forEach(itemStack -> - result.addAll(this.generateItemStackDescription(itemStack))); - } - break; - } - case LEVEL_REWARD_COMMANDS: - { - if (status.isUnlocked() && !status.isComplete() && !level.getRewardCommands().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.level-description.reward-commands")); - - for (String command : level.getRewardCommands()) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command.replace("[player]", user.getName()).replace("[SELF]", ""))); - } - } - break; - } - } - }); - - result.replaceAll(x -> x.replace("[label]", this.topLabel)); - - return result; - } - - - // --------------------------------------------------------------------- - // Section: ItemStack Description - // --------------------------------------------------------------------- - - - /** - * This method generates description for given item stack object. - * @param itemStack Object which lore must be generated - * @return List with generated description - */ - @SuppressWarnings("deprecation") - protected List generateItemStackDescription(ItemStack itemStack) - { - List result = new ArrayList<>(); - - result.add(this.user.getTranslation("challenges.gui.item-description.item", - "[item]", Util.prettifyText(itemStack.getType().name()), - "[count]", Integer.toString(itemStack.getAmount()))); - - if (itemStack.hasItemMeta()) - { - ItemMeta meta = itemStack.getItemMeta(); - - if (meta.hasDisplayName()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.item-name", - "[name]", meta.getDisplayName())); - } - - if (meta.hasLore()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.item-lore")); - result.addAll(meta.getLore()); - } - - if (meta instanceof BookMeta) - { - result.add(this.user.getTranslation("challenges.gui.item-description.book-meta", - "[author]", ((BookMeta) meta).getAuthor(), - "[title]", ((BookMeta) meta).getTitle())); - } - else if (meta instanceof EnchantmentStorageMeta) - { - ((EnchantmentStorageMeta) meta).getStoredEnchants().forEach(((enchantment, level) -> { - result.add(this.user.getTranslation("challenges.gui.item-description.item-enchant", - "[enchant]", enchantment.getKey().getKey(), "[level]", Integer.toString(level))); - })); - } - else if (meta instanceof KnowledgeBookMeta) - { - result.add(this.user.getTranslation("challenges.gui.item-description.recipe-count", - "[count]", Integer.toString(((KnowledgeBookMeta) meta).getRecipes().size()))); - } - else if (meta instanceof LeatherArmorMeta) - { - result.add(this.user.getTranslation("challenges.gui.item-description.armor-color", - "[color]", ((LeatherArmorMeta) meta).getColor().toString())); - } - else if (meta instanceof PotionMeta) - { - PotionData data = ((PotionMeta) meta).getBasePotionData(); - - if (data.isExtended() && data.isUpgraded()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.potion-type-extended-upgraded", - "[name]", Util.prettifyText(data.getType().name()))); - } - else if (data.isUpgraded()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.potion-type-upgraded", - "[name]", Util.prettifyText(data.getType().name()))); - } - else if (data.isExtended()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.potion-type-extended", - "[name]", Util.prettifyText(data.getType().name()))); - } - else - { - result.add(this.user.getTranslation("challenges.gui.item-description.potion-type", - "[name]", Util.prettifyText(data.getType().name()))); - } - - if (((PotionMeta) meta).hasCustomEffects()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.custom-effects")); - - ((PotionMeta) meta).getCustomEffects().forEach(potionEffect -> - result.add(this.user.getTranslation("challenges.gui.item-description.potion-effect", - "[effect]", Util.prettifyText(potionEffect.getType().getName()), - "[duration]", Integer.toString(potionEffect.getDuration()), - "[amplifier]", Integer.toString(potionEffect.getAmplifier())))); - } - } - else if (meta instanceof SkullMeta) - { - if (((SkullMeta) meta).getOwningPlayer() != null) - { - result.add(this.user.getTranslation("challenges.gui.item-description.skull-owner", - "[owner]", ((SkullMeta) meta).getOwningPlayer().getName())); - } - } - else if (meta instanceof SpawnEggMeta) - { - result.add(this.user.getTranslation("challenges.gui.item-description.egg-meta", - "[mob]", Util.prettifyText(((SpawnEggMeta) meta).getSpawnedType().name()))); - } - else if (meta instanceof TropicalFishBucketMeta) - { - if (((TropicalFishBucketMeta) meta).hasVariant()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.fish-meta", - "[pattern]", Util.prettifyText(((TropicalFishBucketMeta) meta).getPattern().name()), - "[pattern-color]", Util.prettifyText(((TropicalFishBucketMeta) meta).getPatternColor().name()), - "[body-color]", Util.prettifyText(((TropicalFishBucketMeta) meta).getBodyColor().name()))); - } - } - - if (meta.hasEnchants()) - { - itemStack.getEnchantments().forEach((enchantment, level) -> { - result.add(this.user.getTranslation("challenges.gui.item-description.item-enchant", - "[enchant]", - enchantment.getKey().getKey(), - "[level]", - Integer.toString(level))); - }); - } - } - - return result; - } - - - // --------------------------------------------------------------------- - // Section: Chat Input Methods - // --------------------------------------------------------------------- - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on inputText in - * chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - * @param message Message that will be set in player text field when clicked on question. - */ - protected void getFriendlyName(Consumer consumer, @NonNull String question, @Nullable String message) - { - final User user = this.user; - - Conversation conversation = - new ConversationFactory(BentoBox.getInstance()).withFirstPrompt( - new StringPrompt() - { - /** - * @see Prompt#getPromptText(ConversationContext) - */ - @Override - public String getPromptText(ConversationContext conversationContext) - { - // Close input GUI. - user.closeInventory(); - - if (message != null) - { - // Create Edit Text message. - TextComponent component = new TextComponent(user.getTranslation("challenges.gui.descriptions.admin.click-to-edit")); - component.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, message)); - // Send question and message to player. - user.getPlayer().spigot().sendMessage(component); - } - - // There are no editable message. Just return question. - return question; - } - - - /** - * @see Prompt#acceptInput(ConversationContext, String) - */ - @Override - public Prompt acceptInput(ConversationContext conversationContext, String answer) - { - // Add answer to consumer. - consumer.accept(answer); - // End conversation - return Prompt.END_OF_CONVERSATION; - } - }). - withLocalEcho(false). - // On cancel conversation will be closed. - withEscapeSequence("cancel"). - // Use null value in consumer to detect if user has abandoned conversation. - addConversationAbandonedListener(abandonedEvent -> - { - if (!abandonedEvent.gracefulExit()) - { - consumer.accept(null); - } - }). - withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). - buildConversation(user.getPlayer()); - - conversation.begin(); - } -} - diff --git a/src/main/java/world/bentobox/challenges/panel/CommonPagedPanel.java b/src/main/java/world/bentobox/challenges/panel/CommonPagedPanel.java new file mode 100644 index 0000000..947c10e --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/CommonPagedPanel.java @@ -0,0 +1,267 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +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.ChallengesAddon; +import world.bentobox.challenges.utils.Constants; + + +/** + * This panel implements common things for Paged pages. + */ +public abstract class CommonPagedPanel extends CommonPanel +{ + /** + * Instantiates a new Common paged panel. + * + * @param addon the addon + * @param user the user + * @param world the world + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + protected CommonPagedPanel(ChallengesAddon addon, + User user, + World world, String topLabel, String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + } + + + /** + * Instantiates a new Common paged panel. + * + * @param parentPanel the parent panel + */ + protected CommonPagedPanel(@NonNull CommonPanel parentPanel) + { + super(parentPanel); + } + + + /** + * This method is called when filter value is updated. + */ + protected abstract void updateFilters(); + + + /** + * Create element button panel item. + * + * @param object the object + * @return the panel item + */ + protected abstract PanelItem createElementButton(T object); + + + /** + * Populate elements. + * + * @param panelBuilder the panel builder + * @param objectList the object list + */ + protected void populateElements(PanelBuilder panelBuilder, List objectList) + { + final int MAX_ELEMENTS = 21; + final int size = objectList.size(); + + if (this.pageIndex < 0) + { + this.pageIndex = size / MAX_ELEMENTS; + } + else if (this.pageIndex > (size / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int objectIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (objectIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + objectIndex < size && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createElementButton(objectList.get(objectIndex++))); + } + + index++; + } + + if (size > MAX_ELEMENTS && !(1.0 * size / MAX_ELEMENTS <= this.pageIndex + 1)) + { + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + + } + + if (this.pageIndex > 0) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + } + + // Add search button only if there is more than MAX_ELEMENTS objects or searchString + // is not blank. + if (!this.searchString.isBlank() || objectList.size() > MAX_ELEMENTS) + { + panelBuilder.item(40, this.getButton(CommonButtons.SEARCH)); + } + } + + + /** + * 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) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + if (button == CommonButtons.NEXT) + { + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_NUMBER, String.valueOf(this.pageIndex + 2))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-next")); + + icon = new ItemStack(Material.OAK_SIGN, this.pageIndex + 2); + clickHandler = (panel, user, clickType, slot) -> + { + this.pageIndex++; + this.build(); + return true; + }; + } + else if (button == CommonButtons.PREVIOUS) + { + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_NUMBER, String.valueOf(this.pageIndex))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-previous")); + + icon = new ItemStack(Material.OAK_SIGN, Math.max(1, this.pageIndex)); + clickHandler = (panel, user, clickType, slot) -> + { + this.pageIndex--; + this.build(); + return true; + }; + } + else if (button == CommonButtons.SEARCH) + { + description.add(this.user.getTranslation(reference + "description")); + + if (this.searchString != null && !this.searchString.isEmpty()) + { + description.add(this.user.getTranslation(reference + "search", + Constants.PARAMETER_VALUE, this.searchString)); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-edit")); + + if (this.searchString != null && !this.searchString.isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-clear")); + } + + icon = new ItemStack(Material.ANVIL); + + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + // Clear string. + this.searchString = ""; + this.updateFilters(); + // Rebuild gui. + this.build(); + } + else + { + // Create consumer that process description change + Consumer consumer = value -> + { + if (value != null) + { + this.searchString = value; + this.updateFilters(); + } + + this.build(); + }; + + // start conversation + ConversationUtils.createStringInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-search"), + user.getTranslation(Constants.CONVERSATIONS + "search-updated")); + } + + return true; + }; + } + else + { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * Next and Previous Buttons. + */ + private enum CommonButtons + { + NEXT, + PREVIOUS, + SEARCH + } + + + /** + * Current page index. + */ + private int pageIndex; + + /** + * Text that contains filter string. + */ + protected String searchString = ""; +} diff --git a/src/main/java/world/bentobox/challenges/panel/CommonPanel.java b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java new file mode 100644 index 0000000..83f2ecd --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java @@ -0,0 +1,1103 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import java.time.*; +import java.util.*; +import java.util.stream.Collectors; + +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.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.database.object.requirements.*; +import world.bentobox.challenges.managers.ChallengesManager; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.LevelStatus; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains common methods for all panels. + */ +public abstract class CommonPanel +{ + /** + * This is default constructor for all classes that extends CommonPanel. + * + * @param addon ChallengesAddon instance. + * @param user User who opens panel. + */ + protected CommonPanel(ChallengesAddon addon, User user, World world, String topLabel, String permissionPrefix) + { + this.addon = addon; + this.world = world; + this.manager = addon.getChallengesManager(); + this.user = user; + + this.topLabel = topLabel; + this.permissionPrefix = permissionPrefix; + + this.parentPanel = null; + + this.returnButton = new PanelItemBuilder(). + name(this.user.getTranslation(Constants.BUTTON + "quit.name")). + description(this.user.getTranslationOrNothing(Constants.BUTTON + "quit.description")). + description(""). + description(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-quit")). + icon(Material.OAK_DOOR). + clickHandler((panel, user1, clickType, i) -> { + this.user.closeInventory(); + return true; + }).build(); + } + + + /** + * This is default constructor for all classes that extends CommonPanel. + * + * @param parentPanel Parent panel of current panel. + */ + protected CommonPanel(@NonNull CommonPanel parentPanel) + { + this.addon = parentPanel.addon; + this.manager = parentPanel.manager; + this.user = parentPanel.user; + this.world = parentPanel.world; + + this.topLabel = parentPanel.topLabel; + this.permissionPrefix = parentPanel.permissionPrefix; + + this.parentPanel = parentPanel; + + this.returnButton = new PanelItemBuilder(). + name(this.user.getTranslation(Constants.BUTTON + "return.name")). + description(this.user.getTranslationOrNothing(Constants.BUTTON + "return.description")). + description(""). + description(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-return")). + icon(Material.OAK_DOOR). + clickHandler((panel, user1, clickType, i) -> { + this.parentPanel.build(); + return true; + }).build(); + } + + + /** + * This method allows building panel. + */ + protected abstract void build(); + + + /** + * This method reopens given panel. + * @param panel Panel that must be reopened. + */ + public static void reopen(CommonPanel panel) + { + panel.build(); + } + + +// --------------------------------------------------------------------- +// Section: Common methods +// --------------------------------------------------------------------- + + + /** + * This method generates and returns given challenge description. It is used here to avoid multiple + * duplicates, as it would be nice to have single place where challenge could be generated. + * @param challenge Challenge which description must be generated. + * @param target target player. + * @return List of strings that will be used in challenges description. + */ + protected List generateChallengeDescription(Challenge challenge, @Nullable User target) + { + // Some values to avoid over checking. + final boolean isCompletedOnce = target != null && + this.manager.isChallengeComplete(target.getUniqueId(), this.world, challenge); + + final long doneTimes = target != null && challenge.isRepeatable() ? + this.manager.getChallengeTimes(target, this.world, challenge) : (isCompletedOnce ? 0 : 1); + + boolean isCompletedAll = isCompletedOnce && + (!challenge.isRepeatable() || + challenge.getMaxTimes() > 0 && doneTimes >= challenge.getMaxTimes()); + + final String reference = Constants.DESCRIPTIONS + "challenge."; + + // Get description from custom translations + String description = this.user.getTranslationOrNothing( + "challenges.challenges." + challenge.getUniqueId() + ".description"); + + if (description.isEmpty()) + { + // Get data from object in single string. + description = Util.translateColorCodes(String.join("\n", challenge.getDescription())); + } + + // Non-memory optimal code used for easier debugging and nicer code layout for my eye :) + // Get status in single string + String status = this.generateChallengeStatus(isCompletedOnce, + isCompletedAll, + doneTimes, + challenge.getMaxTimes()); + // Get requirements in single string + String requirements = isCompletedAll ? "" : this.generateRequirements(challenge, target); + // Get rewards in single string + String rewards = isCompletedAll ? "" : this.generateRewards(challenge, isCompletedOnce); + // Get coolDown in singe string + String coolDown = isCompletedAll || challenge.getTimeout() <= 0 ? "" : + this.generateCoolDown(challenge, target); + + if (!description.replaceAll("(?m)^[ \\t]*\\r?\\n", "").isEmpty()) + { + String returnString = this.user.getTranslationOrNothing(reference + "lore", + "[requirements]", requirements, + "[rewards]", rewards, + "[status]", status, + "[cooldown]", coolDown); + + // remove empty lines from the generated text. + List collect = + Arrays.stream(returnString.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). + split("\n")). + collect(Collectors.toList()); + + // find and replace description from collected blocks. + + for (int i = 0; i < collect.size(); i++) + { + if (collect.get(i).contains(Constants.PARAMETER_DESCRIPTION)) + { + collect.set(i, collect.get(i).replace(Constants.PARAMETER_DESCRIPTION, description)); + } + } + + return collect; + } + else + { + String returnString = this.user.getTranslationOrNothing(reference + "lore", + Constants.PARAMETER_DESCRIPTION, description, + "[requirements]", requirements, + "[rewards]", rewards, + "[status]", status, + "[cooldown]", coolDown); + + // Remove empty lines and returns as a list. + + return Arrays.stream(returnString.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). + split("\n")). + collect(Collectors.toList()); + } + } + + + /** + * Generate cool down string. + * + * @param challenge the challenge + * @param target the target + * @return the string + */ + private String generateCoolDown(Challenge challenge, @Nullable User target) + { + final String reference = Constants.DESCRIPTIONS + "challenge.cooldown."; + + String coolDown; + + if (target != null && this.manager.isBreachingTimeOut(target, this.world, challenge)) + { + long missing = this.manager.getLastCompletionDate(this.user, this.world, challenge) + + challenge.getTimeout() - System.currentTimeMillis(); + + coolDown = this.user.getTranslation(reference + "wait-time", + "[time]", + Utils.parseDuration(Duration.ofMillis(missing), this.user)); + } + else + { + coolDown = ""; + } + + String timeout = this.user.getTranslation(reference + "timeout", + "[time]", + Utils.parseDuration(Duration.ofMillis(challenge.getTimeout()), this.user)); + + return this.user.getTranslation(reference + "lore", + "[timeout]", timeout, + "[wait-time]", coolDown); + } + + + /** + * This method generate requirements description for given challenge. + * @param challenge Challenge which requirements must be generated. + * @return Lore message with requirements. + */ + private String generateRequirements(Challenge challenge, @Nullable User target) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements."; + + String environment; + + if (challenge.getEnvironment().isEmpty() || challenge.getEnvironment().size() == 3) + { + // If challenge can be completed everywhere, do not display requirement. + environment = ""; + } + else if (challenge.getEnvironment().size() == 1) + { + environment = this.user.getTranslationOrNothing(reference + "environment-single", + Constants.PARAMETER_ENVIRONMENT, + Utils.prettifyObject(challenge.getEnvironment().iterator().next(), this.user)); + } + else + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "environment-title")); + challenge.getEnvironment().stream().sorted().forEach(en -> + { + builder.append("\n"); + builder.append(this.user.getTranslationOrNothing(reference + "environment-single", + Constants.PARAMETER_ENVIRONMENT, + Utils.prettifyObject(en, this.user))); + }); + + environment = builder.toString(); + } + + String permissions; + + if (!challenge.getRequirements().getRequiredPermissions().isEmpty()) + { + // Yes list duplication for complete menu. + List missingPermissions = challenge.getRequirements().getRequiredPermissions().stream(). + filter(permission -> target == null || !target.hasPermission(permission)). + sorted().toList(); + + StringBuilder permissionBuilder = new StringBuilder(); + + if (missingPermissions.size() == 1) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "permission-single", + Constants.PARAMETER_PERMISSION, missingPermissions.get(0))); + } + else if (!missingPermissions.isEmpty()) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "permissions-title")); + missingPermissions.forEach(permission -> + { + permissionBuilder.append("\n"); + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "permissions-list", + Constants.PARAMETER_PERMISSION, permission)); + }); + } + + permissions = permissionBuilder.toString(); + } + else + { + permissions = ""; + } + + String typeRequirement = switch (challenge.getChallengeType()) { + case INVENTORY_TYPE -> this.generateInventoryChallenge(challenge.getRequirements()); + case ISLAND_TYPE -> this.generateIslandChallenge(challenge.getRequirements()); + case OTHER_TYPE -> this.generateOtherChallenge(challenge.getRequirements()); + case STATISTIC_TYPE -> this.generateStatisticChallenge(challenge.getRequirements()); + }; + + return this.user.getTranslationOrNothing(reference + "lore", + Constants.PARAMETER_ENVIRONMENT, environment, + "[type-requirement]", typeRequirement, + "[permissions]", permissions); + } + + + /** + * This method generates lore message for island requirement. + * @param requirement Island Requirement. + * @return Requirement lore message. + */ + private String generateIslandChallenge(IslandRequirements requirement) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements.island."; + + String blocks; + + if (!requirement.getRequiredBlocks().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "blocks-title")); + requirement.getRequiredBlocks().entrySet().stream(). + sorted(Map.Entry.comparingByKey()). + forEach(entry -> + { + builder.append("\n"); + + if (entry.getValue() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "blocks-value", + Constants.PARAMETER_NUMBER, String.valueOf(entry.getValue()), + Constants.PARAMETER_MATERIAL, Utils.prettifyObject(entry.getKey(), this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "block-value", + Constants.PARAMETER_MATERIAL, Utils.prettifyObject(entry.getKey(), this.user))); + } + }); + + blocks = builder.toString(); + } + else + { + blocks = ""; + } + + String entities; + + if (!requirement.getRequiredEntities().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "entities-title")); + requirement.getRequiredEntities().entrySet().stream(). + sorted(Map.Entry.comparingByKey()). + forEach(entry -> + { + builder.append("\n"); + + if (entry.getValue() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "entities-value", + Constants.PARAMETER_NUMBER, String.valueOf(entry.getValue()), + Constants.PARAMETER_ENTITY, Utils.prettifyObject(entry.getKey(), this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "entity-value", + Constants.PARAMETER_ENTITY, Utils.prettifyObject(entry.getKey(), this.user))); + } + }); + + entities = builder.toString(); + } + else + { + entities = ""; + } + + String searchRadius = this.user.getTranslationOrNothing(reference + "search-radius", + Constants.PARAMETER_NUMBER, String.valueOf(requirement.getSearchRadius())); + + String warningBlocks = requirement.isRemoveBlocks() ? + this.user.getTranslationOrNothing(reference + "warning-block") : ""; + String warningEntities = requirement.isRemoveEntities() ? + this.user.getTranslationOrNothing(reference + "warning-entity") : ""; + + return this.user.getTranslationOrNothing(reference + "lore", + "[blocks]", blocks, + "[entities]", entities, + "[warning-block]", warningBlocks, + "[warning-entity]", warningEntities, + "[search-radius]", searchRadius); + } + + + /** + * This method generates lore message for inventory requirement. + * @param requirement Inventory Requirement. + * @return Requirement lore message. + */ + private String generateInventoryChallenge(InventoryRequirements requirement) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements.inventory."; + + String items; + + if (!requirement.getRequiredItems().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "item-title")); + Utils.groupEqualItems(requirement.getRequiredItems(), requirement.getIgnoreMetaData()).stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + { + builder.append("\n"); + + if (itemStack.getAmount() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "items-value", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "item-value", + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + }); + + items = builder.toString(); + } + else + { + items = ""; + } + + String warning = requirement.isTakeItems() ? + this.user.getTranslationOrNothing(reference + "warning") : ""; + + return this.user.getTranslationOrNothing(reference + "lore", + "[items]", items, + "[warning]", warning); + } + + + /** + * This method generates lore message for other requirement. + * @param requirement Other Requirement. + * @return Requirement lore message. + */ + private String generateOtherChallenge(OtherRequirements requirement) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements.other."; + + String experience = requirement.getRequiredExperience() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "experience", + "[number]", String.valueOf(requirement.getRequiredExperience())); + + String experienceWarning = requirement.getRequiredExperience() > 0 && requirement.isTakeExperience() ? + this.user.getTranslationOrNothing(reference + "experience-warning") : ""; + + String money = !this.addon.isEconomyProvided() || requirement.getRequiredMoney() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "money", + "[number]", String.valueOf(requirement.getRequiredMoney())); + + String moneyWarning = this.addon.isEconomyProvided() && + requirement.getRequiredMoney() > 0 && + requirement.isTakeMoney() ? + this.user.getTranslationOrNothing(reference + "money-warning") : ""; + + String level = !this.addon.isLevelProvided() || requirement.getRequiredIslandLevel() <= 0 ? "" : + this.user.getTranslationOrNothing(reference, + "[number]", String.valueOf(requirement.getRequiredIslandLevel())); + + return this.user.getTranslationOrNothing(reference + "lore", + "[experience]", experience, + "[experience-warning]", experienceWarning, + "[money]", money, + "[money-warning]", moneyWarning, + "[level]", level); + } + + + /** + * This method generates lore message for Statistic requirement. + * @param requirement Statistic Requirement. + * @return Requirement lore message. + */ + private String generateStatisticChallenge(StatisticRequirements requirement) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements.statistic."; + + String statistic; + + if (requirement.getStatistic() == null) + { + // Challenges by default comes with empty statistic field. + return ""; + } + + switch (requirement.getStatistic().getType()) + { + case UNTYPED -> statistic = this.user.getTranslationOrNothing(reference + "statistic", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[number]", String.valueOf(requirement.getAmount())); + case ITEM, BLOCK -> { + if (requirement.getAmount() > 1) + { + statistic = this.user.getTranslationOrNothing(reference + "multiple-target", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[number]", String.valueOf(requirement.getAmount()), + "[target]", Utils.prettifyObject(requirement.getMaterial(), this.user)); + } + else + { + statistic = this.user.getTranslationOrNothing(reference + "single-target", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[target]", Utils.prettifyObject(requirement.getMaterial(), this.user)); + } + } + case ENTITY -> { + if (requirement.getAmount() > 1) + { + statistic = this.user.getTranslationOrNothing(reference + "multiple-target", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[number]", String.valueOf(requirement.getAmount()), + "[target]", Utils.prettifyObject(requirement.getEntity(), this.user)); + } + else + { + statistic = this.user.getTranslationOrNothing(reference + "single-target", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[target]", Utils.prettifyObject(requirement.getEntity(), this.user)); + } + } + default -> statistic = ""; + } + + String warning = requirement.isReduceStatistic() ? + this.user.getTranslationOrNothing(reference + "warning") : ""; + + return this.user.getTranslationOrNothing(reference + "lore", + "[statistic]", statistic, + "[warning]", warning); + } + + + /** + * This message generates challenge status description. + * @param completedOnce Indicate that challenge is completed at least one time. + * @param completedAll Indicate that challenge is not repeatable anymore. + * @param completionCount Number of completion count. + * @param maxCompletions Number of max completion count. + * @return String with a text that will be generated for status. + */ + private String generateChallengeStatus(boolean completedOnce, + boolean completedAll, + long completionCount, + int maxCompletions) + { + final String reference = Constants.DESCRIPTIONS + "challenge.status."; + + if (completedAll) + { + if (maxCompletions > 1) + { + return this.user.getTranslationOrNothing(reference + "completed-times-reached", + Constants.PARAMETER_MAX, String.valueOf(maxCompletions)); + } + else + { + return this.user.getTranslationOrNothing(reference + "completed"); + } + } + else if (completedOnce) + { + if (maxCompletions > 0) + { + return this.user.getTranslationOrNothing(reference + "completed-times-of", + Constants.PARAMETER_MAX, String.valueOf(maxCompletions), + Constants.PARAMETER_NUMBER, String.valueOf(completionCount)); + } + else + { + return this.user.getTranslationOrNothing(reference + "completed-times", + Constants.PARAMETER_NUMBER, String.valueOf(completionCount)); + } + } + else + { + return ""; + } + } + + + /** + * This method creates reward lore text. + * @param challenge Challenge which reward lore must be generated. + * @param isRepeating Boolean that indicate if it is repeating reward or first time. + * @return Reward text. + */ + private String generateRewards(Challenge challenge, boolean isRepeating) + { + if (isRepeating) + { + return this.generateRepeatReward(challenge); + } + else + { + return this.generateReward(challenge); + } + } + + + /** + * This method creates repeat reward lore text. + * @param challenge Challenge which reward lore must be generated. + * @return Reward text. + */ + private String generateRepeatReward(Challenge challenge) + { + final String reference = Constants.DESCRIPTIONS + "challenge.rewards."; + + String items; + + if (!challenge.getRepeatItemReward().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "item-title")); + Utils.groupEqualItems(challenge.getRepeatItemReward(), challenge.getIgnoreRewardMetaData()).stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + { + builder.append("\n"); + + if (itemStack.getAmount() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "items-value", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "item-value", + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + }); + + items = builder.toString(); + } + else + { + items = ""; + } + + String experience = challenge.getRepeatExperienceReward() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "experience", + "[number]", String.valueOf(challenge.getRepeatExperienceReward())); + + String money = !this.addon.isEconomyProvided() || challenge.getRepeatMoneyReward() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "money", + "[number]", String.valueOf(challenge.getRepeatMoneyReward())); + + String commands; + + if (!challenge.getRepeatRewardCommands().isEmpty()) + { + StringBuilder permissionBuilder = new StringBuilder(); + + if (!challenge.getRepeatRewardCommands().isEmpty()) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "commands-title")); + + challenge.getRepeatRewardCommands().forEach(command -> + { + permissionBuilder.append("\n"); + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "command", + "[command]", command)); + }); + } + + commands = permissionBuilder.toString(); + } + else + { + commands = ""; + } + + if (challenge.getRepeatRewardText().isEmpty() && + items.isEmpty() && + experience.isEmpty() && + money.isEmpty() && + commands.isEmpty()) + { + // If everything is empty, do not return anything. + return ""; + } + + String rewardText = this.user.getTranslationOrNothing( + "challenges.challenges." + challenge.getUniqueId() + ".repeat-reward-text"); + + if (rewardText.isEmpty()) + { + rewardText = Util.translateColorCodes(String.join("\n", challenge.getRepeatRewardText())); + } + + return this.user.getTranslationOrNothing(reference + "lore", + "[text]", rewardText, + "[items]", items, + "[experience]", experience, + "[money]", money, + "[commands]", commands); + } + + + /** + * This method creates reward lore text. + * @param challenge Challenge which reward lore must be generated. + * @return Reward text. + */ + private String generateReward(Challenge challenge) + { + final String reference = Constants.DESCRIPTIONS + "challenge.rewards."; + + String items; + + if (!challenge.getRewardItems().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "item-title")); + Utils.groupEqualItems(challenge.getRewardItems(), challenge.getIgnoreRewardMetaData()).stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + { + builder.append("\n"); + + if (itemStack.getAmount() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "items-value", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "item-value", + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + }); + + items = builder.toString(); + } + else + { + items = ""; + } + + String experience = challenge.getRewardExperience() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "experience", + "[number]", String.valueOf(challenge.getRewardExperience())); + + String money = !this.addon.isEconomyProvided() || challenge.getRewardMoney() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "money", + "[number]", String.valueOf(challenge.getRewardMoney())); + + String commands; + + if (!challenge.getRewardCommands().isEmpty()) + { + StringBuilder permissionBuilder = new StringBuilder(); + + if (!challenge.getRewardCommands().isEmpty()) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "commands-title")); + + challenge.getRewardCommands().forEach(command -> + { + permissionBuilder.append("\n"); + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "command", + "[command]", command)); + }); + } + + commands = permissionBuilder.toString(); + } + else + { + commands = ""; + } + + if (challenge.getRewardText().isEmpty() && + items.isEmpty() && + experience.isEmpty() && + money.isEmpty() && + commands.isEmpty()) + { + // If everything is empty, do not return anything. + return ""; + } + + String rewardText = this.user.getTranslationOrNothing( + "challenges.challenges." + challenge.getUniqueId() + ".reward-text"); + + if (rewardText.isEmpty()) + { + rewardText = Util.translateColorCodes(String.join("\n", challenge.getRewardText())); + } + + return this.user.getTranslationOrNothing(reference + "lore", + "[text]", rewardText, + "[items]", items, + "[experience]", experience, + "[money]", money, + "[commands]", commands); + } + + + /** + * This method generates level description string. + * @param level Level which string must be generated. + * @return List with generated description. + */ + protected List generateLevelDescription(ChallengeLevel level) + { + final String reference = Constants.DESCRIPTIONS + "level."; + + // Non-memory optimal code used for easier debugging and nicer code layout for my eye :) + // Get status in single string + String status = ""; + // Get requirements in single string + String waiver = this.manager.isLastLevel(level, this.world) ? "" : + this.user.getTranslationOrNothing(reference + "waiver", + "[number]", String.valueOf(level.getWaiverAmount())); + // Get rewards in single string + String rewards = this.generateReward(level); + + String returnString = this.user.getTranslation(reference + "lore", + "[text]", Util.translateColorCodes(level.getUnlockMessage()), + "[waiver]", waiver, + "[rewards]", rewards, + "[status]", status); + + // Remove empty lines and returns as a list. + + return Arrays.stream(returnString.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). + split("\n")). + collect(Collectors.toList()); + } + + + /** + * This method generates level description string. + * @param levelStatus Level which string must be generated. + * @param user User who calls generation. + * @return List with generated description. + */ + protected List generateLevelDescription(LevelStatus levelStatus, User user) + { + ChallengeLevel level = levelStatus.getLevel(); + + final String reference = Constants.DESCRIPTIONS + "level."; + + // Non-memory optimal code used for easier debugging and nicer code layout for my eye :) + // Get status in single string + String status = this.generateLevelStatus(levelStatus); + // Get requirements in single string + String waiver = this.manager.isLastLevel(level, this.world) || + !levelStatus.isUnlocked() || + levelStatus.isComplete() ? + "" : this.user.getTranslationOrNothing(reference + "waiver", + "[number]", String.valueOf(level.getWaiverAmount())); + // Get rewards in single string + String rewards = !levelStatus.isUnlocked() ? "" : this.generateReward(level); + + String description = this.user.getTranslationOrNothing( + "challenges.levels." + level.getUniqueId() + ".description"); + + if (description.isEmpty()) + { + description = Util.translateColorCodes(String.join("\n", level.getUnlockMessage())); + } + + String returnString = this.user.getTranslation(reference + "lore", + "[text]", description, + "[waiver]", waiver, + "[rewards]", rewards, + "[status]", status); + + // Remove empty lines and returns as a list. + + return Arrays.stream(returnString.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). + split("\n")). + collect(Collectors.toList()); + } + + + /** + * This method generates level status description. + * @param levelStatus Level status which description must be generated. + * @return Level status text. + */ + private String generateLevelStatus(LevelStatus levelStatus) + { + final String reference = Constants.DESCRIPTIONS + "level.status."; + + if (!levelStatus.isUnlocked()) + { + return this.user.getTranslationOrNothing(reference + "locked") + "\n" + + this.user.getTranslationOrNothing(reference + "missing-challenges", + "[number]", String.valueOf(levelStatus.getNumberOfChallengesStillToDo())); + } + else if (levelStatus.isComplete()) + { + return this.user.getTranslationOrNothing(reference + "completed"); + } + else + { + ChallengeLevel level = levelStatus.getLevel(); + // Check if unlock message should appear. + int doneChallenges = (int) level.getChallenges().stream(). + filter(challenge -> this.addon.getChallengesManager().isChallengeComplete(user.getUniqueId(), world, challenge)). + count(); + + return this.user.getTranslation(reference + "completed-challenges-of", + "[number]", String.valueOf(doneChallenges), + "[max]", String.valueOf(level.getChallenges().size())); + } + } + + + /** + * This method creates reward lore text. + * @param level ChallengeLevel which reward lore must be generated. + * @return Reward text. + */ + private String generateReward(ChallengeLevel level) + { + final String reference = Constants.DESCRIPTIONS + "level.rewards."; + + String items; + + if (!level.getRewardItems().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "item-title")); + Utils.groupEqualItems(level.getRewardItems(), level.getIgnoreRewardMetaData()).stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + { + builder.append("\n"); + + if (itemStack.getAmount() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "items-value", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "item-value", + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + }); + + items = builder.toString(); + } + else + { + items = ""; + } + + String experience = level.getRewardExperience() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "experience", + "[number]", String.valueOf(level.getRewardExperience())); + + String money = !this.addon.isEconomyProvided() || level.getRewardMoney() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "money", + "[number]", String.valueOf(level.getRewardMoney())); + + String commands; + + if (!level.getRewardCommands().isEmpty()) + { + StringBuilder permissionBuilder = new StringBuilder(); + + if (!level.getRewardCommands().isEmpty()) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "commands-title")); + + level.getRewardCommands().forEach(command -> + { + permissionBuilder.append("\n"); + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "command", + "[command]", command)); + }); + } + + commands = permissionBuilder.toString(); + } + else + { + commands = ""; + } + + if (level.getRewardText().isEmpty() && + items.isEmpty() && + experience.isEmpty() && + money.isEmpty() && + commands.isEmpty()) + { + // If everything is empty, do not return anything. + return ""; + } + + String rewardText = this.user.getTranslationOrNothing( + "challenges.levels." + level.getUniqueId() + ".reward-text"); + + if (rewardText.isEmpty()) + { + rewardText = Util.translateColorCodes(String.join("\n", level.getRewardText())); + } + + return this.user.getTranslationOrNothing(reference + "lore", + "[text]", rewardText, + "[items]", items, + "[experience]", experience, + "[money]", money, + "[commands]", commands); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * This variable stores parent gui. + */ + @Nullable + protected final CommonPanel parentPanel; + + /** + * Variable stores Challenges addon. + */ + protected final ChallengesAddon addon; + + /** + * Variable stores Challenges addon manager. + */ + protected final ChallengesManager manager; + + /** + * Variable stores world in which panel is referred to. + */ + protected final World world; + + /** + * Variable stores user who created this panel. + */ + protected final User user; + + /** + * Variable stores top label of command from which panel was called. + */ + protected final String topLabel; + + /** + * Variable stores permission prefix of command from which panel was called. + */ + protected final String permissionPrefix; + + /** + * This object holds PanelItem that allows to return to previous panel. + */ + protected PanelItem returnButton; +} diff --git a/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java b/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java new file mode 100644 index 0000000..d343906 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java @@ -0,0 +1,564 @@ +// +// Created by BONNe +// Copyright - 2020 +// + + +package world.bentobox.challenges.panel; + + +import org.apache.commons.lang.ArrayUtils; +import org.bukkit.ChatColor; +import org.bukkit.conversations.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +public class ConversationUtils +{ + // --------------------------------------------------------------------- + // Section: Conversation API implementation + // --------------------------------------------------------------------- + + + /** + * This method will close opened gui and writes question in chat. After players answers on question in chat, message + * will trigger consumer and gui will reopen. Success and fail messages can be implemented like that, as user's chat + * options are disabled while it is in conversation. + * + * @param consumer Consumer that accepts player output text. + * @param question Message that will be displayed in chat when player triggers conversion. + * @param successMessage Message that will be displayed on successful operation. + * @param user User who is targeted with current confirmation. + */ + public static void createConfirmation(Consumer consumer, + User user, + @NotNull String question, + @Nullable String successMessage) + { + ValidatingPrompt confirmationPrompt = new ValidatingPrompt() + { + /** + * Is input valid boolean. + * + * @param context the context + * @param input the input + * @return the boolean + */ + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) + { + // Get valid strings from translations + String validEntry = user.getTranslation(Constants.CONVERSATIONS + "confirm-string") + + "," + user.getTranslation(Constants.CONVERSATIONS + "deny-string") + + "," + user.getTranslation(Constants.CONVERSATIONS + "exit-string") + + "," + user.getTranslation(Constants.CONVERSATIONS + "cancel-string"); + + // Split and check if they exist in valid entries. + String[] accepted = validEntry.toLowerCase().replaceAll("\\s", "").split(","); + return ArrayUtils.contains(accepted, input.toLowerCase()); + } + + + /** + * Accept validated input prompt. + * + * @param context the context + * @param input the input + * @return the prompt + */ + @Override + protected Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) + { + String validEntry = user.getTranslation(Constants.CONVERSATIONS + "confirm-string").toLowerCase(); + + if (ArrayUtils.contains(validEntry.replaceAll("\\s", "").split(","), input.toLowerCase())) + { + // Add answer to consumer. + consumer.accept(true); + // Return message about success. + return ConversationUtils.endMessagePrompt(successMessage); + } + else + { + // Add answer to consumer. + consumer.accept(false); + + // Return message about failed operation. + return ConversationUtils.endMessagePrompt( + user.getTranslation(Constants.CONVERSATIONS + "cancelled")); + } + } + + + /** + * @see Prompt#getPromptText(ConversationContext) + */ + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext conversationContext) + { + // Close input GUI. + user.closeInventory(); + // There are no editable message. Just return question. + return question; + } + }; + + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(confirmationPrompt). + withLocalEcho(false). + withTimeout(90). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This method will close opened gui and writes question in chat. After players answers on question in chat, message + * will trigger consumer and gui will reopen. Be aware, consumer does not return (and validate) sanitized value, + * while sanitization is done in failure for better informing. Proper implementation would be with adding new + * consumer for failure message. + * + * @param consumer Consumer that accepts player output text. + * @param validation Function that validates if input value is acceptable. + * @param question Message that will be displayed in chat when player triggers conversion. + * @param failTranslationLocation Message that will be displayed on failed operation. + * @param user User who is targeted with current confirmation. + */ + public static void createIDStringInput(Consumer consumer, + Function validation, + User user, + @NotNull String question, + @Nullable String successMessage, + @Nullable String failTranslationLocation) + { + ValidatingPrompt validatingPrompt = new ValidatingPrompt() + { + /** + * Gets the text to display to the user when + * this prompt is first presented. + * + * @param context Context information about the + * conversation. + * @return The text to display. + */ + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + // Close input GUI. + user.closeInventory(); + + // There are no editable message. Just return question. + return question; + } + + + /** + * Override this method to check the validity of + * the player's input. + * + * @param context Context information about the + * conversation. + * @param input The player's raw console input. + * @return True or false depending on the + * validity of the input. + */ + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) + { + return validation.apply(input); + } + + + /** + * Optionally override this method to + * display an additional message if the + * user enters an invalid input. + * + * @param context Context information + * about the conversation. + * @param invalidInput The invalid input + * provided by the user. + * @return A message explaining how to + * correct the input. + */ + @Override + protected String getFailedValidationText(@NotNull ConversationContext context, + @NotNull String invalidInput) + { + return user.getTranslation(failTranslationLocation, + Constants.PARAMETER_ID, + Utils.sanitizeInput(invalidInput)); + } + + + /** + * Override this method to accept and processes + * the validated input from the user. Using the + * input, the next Prompt in the prompt graph + * should be returned. + * + * @param context Context information about the + * conversation. + * @param input The validated input text from + * the user. + * @return The next Prompt in the prompt graph. + */ + @Override + protected Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) + { + // Add answer to consumer. + consumer.accept(input); + // Send message that it is accepted. + return ConversationUtils.endMessagePrompt(successMessage); + } + }; + + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(validatingPrompt). + withLocalEcho(false). + withTimeout(90). + // On cancel conversation will be closed. + withEscapeSequence(user.getTranslation(Constants.CONVERSATIONS + "cancel-string")). + // Use null value in consumer to detect if user has abandoned conversation. + addConversationAbandonedListener(ConversationUtils.getAbandonListener(consumer, user)). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This method will close opened gui and writes inputText in chat. After players answers on inputText in chat, + * message will trigger consumer and gui will reopen. + * + * @param consumer Consumer that accepts player output text. + * @param question Message that will be displayed in chat when player triggers conversion. + */ + public static void createNumericInput(Consumer consumer, + @NotNull User user, + @NotNull String question, + Number minValue, + Number maxValue) + { + // Create NumericPromt instance that will validate and process input. + NumericPrompt numberPrompt = new NumericPrompt() + { + /** + * Override this method to perform some action + * with the user's integer response. + * + * @param context Context information about the + * conversation. + * @param input The user's response as a {@link + * Number}. + * @return The next {@link Prompt} in the prompt + * graph. + */ + @Override + protected Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull Number input) + { + // Add answer to consumer. + consumer.accept(input); + // End conversation + return Prompt.END_OF_CONVERSATION; + } + + + /** + * Override this method to do further validation on the numeric player + * input after the input has been determined to actually be a number. + * + * @param context Context information about the conversation. + * @param input The number the player provided. + * @return The validity of the player's input. + */ + @Override + protected boolean isNumberValid(@NotNull ConversationContext context, Number input) + { + return input.doubleValue() >= minValue.doubleValue() && + input.doubleValue() <= maxValue.doubleValue(); + } + + + /** + * Optionally override this method to display an additional message if the + * user enters an invalid number. + * + * @param context Context information about the conversation. + * @param invalidInput The invalid input provided by the user. + * @return A message explaining how to correct the input. + */ + @Override + protected String getInputNotNumericText(@NotNull ConversationContext context, @NotNull String invalidInput) + { + return user.getTranslation(Constants.CONVERSATIONS + "numeric-only", Constants.PARAMETER_VALUE, invalidInput); + } + + + /** + * Optionally override this method to display an additional message if the + * user enters an invalid numeric input. + * + * @param context Context information about the conversation. + * @param invalidInput The invalid input provided by the user. + * @return A message explaining how to correct the input. + */ + @Override + protected String getFailedValidationText(@NotNull ConversationContext context, Number invalidInput) + { + return user.getTranslation(Constants.CONVERSATIONS + "not-valid-value", + Constants.PARAMETER_VALUE, invalidInput.toString(), + Constants.PARAMETER_MIN, Double.toString(minValue.doubleValue()), + Constants.PARAMETER_MAX, Double.toString(maxValue.doubleValue())); + } + + + /** + * @see Prompt#getPromptText(ConversationContext) + */ + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext conversationContext) + { + // Close input GUI. + user.closeInventory(); + // There are no editable message. Just return question. + return question; + } + }; + + // Init conversation api. + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(numberPrompt). + withLocalEcho(false). + withTimeout(90). + withEscapeSequence(user.getTranslation(Constants.CONVERSATIONS + "cancel-string")). + // Use null value in consumer to detect if user has abandoned conversation. + addConversationAbandonedListener(ConversationUtils.getAbandonListener(consumer, user)). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This method will close opened gui and writes question in chat. After players answers on question in chat, message + * will trigger consumer and gui will reopen. Be aware, consumer does not return (and validate) sanitized value, + * while sanitization is done in failure for better informing. Proper implementation would be with adding new + * consumer for failure message. + * + * @param consumer Consumer that accepts player output text. + * @param question Message that will be displayed in chat when player triggers conversion. + * @param user User who is targeted with current confirmation. + */ + public static void createStringListInput(Consumer> consumer, + User user, + @NotNull String question, + @NotNull String successMessage) + { + final String SESSION_CONSTANT = Constants.CONVERSATIONS + user.getUniqueId(); + + // Successful message about completing. + MessagePrompt messagePrompt = new MessagePrompt() + { + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + if (context.getSessionData(SESSION_CONSTANT) instanceof List description) + { + consumer.accept((List) description); + return successMessage; + } + else + { + return user.getTranslation(Constants.CONVERSATIONS + "cancelled"); + } + } + + + @Override + protected @Nullable Prompt getNextPrompt(@NotNull ConversationContext context) + { + return Prompt.END_OF_CONVERSATION; + } + }; + + // Text input message. + StringPrompt stringPrompt = new StringPrompt() + { + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + user.closeInventory(); + + if (context.getSessionData(SESSION_CONSTANT) != null) + { + StringBuilder sb = new StringBuilder(); + sb.append(user.getTranslation(Constants.CONVERSATIONS + "written-text")); + sb.append(System.getProperty("line.separator")); + + for (String line : ((List) context.getSessionData(SESSION_CONSTANT))) + { + sb.append(line); + sb.append(System.getProperty("line.separator")); + } + + return sb.toString(); + } + + return question; + } + + + @Override + public Prompt acceptInput(@NotNull ConversationContext context, @Nullable String input) + { + String[] exit = user.getTranslation(Constants.CONVERSATIONS + "exit-string"). + toLowerCase().replaceAll("\\s", ""). + split(","); + + if (input != null && ArrayUtils.contains(exit, input.toLowerCase())) + { + return messagePrompt; + } + + List desc = new ArrayList<>(); + + if (context.getSessionData(SESSION_CONSTANT) instanceof List list) + { + desc = (List) list; + } + if (input != null) { + desc.add(ChatColor.translateAlternateColorCodes('&', input)); + } + context.setSessionData(SESSION_CONSTANT, desc); + return this; + } + }; + + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(stringPrompt). + withModality(true). + withLocalEcho(false). + withTimeout(90). + withEscapeSequence(user.getTranslation(Constants.CONVERSATIONS + "cancel-string")). + addConversationAbandonedListener(ConversationUtils.getAbandonListener(consumer, user)). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This method will close opened gui and writes question in chat. After players answers on question in chat, message + * will trigger consumer and gui will reopen. + * + * @param consumer Consumer that accepts player output text. + * @param question Message that will be displayed in chat when player triggers conversion. + * @param user User who is targeted with current confirmation. + */ + public static void createStringInput(Consumer consumer, + User user, + @NotNull String question, + @Nullable String successMessage) + { + // Text input message. + StringPrompt stringPrompt = new StringPrompt() + { + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + user.closeInventory(); + return question; + } + + + @Override + @NotNull + public Prompt acceptInput(@NotNull ConversationContext context, @Nullable String input) + { + consumer.accept(input); + return ConversationUtils.endMessagePrompt(successMessage); + } + }; + + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(stringPrompt). + // On cancel conversation will be closed. + withLocalEcho(false). + withTimeout(90). + withEscapeSequence(user.getTranslation(Constants.CONVERSATIONS + "cancel-string")). + // Use null value in consumer to detect if user has abandoned conversation. + addConversationAbandonedListener(ConversationUtils.getAbandonListener(consumer, user)). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This is just a simple end message prompt that displays requested message. + * + * @param message Message that will be displayed. + * @return MessagePrompt that displays given message and exists from conversation. + */ + private static MessagePrompt endMessagePrompt(@Nullable String message) + { + return new MessagePrompt() + { + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + return message == null ? "" : message; + } + + + @Override + @Nullable + protected Prompt getNextPrompt(@NotNull ConversationContext context) + { + return Prompt.END_OF_CONVERSATION; + } + }; + } + + + /** + * This method creates and returns abandon listener for every conversation. + * + * @param consumer Consumer which must return null value. + * @param user User who was using conversation. + * @return ConversationAbandonedListener instance. + */ + private static ConversationAbandonedListener getAbandonListener(Consumer consumer, User user) + { + return abandonedEvent -> + { + if (!abandonedEvent.gracefulExit()) + { + consumer.accept(null); + // send cancell message + abandonedEvent.getContext().getForWhom().sendRawMessage( + user.getTranslation(Constants.CONVERSATIONS + "prefix") + + user.getTranslation(Constants.CONVERSATIONS + "cancelled")); + } + }; + } +} diff --git a/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java b/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java deleted file mode 100644 index d60e7a4..0000000 --- a/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java +++ /dev/null @@ -1,143 +0,0 @@ -package world.bentobox.challenges.panel; - - -import java.util.List; -import java.util.Optional; - -import org.bukkit.Material; -import org.bukkit.World; - -import world.bentobox.bentobox.api.addons.GameModeAddon; -import world.bentobox.bentobox.api.commands.CompositeCommand; -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.utils.GuiUtils; - - -/** - * This class process GameModeGui opening. - */ -public class GameModesGUI extends CommonGUI -{ - /** - * @param adminMode - boolean that indicate if Gui is in admin mode. - * @param gameModeAddons - List with GameModes where Challenges addon is integrated. - * @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 GameModesGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix, - boolean adminMode, - List gameModeAddons) - { - super(addon, world, user, topLabel, permissionPrefix); - this.adminMode = adminMode; - this.gameModeAddons = gameModeAddons; - } - - - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user). - name("challenges.gui.title.game-modes"); - - GuiUtils.fillBorder(panelBuilder, this.adminMode ? - Material.BLACK_STAINED_GLASS_PANE : - Material.BLUE_STAINED_GLASS_PANE); - - int elementIndex; - - if (this.gameModeAddons.size() < 8) - { - if (this.gameModeAddons.size() == 7) - { - elementIndex = 19; - } - else - { - elementIndex = 22 - this.gameModeAddons.size() / 2; - } - } - else - { - elementIndex = 10; - } - - for (GameModeAddon gameModeAddon : this.gameModeAddons) - { - if (!panelBuilder.slotOccupied(elementIndex)) - { - panelBuilder.item(elementIndex++, this.createGameModeIcon(gameModeAddon)); - } - else - { - // Find first open slot - while (panelBuilder.slotOccupied(elementIndex)) - { - elementIndex++; - } - } - } - - panelBuilder.build(); - } - - - /** - * This method creates icon that will display given GameMode addon. - * @param gameModeAddon GameMode addon. - * @return PanelItem that acts as icon for given GameMode. - */ - private PanelItem createGameModeIcon(GameModeAddon gameModeAddon) - { - return new PanelItemBuilder(). - name(gameModeAddon.getDescription().getName()). - description(gameModeAddon.getDescription().getDescription()). - icon(Material.PAPER). - clickHandler((panel, user, clickType, slot) -> { - Optional command; - - if (this.adminMode) - { - command = gameModeAddon.getAdminCommand(); - } - else - { - command = gameModeAddon.getPlayerCommand(); - } - - command.ifPresent(compositeCommand -> - user.performCommand(compositeCommand.getTopLabel() + " challenges")); - - return true; - }). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * List with game mode addons which must be showed in current GUI. - */ - private List gameModeAddons; - - /** - * Stores if current GUI is in Admin Mode or not. - */ - private boolean adminMode; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java deleted file mode 100644 index 216d6f1..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java +++ /dev/null @@ -1,736 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.Locale; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.conversations.*; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; - -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.ChallengesAddon; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.ChallengeTypeGUI; -import world.bentobox.challenges.panel.util.ConfirmationGUI; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.utils.Utils; -import world.bentobox.challenges.web.WebManager; - - -/** - * 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; - - /** - * This indicate if wipe button should clear all data, or only challenges. - */ - private boolean wipeAll; - - - // --------------------------------------------------------------------- - // 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, - EDIT_SETTINGS, - DEFAULT_IMPORT_CHALLENGES, - DEFAULT_EXPORT_CHALLENGES, - /** - * Allows to remove whole database - */ - COMPLETE_WIPE, - /** - * Allows to remove only challenges and levels - */ - CHALLENGE_WIPE, - /** - * Allows to remove only players data - */ - USER_WIPE, - /** - * Allows to access Web Library - */ - LIBRARY - } - - - // --------------------------------------------------------------------- - // Section: Constructor - // --------------------------------------------------------------------- - - /** - * @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 AdminGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix) - { - super(addon, world, user, topLabel, permissionPrefix); - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * {@inheritDoc} - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.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 All Player Data removal. - panelBuilder.item(28, this.createButton(Button.USER_WIPE)); - - // 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.DEFAULT_IMPORT_CHALLENGES)); - panelBuilder.item(24, this.createButton(Button.LIBRARY)); - - // Not added as I do not think admins should use it. It still will be able via command. - // panelBuilder.item(33, this.createButton(Button.DEFAULT_EXPORT_CHALLENGES)); - - // Edit Addon Settings - panelBuilder.item(16, this.createButton(Button.EDIT_SETTINGS)); - - // Button that deletes everything from challenges addon - - if (this.wipeAll) - { - panelBuilder.item(34, this.createButton(Button.COMPLETE_WIPE)); - } - else - { - panelBuilder.item(34, this.createButton(Button.CHALLENGE_WIPE)); - } - - 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; - String description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - String permissionSuffix; - - switch (button) - { - case COMPLETE_USER_CHALLENGES: - permissionSuffix = COMPLETE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.complete"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.complete"); - 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.buttons.admin.reset"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.reset"); - 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.buttons.admin.create-challenge"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.create-challenge"); - icon = new ItemStack(Material.BOOK); - clickHandler = (panel, user, clickType, slot) -> { - - this.getNewUniqueID(challenge -> - { - if (challenge == null) - { - // Build Admin Gui if input is null. - this.build(); - } - else - { - String uniqueId = Utils.getGameMode(this.world) + "_" + challenge; - - ChallengeTypeGUI.open(user, - this.addon.getChallengesSettings().getLoreLineLength(), - (type, requirements) -> new EditChallengeGUI(this.addon, - this.world, - this.user, - this.addon.getChallengesManager().createChallenge(uniqueId, type, requirements), - this.topLabel, - this.permissionPrefix, - this).build()); - } - }, - input -> { - String uniqueId = Utils.getGameMode(this.world) + "_" + input; - return !this.addon.getChallengesManager().containsChallenge(uniqueId); - }, - this.user.getTranslation("challenges.gui.questions.admin.unique-id") - ); - - return true; - }; - glow = false; - - break; - case ADD_LEVEL: - permissionSuffix = ADD; - - name = this.user.getTranslation("challenges.gui.buttons.admin.create-level"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.create-level"); - icon = new ItemStack(Material.BOOK); - clickHandler = (panel, user, clickType, slot) -> { - - this.getNewUniqueID(level -> - { - if (level == null) - { - // Build Admin Gui if input is null. - this.build(); - } - else - { - String newName = Utils.getGameMode(this.world) + "_" + level; - - new EditLevelGUI(this.addon, - this.world, - this.user, - this.addon.getChallengesManager().createLevel(newName, this.world), - this.topLabel, - this.permissionPrefix, - this).build(); - } - }, - input -> { - String newName = Utils.getGameMode(this.world) + "_" + input; - return !this.addon.getChallengesManager().containsLevel(newName); - }, - this.user.getTranslation("challenges.gui.questions.admin.unique-id") - ); - - return true; - }; - glow = false; - - break; - case EDIT_CHALLENGE: - permissionSuffix = EDIT; - - name = this.user.getTranslation("challenges.gui.buttons.admin.edit-challenge"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.edit-challenge"); - 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.buttons.admin.edit-level"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.edit-level"); - 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.buttons.admin.delete-challenge"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.delete-challenge"); - 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.buttons.admin.delete-level"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.delete-level"); - 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 DEFAULT_IMPORT_CHALLENGES: - { - permissionSuffix = DEFAULT; - - name = this.user.getTranslation("challenges.gui.buttons.admin.default-import"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.default-import"); - icon = new ItemStack(Material.HOPPER); - clickHandler = (panel, user, clickType, slot) -> { - // Run import command. - this.user.performCommand(this.topLabel + " " + CHALLENGES + " " + DEFAULT + " " + IMPORT); - - return true; - }; - glow = false; - - break; - } - case DEFAULT_EXPORT_CHALLENGES: - { - permissionSuffix = DEFAULT; - - name = this.user.getTranslation("challenges.gui.buttons.admin.default-export"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.default-export"); - 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 + " " + DEFAULT + " " + GENERATE + - (this.overwriteMode ? " overwrite" : "")); - } - return true; - }; - glow = this.overwriteMode; - - break; - } - case EDIT_SETTINGS: - { - permissionSuffix = SETTINGS; - - name = this.user.getTranslation("challenges.gui.buttons.admin.settings"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.settings"); - 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; - } - case COMPLETE_WIPE: - { - permissionSuffix = WIPE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.complete-wipe"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.complete-wipe"); - icon = new ItemStack(Material.TNT); - clickHandler = (panel, user, clickType, slot) -> { - - if (clickType.isRightClick()) - { - this.wipeAll = false; - this.build(); - } - else - { - new ConfirmationGUI(this.user, value -> { - if (value) - { - this.addon.getChallengesManager().wipeDatabase(false); - this.user.sendMessage("challenges.messages.admin.complete-wipe"); - } - - this.build(); - }); - } - - return true; - }; - glow = true; - - break; - } - case CHALLENGE_WIPE: - { - permissionSuffix = WIPE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.challenge-wipe"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.challenge-wipe"); - icon = new ItemStack(Material.TNT); - clickHandler = (panel, user, clickType, slot) -> { - - if (clickType.isRightClick()) - { - this.wipeAll = true; - this.build(); - } - else - { - new ConfirmationGUI(this.user, value -> { - if (value) - { - this.addon.getChallengesManager().wipeDatabase(false); - this.user.sendMessage("challenges.messages.admin.challenge-wipe"); - } - - this.build(); - }); - } - - return true; - }; - glow = false; - - break; - } - case USER_WIPE: - { - permissionSuffix = WIPE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.players-wipe"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.players-wipe"); - icon = new ItemStack(Material.TNT); - clickHandler = (panel, user, clickType, slot) -> { - - new ConfirmationGUI(this.user, value -> { - if (value) - { - this.addon.getChallengesManager().wipePlayers(); - this.user.sendMessage("challenges.messages.admin.players-wipe"); - } - - this.build(); - }); - - return true; - }; - glow = false; - - break; - } - case LIBRARY: - { - permissionSuffix = DOWNLOAD; - - name = this.user.getTranslation("challenges.gui.buttons.admin.library"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.library"); - - if (WebManager.isEnabled()) - { - icon = new ItemStack(Material.COBWEB); - } - else - { - description += "|" + this.user.getTranslation("challenges.gui.descriptions.admin.download-disabled"); - icon = new ItemStack(Material.STRUCTURE_VOID); - } - - clickHandler = (panel, user, clickType, slot) -> { - if (WebManager.isEnabled()) - { - ListLibraryGUI.open(this); - } - - 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 PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - glow(glow). - clickHandler(clickHandler). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Conversation -// --------------------------------------------------------------------- - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on - * inputText in chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - */ - private void getNewUniqueID(Consumer consumer, - Function stringValidation, - @NonNull String question) - { - final User user = this.user; - - Conversation conversation = - new ConversationFactory(BentoBox.getInstance()).withFirstPrompt( - new ValidatingPrompt() - { - - /** - * Gets the text to display to the user when - * this prompt is first presented. - * - * @param context Context information about the - * conversation. - * @return The text to display. - */ - @Override - public String getPromptText(ConversationContext context) - { - // Close input GUI. - user.closeInventory(); - - // There are no editable message. Just return question. - return question; - } - - - /** - * Override this method to check the validity of - * the player's input. - * - * @param context Context information about the - * conversation. - * @param input The player's raw console input. - * @return True or false depending on the - * validity of the input. - */ - @Override - protected boolean isInputValid(ConversationContext context, String input) - { - return stringValidation.apply(GuiUtils.sanitizeInput(input)); - } - - - /** - * Optionally override this method to - * display an additional message if the - * user enters an invalid input. - * - * @param context Context information - * about the conversation. - * @param invalidInput The invalid input - * provided by the user. - * @return A message explaining how to - * correct the input. - */ - @Override - protected String getFailedValidationText(ConversationContext context, - String invalidInput) - { - return user.getTranslation("challenges.errors.unique-id", "[id]", GuiUtils.sanitizeInput(invalidInput)); - } - - - /** - * Override this method to accept and processes - * the validated input from the user. Using the - * input, the next Prompt in the prompt graph - * should be returned. - * - * @param context Context information about the - * conversation. - * @param input The validated input text from - * the user. - * @return The next Prompt in the prompt graph. - */ - @Override - protected Prompt acceptValidatedInput(ConversationContext context, String input) - { - // Add answer to consumer. - consumer.accept(GuiUtils.sanitizeInput(input)); - // End conversation - return Prompt.END_OF_CONVERSATION; - } - }). - // On cancel conversation will be closed. - withEscapeSequence("cancel"). - // Use null value in consumer to detect if user has abandoned conversation. - addConversationAbandonedListener(abandonedEvent -> - { - if (!abandonedEvent.gracefulExit()) - { - consumer.accept(null); - } - }). - withLocalEcho(false). - withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). - buildConversation(user.getPlayer()); - - conversation.begin(); - } -} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/AdminPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/AdminPanel.java new file mode 100644 index 0000000..5a1dd21 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/AdminPanel.java @@ -0,0 +1,593 @@ +package world.bentobox.challenges.panel.admin; + + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +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.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.ChallengeTypeSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; +import world.bentobox.challenges.web.WebManager; + + +/** + * This class contains Main + */ +public class AdminPanel extends CommonPanel +{ + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- + + /** + * @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.) + */ + private AdminPanel(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + new AdminPanel(addon, world, user, topLabel, permissionPrefix).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "admin-gui")); + + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(10, this.createButton(Button.COMPLETE_USER_CHALLENGES)); + panelBuilder.item(19, this.createButton(Button.RESET_USER_CHALLENGES)); + + // Add All Player Data removal. + panelBuilder.item(28, this.createButton(Button.USER_WIPE)); + + // 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(14, this.createButton(Button.IMPORT_TEMPLATE)); + panelBuilder.item(15, this.createButton(Button.IMPORT_DATABASE)); + panelBuilder.item(33, this.createButton(Button.LIBRARY)); + // Export Challenges + panelBuilder.item(24, this.createButton(Button.EXPORT_CHALLENGES)); + + // Edit Addon Settings + panelBuilder.item(16, this.createButton(Button.EDIT_SETTINGS)); + + // Button that deletes everything from challenges addon + + if (this.wipeAll) + { + panelBuilder.item(34, this.createButton(Button.COMPLETE_WIPE)); + } + else + { + panelBuilder.item(34, this.createButton(Button.CHALLENGE_WIPE)); + } + + 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) + { + final String name = this.user.getTranslation(Constants.BUTTON + button.name().toLowerCase() + ".name"); + List description = new ArrayList<>(3); + description.add(this.user.getTranslation(Constants.BUTTON + button.name().toLowerCase() + ".description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + + switch (button) + { + case COMPLETE_USER_CHALLENGES -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + ListUsersPanel.open(this, ListUsersPanel.Mode.COMPLETE); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case RESET_USER_CHALLENGES -> { + icon = new ItemStack(Material.WRITABLE_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.resetAllMode = !this.resetAllMode; + this.build(); + } + else + { + ListUsersPanel.open(this, + this.resetAllMode ? ListUsersPanel.Mode.RESET_ALL : ListUsersPanel.Mode.RESET); + } + + return true; + }; + glow = this.resetAllMode; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "left-click-to-open")); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "right-click-to-reset-all")); + } + case ADD_CHALLENGE -> { + icon = new ItemStack(Material.BOOK); + clickHandler = (panel, user, clickType, slot) -> { + + String gameModePrefix = Utils.getGameMode(this.world).toLowerCase() + "_"; + + // This consumer process new bundle creating with a name and id from given + // consumer value.. + Consumer challengeIdConsumer = value -> { + if (value != null) + { + ChallengeTypeSelector.open(this.user, + (type, requirements) -> EditChallengePanel.open(this, + this.addon.getChallengesManager().createChallenge( + gameModePrefix + Utils.sanitizeInput(value), + value, + type, + requirements))); + } + else + { + // Operation is canceled. Open this panel again. + this.build(); + } + }; + + // This function checks if generator with a given ID already exist. + Function validationFunction = uniqueId -> + !this.addon.getChallengesManager().containsChallenge(gameModePrefix + Utils.sanitizeInput(uniqueId)); + + // Call a conversation API to get input string. + ConversationUtils.createIDStringInput(challengeIdConsumer, + validationFunction, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "write-name"), + this.user.getTranslation(Constants.CONVERSATIONS + "new-object-created", + Constants.PARAMETER_WORLD, this.world.getName()), + Constants.CONVERSATIONS + "object-already-exists"); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-create")); + } + case ADD_LEVEL -> { + icon = new ItemStack(Material.BOOK); + clickHandler = (panel, user, clickType, slot) -> { + + String gameModePrefix = Utils.getGameMode(this.world).toLowerCase() + "_"; + + // This consumer process new bundle creating with a name and id from given + // consumer value.. + Consumer levelIdConsumer = value -> { + if (value != null) + { + EditLevelPanel.open(this, + this.addon.getChallengesManager().createLevel( + gameModePrefix + Utils.sanitizeInput(value), + value, + world)); + } + else + { + // Operation is canceled. Open this panel again. + this.build(); + } + }; + + // This function checks if generator with a given ID already exist. + Function validationFunction = uniqueId -> + !this.addon.getChallengesManager().containsLevel(gameModePrefix + Utils.sanitizeInput(uniqueId)); + + // Call a conversation API to get input string. + ConversationUtils.createIDStringInput(levelIdConsumer, + validationFunction, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "write-name"), + this.user.getTranslation(Constants.CONVERSATIONS + "new-object-created", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + Constants.CONVERSATIONS + "object-already-exists"); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-create")); + } + case EDIT_CHALLENGE -> { + icon = new ItemStack(Material.ANVIL); + clickHandler = (panel, user, clickType, slot) -> { + ListChallengesPanel.open(this, ListChallengesPanel.Mode.EDIT); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case EDIT_LEVEL -> { + icon = new ItemStack(Material.ANVIL); + clickHandler = (panel, user, clickType, slot) -> { + ListLevelsPanel.open(this, ListLevelsPanel.Mode.EDIT); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case DELETE_CHALLENGE -> { + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ListChallengesPanel.open(this, ListChallengesPanel.Mode.DELETE); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case DELETE_LEVEL -> { + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ListLevelsPanel.open(this, ListLevelsPanel.Mode.DELETE); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case EDIT_SETTINGS -> { + icon = new ItemStack(Material.CRAFTING_TABLE); + clickHandler = (panel, user, clickType, slot) -> { + EditSettingsPanel.open(this); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case IMPORT_DATABASE -> { + icon = new ItemStack(Material.BOOKSHELF); + clickHandler = (panel, user, clickType, slot) -> { + LibraryPanel.open(this, LibraryPanel.Library.DATABASE); + return true; + }; + glow = true; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case IMPORT_TEMPLATE -> { + icon = new ItemStack(Material.BOOKSHELF); + clickHandler = (panel, user, clickType, slot) -> { + LibraryPanel.open(this, LibraryPanel.Library.TEMPLATE); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case EXPORT_CHALLENGES -> { + icon = new ItemStack(Material.HOPPER); + clickHandler = (panel, user, clickType, slot) -> { + + // This consumer process file exporting after user input is returned. + Consumer fileNameConsumer = value -> { + if (value != null) + { + this.addon.getImportManager().generateDatabaseFile(this.user, + this.world, + Utils.sanitizeInput(value)); + } + + this.build(); + }; + + // This function checks if file can be created. + Function validationFunction = fileName -> + { + String sanitizedName = Utils.sanitizeInput(fileName); + return !new File(this.addon.getDataFolder(), + sanitizedName.endsWith(".json") ? sanitizedName : sanitizedName + ".json").exists(); + }; + + // Call a conversation API to get input string. + ConversationUtils.createIDStringInput(fileNameConsumer, + validationFunction, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "exported-file-name"), + this.user.getTranslation(Constants.CONVERSATIONS + "database-export-completed", + Constants.PARAMETER_WORLD, world.getName()), + Constants.CONVERSATIONS + "file-name-exist"); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-export")); + } + case LIBRARY -> { + if (WebManager.isEnabled()) + { + icon = new ItemStack(Material.COBWEB); + } + else + { + icon = new ItemStack(Material.STRUCTURE_VOID); + } + + clickHandler = (panel, user, clickType, slot) -> { + if (WebManager.isEnabled()) + { + LibraryPanel.open(this, LibraryPanel.Library.WEB); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case COMPLETE_WIPE -> { + icon = new ItemStack(Material.TNT); + clickHandler = (panel, user, clickType, slot) -> { + + if (clickType.isRightClick()) + { + this.wipeAll = false; + this.build(); + } + else + { + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().wipeDatabase(this.wipeAll, + Utils.getGameMode(this.world)); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-all-data-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + this.user.getTranslation(Constants.CONVERSATIONS + "all-data-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world))); + } + + return true; + }; + glow = true; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "left-click-to-wipe")); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "right-click-to-switch")); + } + case CHALLENGE_WIPE -> { + icon = new ItemStack(Material.TNT); + clickHandler = (panel, user, clickType, slot) -> { + + if (clickType.isRightClick()) + { + this.wipeAll = true; + this.build(); + } + else + { + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().wipeDatabase(this.wipeAll, + Utils.getGameMode(this.world)); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-challenge-data-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + this.user.getTranslation(Constants.CONVERSATIONS + "challenge-data-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world))); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "left-click-to-wipe")); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "right-click-to-switch")); + } + case USER_WIPE -> { + icon = new ItemStack(Material.TNT); + clickHandler = (panel, user, clickType, slot) -> { + + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().wipePlayers(Utils.getGameMode(this.world)); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-user-data-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + this.user.getTranslation(Constants.CONVERSATIONS + "user-data-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world))); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-wipe")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + // --------------------------------------------------------------------- + // 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, + EDIT_SETTINGS, + IMPORT_DATABASE, + IMPORT_TEMPLATE, + EXPORT_CHALLENGES, + /** + * Allows to remove whole database + */ + COMPLETE_WIPE, + /** + * Allows to remove only challenges and levels + */ + CHALLENGE_WIPE, + /** + * Allows to remove only players data + */ + USER_WIPE, + /** + * Allows to access Web Library + */ + LIBRARY + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * This indicates if Reset Challenges must work as reset all. + */ + private boolean resetAllMode; + + /** + * This indicates if wipe button should clear all data, or only challenges. + */ + private boolean wipeAll; +} \ 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 deleted file mode 100644 index 837e7ee..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java +++ /dev/null @@ -1,1531 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; - -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.util.Util; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.requirements.InventoryRequirements; -import world.bentobox.challenges.database.object.requirements.IslandRequirements; -import world.bentobox.challenges.database.object.requirements.OtherRequirements; -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.SelectBlocksGUI; -import world.bentobox.challenges.panel.util.SelectEnvironmentGUI; -import world.bentobox.challenges.panel.util.StringListGUI; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.utils.Utils; - - -/** - * This class contains all necessary methods that creates GUI and allow to edit challenges - * properties. - */ -public class EditChallengeGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructors - // --------------------------------------------------------------------- - - - /** - * @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 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); - } - - - /** - * @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 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; - - // Set line length. - this.lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - } - - - // --------------------------------------------------------------------- - // Section: Panel Creation related methods - // --------------------------------------------------------------------- - - - /** - * {@inheritDoc} - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.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); - - // Every time when this GUI is build, save challenge - // This will ensure that all main things will be always stored - this.addon.getChallengesManager().saveChallenge(this.challenge); - - 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(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.createRequirementButton(RequirementButton.REQUIRED_ENTITIES)); - panelBuilder.item(28, this.createRequirementButton(RequirementButton.REMOVE_ENTITIES)); - - panelBuilder.item(21, this.createRequirementButton(RequirementButton.REQUIRED_BLOCKS)); - panelBuilder.item(30, this.createRequirementButton(RequirementButton.REMOVE_BLOCKS)); - - panelBuilder.item(23, this.createRequirementButton(RequirementButton.SEARCH_RADIUS)); - panelBuilder.item(25, this.createRequirementButton(RequirementButton.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.createRequirementButton(RequirementButton.REQUIRED_ITEMS)); - panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_ITEMS)); - - panelBuilder.item(25, this.createRequirementButton(RequirementButton.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.createRequirementButton(RequirementButton.REQUIRED_EXPERIENCE)); - panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_EXPERIENCE)); - - panelBuilder.item(12, this.createRequirementButton(RequirementButton.REQUIRED_MONEY)); - panelBuilder.item(21, this.createRequirementButton(RequirementButton.REMOVE_MONEY)); - - panelBuilder.item(23, this.createRequirementButton(RequirementButton.REQUIRED_LEVEL)); - - panelBuilder.item(25, this.createRequirementButton(RequirementButton.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.createRewardButton(RewardButton.REWARD_TEXT)); - panelBuilder.item(19, this.createRewardButton(RewardButton.REWARD_COMMANDS)); - - panelBuilder.item(11, this.createRewardButton(RewardButton.REWARD_ITEM)); - panelBuilder.item(20, this.createRewardButton(RewardButton.REWARD_EXPERIENCE)); - panelBuilder.item(29, this.createRewardButton(RewardButton.REWARD_MONEY)); - - panelBuilder.item(22, this.createRewardButton(RewardButton.REPEATABLE)); - - if (this.challenge.isRepeatable()) - { - panelBuilder.item(31, this.createRewardButton(RewardButton.REPEAT_COUNT)); - - panelBuilder.item(15, this.createRewardButton(RewardButton.REPEAT_REWARD_TEXT)); - panelBuilder.item(24, this.createRewardButton(RewardButton.REPEAT_REWARD_COMMANDS)); - - panelBuilder.item(16, this.createRewardButton(RewardButton.REPEAT_REWARD_ITEM)); - panelBuilder.item(25, this.createRewardButton(RewardButton.REPEAT_REWARD_EXPERIENCE)); - panelBuilder.item(34, this.createRewardButton(RewardButton.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; - String description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (menuType) - { - case PROPERTIES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.properties"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.properties"); - 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.buttons.admin.requirements"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.requirements"); - 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.buttons.admin.rewards"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.rewards"); - 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 PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - 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 DEPLOYED: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.deployment"); - - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.deployment")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.challenge.isDeployed() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.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.buttons.admin.icon"); - description = Collections.singletonList(this.user.getTranslation( - "challenges.gui.descriptions.admin.icon-challenge")); - icon = this.challenge.getIcon(); - clickHandler = (panel, user, clickType, slot) -> { - - new SelectBlocksGUI(this.user, true, (status, materials) -> { - if (status) - { - materials.forEach(material -> - this.challenge.setIcon(new ItemStack(material))); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case DESCRIPTION: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.description"); - description = Collections.singletonList( - this.user.getTranslation("challenges.gui.descriptions.admin.description")); - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getDescription(), - this.lineLength, - (status, value) -> { - if (status) - { - this.challenge.setDescription(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case ORDER: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.order"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.order")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getOrder()))); - - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getOrder(), - -1, - 9999, - this.lineLength, - (status, value) -> { - if (status) - { - this.challenge.setOrder(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case ENVIRONMENT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.environment"); - - description = new ArrayList<>(4); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.environment")); - - description.add((this.challenge.getEnvironment().contains(World.Environment.NORMAL) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.normal")); - description.add((this.challenge.getEnvironment().contains(World.Environment.NETHER) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.nether")); - description.add((this.challenge.getEnvironment().contains(World.Environment.THE_END) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.the-end")); - - 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.buttons.admin.remove-on-complete"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-on-complete")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.challenge.isRemoveWhenCompleted() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - 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.buttons.admin.name"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.name-challenge")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", this.challenge.getFriendlyName())); - - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - - this.getFriendlyName(reply -> { - if (reply != null) - { - this.challenge.setFriendlyName(reply); - } - - this.build(); - }, - this.user.getTranslation("challenges.gui.questions.admin.challenge-name"), - this.challenge.getFriendlyName() - ); - - return true; - }; - glow = false; - break; - } - - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for requirements menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createRequirementButton(RequirementButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case REQUIRED_PERMISSIONS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-permissions"); - description = new ArrayList<>(this.challenge.getRequirements().getRequiredPermissions().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-permissions")); - - for (String permission : this.challenge.getRequirements().getRequiredPermissions()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.permission", - "[permission]", permission)); - } - - icon = new ItemStack(Material.REDSTONE_LAMP); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRequirements().getRequiredPermissions(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.getRequirements().setRequiredPermissions(new HashSet<>(value)); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case REQUIRED_ENTITIES: - case REMOVE_ENTITIES: - case REQUIRED_BLOCKS: - case REMOVE_BLOCKS: - case SEARCH_RADIUS: - { - return this.createIslandRequirementButton(button); - } - - case REQUIRED_ITEMS: - case REMOVE_ITEMS: - { - return this.createInventoryRequirementButton(button); - } - - case REQUIRED_EXPERIENCE: - case REMOVE_EXPERIENCE: - case REQUIRED_LEVEL: - case REQUIRED_MONEY: - case REMOVE_MONEY: - { - return this.createOtherRequirementButton(button); - } - - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for island requirements menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createIslandRequirementButton(RequirementButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - final IslandRequirements requirements = this.challenge.getRequirements(); - - switch (button) - { - case REQUIRED_ENTITIES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-entities"); - - description = new ArrayList<>(requirements.getRequiredEntities().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-entities")); - - for (Map.Entry entry : requirements.getRequiredEntities().entrySet()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.entity", - "[entity]", Util.prettifyText(entry.getKey().name()), - "[count]", Integer.toString(entry.getValue()))); - } - - icon = new ItemStack(Material.CREEPER_HEAD); - clickHandler = (panel, user, clickType, slot) -> { - new ManageEntitiesGUI(this.addon, - this.world, - this.user, - requirements.getRequiredEntities(), - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - break; - } - case REMOVE_ENTITIES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-entities"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-entities")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isRemoveEntities() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setRemoveEntities(!requirements.isRemoveEntities()); - - this.build(); - return true; - }; - glow = requirements.isRemoveEntities(); - break; - } - case REQUIRED_BLOCKS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-blocks"); - - description = new ArrayList<>(requirements.getRequiredBlocks().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-blocks")); - - for (Map.Entry entry : requirements.getRequiredBlocks().entrySet()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.block", - "[block]", entry.getKey().name(), - "[count]", Integer.toString(entry.getValue()))); - } - - icon = new ItemStack(Material.STONE); - clickHandler = (panel, user, clickType, slot) -> { - new ManageBlocksGUI(this.addon, - this.world, - this.user, - requirements.getRequiredBlocks(), - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - break; - } - case REMOVE_BLOCKS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-blocks"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-blocks")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isRemoveBlocks() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setRemoveBlocks(!requirements.isRemoveBlocks()); - - this.build(); - return true; - }; - glow = requirements.isRemoveBlocks(); - break; - } - case SEARCH_RADIUS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.search-radius"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.search-radius")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(requirements.getSearchRadius()))); - - icon = new ItemStack(Material.COBBLESTONE_WALL); - - // Search radius should not be larger then island radius. - int maxSearchDistance = - this.addon.getPlugin().getIWM().getAddon(this.world).map(gameModeAddon -> - gameModeAddon.getWorldSettings().getIslandDistance()).orElse(100); - - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - requirements.getSearchRadius(), - 0, - maxSearchDistance, - this.lineLength, - (status, value) -> { - if (status) - { - requirements.setSearchRadius(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for inventory requirements menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createInventoryRequirementButton(RequirementButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - final InventoryRequirements requirements = this.challenge.getRequirements(); - - switch (button) - { - case REQUIRED_ITEMS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-items"); - - description = new ArrayList<>(requirements.getRequiredItems().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-items")); - - Utils.groupEqualItems(requirements.getRequiredItems()).forEach(itemStack -> - description.addAll(this.generateItemStackDescription(itemStack))); - - icon = new ItemStack(Material.CHEST); - clickHandler = (panel, user, clickType, slot) -> { - new ItemSwitchGUI(this.user, - requirements.getRequiredItems(), - this.lineLength, - (status, value) -> { - if (status) - { - requirements.setRequiredItems(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REMOVE_ITEMS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-items"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-items")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isTakeItems() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setTakeItems(!requirements.isTakeItems()); - - this.build(); - return true; - }; - glow = requirements.isTakeItems(); - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for other requirements menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createOtherRequirementButton(RequirementButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - final OtherRequirements requirements = this.challenge.getRequirements(); - - switch (button) - { - case REQUIRED_EXPERIENCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-experience"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-experience")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(requirements.getRequiredExperience()))); - - icon = new ItemStack(Material.EXPERIENCE_BOTTLE); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - requirements.getRequiredExperience(), - 0, - this.lineLength, - (status, value) -> { - if (status) - { - requirements.setRequiredExperience(value); - } - - this.build(); - }); - return true; - }; - glow = false; - break; - } - case REMOVE_EXPERIENCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-experience"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-experience")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isTakeExperience() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setTakeExperience(!requirements.isTakeExperience()); - - this.build(); - return true; - }; - glow = requirements.isTakeExperience(); - break; - } - case REQUIRED_LEVEL: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-level"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-level")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Long.toString(requirements.getRequiredIslandLevel()))); - - icon = new ItemStack(this.addon.isLevelProvided() ? Material.BEACON : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - (int) requirements.getRequiredIslandLevel(), - lineLength, - (status, value) -> { - if (status) - { - requirements.setRequiredIslandLevel(value); - } - - this.build(); - }); - - return true; - }; - - glow = false; - break; - } - case REQUIRED_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-money"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-money")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Double.toString(requirements.getRequiredMoney()))); - - icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - (int) requirements.getRequiredMoney(), - 0, - lineLength, - (status, value) -> { - if (status) - { - requirements.setRequiredMoney(value); - } - - this.build(); - }); - return true; - }; - - glow = false; - break; - } - case REMOVE_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-money"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-money")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isTakeMoney() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(this.addon.isEconomyProvided() ? Material.LEVER : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setTakeMoney(!requirements.isTakeMoney()); - - this.build(); - return true; - }; - - glow = requirements.isTakeMoney(); - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for rewards menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createRewardButton(RewardButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case REWARD_TEXT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-text"); - description = new ArrayList<>(2); - description - .add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-text")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", "|" + this.challenge.getRewardText())); - - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRewardText(), - lineLength, - (status, value) -> { - if (status) - { - String singleLineMessage = value.stream(). - map(s -> s + "|"). - collect(Collectors.joining()); - - if (singleLineMessage.endsWith("|")) - { - singleLineMessage = singleLineMessage - .substring(0, singleLineMessage.length() - 1); - } - - this.challenge.setRewardText(singleLineMessage); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_ITEM: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-items"); - - description = new ArrayList<>(this.challenge.getRewardItems().size() + 1); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.reward-items")); - - Utils.groupEqualItems(this.challenge.getRewardItems()).forEach(itemStack -> - description.addAll(this.generateItemStackDescription(itemStack))); - - icon = new ItemStack(Material.CHEST); - clickHandler = (panel, user, clickType, slot) -> { - new ItemSwitchGUI(this.user, - this.challenge.getRewardItems(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRewardItems(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_EXPERIENCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-experience"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.reward-experience")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[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, - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRewardExperience(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-money"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.reward-money")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getRewardMoney()))); - - icon = new ItemStack( - this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getRewardMoney(), - 0, - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRewardMoney(value); - } - - this.build(); - }); - - return true; - }; - - - glow = false; - break; - } - case REWARD_COMMANDS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-commands"); - description = new ArrayList<>(this.challenge.getRewardCommands().size() + 1); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.reward-commands")); - - for (String command : this.challenge.getRewardCommands()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command)); - } - - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRewardCommands(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRewardCommands(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case REPEATABLE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.repeatable"); - description = new ArrayList<>(2); - description - .add(this.user.getTranslation("challenges.gui.descriptions.admin.repeatable")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.challenge.isRepeatable() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.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.buttons.admin.repeat-count"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-count")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[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, - lineLength, - (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.buttons.admin.repeat-reward-text"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-text")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", "|" + this.challenge.getRepeatRewardText())); - - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRepeatRewardText(), - lineLength, - (status, value) -> { - if (status) - { - String singleLineMessage = value.stream(). - map(s -> s + "|"). - collect(Collectors.joining()); - - if (singleLineMessage.endsWith("|")) - { - singleLineMessage = singleLineMessage - .substring(0, singleLineMessage.length() - 1); - } - - this.challenge.setRepeatRewardText(singleLineMessage); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REPEAT_REWARD_ITEM: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.repeat-reward-items"); - - description = new ArrayList<>(this.challenge.getRepeatItemReward().size() + 1); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-items")); - - Utils.groupEqualItems(this.challenge.getRepeatItemReward()).forEach(itemStack -> - description.addAll(this.generateItemStackDescription(itemStack))); - - icon = new ItemStack(Material.TRAPPED_CHEST); - clickHandler = (panel, user, clickType, slot) -> { - new ItemSwitchGUI(this.user, - this.challenge.getRepeatItemReward(), - lineLength, - (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.buttons.admin.repeat-reward-experience"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-experience")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[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, - lineLength, - (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.buttons.admin.repeat-reward-money"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-money")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getRepeatMoneyReward()))); - - icon = new ItemStack( - this.addon.isEconomyProvided() ? Material.GOLD_NUGGET : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getRepeatMoneyReward(), - 0, - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRepeatMoneyReward(value); - } - - this.build(); - }); - - return true; - }; - - glow = false; - break; - } - case REPEAT_REWARD_COMMANDS: - { - name = - this.user.getTranslation("challenges.gui.buttons.admin.repeat-reward-commands"); - description = new ArrayList<>(this.challenge.getRepeatRewardCommands().size() + 1); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-commands")); - - for (String command : this.challenge.getRepeatRewardCommands()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command)); - } - - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRepeatRewardCommands(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRepeatRewardCommands(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - // --------------------------------------------------------------------- - // 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, - DEPLOYED, - ICON, - DESCRIPTION, - ORDER, - ENVIRONMENT, - REMOVE_ON_COMPLETE, - } - - - /** - * Represents different rewards buttons that are used in menus. - */ - private enum RewardButton - { - 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, - } - - - /** - * Represents different requirement buttons that are used in menus. - */ - private enum RequirementButton - { - 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, - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * Variable holds challenge thats needs editing. - */ - private Challenge challenge; - - /** - * Variable holds current active menu. - */ - private MenuType currentMenuType; - - /** - * LineLength variable. - */ - private final int lineLength; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java new file mode 100644 index 0000000..db7e50b --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java @@ -0,0 +1,2121 @@ +package world.bentobox.challenges.panel.admin; + + +import java.time.Duration; +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +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.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.requirements.InventoryRequirements; +import world.bentobox.challenges.database.object.requirements.IslandRequirements; +import world.bentobox.challenges.database.object.requirements.OtherRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.*; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary methods that creates GUI and allow to edit challenges + * properties. + */ +public class EditChallengePanel extends CommonPanel +{ + // --------------------------------------------------------------------- + // Section: Constructors + // --------------------------------------------------------------------- + + + /** + * @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 challenge - challenge that needs editing + */ + private EditChallengePanel(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + Challenge challenge) + { + super(addon, user, world, topLabel, permissionPrefix); + this.challenge = challenge; + this.currentMenuType = MenuType.PROPERTIES; + } + + + /** + * @param panel Parent panel + * @param challenge challenge that needs editing. + */ + private EditChallengePanel(CommonPanel panel, Challenge challenge) + { + super(panel); + this.challenge = challenge; + // Default panel should be Properties. + this.currentMenuType = MenuType.PROPERTIES; + } + + + /** + * Open the Challenges Edit GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + * @param challenge - challenge that needs editing + */ + public static void open(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + Challenge challenge) + { + new EditChallengePanel(addon, user, world, topLabel, permissionPrefix, challenge).build(); + } + + + /** + * Open the Challenges Edit GUI. + * + * @param panel - Parent Panel + * @param challenge - challenge that needs editing + */ + public static void open(CommonPanel panel, Challenge challenge) + { + new EditChallengePanel(panel, challenge).build(); + } + + + // --------------------------------------------------------------------- + // Section: Panel Creation related methods + // --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "edit-challenge", + "[challenge]", this.challenge.getFriendlyName())); + + PanelUtils.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_TYPE -> this.buildInventoryRequirementsPanel(panelBuilder); + case ISLAND_TYPE -> this.buildIslandRequirementsPanel(panelBuilder); + case OTHER_TYPE -> this.buildOtherRequirementsPanel(panelBuilder); + case STATISTIC_TYPE -> this.buildStatisticRequirementsPanel(panelBuilder); + } + } + else if (this.currentMenuType.equals(MenuType.REWARDS)) + { + this.buildRewardsPanel(panelBuilder); + } + + panelBuilder.item(44, this.returnButton); + + // Every time when this GUI is build, save challenge + // This will ensure that all main things will be always stored + this.addon.getChallengesManager().saveChallenge(this.challenge); + // If for some reason challenge is not loaded, do it. + this.addon.getChallengesManager().loadChallenge(this.challenge, false, null, true); + + panelBuilder.build(); + } + + + /** + * This class populate ChallengesEditGUI with main challenge settings. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildMainPropertiesPanel(PanelBuilder panelBuilder) + { + panelBuilder.listener(new IconChanger()); + + panelBuilder.item(10, this.createButton(Button.NAME)); + 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.createRequirementButton(RequirementButton.REQUIRED_ENTITIES)); + panelBuilder.item(28, this.createRequirementButton(RequirementButton.REMOVE_ENTITIES)); + + panelBuilder.item(21, this.createRequirementButton(RequirementButton.REQUIRED_BLOCKS)); + panelBuilder.item(30, this.createRequirementButton(RequirementButton.REMOVE_BLOCKS)); + + panelBuilder.item(23, this.createRequirementButton(RequirementButton.SEARCH_RADIUS)); + panelBuilder.item(25, this.createRequirementButton(RequirementButton.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.createRequirementButton(RequirementButton.REQUIRED_ITEMS)); + panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_ITEMS)); + + if (!this.challenge.getRequirements().getRequiredItems().isEmpty()) + { + panelBuilder.item(12, this.createRequirementButton(RequirementButton.ADD_IGNORED_META)); + + if (!this.challenge.getRequirements().getIgnoreMetaData().isEmpty()) + { + panelBuilder.item(21, this.createRequirementButton(RequirementButton.REMOVE_IGNORED_META)); + } + } + + panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); + } + + + /** + * 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.createRequirementButton(RequirementButton.REQUIRED_EXPERIENCE)); + panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_EXPERIENCE)); + + panelBuilder.item(12, this.createRequirementButton(RequirementButton.REQUIRED_MONEY)); + panelBuilder.item(21, this.createRequirementButton(RequirementButton.REMOVE_MONEY)); + + panelBuilder.item(23, this.createRequirementButton(RequirementButton.REQUIRED_LEVEL)); + + panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); + } + + + /** + * This class populates ChallengesEditGUI with other challenges requirement elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildStatisticRequirementsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createRequirementButton(RequirementButton.STATISTIC)); + panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_STATISTIC)); + + panelBuilder.item(11, this.createRequirementButton(RequirementButton.STATISTIC_AMOUNT)); + + StatisticRequirements requirements = this.challenge.getRequirements(); + + if (requirements.getStatistic() != null) + { + switch (requirements.getStatistic().getType()) + { + case ITEM -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_ITEMS)); + case BLOCK -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_BLOCKS)); + case ENTITY -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_ENTITIES)); + default -> {} + } + } + + panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); + } + + + /** + * 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.createRewardButton(RewardButton.REWARD_TEXT)); + panelBuilder.item(19, this.createRewardButton(RewardButton.REWARD_COMMANDS)); + + panelBuilder.item(11, this.createRewardButton(RewardButton.REWARD_ITEMS)); + panelBuilder.item(20, this.createRewardButton(RewardButton.REWARD_EXPERIENCE)); + panelBuilder.item(29, this.createRewardButton(RewardButton.REWARD_MONEY)); + + panelBuilder.item(22, this.createRewardButton(RewardButton.REPEATABLE)); + + if (!this.challenge.getRewardItems().isEmpty() || !this.challenge.getRepeatItemReward().isEmpty()) + { + panelBuilder.item(31, this.createRewardButton(RewardButton.ADD_IGNORED_META)); + } + + if (!this.challenge.getIgnoreRewardMetaData().isEmpty()) + { + panelBuilder.item(32, this.createRewardButton(RewardButton.REMOVE_IGNORED_META)); + } + + if (this.challenge.isRepeatable()) + { + panelBuilder.item(13, this.createRewardButton(RewardButton.COOL_DOWN)); + panelBuilder.item(23, this.createRewardButton(RewardButton.REPEAT_COUNT)); + + panelBuilder.item(15, this.createRewardButton(RewardButton.REPEAT_REWARD_TEXT)); + panelBuilder.item(24, this.createRewardButton(RewardButton.REPEAT_REWARD_COMMANDS)); + + panelBuilder.item(16, this.createRewardButton(RewardButton.REPEAT_REWARD_ITEMS)); + panelBuilder.item(25, this.createRewardButton(RewardButton.REPEAT_REWARD_EXPERIENCE)); + panelBuilder.item(34, this.createRewardButton(RewardButton.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) + { + final String reference = Constants.BUTTON + menuType.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (menuType) + { + case PROPERTIES -> { + 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); + } + case REQUIREMENTS -> { + icon = new ItemStack(Material.HOPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REQUIREMENTS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REQUIREMENTS); + } + case REWARDS -> { + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REWARDS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REWARDS); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for default main menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case NAME -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NAME, this.challenge.getFriendlyName())); + + icon = new ItemStack(Material.NAME_TAG); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer consumer = value -> + { + if (value != null) + { + this.challenge.setFriendlyName(value); + } + + this.build(); + }; + + // start conversation + ConversationUtils.createStringInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-name"), + user.getTranslation(Constants.CONVERSATIONS + "name-changed")); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case DEPLOYED -> { + description.add(this.user.getTranslation(reference + + (this.challenge.isDeployed() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + if (this.challenge.isValid()) + { + this.challenge.setDeployed(!this.challenge.isDeployed()); + } + else + { + Utils.sendMessage(this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "invalid-challenge", + "[challenge]", this.challenge.getFriendlyName())); + this.challenge.setDeployed(false); + } + + this.build(); + return true; + }; + glow = this.challenge.isDeployed(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case ICON -> { + icon = this.challenge.getIcon(); + clickHandler = (panel, user, clickType, i) -> + { + this.selectedButton = button; + this.build(); + return true; + }; + glow = this.selectedButton == button; + + if (this.selectedButton != button) + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-on-item")); + } + } + case DESCRIPTION -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + this.challenge.getDescription().forEach(line -> description.add(Util.translateColorCodes(line))); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setDescription(value); + } + + this.build(); + }; + + if (!this.challenge.getDescription().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-description"), + user.getTranslation(Constants.CONVERSATIONS + "description-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getDescription().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case ORDER -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getOrder()))); + + icon = new ItemStack(Material.HOPPER, Math.max(1, this.challenge.getOrder())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setOrder(number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case ENVIRONMENT -> { + description.add(this.user.getTranslation(this.challenge.getEnvironment().contains(World.Environment.NORMAL) ? + reference + "enabled" : reference + "disabled") + + Utils.prettifyObject(World.Environment.NORMAL, this.user)); + description.add(this.user.getTranslation(this.challenge.getEnvironment().contains(World.Environment.NETHER) ? + reference + "enabled" : reference + "disabled") + + Utils.prettifyObject(World.Environment.NETHER, this.user)); + description.add(this.user.getTranslation(this.challenge.getEnvironment().contains(World.Environment.THE_END) ? + reference + "enabled" : reference + "disabled") + + Utils.prettifyObject(World.Environment.THE_END, this.user)); + + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + EnvironmentSelector.open(this.user, + this.challenge.getEnvironment(), + (status, value) -> { + if (status) + { + this.challenge.setEnvironment(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + } + case REMOVE_ON_COMPLETE -> { + description.add(this.user.getTranslation(reference + + (this.challenge.isRemoveWhenCompleted() ? "enabled" : "disabled"))); + + 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(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for requirements menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createRequirementButton(RequirementButton button) + { + switch (button) + { + case REQUIRED_PERMISSIONS -> { + String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + if (this.challenge.getRequirements().getRequiredPermissions().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + this.challenge.getRequirements().getRequiredPermissions().forEach(permission -> + description.add(this.user.getTranslation(reference + "permission", + "[permission]", permission))); + } + + ItemStack icon = new ItemStack(Material.REDSTONE_LAMP); + + PanelItem.ClickHandler clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.getRequirements().setRequiredPermissions(new HashSet<>(value)); + } + + this.build(); + }; + + if (!this.challenge.getRequirements().getRequiredPermissions().isEmpty() && + clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-permissions"), + user.getTranslation(Constants.CONVERSATIONS + "permissions-changed")); + } + + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRequirements().getRequiredPermissions().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + // Buttons for Island Requirements + case REQUIRED_ENTITIES, REMOVE_ENTITIES, REQUIRED_BLOCKS, REMOVE_BLOCKS, SEARCH_RADIUS -> { + return this.createIslandRequirementButton(button); + } + // Buttons for Inventory Requirements + case REQUIRED_ITEMS, REMOVE_ITEMS, ADD_IGNORED_META, REMOVE_IGNORED_META -> { + return this.createInventoryRequirementButton(button); + } + // Buttons for Other Requirements + case REQUIRED_EXPERIENCE, REMOVE_EXPERIENCE, REQUIRED_LEVEL, REQUIRED_MONEY, REMOVE_MONEY -> { + return this.createOtherRequirementButton(button); + } + // Buttons for Statistic Requirements + case STATISTIC, STATISTIC_BLOCKS, STATISTIC_ITEMS, STATISTIC_ENTITIES, STATISTIC_AMOUNT, REMOVE_STATISTIC -> { + return this.createStatisticRequirementButton(button); + } + // Default behaviour. + default -> { + return PanelItem.empty(); + } + } + } + + + /** + * This method creates buttons for island requirements menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createIslandRequirementButton(RequirementButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final IslandRequirements requirements = this.challenge.getRequirements(); + + switch (button) + { + case REQUIRED_ENTITIES -> { + if (requirements.getRequiredEntities().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + requirements.getRequiredEntities().forEach((entity, count) -> + description.add(this.user.getTranslation(reference + "list", + "[entity]", Utils.prettifyObject(entity, this.user), + "[number]", String.valueOf(count)))); + } + + icon = new ItemStack(Material.CREEPER_HEAD); + clickHandler = (panel, user, clickType, slot) -> { + ManageEntitiesPanel.open(this, requirements.getRequiredEntities()); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_ENTITIES -> { + description.add(this.user.getTranslation(reference + + (requirements.isRemoveEntities() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setRemoveEntities(!requirements.isRemoveEntities()); + this.build(); + return true; + }; + glow = requirements.isRemoveEntities(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case REQUIRED_BLOCKS -> { + if (requirements.getRequiredBlocks().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + requirements.getRequiredBlocks().forEach((block, count) -> + description.add(this.user.getTranslation(reference + "list", + "[block]", Utils.prettifyObject(block, this.user), + "[number]", String.valueOf(count)))); + } + + icon = new ItemStack(Material.STONE); + clickHandler = (panel, user, clickType, slot) -> { + ManageBlocksPanel.open(this, requirements.getRequiredBlocks()); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_BLOCKS -> { + description.add(this.user.getTranslation(reference + + (requirements.isRemoveBlocks() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setRemoveBlocks(!requirements.isRemoveBlocks()); + this.build(); + return true; + }; + glow = requirements.isRemoveBlocks(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case SEARCH_RADIUS -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getSearchRadius()))); + icon = new ItemStack(Material.COBBLESTONE_WALL); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setSearchRadius(number.intValue()); + } + + // reopen panel + this.build(); + }; + + int maxSearchDistance = + this.addon.getPlugin().getIWM().getAddon(this.world).map(gameModeAddon -> + gameModeAddon.getWorldSettings().getIslandDistance()).orElse(100); + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 1, + maxSearchDistance); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for inventory requirements menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createInventoryRequirementButton(RequirementButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final InventoryRequirements requirements = this.challenge.getRequirements(); + + switch (button) + { + case REQUIRED_ITEMS -> { + if (requirements.getRequiredItems().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + Utils.groupEqualItems(requirements.getRequiredItems(), requirements.getIgnoreMetaData()). + stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + ItemSelector.open(this.user, + requirements.getRequiredItems(), + (status, value) -> { + if (status) + { + requirements.setRequiredItems(value); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_ITEMS -> { + description.add(this.user.getTranslation(reference + + (requirements.isTakeItems() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setTakeItems(!requirements.isTakeItems()); + this.build(); + return true; + }; + glow = requirements.isTakeItems(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case ADD_IGNORED_META -> { + if (requirements.getIgnoreMetaData().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + requirements.getIgnoreMetaData().stream(). + sorted(Comparator.comparing(Material::name)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.GREEN_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (requirements.getRequiredItems().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + requirements.getRequiredItems().stream(). + map(ItemStack::getType). + forEach(collection::remove); + collection.addAll(requirements.getIgnoreMetaData()); + + if (Material.values().length == collection.size()) + { + // If there are no items anymore, then do not allow opening gui. + return true; + } + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + materials.addAll(requirements.getIgnoreMetaData()); + requirements.setIgnoreMetaData(new HashSet<>(materials)); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_IGNORED_META -> { + icon = new ItemStack(Material.RED_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (requirements.getIgnoreMetaData().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + collection.removeAll(requirements.getIgnoreMetaData()); + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + requirements.getIgnoreMetaData().removeAll(materials); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for other requirements menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createOtherRequirementButton(RequirementButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final OtherRequirements requirements = this.challenge.getRequirements(); + + switch (button) + { + case REQUIRED_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getRequiredExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setRequiredExperience(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + + (requirements.isTakeExperience() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setTakeExperience(!requirements.isTakeExperience()); + this.build(); + return true; + }; + glow = requirements.isTakeExperience(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case REQUIRED_LEVEL -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getRequiredIslandLevel()))); + icon = new ItemStack(this.addon.isLevelProvided() ? Material.BEACON : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setRequiredIslandLevel(number.longValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REQUIRED_MONEY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getRequiredMoney()))); + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setRequiredMoney(number.doubleValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Double.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_MONEY -> { + description.add(this.user.getTranslation(reference + + (requirements.isTakeMoney() ? "enabled" : "disabled"))); + + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.LEVER : Material.BARRIER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setTakeMoney(!requirements.isTakeMoney()); + this.build(); + return true; + }; + glow = requirements.isTakeMoney(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * Creates a button for statistic requirements. + * @param button Button that must be created. + * @return PanelItem button. + */ + private PanelItem createStatisticRequirementButton(RequirementButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final StatisticRequirements requirements = this.challenge.getRequirements(); + + switch (button) + { + case STATISTIC -> { + description.add(this.user.getTranslation(reference + "value", + "[statistic]", Utils.prettifyObject(requirements.getStatistic(), this.user))); + + icon = new ItemStack(requirements.getStatistic() == null ? Material.BARRIER : Material.PAPER); + clickHandler = (panel, user, clickType, slot) -> { + StatisticSelector.open(this.user, (status, statistic) -> { + if (status) + { + requirements.setStatistic(statistic); + requirements.setMaterial(null); + requirements.setEntity(null); + requirements.setAmount(0); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_AMOUNT -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getAmount()))); + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setAmount(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_STATISTIC -> { + description.add(this.user.getTranslation(reference + + (requirements.isReduceStatistic() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setReduceStatistic(!requirements.isReduceStatistic()); + this.build(); + return true; + }; + glow = requirements.isReduceStatistic(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case STATISTIC_BLOCKS -> { + description.add(this.user.getTranslation(reference + "value", + "[block]", Utils.prettifyObject(requirements.getMaterial(), this.user))); + + icon = requirements.getMaterial() == null ? + new ItemStack(Material.BARRIER) : + new ItemStack(requirements.getMaterial()); + clickHandler = (panel, user, clickType, slot) -> { + SingleBlockSelector.open(this.user, + SingleBlockSelector.Mode.BLOCKS, + (status, block) -> { + if (status) + { + requirements.setMaterial(block); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_ITEMS -> { + description.add(this.user.getTranslation(reference + "value", + "[item]", Utils.prettifyObject(requirements.getMaterial(), this.user))); + + icon = requirements.getMaterial() == null ? + new ItemStack(Material.BARRIER) : + new ItemStack(requirements.getMaterial()); + clickHandler = (panel, user, clickType, slot) -> { + SingleBlockSelector.open(this.user, + SingleBlockSelector.Mode.ITEMS, + (status, block) -> { + if (status) + { + requirements.setMaterial(block); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_ENTITIES -> { + description.add(this.user.getTranslation(reference + "value", + "[entity]", Utils.prettifyObject(requirements.getEntity(), this.user))); + + icon = requirements.getEntity() == null ? + new ItemStack(Material.BARRIER) : + new ItemStack(PanelUtils.getEntityEgg(requirements.getEntity())); + clickHandler = (panel, user, clickType, slot) -> { + SingleEntitySelector.open(this.user, + true, + (status, entity) -> { + if (status) + { + requirements.setEntity(entity); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for rewards menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createRewardButton(RewardButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case REWARD_TEXT -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + description.add(Util.translateColorCodes(this.challenge.getRewardText())); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setRewardText(String.join("\n", value)); + } + + this.build(); + }; + + if (!this.challenge.getRewardText().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-reward-text"), + user.getTranslation(Constants.CONVERSATIONS + "reward-text-changed")); + } + + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRewardText().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case REWARD_ITEMS -> { + + if (this.challenge.getRewardItems().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + Utils.groupEqualItems(this.challenge.getRewardItems(), this.challenge.getIgnoreRewardMetaData()). + stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + ItemSelector.open(this.user, + this.challenge.getRewardItems(), + (status, value) -> { + if (status) + { + this.challenge.setRewardItems(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getRewardExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setRewardExperience(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_MONEY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getRewardMoney()))); + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setRewardMoney(number.doubleValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Double.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_COMMANDS -> { + icon = new ItemStack(Material.COMMAND_BLOCK); + + description.add(this.user.getTranslation(reference + "value")); + description.addAll(this.challenge.getRewardCommands()); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setRewardCommands(value); + } + + this.build(); + }; + + if (!this.challenge.getRewardCommands().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-reward-commands"), + user.getTranslation(Constants.CONVERSATIONS + "reward-commands-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRewardCommands().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case REPEATABLE -> { + description.add(this.user.getTranslation(reference + + (this.challenge.isRepeatable() ? "enabled" : "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(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case REPEAT_COUNT -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getMaxTimes()))); + icon = new ItemStack(Material.COBBLESTONE_WALL); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setMaxTimes(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case COOL_DOWN -> { + description.add(this.user.getTranslation(reference + "value", + "[time]", + Utils.parseDuration(Duration.ofMillis(this.challenge.getTimeout()), this.user))); + icon = new ItemStack(Material.CLOCK); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setTimeout(number.longValue() * 1000); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-seconds"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REPEAT_REWARD_TEXT -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + description.add(Util.translateColorCodes(this.challenge.getRepeatRewardText())); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setRepeatRewardText(String.join("\n", value)); + } + + this.build(); + }; + + if (!this.challenge.getRepeatRewardText().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-repeat-reward-text"), + user.getTranslation(Constants.CONVERSATIONS + "repeat-reward-text-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRepeatRewardText().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case REPEAT_REWARD_ITEMS -> { + + if (this.challenge.getRepeatItemReward().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + Utils.groupEqualItems(this.challenge.getRepeatItemReward(), this.challenge.getIgnoreRewardMetaData()). + stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + ItemSelector.open(this.user, + this.challenge.getRewardItems(), + (status, value) -> { + if (status) + { + this.challenge.setRepeatItemReward(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REPEAT_REWARD_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getRepeatExperienceReward()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setRepeatExperienceReward(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REPEAT_REWARD_MONEY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getRepeatMoneyReward()))); + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_NUGGET : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setRepeatMoneyReward(number.doubleValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Double.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REPEAT_REWARD_COMMANDS -> { + icon = new ItemStack(Material.COMMAND_BLOCK); + + description.add(this.user.getTranslation(reference + "value")); + description.addAll(this.challenge.getRepeatRewardCommands()); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setRepeatRewardCommands(value); + } + + this.build(); + }; + + if (!this.challenge.getRepeatRewardCommands().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-repeat-reward-commands"), + user.getTranslation(Constants.CONVERSATIONS + "repeat-reward-commands-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRepeatRewardCommands().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case ADD_IGNORED_META -> { + if (this.challenge.getIgnoreRewardMetaData().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + this.challenge.getIgnoreRewardMetaData().stream(). + sorted(Comparator.comparing(Material::name)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.GREEN_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (this.challenge.getRewardItems().isEmpty() && + this.challenge.getRepeatItemReward().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + this.challenge.getRewardItems().stream(). + map(ItemStack::getType). + forEach(collection::remove); + this.challenge.getRepeatItemReward().stream(). + map(ItemStack::getType). + forEach(collection::remove); + collection.addAll(this.challenge.getIgnoreRewardMetaData()); + + if (Material.values().length == collection.size()) + { + // If there are no items anymore, then do not allow opening gui. + return true; + } + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + materials.addAll(this.challenge.getIgnoreRewardMetaData()); + this.challenge.setIgnoreRewardMetaData(new HashSet<>(materials)); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_IGNORED_META -> { + icon = new ItemStack(Material.RED_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (this.challenge.getIgnoreRewardMetaData().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + collection.removeAll(this.challenge.getIgnoreRewardMetaData()); + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + this.challenge.getIgnoreRewardMetaData().removeAll(materials); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + // --------------------------------------------------------------------- + // Section: Classes + // --------------------------------------------------------------------- + + + /** + * This class allows changing icon for Generator Tier + */ + private class IconChanger implements PanelListener + { + /** + * Process inventory click. If generator icon is selected and user clicks on item in his inventory, then change + * icon to the item from inventory. + * + * @param user the user + * @param event the event + */ + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // Handle icon changing + if (EditChallengePanel.this.selectedButton != null && + event.getCurrentItem() != null && + !event.getCurrentItem().getType().equals(Material.AIR) && + event.getRawSlot() > 44) + { + // set material and amount only. Other data should be removed. + + if (EditChallengePanel.this.selectedButton == Button.ICON) + { + EditChallengePanel.this.challenge.setIcon(event.getCurrentItem().clone()); + // Deselect icon + EditChallengePanel.this.selectedButton = null; + // Rebuild icon + EditChallengePanel.this.build(); + } + } + } + + + /** + * On inventory close. + * + * @param event the event + */ + @Override + public void onInventoryClose(InventoryCloseEvent event) + { + // Do nothing + } + + + /** + * Setup current listener. + */ + @Override + public void setup() + { + // Do nothing + } + } + + + // --------------------------------------------------------------------- + // 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, + DEPLOYED, + ICON, + DESCRIPTION, + ORDER, + ENVIRONMENT, + REMOVE_ON_COMPLETE, + } + + + /** + * Represents different rewards buttons that are used in menus. + */ + private enum RewardButton + { + REWARD_TEXT, + REWARD_ITEMS, + REWARD_EXPERIENCE, + REWARD_MONEY, + REWARD_COMMANDS, + + REPEATABLE, + REPEAT_COUNT, + COOL_DOWN, + + REPEAT_REWARD_TEXT, + REPEAT_REWARD_ITEMS, + REPEAT_REWARD_EXPERIENCE, + REPEAT_REWARD_MONEY, + REPEAT_REWARD_COMMANDS, + + ADD_IGNORED_META, + REMOVE_IGNORED_META, + } + + + /** + * Represents different requirement buttons that are used in menus. + */ + private enum RequirementButton + { + REQUIRED_ENTITIES, + REMOVE_ENTITIES, + REQUIRED_BLOCKS, + REMOVE_BLOCKS, + SEARCH_RADIUS, + REQUIRED_PERMISSIONS, + REQUIRED_ITEMS, + REMOVE_ITEMS, + ADD_IGNORED_META, + REMOVE_IGNORED_META, + REQUIRED_EXPERIENCE, + REMOVE_EXPERIENCE, + REQUIRED_LEVEL, + REQUIRED_MONEY, + REMOVE_MONEY, + STATISTIC, + STATISTIC_BLOCKS, + STATISTIC_ITEMS, + STATISTIC_ENTITIES, + STATISTIC_AMOUNT, + REMOVE_STATISTIC, + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * Variable holds challenge thats needs editing. + */ + private final Challenge challenge; + + + private Button selectedButton; + + /** + * Variable holds current active menu. + */ + private MenuType currentMenuType; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java deleted file mode 100644 index bd6b841..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java +++ /dev/null @@ -1,764 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.inventory.ItemStack; - -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.SelectBlocksGUI; -import world.bentobox.challenges.panel.util.SelectChallengeGUI; -import world.bentobox.challenges.panel.util.StringListGUI; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.utils.Utils; - - -/** - * This class contains all necessary elements to create Levels Edit GUI. - */ -public class EditLevelGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructors - // --------------------------------------------------------------------- - - - /** - * @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 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); - } - - - /** - * @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 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.title.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); - - // Save challenge level every time this gui is build. - // It will ensure that changes are stored in database. - this.addon.getChallengesManager().saveLevel(this.challengeLevel); - - 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(28, this.createButton(Button.CLOSED_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; - String description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (menuType) - { - case PROPERTIES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.properties"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.properties"); - 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.buttons.admin.challenges"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.challenges"); - 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.buttons.admin.rewards"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.rewards"); - 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 PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * 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(ChatColor.translateAlternateColorCodes('&', challenge.getFriendlyName())). - description(GuiUtils.stringSplit( - challenge.getDescription(), - this.addon.getChallengesSettings().getLoreLineLength())). - 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; - - final int lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - - switch (button) - { - case NAME: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.name"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.name-level")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", this.challengeLevel.getFriendlyName())); - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - - this.getFriendlyName(reply -> - { - if (reply != null) - { - this.challengeLevel.setFriendlyName(reply); - } - this.build(); - }, - this.user.getTranslation("challenges.gui.questions.admin.level-name"), - this.challengeLevel.getFriendlyName() - ); - - return true; - }; - glow = false; - break; - } - case ICON: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.icon"); - description = Collections.singletonList(this.user.getTranslation( - "challenges.gui.descriptions.admin.icon-level")); - icon = this.challengeLevel.getIcon(); - clickHandler = (panel, user, clickType, slot) -> { - - new SelectBlocksGUI(this.user, true, (status, materials) -> { - if (status) - { - materials.forEach(material -> - this.challengeLevel.setIcon(new ItemStack(material))); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case CLOSED_ICON: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.locked-icon"); - description = Collections.singletonList(this.user.getTranslation( - "challenges.gui.descriptions.admin.locked-icon")); - - boolean isNull = this.challengeLevel.getLockedIcon() == null; - - if (isNull) - { - icon = new ItemStack(Material.BARRIER); - } - else - { - icon = this.challengeLevel.getLockedIcon().clone(); - } - - clickHandler = (panel, user, clickType, slot) -> { - new SelectBlocksGUI(this.user, true, (status, materials) -> { - if (status) - { - materials.forEach(material -> - this.challengeLevel.setLockedIcon(new ItemStack(material))); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case UNLOCK_MESSAGE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.description"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.description")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", "|" + this.challengeLevel.getUnlockMessage())); - icon = new ItemStack(Material.WRITABLE_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, this.challengeLevel.getUnlockMessage(), lineLength, (status, value) -> { - if (status) - { - String singleLineMessage = value.stream(). - map(s -> s + "|"). - collect(Collectors.joining()); - - if (singleLineMessage.endsWith("|")) - { - singleLineMessage = singleLineMessage.substring(0, singleLineMessage.length() - 1); - } - - this.challengeLevel.setUnlockMessage(singleLineMessage); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case ORDER: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.order"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.order")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[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, lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setOrder(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case WAIVER_AMOUNT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.waiver-amount"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.waiver-amount")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[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, lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setWaiverAmount(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case REWARD_DESCRIPTION: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-text"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-text-level")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", "|" + this.challengeLevel.getRewardText())); - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, this.challengeLevel.getRewardText(), lineLength, (status, value) -> { - if (status) - { - String singleLineMessage = value.stream(). - map(s -> s + "|"). - collect(Collectors.joining()); - - if (singleLineMessage.endsWith("|")) - { - singleLineMessage = singleLineMessage.substring(0, singleLineMessage.length() - 1); - } - - this.challengeLevel.setRewardText(singleLineMessage); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_ITEM: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-items"); - - description = new ArrayList<>(this.challengeLevel.getRewardItems().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-items")); - - Utils.groupEqualItems(this.challengeLevel.getRewardItems()).forEach(itemStack -> - description.addAll(this.generateItemStackDescription(itemStack))); - - icon = new ItemStack(Material.CHEST); - clickHandler = (panel, user, clickType, slot) -> { - new ItemSwitchGUI(this.user, this.challengeLevel.getRewardItems(), lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setRewardItems(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_EXPERIENCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-experience"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-experience")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[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, lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setRewardExperience(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-money"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-money")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challengeLevel.getRewardMoney()))); - - icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, this.challengeLevel.getRewardMoney(), 0, lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setRewardMoney(value); - } - - this.build(); - }); - - return true; - }; - - glow = false; - break; - } - case REWARD_COMMANDS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-commands"); - description = new ArrayList<>(this.challengeLevel.getRewardCommands().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-commands")); - - for (String command : this.challengeLevel.getRewardCommands()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command)); - } - - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, this.challengeLevel.getRewardCommands(), lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setRewardCommands(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case ADD_CHALLENGE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.add-challenge"); - description = Collections.singletonList(this.user.getTranslation("challenges.gui.descriptions.admin.add-challenge")); - icon = new ItemStack(Material.WATER_BUCKET); - clickHandler = (panel, user, clickType, slot) -> { - ChallengesManager manager = this.addon.getChallengesManager(); - - // Get all challenge that is not in current level. - List challengeList = manager.getAllChallenges(this.world); - challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel)); - - // Generate descriptions for these challenges - Map> challengeDescriptionMap = challengeList.stream(). - collect(Collectors.toMap(challenge -> challenge, - challenge -> this.generateChallengeDescription(challenge, this.user.getPlayer()), - (a, b) -> b, - () -> new LinkedHashMap<>(challengeList.size()))); - - // Open select gui - new SelectChallengeGUI(this.user, challengeDescriptionMap, lineLength, (status, valueSet) -> { - if (status) - { - valueSet.forEach(challenge -> manager.addChallengeToLevel(challenge, this.challengeLevel)); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REMOVE_CHALLENGE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-challenge"); - description = Collections.singletonList(this.user.getTranslation("challenges.gui.descriptions.admin.remove-challenge")); - icon = new ItemStack(Material.LAVA_BUCKET); - clickHandler = (panel, user, clickType, slot) -> { - ChallengesManager manager = this.addon.getChallengesManager(); - - // Get all challenge that is in current level. - List challengeList = manager.getLevelChallenges(this.challengeLevel); - - // Generate descriptions for these challenges - Map> challengeDescriptionMap = challengeList.stream(). - collect(Collectors.toMap(challenge -> challenge, - challenge -> this.generateChallengeDescription(challenge, this.user.getPlayer()), - (a, b) -> b, - () -> new LinkedHashMap<>(challengeList.size()))); - - // Open select gui - new SelectChallengeGUI(this.user, challengeDescriptionMap, lineLength, (status, valueSet) -> { - if (status) - { - valueSet.forEach(challenge -> manager.removeChallengeFromLevel(challenge, this.challengeLevel)); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - default: - return null; - } - - - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * Represents different buttons that could be in menus. - */ - private enum Button - { - NAME, - ICON, - CLOSED_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/EditLevelPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java new file mode 100644 index 0000000..826e4c8 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java @@ -0,0 +1,998 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +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.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.managers.ChallengesManager; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.ItemSelector; +import world.bentobox.challenges.panel.util.ChallengeSelector; +import world.bentobox.challenges.panel.util.MultiBlockSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary elements to create Levels Edit GUI. + */ +public class EditLevelPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructors + // --------------------------------------------------------------------- + + + /** + * @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 challengeLevel ChallengeLevel that must be edited. + */ + private EditLevelPanel(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + ChallengeLevel challengeLevel) + { + super(addon, user, world, topLabel, permissionPrefix); + this.challengeLevel = challengeLevel; + this.currentMenuType = MenuType.PROPERTIES; + } + + + /** + * @param challengeLevel ChallengeLevel that must be edited. + */ + private EditLevelPanel(CommonPanel parentGUI, ChallengeLevel challengeLevel) + { + super(parentGUI); + this.challengeLevel = challengeLevel; + this.currentMenuType = MenuType.PROPERTIES; + } + + + /** + * Open the Challenges Level Edit GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + * @param level - level that needs editing + */ + public static void open(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + ChallengeLevel level) + { + new EditLevelPanel(addon, user, world, topLabel, permissionPrefix, level).build(); + } + + + /** + * Open the Challenges Level Edit GUI. + * + * @param panel - Parent Panel + * @param level - level that needs editing + */ + public static void open(CommonPanel panel, ChallengeLevel level) + { + new EditLevelPanel(panel, level).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + // Do nothing here. + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "edit-level", + "[level]", this.challengeLevel.getFriendlyName())); + + PanelUtils.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); + + // Save challenge level every time this gui is build. + // It will ensure that changes are stored in database. + this.addon.getChallengesManager().saveLevel(this.challengeLevel); + + panelBuilder.build(); + } + + + /** + * This class populate LevelsEditGUI with main level settings. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildMainPropertiesPanel(PanelBuilder panelBuilder) + { + panelBuilder.listener(new IconChanger()); + + panelBuilder.item(10, this.createButton(Button.NAME)); + + panelBuilder.item(19, this.createButton(Button.ICON)); + panelBuilder.item(28, this.createButton(Button.LOCKED_ICON)); + panelBuilder.item(22, this.createButton(Button.DESCRIPTION)); + 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_TEXT)); + panelBuilder.item(21, this.createButton(Button.REWARD_COMMANDS)); + + panelBuilder.item(13, this.createButton(Button.REWARD_ITEMS)); + panelBuilder.item(22, this.createButton(Button.REWARD_EXPERIENCE)); + panelBuilder.item(31, this.createButton(Button.REWARD_MONEY)); + + if (!this.challengeLevel.getRewardItems().isEmpty()) + { + panelBuilder.item(33, this.createButton(Button.ADD_IGNORED_META)); + } + + if (!this.challengeLevel.getIgnoreRewardMetaData().isEmpty()) + { + panelBuilder.item(34, this.createButton(Button.REMOVE_IGNORED_META)); + } + } + + + /** + * 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).stream(). + filter(challenge -> this.searchString.isBlank() || + challenge.getFriendlyName().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getUniqueId().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getChallengeType().name().toLowerCase().contains(this.searchString)). + collect(Collectors.toList()); + + this.populateElements(panelBuilder, challengeList); + + panelBuilder.item(39, this.createButton(Button.ADD_CHALLENGES)); + panelBuilder.item(41, this.createButton(Button.REMOVE_CHALLENGES)); + } + + + // --------------------------------------------------------------------- + // 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) + { + final String reference = Constants.BUTTON + menuType.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (menuType) + { + case PROPERTIES -> { + 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); + } + case CHALLENGES -> { + icon = new ItemStack(Material.RAIL); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.CHALLENGES; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.CHALLENGES); + } + case REWARDS -> { + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REWARDS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REWARDS); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates 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. + */ + @Override + protected PanelItem createElementButton(Challenge challenge) + { + return new PanelItemBuilder(). + name(Util.translateColorCodes(challenge.getFriendlyName())). + description(this.generateChallengeDescription(challenge, null)). + description(""). + description(this.user.getTranslation(Constants.TIPS + "click-to-edit")). + icon(challenge.getIcon()). + clickHandler((panel, user, clickType, slot) -> { + // Open challenges edit screen. + EditChallengePanel.open(this, challenge); + 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) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case REWARD_TEXT -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + description.add(Util.translateColorCodes(this.challengeLevel.getRewardText())); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challengeLevel.setRewardText(String.join("\n", value)); + } + + this.build(); + }; + + if (!this.challengeLevel.getRewardText().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-reward-text"), + user.getTranslation(Constants.CONVERSATIONS + "reward-text-changed")); + } + + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challengeLevel.getRewardText().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case REWARD_ITEMS -> { + + if (this.challengeLevel.getRewardItems().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + Utils.groupEqualItems(this.challengeLevel.getRewardItems(), this.challengeLevel.getIgnoreRewardMetaData()). + stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + ItemSelector.open(this.user, + this.challengeLevel.getRewardItems(), + (status, value) -> { + if (status) + { + this.challengeLevel.setRewardItems(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challengeLevel.getRewardExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challengeLevel.setRewardExperience(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_MONEY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challengeLevel.getRewardMoney()))); + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challengeLevel.setRewardMoney(number.doubleValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Double.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_COMMANDS -> { + icon = new ItemStack(Material.COMMAND_BLOCK); + + description.add(this.user.getTranslation(reference + "value")); + description.addAll(this.challengeLevel.getRewardCommands()); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challengeLevel.setRewardCommands(value); + } + + this.build(); + }; + + if (!this.challengeLevel.getRewardCommands().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-reward-commands"), + user.getTranslation(Constants.CONVERSATIONS + "reward-commands-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challengeLevel.getRewardCommands().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case ADD_IGNORED_META -> { + if (this.challengeLevel.getIgnoreRewardMetaData().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + this.challengeLevel.getIgnoreRewardMetaData().stream(). + sorted(Comparator.comparing(Material::name)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.GREEN_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (this.challengeLevel.getRewardItems().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + this.challengeLevel.getRewardItems().stream(). + map(ItemStack::getType). + forEach(collection::remove); + collection.addAll(this.challengeLevel.getIgnoreRewardMetaData()); + + if (Material.values().length == collection.size()) + { + // If all materials are blocked, then do not allow to open gui. + return true; + } + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + materials.addAll(this.challengeLevel.getIgnoreRewardMetaData()); + this.challengeLevel.setIgnoreRewardMetaData(new HashSet<>(materials)); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_IGNORED_META -> { + icon = new ItemStack(Material.RED_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (this.challengeLevel.getIgnoreRewardMetaData().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + collection.removeAll(this.challengeLevel.getIgnoreRewardMetaData()); + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + this.challengeLevel.getIgnoreRewardMetaData().removeAll(materials); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + case NAME -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NAME, this.challengeLevel.getFriendlyName())); + + icon = new ItemStack(Material.NAME_TAG); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer consumer = value -> + { + if (value != null) + { + this.challengeLevel.setFriendlyName(value); + } + + this.build(); + }; + + // start conversation + ConversationUtils.createStringInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-name"), + user.getTranslation(Constants.CONVERSATIONS + "name-changed")); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case DESCRIPTION -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + description.add(Util.translateColorCodes(this.challengeLevel.getUnlockMessage())); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challengeLevel.setUnlockMessage(String.join("\n", value)); + } + + this.build(); + }; + + if (!this.challengeLevel.getUnlockMessage().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-description"), + user.getTranslation(Constants.CONVERSATIONS + "description-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challengeLevel.getUnlockMessage().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case ICON, LOCKED_ICON -> { + icon = button == Button.LOCKED_ICON ? + this.challengeLevel.getLockedIcon() : + this.challengeLevel.getIcon(); + + clickHandler = (panel, user, clickType, i) -> + { + this.selectedButton = button; + this.build(); + return true; + }; + + if (this.selectedButton != button) + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-on-item")); + } + + glow = this.selectedButton == button; + } + case ORDER -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challengeLevel.getOrder()))); + + icon = new ItemStack(Material.HOPPER, Math.max(1, this.challengeLevel.getOrder())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challengeLevel.setOrder(number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case WAIVER_AMOUNT -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challengeLevel.getWaiverAmount()))); + + icon = new ItemStack(Material.HOPPER, Math.max(1, this.challengeLevel.getWaiverAmount())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challengeLevel.setWaiverAmount(number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case ADD_CHALLENGES -> { + icon = new ItemStack(Material.WATER_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ChallengesManager manager = this.addon.getChallengesManager(); + + // Get all challenge that is not in current level. + List challengeList = manager.getAllChallenges(this.world); + challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel)); + + // Generate descriptions for these challenges + Map> challengeDescriptionMap = challengeList.stream(). + collect(Collectors.toMap(challenge -> challenge, + challenge -> this.generateChallengeDescription(challenge, null), + (a, b) -> b, + () -> new LinkedHashMap<>(challengeList.size()))); + + // Open select gui + ChallengeSelector.open(this.user, + Material.BLUE_STAINED_GLASS_PANE, + challengeDescriptionMap, + (status, valueSet) -> { + if (status) + { + valueSet.forEach(challenge -> + manager.addChallengeToLevel(challenge, this.challengeLevel)); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_CHALLENGES -> { + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ChallengesManager manager = this.addon.getChallengesManager(); + + // Get all challenge that is in current level. + List challengeList = manager.getLevelChallenges(this.challengeLevel); + + // Generate descriptions for these challenges + Map> challengeDescriptionMap = challengeList.stream(). + collect(Collectors.toMap(challenge -> challenge, + challenge -> this.generateChallengeDescription(challenge, null), + (a, b) -> b, + () -> new LinkedHashMap<>(challengeList.size()))); + + // Open select gui + ChallengeSelector.open(this.user, + Material.RED_STAINED_GLASS_PANE, + challengeDescriptionMap, + (status, valueSet) -> { + if (status) + { + valueSet.forEach(challenge -> + manager.removeChallengeFromLevel(challenge, this.challengeLevel)); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + // --------------------------------------------------------------------- + // Section: Classes + // --------------------------------------------------------------------- + + + /** + * This class allows changing icon for Generator Tier + */ + private class IconChanger implements PanelListener + { + /** + * Process inventory click. If generator icon is selected and user clicks on item in his inventory, then change + * icon to the item from inventory. + * + * @param user the user + * @param event the event + */ + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // Handle icon changing + if (EditLevelPanel.this.selectedButton != null && + event.getCurrentItem() != null && + !event.getCurrentItem().getType().equals(Material.AIR) && + event.getRawSlot() > 44) + { + // set material and amount only. Other data should be removed. + + if (EditLevelPanel.this.selectedButton == Button.ICON) + { + EditLevelPanel.this.challengeLevel.setIcon(event.getCurrentItem().clone()); + // Deselect icon + EditLevelPanel.this.selectedButton = null; + // Rebuild icon + EditLevelPanel.this.build(); + } + else if (EditLevelPanel.this.selectedButton == Button.LOCKED_ICON) + { + EditLevelPanel.this.challengeLevel.setLockedIcon(event.getCurrentItem().clone()); + // Deselect icon + EditLevelPanel.this.selectedButton = null; + // Rebuild icon + EditLevelPanel.this.build(); + } + } + } + + + /** + * On inventory close. + * + * @param event the event + */ + @Override + public void onInventoryClose(InventoryCloseEvent event) + { + // Do nothing + } + + + /** + * Setup current listener. + */ + @Override + public void setup() + { + // Do nothing + } + } + + + // --------------------------------------------------------------------- + // Section: Enums + // --------------------------------------------------------------------- + + + /** + * Represents different buttons that could be in menus. + */ + private enum Button + { + NAME, + ICON, + LOCKED_ICON, + DESCRIPTION, + ORDER, + WAIVER_AMOUNT, + + REWARD_TEXT, + REWARD_ITEMS, + REWARD_EXPERIENCE, + REWARD_MONEY, + REWARD_COMMANDS, + + ADD_IGNORED_META, + REMOVE_IGNORED_META, + + ADD_CHALLENGES, + REMOVE_CHALLENGES + } + + + /** + * 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 final ChallengeLevel challengeLevel; + + /** + * Variable holds current active menu. + */ + private MenuType currentMenuType; + + private Button selectedButton; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java deleted file mode 100644 index 860de1a..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java +++ /dev/null @@ -1,638 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import org.bukkit.Material; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.Nullable; - -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.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.config.SettingsUtils.ChallengeLore; -import world.bentobox.challenges.config.SettingsUtils.LevelLore; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class allows to change Input ItemStacks to different ItemStacks. - */ -public class EditLoreGUI extends CommonGUI -{ - public EditLoreGUI(CommonGUI parent, LoreType loreType) - { - super(parent); - - this.lore = loreType; - this.activeValues = new ArrayList<>(); - - switch (this.lore) - { - case CHALLENGES: - - for (ChallengeLore lore : this.addon.getChallengesSettings().getChallengeLoreMessage()) - { - this.activeValues.add(lore.name()); - } - - break; - case LEVELS: - - for (LevelLore lore : this.addon.getChallengesSettings().getLevelLoreMessage()) - { - this.activeValues.add(lore.name()); - } - - break; - } - } - - - /** - * This is static call method for easier GUI opening. - * @param parent Parent GUI. - * @param loreType loreType that will be edited. - */ - public static void open(CommonGUI parent, LoreType loreType) - { - new EditLoreGUI(parent, loreType).build(); - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * This method builds panel that allows to change given number value. - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(this.user.getTranslation("challenges.gui.title.admin.lore-edit")). - user(this.user). - listener(new CustomPanelListener()); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - // Define all active buttons - panelBuilder.item(1, this.getButton(Button.SAVE)); - - panelBuilder.item(3, this.getButton(Button.ADD)); - panelBuilder.item(4, this.getButton(Button.REMOVE)); - - // TODO: Need 2 View Buttons - // One for closes / One for opened. - // panelBuilder.item(6, this.getButton(Button.VIEW)); - - panelBuilder.item(44, this.returnButton); - - // necessary as I have a border around this GUI - int currentIndex = 10; - - // Only 21 elements will be displayed. On porpoise! - for (int i = 0; i < this.activeValues.size() || i > 21; i++) - { - panelBuilder.item(currentIndex++, this.getLoreButton(this.activeValues.get(i))); - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - } - - 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.admin.save"); - description = Collections.emptyList(); - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - - switch (this.lore) - { - case CHALLENGES: - { - List lore = this.activeValues.stream(). - map(ChallengeLore::valueOf). - collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size()))); - - this.addon.getChallengesSettings().setChallengeLoreMessage(lore); - - break; - } - case LEVELS: - { - List lore = this.activeValues.stream(). - map(LevelLore::valueOf). - collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size()))); - - this.addon.getChallengesSettings().setLevelLoreMessage(lore); - - break; - } - } - - // Save and return to parent gui. - this.parentGUI.build(); - - return true; - }; - break; - } - case ADD: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.add"); - description = Collections.emptyList(); - icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - new AddLoreElementGUI(element -> { - this.activeValues.add(element); - this.build(); - }); - - return true; - }; - - break; - } - case REMOVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-selected"); - description = Collections.emptyList(); - icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - new RemoveLoreElementGUI((element, index) -> { - if (this.activeValues.get(index).equals(element)) - { - this.activeValues.remove(element); - } - - this.build(); - }); - - return true; - }; - - break; - } - case VIEW: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.view"); - description = Collections.emptyList(); - icon = new ItemStack(Material.YELLOW_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - return true; - }; - - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - glow(false). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates button for lore element. - * @param loreElement String that represents current lore element. - * @return PanelItem. - */ - @Nullable - private PanelItem getLoreButton(String loreElement) - { - switch (this.lore) - { - case CHALLENGES: - return this.getChallengeLoreButton(loreElement); - case LEVELS: - return this.getLevelLoreButton(loreElement); - default: - // this should never happen! - return null; - } - } - - - /** - * This method creates button for challenge lore element. - * @param loreElement String that represents current challenge lore element. - * @return PanelItem. - */ - private PanelItem getChallengeLoreButton(String loreElement) - { - Material icon; - String name = loreElement; - List description = new ArrayList<>(); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "lore." + loreElement.toLowerCase())); - - PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> true; - - switch (ChallengeLore.valueOf(loreElement)) - { - case LEVEL: - { - icon = Material.DIRT; - break; - } - case STATUS: - { - icon = Material.LEVER; - break; - } - case COUNT: - { - icon = Material.REPEATER; - break; - } - case DESCRIPTION: - { - icon = Material.WRITTEN_BOOK; - break; - } - case WARNINGS: - { - icon = Material.LAVA_BUCKET; - break; - } - case ENVIRONMENT: - { - icon = Material.GLASS; - break; - } - case REQUIREMENTS: - { - icon = Material.HOPPER; - break; - } - case REWARD_TEXT: - { - icon = Material.PAPER; - break; - } - case REWARD_OTHER: - { - icon = Material.CHEST; - break; - } - case REWARD_ITEMS: - { - icon = Material.TRAPPED_CHEST; - break; - } - case REWARD_COMMANDS: - { - icon = Material.COMMAND_BLOCK; - break; - } - default: - { - icon = Material.BARRIER; - break; - } - } - - return new PanelItemBuilder(). - name(name). - icon(icon). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - clickHandler(clickHandler). - glow(false). - build(); - } - - - /** - * This method creates button for challenge level lore element. - * @param loreElement String that represents current challenge level lore element. - * @return PanelItem. - */ - private PanelItem getLevelLoreButton(String loreElement) - { - Material icon; - String name = loreElement; - List description = new ArrayList<>(); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "lore." + loreElement.toLowerCase())); - - PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> true; - - switch (LevelLore.valueOf(loreElement)) - { - case LEVEL_STATUS: - { - icon = Material.DIRT; - break; - } - case CHALLENGE_COUNT: - { - icon = Material.REPEATER; - break; - } - case UNLOCK_MESSAGE: - { - icon = Material.WRITTEN_BOOK; - break; - } - case WAIVER_AMOUNT: - { - icon = Material.COMPARATOR; - break; - } - case LEVEL_REWARD_TEXT: - { - icon = Material.PAPER; - break; - } - case LEVEL_REWARD_OTHER: - { - icon = Material.CHEST; - break; - } - case LEVEL_REWARD_ITEMS: - { - icon = Material.TRAPPED_CHEST; - break; - } - case LEVEL_REWARD_COMMANDS: - { - icon = Material.COMMAND_BLOCK; - break; - } - default: - { - icon = Material.BARRIER; - break; - } - } - - return new PanelItemBuilder(). - name(name). - icon(icon). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - clickHandler(clickHandler). - glow(false). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Select GUI - // --------------------------------------------------------------------- - - - /** - * This class opens new GUI that add an element from all available lore values. - */ - private class AddLoreElementGUI - { - private AddLoreElementGUI(Consumer selectedElement) - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(EditLoreGUI.this.user.getTranslation("challenges.gui.title.admin.lore-add")). - user(EditLoreGUI.this.user); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - int currentIndex = 10; - - List values = new ArrayList<>(); - - // Populate list with all elements. - switch (EditLoreGUI.this.lore) - { - case CHALLENGES: - for (ChallengeLore value : ChallengeLore.values()) - { - values.add(value.name()); - } - break; - case LEVELS: - for (LevelLore value : LevelLore.values()) - { - values.add(value.name()); - } - break; - } - - for (String value : values) - { - PanelItem item = EditLoreGUI.this.getLoreButton(value); - if (item != null) { - item.setClickHandler((panel, user1, clickType, slot) -> { - selectedElement.accept(value); - return true; - }); - - panelBuilder.item(currentIndex++, item); - - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - - // Just in case. Should never occur. - if (currentIndex > 35) - { - break; - } - } - } - - panelBuilder.build(); - } - } - - - /** - * This class opens new GUI that remove an element from all available lore values. - */ - private class RemoveLoreElementGUI - { - private RemoveLoreElementGUI(BiConsumer selectedElement) - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(EditLoreGUI.this.user.getTranslation("challenges.gui.title.admin.lore-remove")). - user(EditLoreGUI.this.user); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - int currentIndex = 10; - - List values = EditLoreGUI.this.activeValues; - - for (int i = 0; i < values.size(); i++) - { - final int counter = i; - - String value = values.get(counter); - PanelItem item = EditLoreGUI.this.getLoreButton(value); - if (item != null) { - item.setClickHandler((panel, user1, clickType, slot) -> { - selectedElement.accept(value, counter); - return true; - }); - - panelBuilder.item(currentIndex++, item); - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - - // Just in case. Should never occur. - if (currentIndex > 35) - { - break; - } - } - } - - panelBuilder.build(); - } - } - - - // --------------------------------------------------------------------- - // Section: Private classes - // --------------------------------------------------------------------- - - - /** - * 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 || - event.getRawSlot() < 35 || - event.getRawSlot() % 9 == 0 || - event.getRawSlot() % 9 == 8); - } - } - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * This enum holds all button values in current gui. - */ - private enum Button - { - SAVE, - ADD, - REMOVE, - VIEW, - RETURN - } - - - /** - * This enum holds which Lore is edited with current GUI. - */ - public enum LoreType - { - CHALLENGES, - LEVELS, - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * Lore that will be edited with current GUI. - */ - private final LoreType lore; - - /** - * List of lore elements that are currently enabled. - */ - private List activeValues; - - - // --------------------------------------------------------------------- - // Section: Constants - // --------------------------------------------------------------------- - - - private final static String REFERENCE_DESCRIPTION = "challenges.gui.descriptions.admin."; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java deleted file mode 100644 index c808dac..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java +++ /dev/null @@ -1,606 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.inventory.ItemStack; - -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.config.Settings; -import world.bentobox.challenges.config.SettingsUtils.GuiMode; -import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; -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; -import world.bentobox.challenges.utils.Utils; - - -/** - * This Class creates GUI that allows to change Challenges Addon Settings via in-game - * menu. - */ -public class EditSettingsGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructors - // --------------------------------------------------------------------- - - - /** - * @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 EditSettingsGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix) - { - this(addon, world, user, topLabel, permissionPrefix, null); - } - - - /** - * @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 EditSettingsGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - super(addon, world, user, topLabel, permissionPrefix, parentGUI); - this.settings = this.addon.getChallengesSettings(); - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.settings-title")); - - GuiUtils.fillBorder(panelBuilder); - - panelBuilder.item(10, this.getSettingsButton(Button.ENABLE_TITLE)); - - if (this.settings.isShowCompletionTitle()) - { - panelBuilder.item(19, this.getSettingsButton(Button.TITLE_SHOWTIME)); - } - - panelBuilder.item(28, this.getSettingsButton(Button.BROADCAST)); - - panelBuilder.item(11, this.getSettingsButton(Button.GLOW_COMPLETED)); - panelBuilder.item(20, this.getSettingsButton(Button.REMOVE_COMPLETED)); - panelBuilder.item(29, this.getSettingsButton(Button.VISIBILITY_MODE)); - - panelBuilder.item(21, this.getSettingsButton(Button.LOCKED_LEVEL_ICON)); - panelBuilder.item(30, this.getSettingsButton(Button.FREE_AT_TOP)); - - panelBuilder.item(22, this.getSettingsButton(Button.GAMEMODE_GUI)); - - if (this.settings.isUseCommonGUI()) - { - // This should be active only when single gui is enabled. - panelBuilder.item(31, this.getSettingsButton(Button.GAMEMODE_GUI_VIEW_MODE)); - } - - panelBuilder.item(14, this.getSettingsButton(Button.LORE_LENGTH)); - panelBuilder.item(23, this.getSettingsButton(Button.CHALLENGE_LORE)); - panelBuilder.item(32, this.getSettingsButton(Button.LEVEL_LORE)); - - panelBuilder.item(24, this.getSettingsButton(Button.HISTORY)); - - if (this.settings.isStoreHistory()) - { - panelBuilder.item(33, this.getSettingsButton(Button.PURGE_HISTORY)); - } - - panelBuilder.item(25, this.getSettingsButton(Button.RESET_CHALLENGES)); - panelBuilder.item(34, this.getSettingsButton(Button.STORE_MODE)); - - // Return Button - panelBuilder.item(44, this.returnButton); - - // Save Settings every time this GUI is created. It will avoid issues with - // Overwritten setting after server stop. - this.addon.saveSettings(); - - panelBuilder.build(); - } - - - private PanelItem getSettingsButton(Button button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case RESET_CHALLENGES: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reset-on-new")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isResetChallenges() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.reset-on-new"); - icon = new ItemStack(Material.LAVA_BUCKET); - - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setResetChallenges( - !this.settings.isResetChallenges()); - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - - glow = this.settings.isResetChallenges(); - break; - } - case BROADCAST: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.broadcast")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isBroadcastMessages() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - description = new ArrayList<>(2); - - name = this.user.getTranslation("challenges.gui.buttons.admin.broadcast"); - icon = new ItemStack(Material.JUKEBOX); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setBroadcastMessages( - !this.settings.isBroadcastMessages()); - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isBroadcastMessages(); - - break; - } - case REMOVE_COMPLETED: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-completed")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isRemoveCompleteOneTimeChallenges() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-completed"); - icon = new ItemStack(Material.MAGMA_BLOCK); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setRemoveCompleteOneTimeChallenges( - !this.settings.isRemoveCompleteOneTimeChallenges()); - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isRemoveCompleteOneTimeChallenges(); - - break; - } - case LORE_LENGTH: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.line-length")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.settings.getLoreLineLength()))); - name = this.user.getTranslation("challenges.gui.buttons.admin.line-length"); - icon = new ItemStack(Material.ANVIL); - clickHandler = (panel, user1, clickType, i) -> { - new NumberGUI(this.user, - this.settings.getLoreLineLength(), - 0, - this.settings.getLoreLineLength(), - (status, value) -> { - if (status) - { - this.settings.setLoreLineLength(value); - } - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - }); - - return true; - }; - glow = false; - break; - } - case LEVEL_LORE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.level-lore")); - name = this.user.getTranslation("challenges.gui.buttons.admin.level-lore"); - icon = new ItemStack(Material.MAP); - clickHandler = (panel, user1, clickType, i) -> { - - EditLoreGUI.open(this, EditLoreGUI.LoreType.LEVELS); - - return true; - }; - glow = false; - break; - } - case CHALLENGE_LORE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.challenge-lore")); - name = this.user.getTranslation("challenges.gui.buttons.admin.challenge-lore"); - icon = new ItemStack(Material.PAPER); - clickHandler = (panel, user1, clickType, i) -> { - - EditLoreGUI.open(this, EditLoreGUI.LoreType.CHALLENGES); - - return true; - }; - glow = false; - break; - } - case FREE_AT_TOP: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.free-at-top")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isAddCompletedGlow() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.free-at-top"); - icon = new ItemStack(Material.FILLED_MAP); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setFreeChallengesFirst(!this.settings.isFreeChallengesFirst()); - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isFreeChallengesFirst(); - break; - } - case GLOW_COMPLETED: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.glow")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isAddCompletedGlow() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - - name = this.user.getTranslation("challenges.gui.buttons.admin.glow"); - icon = new ItemStack(Material.GLOWSTONE); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setAddCompletedGlow(!this.settings.isAddCompletedGlow()); - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isAddCompletedGlow(); - break; - } - case GAMEMODE_GUI_VIEW_MODE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.gui-view-mode")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST) ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.gui-view-mode"); - icon = new ItemStack(Material.STONE_BUTTON); - clickHandler = (panel, user1, clickType, i) -> { - - if (this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST)) - { - this.settings.setUserGuiMode(GuiMode.CURRENT_WORLD); - } - else - { - this.settings.setUserGuiMode(GuiMode.GAMEMODE_LIST); - } - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST); - break; - } - case GAMEMODE_GUI: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.gui-mode")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isUseCommonGUI() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.gui-mode"); - icon = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setUseCommonGUI(!this.settings.isUseCommonGUI()); - // We cannot use single item changing as this option enabling/disabling will change other - // option visibility. - this.build(); - return true; - }; - glow = this.settings.isUseCommonGUI(); - break; - } - case HISTORY: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.history-store")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isStoreHistory() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.history-store"); - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setStoreHistory(!this.settings.isStoreHistory()); - - // Need to rebuild all as new buttons will show up. - this.build(); - return true; - }; - glow = this.settings.isStoreHistory(); - break; - } - case PURGE_HISTORY: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.history-lifespan")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.settings.getLifeSpan()))); - name = this.user.getTranslation("challenges.gui.buttons.admin.history-lifespan"); - icon = new ItemStack(Material.FLINT_AND_STEEL); - clickHandler = (panel, user1, clickType, i) -> { - new NumberGUI(this.user, - this.settings.getLifeSpan(), - 0, - this.settings.getLoreLineLength(), - (status, value) -> { - if (status) - { - this.settings.setLifeSpan(value); - } - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - }); - - return true; - }; - glow = false; - break; - } - case STORE_MODE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.island-store")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isStoreAsIslandData() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.island-store"); - icon = new ItemStack(Material.GRASS_BLOCK); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setStoreAsIslandData(!this.settings.isStoreAsIslandData()); - // TODO: Data Migration must be added here. - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isStoreAsIslandData(); - break; - } - case LOCKED_LEVEL_ICON: - { - description = new ArrayList<>(1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.default-locked-icon")); - name = this.user.getTranslation("challenges.gui.buttons.admin.default-locked-icon"); - icon = this.settings.getLockedLevelIcon(); - clickHandler = (panel, user, clickType, slot) -> { - - new SelectBlocksGUI(this.user, true, (status, materials) -> { - if (status) - { - materials.forEach(material -> - this.settings.setLockedLevelIcon(new ItemStack(material))); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case ENABLE_TITLE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.title-enable")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isShowCompletionTitle() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.title-enable"); - icon = new ItemStack(Material.OAK_SIGN); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setShowCompletionTitle(!this.settings.isShowCompletionTitle()); - - // Need to rebuild all as new buttons will show up. - this.build(); - return true; - }; - glow = this.settings.isShowCompletionTitle(); - break; - } - case TITLE_SHOWTIME: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.title-showtime")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.settings.getTitleShowtime()))); - name = this.user.getTranslation("challenges.gui.buttons.admin.title-showtime"); - icon = new ItemStack(Material.CLOCK); - clickHandler = (panel, user1, clickType, i) -> { - new NumberGUI(this.user, - this.settings.getTitleShowtime(), - 0, - this.settings.getLoreLineLength(), - (status, value) -> { - if (status) - { - this.settings.setTitleShowtime(value); - } - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - }); - - return true; - }; - glow = false; - break; - } - case VISIBILITY_MODE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.visibility-mode"); - - List values = new ArrayList<>(5); - values.add(this.user.getTranslation("challenges.gui.descriptions.admin.visibility-mode")); - - values.add((this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.visibility.visible")); - values.add((this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.visibility.hidden")); - values.add((this.settings.getVisibilityMode().equals(VisibilityMode.TOGGLEABLE) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.visibility.toggleable")); - - values.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]",this.settings.getVisibilityMode().name())); - - description = values; - - if (this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE)) - { - icon = new ItemStack(Material.OAK_PLANKS); - } - else if (this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN)) - { - icon = new ItemStack(Material.OAK_SLAB); - } - else - { - icon = new ItemStack(Material.OAK_BUTTON); - } - - clickHandler = (panel, user, clickType, slot) -> { - if (clickType.isRightClick()) - { - this.settings.setVisibilityMode( - Utils.getPreviousValue(VisibilityMode.values(), - this.settings.getVisibilityMode())); - } - else - { - this.settings.setVisibilityMode( - Utils.getNextValue(VisibilityMode.values(), - this.settings.getVisibilityMode())); - } - - // Rebuild just this icon - panel.getInventory().setItem(slot, - this.getSettingsButton(button).getItem()); - - return true; - }; - glow = false; - break; - } - default: - return new PanelItemBuilder().build(); - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.settings.getLoreLineLength())). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * This enum holds all settings buttons that must have been displayed in this panel. - */ - private enum Button - { - RESET_CHALLENGES, - BROADCAST, - REMOVE_COMPLETED, - LORE_LENGTH, - LEVEL_LORE, - CHALLENGE_LORE, - FREE_AT_TOP, - GAMEMODE_GUI_VIEW_MODE, - GAMEMODE_GUI, - HISTORY, - PURGE_HISTORY, - STORE_MODE, - GLOW_COMPLETED, - LOCKED_LEVEL_ICON, - ENABLE_TITLE, - TITLE_SHOWTIME, - - /** - * This allows to switch between different challenges visibility modes. - */ - VISIBILITY_MODE - } - - - /** - * This allows faster access to challenges settings object. - */ - private Settings settings; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java new file mode 100644 index 0000000..3d0eed9 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java @@ -0,0 +1,576 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +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.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.config.SettingsUtils.GuiMode; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This Class creates GUI that allows to change Challenges Addon Settings via in-game + * menu. + */ +public class EditSettingsPanel extends CommonPanel +{ + // --------------------------------------------------------------------- + // Section: Constructors + // --------------------------------------------------------------------- + + + /** + * @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.) + */ + private EditSettingsPanel(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + this.settings = this.addon.getChallengesSettings(); + } + + + /** + * @param parentGUI Parent GUI. + */ + private EditSettingsPanel(CommonPanel parentGUI) + { + super(parentGUI); + this.settings = this.addon.getChallengesSettings(); + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + new EditSettingsPanel(addon, user, world, topLabel, permissionPrefix).build(); + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI) + { + new EditSettingsPanel(parentGUI).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "settings")); + + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(10, this.getSettingsButton(Button.SHOW_TITLE)); + + if (this.settings.isShowCompletionTitle()) + { + panelBuilder.item(19, this.getSettingsButton(Button.TITLE_SHOWTIME)); + } + + panelBuilder.item(28, this.getSettingsButton(Button.BROADCAST)); + + panelBuilder.item(11, this.getSettingsButton(Button.GLOW_COMPLETED)); + panelBuilder.item(20, this.getSettingsButton(Button.REMOVE_COMPLETED)); + panelBuilder.item(29, this.getSettingsButton(Button.VISIBILITY_MODE)); + + panelBuilder.item(21, this.getSettingsButton(Button.LOCKED_LEVEL_ICON)); + + panelBuilder.item(22, this.getSettingsButton(Button.GAMEMODE_GUI)); + + if (this.settings.isUseCommonGUI()) + { + // This should be active only when single gui is enabled. + panelBuilder.item(31, this.getSettingsButton(Button.ACTIVE_WORLD_LIST)); + } + + panelBuilder.item(24, this.getSettingsButton(Button.STORE_HISTORY)); + + if (this.settings.isStoreHistory()) + { + panelBuilder.item(33, this.getSettingsButton(Button.PURGE_HISTORY)); + } + + panelBuilder.item(25, this.getSettingsButton(Button.RESET_ON_NEW)); + panelBuilder.item(34, this.getSettingsButton(Button.DATA_PER_ISLAND)); + + // Return Button + panelBuilder.item(44, this.returnButton); + panelBuilder.listener(new IconChanger()); + panelBuilder.build(); + } + + + private PanelItem getSettingsButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case RESET_ON_NEW -> { + description.add(this.user.getTranslation(reference + + (this.settings.isResetChallenges() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setResetChallenges(!this.settings.isResetChallenges()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isResetChallenges(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case BROADCAST -> { + description.add(this.user.getTranslation(reference + + (this.settings.isBroadcastMessages() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.JUKEBOX); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setBroadcastMessages(!this.settings.isBroadcastMessages()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isBroadcastMessages(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case REMOVE_COMPLETED -> { + description.add(this.user.getTranslation(reference + + (this.settings.isRemoveCompleteOneTimeChallenges() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.MAGMA_BLOCK); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setRemoveCompleteOneTimeChallenges(!this.settings.isRemoveCompleteOneTimeChallenges()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isRemoveCompleteOneTimeChallenges(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case ACTIVE_WORLD_LIST -> { + description.add(this.user.getTranslation(reference + + (this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST) ? + "disabled" : "enabled"))); + + icon = new ItemStack(Material.STONE_BUTTON); + clickHandler = (panel, user1, clickType, i) -> { + if (this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST)) + { + this.settings.setUserGuiMode(GuiMode.CURRENT_WORLD); + } + else + { + this.settings.setUserGuiMode(GuiMode.GAMEMODE_LIST); + } + + this.addon.saveSettings(); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + return true; + }; + glow = this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case GAMEMODE_GUI -> { + description.add(this.user.getTranslation(reference + + (this.settings.isUseCommonGUI() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setUseCommonGUI(!this.settings.isUseCommonGUI()); + // Need to rebuild more icons + this.build(); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isUseCommonGUI(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case STORE_HISTORY -> { + description.add(this.user.getTranslation(reference + + (this.settings.isStoreHistory() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setStoreHistory(!this.settings.isStoreHistory()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isStoreHistory(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case PURGE_HISTORY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.settings.getLifeSpan()))); + + icon = new ItemStack(Material.FLINT_AND_STEEL, Math.max(1, this.settings.getLifeSpan())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.settings.setLifeSpan(number.intValue()); + this.addon.saveSettings(); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case DATA_PER_ISLAND -> { + description.add(this.user.getTranslation(reference + + (this.settings.isStoreAsIslandData() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.GRASS_BLOCK); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setStoreAsIslandData(!this.settings.isStoreAsIslandData()); + // TODO: Migration + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isStoreAsIslandData(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case GLOW_COMPLETED -> { + description.add(this.user.getTranslation(reference + + (this.settings.isAddCompletedGlow() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.GLOWSTONE); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setAddCompletedGlow(!this.settings.isAddCompletedGlow()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isAddCompletedGlow(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case LOCKED_LEVEL_ICON -> { + icon = this.settings.getLockedLevelIcon(); + + clickHandler = (panel, user, clickType, i) -> + { + if (this.selectedButton != null) + { + this.selectedButton = null; + } + else + { + this.selectedButton = button; + } + + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + return true; + }; + + if (this.selectedButton != button) + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-on-item")); + } + + glow = this.selectedButton == button; + } + case SHOW_TITLE -> { + description.add(this.user.getTranslation(reference + + (this.settings.isShowCompletionTitle() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.OAK_SIGN); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setShowCompletionTitle(!this.settings.isShowCompletionTitle()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isShowCompletionTitle(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case TITLE_SHOWTIME -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.settings.getTitleShowtime()))); + + icon = new ItemStack(Material.CLOCK, Math.max(1, this.settings.getTitleShowtime())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.settings.setTitleShowtime(number.intValue()); + this.addon.saveSettings(); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case VISIBILITY_MODE -> { + description.add(this.user.getTranslation(reference + + (this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE) ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "visible")); + description.add(this.user.getTranslation(reference + + (this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN) ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "hidden")); + description.add(this.user.getTranslation(reference + + (this.settings.getVisibilityMode().equals(VisibilityMode.TOGGLEABLE) ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "toggleable")); + + if (this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE)) + { + icon = new ItemStack(Material.OAK_PLANKS); + } + else if (this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN)) + { + icon = new ItemStack(Material.OAK_SLAB); + } + else + { + icon = new ItemStack(Material.OAK_BUTTON); + } + + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.settings.setVisibilityMode(Utils.getPreviousValue(VisibilityMode.values(), + this.settings.getVisibilityMode())); + } + else + { + this.settings.setVisibilityMode(Utils.getNextValue(VisibilityMode.values(), + this.settings.getVisibilityMode())); + } + + // Rebuild just this icon + panel.getInventory().setItem(slot, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-cycle")); + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-cycle")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + // --------------------------------------------------------------------- + // Section: Classes + // --------------------------------------------------------------------- + + + /** + * This class allows changing icon for Generator Tier + */ + private class IconChanger implements PanelListener + { + /** + * Process inventory click. If generator icon is selected and user clicks on item in his inventory, then change + * icon to the item from inventory. + * + * @param user the user + * @param event the event + */ + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // Handle icon changing + if (EditSettingsPanel.this.selectedButton != null && + event.getCurrentItem() != null && + !event.getCurrentItem().getType().equals(Material.AIR) && + event.getRawSlot() > 44) + { + // set material and amount only. Other data should be removed. + + if (EditSettingsPanel.this.selectedButton == Button.LOCKED_LEVEL_ICON) + { + EditSettingsPanel.this.settings.setLockedLevelIcon(event.getCurrentItem().clone()); + EditSettingsPanel.this.addon.saveSettings(); + + // Deselect icon + EditSettingsPanel.this.selectedButton = null; + EditSettingsPanel.this.build(); + } + } + } + + + /** + * On inventory close. + * + * @param event the event + */ + @Override + public void onInventoryClose(InventoryCloseEvent event) + { + // Do nothing + } + + + /** + * Setup current listener. + */ + @Override + public void setup() + { + // Do nothing + } + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * This enum holds all settings buttons that must have been displayed in this panel. + */ + private enum Button + { + RESET_ON_NEW, + BROADCAST, + REMOVE_COMPLETED, + ACTIVE_WORLD_LIST, + GAMEMODE_GUI, + STORE_HISTORY, + PURGE_HISTORY, + DATA_PER_ISLAND, + GLOW_COMPLETED, + LOCKED_LEVEL_ICON, + SHOW_TITLE, + TITLE_SHOWTIME, + /** + * This allows to switch between different challenges visibility modes. + */ + VISIBILITY_MODE + } + + + /** + * This allows faster access to challenges settings object. + */ + private final Settings settings; + + /** + * Allows changing locked level icon. + */ + private Button selectedButton; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java new file mode 100644 index 0000000..cfa10d3 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java @@ -0,0 +1,471 @@ +package world.bentobox.challenges.panel.admin; + + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.scheduler.BukkitTask; + +import lv.id.bonne.panelutils.PanelUtils; +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.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; +import world.bentobox.challenges.web.object.LibraryEntry; + + +/** + * 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 LibraryPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- + + /** + * @param parentGUI ParentGUI object. + */ + private LibraryPanel(CommonPanel parentGUI, Library mode) + { + super(parentGUI); + + this.mode = mode; + + this.libraryEntries = switch (mode) + { + case WEB -> this.addon.getWebManager().getLibraryEntries(); + case DATABASE -> this.generateDatabaseEntries(); + case TEMPLATE -> this.generateTemplateEntries(); + }; + + this.filterElements = this.libraryEntries; + } + + + /** + * This static method allows to easier open Library GUI. + * @param parentGui ParentGUI object. + * @param mode Library view mode. + */ + public static void open(CommonPanel parentGui, Library mode) + { + new LibraryPanel(parentGui, mode).build(); + } + + +// --------------------------------------------------------------------- +// Section: Data Collectors +// --------------------------------------------------------------------- + + + /** + * This method generates list of database file entries. + * + * @return List of entries for database files. + */ + private List generateDatabaseEntries() + { + File localeDir = this.addon.getDataFolder(); + File[] files = localeDir.listFiles(pathname -> + pathname.getName().endsWith(".json") && pathname.isFile()); + + if (files == null || files.length == 0) + { + // No + return Collections.emptyList(); + } + + return Arrays.stream(files). + map(file -> LibraryEntry.fromTemplate( + file.getName().substring(0, file.getName().length() - 5), + Material.PAPER)). + collect(Collectors.toList()); + } + + + /** + * This method generates list of template file entries. + * + * @return List of entries for template files. + */ + private List generateTemplateEntries() + { + File localeDir = this.addon.getDataFolder(); + File[] files = localeDir.listFiles(pathname -> + pathname.getName().endsWith(".yml") && + pathname.isFile() && + !pathname.getName().equals("config.yml")); + + if (files == null || files.length == 0) + { + // No + return Collections.emptyList(); + } + + return Arrays.stream(files). + map(file -> LibraryEntry.fromTemplate( + file.getName().substring(0, file.getName().length() - 4), + Material.PAPER)). + collect(Collectors.toList()); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.libraryEntries; + } + else + { + this.filterElements = this.libraryEntries.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()) || + element.author().toLowerCase().contains(this.searchString.toLowerCase()) || + element.gameMode().toLowerCase().contains(this.searchString.toLowerCase()) || + element.language().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + if (this.libraryEntries.isEmpty()) + { + Utils.sendMessage(this.user, this.user.getTranslation( + Constants.ERRORS + "no-library-entries")); + return; + } + + // No point to display. Single element. + if (this.libraryEntries.size() == 1 && !this.mode.equals(Library.WEB)) + { + this.generateConfirmationInput(this.libraryEntries.get(0)); + return; + } + + + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "library")); + + PanelUtils.fillBorder(panelBuilder); + + this.populateElements(panelBuilder, this.filterElements); + + if (this.mode == Library.WEB) + { + panelBuilder.item(4, this.createDownloadNow()); + } + + panelBuilder.item(44, this.returnButton); + + panelBuilder.listener(new DownloadCanceller()); + + panelBuilder.build(); + } + + + /** + * This creates download now button, that can skip waiting for automatic request. + * @return PanelItem button that allows to manually download libraries. + */ + private PanelItem createDownloadNow() + { + final String reference = Constants.BUTTON + "download."; + + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + description.add(this.user.getTranslation(reference + + (this.clearCache ? "enabled" : "disabled"))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-download")); + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-toggle")); + + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name")). + description(description). + icon(Material.COBWEB). + glow(this.clearCache); + + itemBuilder.clickHandler((panel, user1, clickType, slot) -> + { + if (clickType.isRightClick()) + { + this.clearCache = !this.clearCache; + panel.getInventory().setItem(slot, this.createDownloadNow().getItem()); + } + else + { + this.addon.getWebManager().requestCatalogGitHubData(this.clearCache); + + // Fix multiclick issue. + if (this.updateTask != null) + { + this.updateTask.cancel(); + } + + // add some delay to rebuilding gui. + this.updateTask = this.addon.getPlugin().getServer().getScheduler().runTaskLater( + this.addon.getPlugin(), + this::build, + 100L); + } + + return true; + }); + + return itemBuilder.build(); + } + + + /** + * This method creates button for given library entry. + * @param libraryEntry LibraryEntry which button must be created. + * @return Entry button. + */ + @Override + protected PanelItem createElementButton(LibraryEntry libraryEntry) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(ChatColor.translateAlternateColorCodes('&', libraryEntry.name())). + description(this.generateEntryDescription(libraryEntry)). + description(""). + description(this.user.getTranslation(Constants.TIPS + "click-to-install")). + icon(libraryEntry.icon()). + glow(false); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + this.generateConfirmationInput(libraryEntry); + return true; + }); + + return itemBuilder.build(); + } + + + /** + * This method generates consumer and calls ConversationAPI for confirmation that processes file downloading, + * importing and gui opening or closing. + * + * @param libraryEntry Entry that must be processed. + */ + private void generateConfirmationInput(LibraryEntry libraryEntry) + { + Consumer consumer = value -> + { + if (value) + { + switch (this.mode) + { + case TEMPLATE -> { + this.addon.getImportManager().importFile(this.user, + this.world, + libraryEntry.name()); + + CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this); + } + case DATABASE -> { + this.addon.getImportManager().importDatabaseFile(this.user, + this.world, + libraryEntry.name()); + + CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this); + } + case WEB -> { + if (!this.blockedForDownland) + { + this.blockedForDownland = true; + + Utils.sendMessage(this.user, this.user.getTranslation( + Constants.MESSAGES + "start-downloading")); + + // Run download task after 5 ticks. + this.updateTask = this.addon.getPlugin().getServer().getScheduler(). + runTaskLaterAsynchronously( + this.addon.getPlugin(), + () -> this.addon.getWebManager().requestEntryGitHubData(this.user, + this.world, + libraryEntry), + 5L); + } + + CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this); + } + } + } + + if (this.mode.equals(Library.WEB) || this.libraryEntries.size() > 1) + { + this.build(); + } + }; + + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-data-replacement", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + this.user.getTranslation(Constants.CONVERSATIONS + "new-challenges-imported", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world))); + } + + + /** + * This method generated description for LibraryEntry object. + * @param entry LibraryEntry object which description must be generated. + * @return List of strings that will be placed in ItemStack lore message. + */ + private List generateEntryDescription(LibraryEntry entry) + { + final String reference = Constants.DESCRIPTIONS + "library."; + + List description = new ArrayList<>(); + + description.add(this.user.getTranslation(reference + "author", + "[author]", entry.author())); + description.add(entry.description()); + + description.add(this.user.getTranslation(reference + "gamemode", + "[gamemode]", entry.gameMode())); + description.add(this.user.getTranslation(reference + "lang", + "[lang]", entry.language())); + description.add(this.user.getTranslation(reference + "version", + "[version]", entry.version())); + + return description; + } + + + /** + * This class allows changing icon for Generator Tier + */ + private class DownloadCanceller implements PanelListener + { + /** + * On inventory click. + * + * @param user the user + * @param event the event + */ + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // do nothing + } + + + /** + * On inventory close. + * + * @param event the event + */ + @Override + public void onInventoryClose(InventoryCloseEvent event) + { + if (LibraryPanel.this.updateTask != null) + { + LibraryPanel.this.updateTask.cancel(); + } + } + + + /** + * Setup current listener. + */ + @Override + public void setup() + { + // Do nothing + } + } + + + /** + * Enum that holds different view modes for current panel. + */ + public enum Library + { + /** + * Mode for templates available in main folder. + */ + TEMPLATE, + /** + * Mode for database files available in main folder. + */ + DATABASE, + /** + * Mode for web library. + */ + WEB + } + + +// --------------------------------------------------------------------- +// Section: Instance Variables +// --------------------------------------------------------------------- + + /** + * Indicates if download now button should trigger cache clearing. + */ + private boolean clearCache; + + /** + * Stores update task that is triggered. + */ + private BukkitTask updateTask = null; + + /** + * This variable will protect against spam-click. + */ + private boolean blockedForDownland; + + /** + * Stores active library that must be searched. + */ + private final Library mode; + + /** + * List of library elements. + */ + private final List libraryEntries; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java deleted file mode 100644 index ccdb1cf..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java +++ /dev/null @@ -1,207 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; - -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 - // --------------------------------------------------------------------- - - - /** - * @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 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); - } - - - /** - * @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 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.title.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(ChatColor.translateAlternateColorCodes('&', challenge.getFriendlyName())). - description(GuiUtils.stringSplit(this.generateChallengeDescription(challenge, this.user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength())). - 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); - } - - this.build(); - }); - 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/ListChallengesPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesPanel.java new file mode 100644 index 0000000..3e047a7 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesPanel.java @@ -0,0 +1,225 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.World; + +import lv.id.bonne.panelutils.PanelUtils; +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.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * 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 ListChallengesPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- + + + /** + * @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 mode - mode that indicate what should do icon clicking. + */ + private ListChallengesPanel(ChallengesAddon addon, + World world, + User user, + Mode mode, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + this.currentMode = mode; + } + + + /** + * @param mode - mode that indicate what should do icon clicking. + */ + private ListChallengesPanel(CommonPanel parentGUI, Mode mode) + { + super(parentGUI); + this.currentMode = mode; + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix, + Mode mode) + { + new ListChallengesPanel(addon, world, user, mode, topLabel, permissionPrefix).build(); + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Mode mode) + { + new ListChallengesPanel(parentGUI, mode).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + // Do nothing here. + } + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "choose-challenge")); + + if (this.currentMode.equals(Mode.DELETE)) + { + PanelUtils.fillBorder(panelBuilder, Material.RED_STAINED_GLASS_PANE); + } + else + { + PanelUtils.fillBorder(panelBuilder); + } + + List challengeList = this.addon.getChallengesManager().getAllChallenges(this.world). + stream(). + filter(challenge -> this.searchString.isBlank() || + challenge.getFriendlyName().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getUniqueId().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getChallengeType().name().toLowerCase().contains(this.searchString)). + collect(Collectors.toList()); + + this.populateElements(panelBuilder, challengeList); + + 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. + */ + @Override + protected PanelItem createElementButton(Challenge challenge) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(Util.translateColorCodes(challenge.getFriendlyName())). + description(this.generateChallengeDescription(challenge, null)). + icon(challenge.getIcon()). + glow(!challenge.isDeployed()); + + if (this.currentMode.equals(Mode.EDIT)) + { + itemBuilder.description(""); + itemBuilder.description(this.user.getTranslation(Constants.TIPS + "click-to-edit")); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + EditChallengePanel.open(this, challenge); + return true; + }); + } + else if (this.currentMode.equals(Mode.DELETE)) + { + itemBuilder.description(""); + itemBuilder.description(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().deleteChallenge(challenge); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-challenge-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world), + Constants.PARAMETER_CHALLENGE, challenge.getFriendlyName()), + this.user.getTranslation(Constants.CONVERSATIONS + "challenge-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world), + Constants.PARAMETER_CHALLENGE, challenge.getFriendlyName())); + 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 final 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 deleted file mode 100644 index 4a9dad5..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java +++ /dev/null @@ -1,209 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; - -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 - // --------------------------------------------------------------------- - - - /** - * @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 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); - } - - - /** - * @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 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.title.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(ChatColor.translateAlternateColorCodes('&', challengeLevel.getFriendlyName())). - description(GuiUtils.stringSplit( - this.generateLevelDescription(challengeLevel, this.user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength())). - 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); - } - - this.build(); - }); - 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/ListLevelsPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsPanel.java new file mode 100644 index 0000000..9b344f5 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsPanel.java @@ -0,0 +1,224 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Material; +import org.bukkit.World; + +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import lv.id.bonne.panelutils.PanelUtils; +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.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class creates GUI that lists all Levels. Clicking on Level icon will be processed + * by input mode. + */ +public class ListLevelsPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- + + + /** + * @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 mode - mode that indicate what should do icon clicking. + */ + private ListLevelsPanel(ChallengesAddon addon, + World world, + User user, + Mode mode, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + this.currentMode = mode; + } + + + /** + * @param mode - mode that indicate what should do icon clicking. + */ + private ListLevelsPanel(CommonPanel parentGUI, Mode mode) + { + super(parentGUI); + this.currentMode = mode; + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix, + Mode mode) + { + new ListLevelsPanel(addon, world, user, mode, topLabel, permissionPrefix).build(); + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Mode mode) + { + new ListLevelsPanel(parentGUI, mode).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + // Do nothing here. + } + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "choose-level")); + + if (this.currentMode.equals(Mode.DELETE)) + { + PanelUtils.fillBorder(panelBuilder, Material.RED_STAINED_GLASS_PANE); + } + else + { + PanelUtils.fillBorder(panelBuilder); + } + + List levelList = this.addon.getChallengesManager().getLevels(this.world). + stream(). + filter(challenge -> this.searchString.isBlank() || + challenge.getFriendlyName().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getUniqueId().toLowerCase().contains(this.searchString.toLowerCase())). + collect(Collectors.toList()); + + this.populateElements(panelBuilder, levelList); + + 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. + */ + @Override + protected PanelItem createElementButton(ChallengeLevel challengeLevel) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(Util.translateColorCodes(challengeLevel.getFriendlyName())). + description(this.generateLevelDescription(challengeLevel)). + icon(challengeLevel.getIcon()); + + if (this.currentMode.equals(Mode.EDIT)) + { + itemBuilder.description(""); + itemBuilder.description(this.user.getTranslation(Constants.TIPS + "click-to-edit")); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + EditLevelPanel.open(this, challengeLevel); + return true; + }); + } + else if (this.currentMode.equals(Mode.DELETE)) + { + itemBuilder.description(""); + itemBuilder.description(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().deleteChallengeLevel(challengeLevel); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-level-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world), + Constants.PARAMETER_LEVEL, challengeLevel.getFriendlyName()), + this.user.getTranslation(Constants.CONVERSATIONS + "level-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world), + Constants.PARAMETER_LEVEL, challengeLevel.getFriendlyName())); + 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 final Mode currentMode; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java deleted file mode 100644 index d153da3..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java +++ /dev/null @@ -1,313 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.scheduler.BukkitTask; - -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.utils.GuiUtils; -import world.bentobox.challenges.web.object.LibraryEntry; - - -/** - * 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 ListLibraryGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructor - // --------------------------------------------------------------------- - - /** - * @param parentGUI ParentGUI object. - */ - public ListLibraryGUI(CommonGUI parentGUI) - { - super(parentGUI); - } - - - /** - * @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 ListLibraryGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix) - { - super(addon, world, user, topLabel, permissionPrefix, null); - } - - - /** - * This static method allows to easier open Library GUI. - * @param parentGui ParentGUI object. - */ - public static void open(CommonGUI parentGui) - { - new ListLibraryGUI(parentGui).build(); - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * {@inheritDoc} - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.library-title")); - - GuiUtils.fillBorder(panelBuilder); - - List libraryEntries = this.addon.getWebManager().getLibraryEntries(); - - final int MAX_ELEMENTS = 21; - - if (this.pageIndex < 0) - { - this.pageIndex = libraryEntries.size() / MAX_ELEMENTS; - } - else if (this.pageIndex > (libraryEntries.size() / MAX_ELEMENTS)) - { - this.pageIndex = 0; - } - - int entryIndex = MAX_ELEMENTS * this.pageIndex; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (entryIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && - entryIndex < libraryEntries.size() && - index < 36) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createEntryIcon(libraryEntries.get(entryIndex++))); - } - - index++; - } - - // Navigation buttons only if necessary - if (libraryEntries.size() > MAX_ELEMENTS) - { - panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); - panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); - } - - panelBuilder.item(4, this.createDownloadNow()); - panelBuilder.item(44, this.createReturnButton()); - - panelBuilder.build(); - } - - - /** - * This creates download now button, that can skip waiting for automatic request. - * @return PanelItem button that allows to manually download libraries. - */ - private PanelItem createDownloadNow() - { - List description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.download")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.clearCache ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - PanelItemBuilder itemBuilder = new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.admin.download")). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - icon(Material.HOPPER). - glow(this.clearCache); - - itemBuilder.clickHandler((panel, user1, clickType, slot) -> - { - if (clickType.isRightClick()) - { - this.clearCache = !this.clearCache; - panel.getInventory().setItem(slot, this.createDownloadNow().getItem()); - } - else - { - this.addon.getWebManager().requestCatalogGitHubData(this.clearCache); - - // Fix multiclick issue. - if (this.updateTask != null) - { - this.updateTask.cancel(); - } - - // add some delay to rebuilding gui. - this.updateTask = this.addon.getPlugin().getServer().getScheduler().runTaskLater( - this.addon.getPlugin(), - this::build, - 100L); - } - - return true; - }); - - return itemBuilder.build(); - } - - - /** - * This creates return button, that allows to exist or return to parent gui, - * @return PanelItem for return button. - */ - private PanelItem createReturnButton() - { - return new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.return")). - icon(Material.OAK_DOOR). - clickHandler((panel, user1, clickType, i) -> { - if (this.updateTask != null) - { - this.updateTask.cancel(); - } - - if (this.parentGUI == null) - { - this.user.closeInventory(); - return true; - } - - this.parentGUI.build(); - return true; - }).build(); - } - - - /** - * This method creates button for given library entry. - * @param libraryEntry LibraryEntry which button must be created. - * @return Entry button. - */ - private PanelItem createEntryIcon(LibraryEntry libraryEntry) - { - PanelItemBuilder itemBuilder = new PanelItemBuilder(). - name(ChatColor.translateAlternateColorCodes('&', libraryEntry.getName())). - description(this.generateEntryDescription(libraryEntry)). - icon(libraryEntry.getIcon()). - glow(false); - - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - - if (!this.blockedForDownland) - { - this.blockedForDownland = true; - - this.user.sendMessage("challenges.messages.admin.start-downloading"); - - // Run download task after 5 ticks. - this.addon.getPlugin().getServer().getScheduler(). - runTaskLaterAsynchronously( - this.addon.getPlugin(), - () -> this.addon.getWebManager().requestEntryGitHubData(this.user, this.world, libraryEntry), - 5L); - - if (this.parentGUI != null) - { - if (this.updateTask != null) - { - this.updateTask.cancel(); - } - - this.parentGUI.build(); - } - else - { - if (this.updateTask != null) - { - this.updateTask.cancel(); - } - - this.user.closeInventory(); - } - } - - return true; - }); - - return itemBuilder.build(); - } - - - /** - * This method generated description for LibraryEntry object. - * @param entry LibraryEntry object which description must be generated. - * @return List of strings that will be placed in ItemStack lore message. - */ - private List generateEntryDescription(LibraryEntry entry) - { - List description = new ArrayList<>(); - - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "library-author", - "[author]", - entry.getAuthor())); - description.add(entry.getDescription()); - - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "library-gamemode", - "[gamemode]", - entry.getForGameMode())); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "library-lang", - "[lang]", - entry.getLanguage())); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "library-version", - "[version]", - entry.getVersion())); - - return GuiUtils.stringSplit(description, - this.addon.getChallengesSettings().getLoreLineLength()); - } - - -// --------------------------------------------------------------------- -// Section: Instance Variables -// --------------------------------------------------------------------- - - /** - * Indicates if download now button should trigger cache clearing. - */ - private boolean clearCache; - - /** - * Stores update task that is triggered. - */ - private BukkitTask updateTask = null; - - /** - * This variable will protect against spam-click. - */ - private boolean blockedForDownland; - - /** - * Reference string to description. - */ - private static final String REFERENCE_DESCRIPTION = "challenges.gui.descriptions.admin."; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java deleted file mode 100644 index e22eead..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java +++ /dev/null @@ -1,334 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.Player; - -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.database.object.Challenge; -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 - // --------------------------------------------------------------------- - - - /** - * @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 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); - } - - - /** - * @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 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.title.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 toggle 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) - { - int lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - - if (this.addon.getIslands().getIsland(this.world, player.getUniqueId()) != null) - { - return new PanelItemBuilder().name(player.getName()).icon(player.getName()).clickHandler( - (panel, user1, clickType, slot) -> { - ChallengesManager manager = this.addon.getChallengesManager(); - Map> challengeDescriptionMap; - - switch (this.operationMode) - { - case COMPLETE: - challengeDescriptionMap = new LinkedHashMap<>(); - - for (Challenge challenge : manager.getAllChallenges(this.world)) - { - if (!manager.isChallengeComplete(player.getUniqueId(), this.world, challenge)) - { - challengeDescriptionMap.put(challenge, this.generateChallengeDescription(challenge, player)); - } - } - - new SelectChallengeGUI(this.user, challengeDescriptionMap, lineLength, (status, valueSet) -> { - if (status) - { - valueSet.forEach(challenge -> manager.setChallengeComplete(player.getUniqueId(), this.world, challenge, this.user.getUniqueId())); - } - - this.build(); - }); - break; - case RESET: - challengeDescriptionMap = new LinkedHashMap<>(); - - for (Challenge challenge : manager.getAllChallenges(this.world)) - { - if (manager.isChallengeComplete(player.getUniqueId(), this.world, challenge)) - { - challengeDescriptionMap.put(challenge, this.generateChallengeDescription(challenge, player)); - } - } - - new SelectChallengeGUI(this.user, challengeDescriptionMap, lineLength, (status, valueSet) -> { - if (status) - { - valueSet.forEach(challenge -> manager.resetChallenge(player.getUniqueId(), this.world, challenge, this.user.getUniqueId())); - } - - this.build(); - }); - break; - case RESET_ALL: - new ConfirmationGUI(this.user, status -> { - if (status) - { - manager.resetAllChallenges(player.getUniqueId(), this.world, this.user.getUniqueId()); - } - - this.build(); - }); - 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"), lineLength)). - 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 description = new ArrayList<>(ViewMode.values().length + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.toggle-user-list")); - description.add((ViewMode.ONLINE == ViewMode.values()[this.modeIndex] ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.admin.mode-online")); - description.add((ViewMode.WITH_ISLAND == ViewMode.values()[this.modeIndex] ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.admin.mode-in-world")); - description.add((ViewMode.IN_WORLD == ViewMode.values()[this.modeIndex] ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.admin.mode-with-island")); - - return new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.admin.toggle-user-list")). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - 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/ListUsersPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersPanel.java new file mode 100644 index 0000000..1fb3596 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersPanel.java @@ -0,0 +1,408 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import lv.id.bonne.panelutils.PanelUtils; +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.Island; +import world.bentobox.bentobox.database.objects.Players; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.util.ChallengeSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains methods that allows to select specific user. + */ +public class ListUsersPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructors + // --------------------------------------------------------------------- + + + /** + * @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 operationMode Indicate what should happen on player icon click. + */ + private ListUsersPanel(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + Mode operationMode) + { + super(addon, user, world, topLabel, permissionPrefix); + this.onlineUsers = this.collectUsers(ViewMode.IN_WORLD); + this.operationMode = operationMode; + this.filterElements = this.onlineUsers; + } + + + /** + * @param operationMode Indicate what should happen on player icon click. + */ + private ListUsersPanel(CommonPanel panel, Mode operationMode) + { + super(panel); + this.onlineUsers = this.collectUsers(ViewMode.IN_WORLD); + this.operationMode = operationMode; + this.filterElements = this.onlineUsers; + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix, + Mode mode) + { + new ListUsersPanel(addon, user, world, topLabel, permissionPrefix, mode).build(); + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Mode mode) + { + new ListUsersPanel(parentGUI, mode).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.onlineUsers; + } + else + { + this.filterElements = this.onlineUsers.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.getDisplayName().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "choose-player")); + + PanelUtils.fillBorder(panelBuilder); + + this.populateElements(panelBuilder, this.filterElements); + + // Add button that allows to toggle different player lists. + panelBuilder.item( 4, this.createToggleButton()); + 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. + */ + @Override + protected PanelItem createElementButton(Player player) + { + final String reference = Constants.BUTTON + "player."; + + Island island = this.addon.getIslands().getIsland(this.world, player.getUniqueId()); + + if (island == null) + { + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", Constants.PARAMETER_NAME, player.getName())). + icon(Material.BARRIER). + description(this.user.getTranslation(reference + "no-island")). + build(); + } + + List description = new ArrayList<>(4); + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_OWNER, this.addon.getPlayers().getName(island.getOwner()))); + + // Is owner in his own island member set? I assume yes. Need testing. + if (island.getMemberSet().size() > 1) + { + description.add(this.user.getTranslation(reference + "members")); + island.getMemberSet().forEach(member -> { + if (member != island.getOwner()) + { + description.add(this.user.getTranslation(reference + "member", + Constants.PARAMETER_NAME, this.addon.getPlayers().getName(member))); + } + }); + } + + description.add(""); + + if (this.operationMode == Mode.RESET_ALL && this.selectedPlayer != null) + { + description.add(this.user.getTranslation(Constants.TIPS + "click-to-reset-all")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", Constants.PARAMETER_NAME, player.getName())). + icon(player.getName()). + description(description). + glow(this.operationMode == Mode.RESET_ALL && this.selectedPlayer == player). + clickHandler((panel, user1, clickType, i) -> { + switch (this.operationMode) + { + case COMPLETE -> { + // Get all challenge that is in current level. + List challengeList = this.manager.getAllChallenges(this.world); + + // Generate descriptions for these challenges + Map> challengeDescriptionMap = challengeList.stream(). + filter(challenge -> !this.manager.isChallengeComplete(player.getUniqueId(), this.world, challenge)). + collect(Collectors.toMap(challenge -> challenge, + challenge -> this.generateChallengeDescription(challenge, User.getInstance(player)), + (a, b) -> b, + () -> new LinkedHashMap<>(challengeList.size()))); + + // Open select gui + ChallengeSelector.open(this.user, + Material.LIME_STAINED_GLASS_PANE, + challengeDescriptionMap, + (status, valueSet) -> { + if (status) + { + valueSet.forEach(challenge -> + manager.setChallengeComplete(player.getUniqueId(), + this.world, + challenge, + this.user.getUniqueId())); + } + + this.build(); + }); + } + case RESET -> { + // Get all challenge that is in current level. + List challengeList = this.manager.getAllChallenges(this.world); + + // Generate descriptions for these challenges + Map> challengeDescriptionMap = challengeList.stream(). + filter(challenge -> this.manager.isChallengeComplete(player.getUniqueId(), this.world, challenge)). + collect(Collectors.toMap(challenge -> challenge, + challenge -> this.generateChallengeDescription(challenge, User.getInstance(player)), + (a, b) -> b, + () -> new LinkedHashMap<>(challengeList.size()))); + + // Open select gui + ChallengeSelector.open(this.user, + Material.ORANGE_STAINED_GLASS_PANE, + challengeDescriptionMap, + (status, valueSet) -> { + if (status) + { + valueSet.forEach(challenge -> + this.manager.resetChallenge(player.getUniqueId(), + this.world, + challenge, + this.user.getUniqueId())); + } + + this.build(); + }); + } + case RESET_ALL -> { + if (this.selectedPlayer == null) + { + this.selectedPlayer = player; + } + else + { + this.manager.resetAllChallenges(player.getUniqueId(), this.world, this.user.getUniqueId()); + this.selectedPlayer = null; + } + + this.build(); + } + } + + return true; + }). + 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) + { + return switch (mode) { + case ONLINE -> new ArrayList<>(Bukkit.getOnlinePlayers()); + case WITH_ISLAND -> this.addon.getPlayers().getPlayers().stream(). + filter(player -> this.addon.getIslands().getIsland(this.world, player.getPlayerUUID()) != null). + map(Players::getPlayer). + collect(Collectors.toList()); + default -> 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() + { + final String reference = Constants.BUTTON + "player_list."; + + List description = new ArrayList<>(5); + + description.add(this.user.getTranslation(reference + "description")); + description.add(this.user.getTranslation(reference + + (ViewMode.ONLINE == this.mode ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "online")); + description.add(this.user.getTranslation(reference + + (ViewMode.WITH_ISLAND == this.mode ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "with_island")); + description.add(this.user.getTranslation(reference + + (ViewMode.IN_WORLD == this.mode ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "in_world")); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-cycle")); + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-cycle")); + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name")). + icon(Material.STONE_BUTTON). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.mode = Utils.getPreviousValue(ViewMode.values(), this.mode); + } + else + { + this.mode = Utils.getNextValue(ViewMode.values(), this.mode); + } + this.onlineUsers = this.collectUsers(this.mode); + + // Reset search + this.searchString = ""; + this.updateFilters(); + + this.build(); + return true; + }).build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * 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: Variables +// --------------------------------------------------------------------- + + /** + * List with players that should be in GUI. + */ + private List onlineUsers; + + /** + * List with players that should be in GUI. + */ + private List filterElements; + + /** + * Current operation mode. + */ + private final Mode operationMode; + + /** + * Current index of view mode + */ + private ViewMode mode = ViewMode.ONLINE; + + /** + * Stores clicked player. + */ + private Player selectedPlayer; +} \ 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 deleted file mode 100644 index eab6371..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java +++ /dev/null @@ -1,241 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Material; -import org.bukkit.World; - -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.title.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) - { - int lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - PanelItemBuilder builder = new PanelItemBuilder(); - - switch (button) - { - case ADD: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.add")); - builder.icon(Material.BUCKET); - builder.clickHandler((panel, user1, clickType, slot) -> { - - new SelectBlocksGUI(this.user, false, new HashSet<>(this.materialList), (status, materials) -> { - if (status) - { - materials.forEach(material -> { - this.materialMap.put(material, 1); - this.materialList.add(material); - }); - } - - this.build(); - }); - return true; - }); - break; - case REMOVE: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.remove-selected")); - builder.description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.descriptions.admin.remove-selected"), lineLength)); - 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))). - description(this.selectedMaterials.contains(material) ? - this.user.getTranslation("challenges.gui.descriptions.admin.selected") : ""). - 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, - this.addon.getChallengesSettings().getLoreLineLength(), - (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/ManageBlocksPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksPanel.java new file mode 100644 index 0000000..cd43adf --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksPanel.java @@ -0,0 +1,312 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.MultiBlockSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class allows to edit material that are in required material map. + */ +public class ManageBlocksPanel extends CommonPagedPanel +{ + private ManageBlocksPanel(CommonPanel parentGUI, Map materialMap) + { + super(parentGUI); + this.materialMap = materialMap; + this.materialList = new ArrayList<>(this.materialMap.keySet()); + + // Sort materials by their ordinal value. + this.materialList.sort(Comparator.comparing(Enum::name)); + + this.selectedMaterials = new HashSet<>(); + + // Init without filters applied. + this.filterElements = this.materialList; + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Map materialMap) + { + new ManageBlocksPanel(parentGUI, materialMap).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.materialList; + } + else + { + this.filterElements = this.materialList.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation(Constants.TITLE + "manage-blocks")); + + // Create nice border. + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(3, this.createButton(Button.ADD_BLOCK)); + panelBuilder.item(5, this.createButton(Button.REMOVE_BLOCK)); + + this.populateElements(panelBuilder, this.filterElements); + + // Add return button. + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + boolean glow; + + switch (button) + { + case ADD_BLOCK -> { + icon = new ItemStack(Material.BUCKET); + clickHandler = (panel, user1, clickType, slot) -> + { + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.BLOCKS, + new HashSet<>(this.materialList), + (status, materials) -> + { + if (status) + { + materials.forEach(material -> + { + this.materialMap.put(material, 1); + this.materialList.add(material); + }); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_BLOCK -> { + + if (!this.selectedMaterials.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedMaterials.forEach(material -> + description.add(this.user.getTranslation(reference + "material", + "[material]", Utils.prettifyObject(material, this.user)))); + } + + icon = new ItemStack(Material.LAVA_BUCKET); + + clickHandler = (panel, user1, clickType, slot) -> + { + if (!this.selectedMaterials.isEmpty()) + { + this.materialMap.keySet().removeAll(this.selectedMaterials); + this.materialList.removeAll(this.selectedMaterials); + this.selectedMaterials.clear(); + this.build(); + } + + return true; + }; + + glow = !this.selectedMaterials.isEmpty(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + glow(glow). + build(); + } + + + /** + * This method creates button for given material. + * @param material material which button must be created. + * @return new Button for material. + */ + @Override + protected PanelItem createElementButton(Material material) + { + final String reference = Constants.BUTTON + "material."; + + List description = new ArrayList<>(); + + if (this.selectedMaterials.contains(material)) + { + description.add(this.user.getTranslation(reference + "selected")); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-choose")); + + if (this.selectedMaterials.contains(material)) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-deselect")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[material]", + Utils.prettifyObject(material, this.user))). + icon(PanelUtils.getMaterialItem(material, this.materialMap.get(material))). + description(description). + 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 + { + Consumer numberConsumer = number -> { + if (number != null) + { + this.materialMap.put(material, number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 1, + Integer.MAX_VALUE); + } + return true; + }). + glow(this.selectedMaterials.contains(material)). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ADD_BLOCK, + REMOVE_BLOCK + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Contains selected materials. + */ + private final Set selectedMaterials; + + /** + * List of materials to avoid order issues. + */ + private final List materialList; + + /** + * List of required materials. + */ + private final Map materialMap; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java deleted file mode 100644 index ed29747..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java +++ /dev/null @@ -1,258 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.EntityType; - -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.title.admin.manage-entities")); - - // create border - GuiUtils.fillBorder(panelBuilder); - - panelBuilder.item(3, this.createButton(Button.ADD)); - panelBuilder.item(5, this.createButton(Button.REMOVE)); - panelBuilder.item(8, this.createButton(Button.SWITCH)); - - 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) - { - int lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - PanelItemBuilder builder = new PanelItemBuilder(); - - switch (button) - { - case ADD: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.add")); - builder.icon(Material.BUCKET); - builder.clickHandler((panel, user1, clickType, slot) -> { - new SelectEntityGUI(this.user, this.requiredEntities.keySet(), this.asEggs, (status, entities) -> { - if (status) - { - entities.forEach(entity -> { - this.requiredEntities.put(entity, 1); - this.entityList.add(entity); - }); - } - - this.build(); - }); - return true; - }); - break; - case REMOVE: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.remove-selected")); - builder.description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.descriptions.admin.remove-selected"), lineLength)); - 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; - case SWITCH: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.show-eggs")); - builder.description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.descriptions.admin.show-eggs"), lineLength)); - builder.icon(this.asEggs ? Material.EGG : Material.PLAYER_HEAD); - builder.clickHandler((panel, user1, clickType, slot) -> { - this.asEggs = !this.asEggs; - 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("_", " "))). - description(this.selectedEntities.contains(entity) ? - this.user.getTranslation("challenges.gui.descriptions.admin.selected") : ""). - 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, - this.addon.getChallengesSettings().getLoreLineLength(), - (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, - SWITCH - } - - -// --------------------------------------------------------------------- -// 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/admin/ManageEntitiesPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesPanel.java new file mode 100644 index 0000000..951467b --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesPanel.java @@ -0,0 +1,330 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.MultiEntitySelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class allows to edit entities that are in required entities map. + */ +public class ManageEntitiesPanel extends CommonPagedPanel +{ + private ManageEntitiesPanel(CommonPanel parentGUI, Map requiredEntities) + { + super(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); + this.filterElements = this.entityList; + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Map requiredEntities) + { + new ManageEntitiesPanel(parentGUI, requiredEntities).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.entityList; + } + else + { + this.filterElements = this.entityList.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation(Constants.TITLE + "manage-entities")); + + // create border + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(3, this.createButton(Button.ADD_ENTITY)); + panelBuilder.item(5, this.createButton(Button.REMOVE_ENTITY)); + panelBuilder.item(8, this.createButton(Button.SWITCH_ENTITY)); + + this.populateElements(panelBuilder, this.filterElements); + + // Add return button. + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + boolean glow; + + switch (button) + { + case ADD_ENTITY -> { + icon = new ItemStack(Material.BUCKET); + clickHandler = (panel, user1, clickType, slot) -> { + MultiEntitySelector.open(this.user, + this.asEggs, + MultiEntitySelector.Mode.ALIVE, + this.requiredEntities.keySet(), + (status, entities) -> { + if (status) + { + entities.forEach(entity -> { + this.requiredEntities.put(entity, 1); + this.entityList.add(entity); + }); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_ENTITY -> { + + if (!this.selectedEntities.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedEntities.forEach(entity -> + description.add(this.user.getTranslation(reference + "entity", + "[entity]", Utils.prettifyObject(entity, this.user)))); + } + + icon = new ItemStack(Material.LAVA_BUCKET); + + clickHandler = (panel, user1, clickType, slot) -> + { + if (!this.selectedEntities.isEmpty()) + { + this.requiredEntities.keySet().removeAll(this.selectedEntities); + this.entityList.removeAll(this.selectedEntities); + this.selectedEntities.clear(); + this.build(); + } + + return true; + }; + + glow = !this.entityList.isEmpty(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + case SWITCH_ENTITY -> { + icon = new ItemStack(this.asEggs ? Material.EGG : Material.PLAYER_HEAD); + + clickHandler = (panel, user1, clickType, slot) -> { + this.asEggs = !this.asEggs; + this.build(); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + glow(glow). + build(); + } + + + /** + * This method creates button for given entity. + * @param entity Entity which button must be created. + * @return new Button for entity. + */ + @Override + protected PanelItem createElementButton(EntityType entity) + { + final String reference = Constants.BUTTON + "entity."; + + List description = new ArrayList<>(); + + if (this.selectedEntities.contains(entity)) + { + description.add(this.user.getTranslation(reference + "selected")); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-choose")); + + if (this.selectedEntities.contains(entity)) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-deselect")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[entity]", + Utils.prettifyObject(entity, this.user))). + icon(this.asEggs ? + PanelUtils.getEntityEgg(entity, this.requiredEntities.get(entity)) : + PanelUtils.getEntityHead(entity, this.requiredEntities.get(entity))). + description(description). + 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 + { + Consumer numberConsumer = number -> { + if (number != null) + { + this.requiredEntities.put(entity, number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 1, + Integer.MAX_VALUE); + } + return true; + }). + glow(this.selectedEntities.contains(entity)). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ADD_ENTITY, + REMOVE_ENTITY, + SWITCH_ENTITY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with entities to avoid list irregularities. + */ + private final List entityList; + + /** + * Set with entities that are selected. + */ + private final Set selectedEntities; + + /** + * Map that contains all entities and their cound. + */ + private final Map requiredEntities; + + /** + * Boolean indicate if entities should be displayed as eggs or mob heads. + */ + private boolean asEggs; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java deleted file mode 100644 index 222e7bb..0000000 --- a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java +++ /dev/null @@ -1,528 +0,0 @@ -package world.bentobox.challenges.panel.user; - - -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.inventory.ItemStack; - -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.config.SettingsUtils.VisibilityMode; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.tasks.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.getAllChallengeLevelStatus(this.user, this.world); - - for (LevelStatus levelStatus : this.levelStatusList) - { - if (levelStatus.isUnlocked()) - { - this.lastSelectedLevel = levelStatus; - } - else - { - break; - } - } - - this.containsChallenges = this.challengesManager.hasAnyChallengeData(this.world); - } - -// --------------------------------------------------------------------- -// Section: Methods -// --------------------------------------------------------------------- - - - /** - * This method builds all necessary elements in GUI panel. - */ - @Override - public void build() - { - // Do not open gui if there is no challenges. - if (!this.containsChallenges) - { - this.addon.logError("There are no challenges set up!"); - this.user.sendMessage("challenges.errors.no-challenges"); - return; - } - - PanelBuilder panelBuilder = new PanelBuilder().user(this.user). - name(this.user.getTranslation("challenges.gui.title.challenges")); - - // TODO: get last completed level. - - int nextItemIndex = 0; - - if (this.addon.getChallengesSettings().isFreeChallengesFirst()) - { - this.addFreeChallenges(panelBuilder, nextItemIndex); - - // Start new row for challenges. - if (panelBuilder.nextSlot() % 9 != 0) - { - nextItemIndex = panelBuilder.nextSlot() - panelBuilder.nextSlot() % 9 + 9; - } - else - { - nextItemIndex = panelBuilder.nextSlot(); - } - } - - this.addChallenges(panelBuilder, nextItemIndex); - - // Start new row for levels. - // Start new row for challenges. - if (panelBuilder.nextSlot() % 9 != 0) - { - nextItemIndex = panelBuilder.nextSlot() - panelBuilder.nextSlot() % 9 + 9; - } - else - { - nextItemIndex = panelBuilder.nextSlot(); - } - - this.addChallengeLevels(panelBuilder, nextItemIndex); - - if (!this.addon.getChallengesSettings().isFreeChallengesFirst()) - { - // Start new row for free challenges. - if (panelBuilder.nextSlot() % 9 != 0) - { - nextItemIndex = panelBuilder.nextSlot() - panelBuilder.nextSlot() % 9 + 9; - } - else - { - nextItemIndex = panelBuilder.nextSlot(); - } - - this.addFreeChallenges(panelBuilder, nextItemIndex); - } - - panelBuilder.build(); - } - - - /** - * This method adds free challenges to panelBuilder. - * @param panelBuilder where free challenges must be added. - * @param firstItemIndex index of first element. - */ - private void addFreeChallenges(PanelBuilder panelBuilder, int firstItemIndex) - { - List freeChallenges = this.challengesManager.getFreeChallenges(this.world); - - if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) - { - freeChallenges.removeIf(challenge -> !challenge.isRepeatable() && - this.challengesManager.isChallengeComplete(this.user, this.world, challenge)); - } - - // Remove all undeployed challenges if VisibilityMode is set to Hidden. - if (this.addon.getChallengesSettings().getVisibilityMode().equals(VisibilityMode.HIDDEN)) - { - freeChallenges.removeIf(challenge -> !challenge.isDeployed()); - } - - final int freeChallengesCount = freeChallenges.size(); - - if (freeChallengesCount > 18) - { - int index = firstItemIndex; - - if (this.freeChallengeIndex > 0) - { - panelBuilder.item(index++, new PanelItemBuilder(). - icon(Material.OAK_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() != firstItemIndex + 18 && currentIndex < freeChallengesCount) - { - panelBuilder.item(index++, this.getChallengeButton(freeChallenges.get(currentIndex++))); - } - - // Check if one challenge is left - if (currentIndex + 1 == freeChallengesCount) - { - panelBuilder.item(index, this.getChallengeButton(freeChallenges.get(currentIndex))); - } - else if (currentIndex < freeChallengesCount) - { - panelBuilder.item(index, new PanelItemBuilder(). - icon(Material.OAK_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(firstItemIndex++, this.getChallengeButton(challenge)); - } - } - } - - - /** - * This method adds last selected level challenges to panelBuilder. - * @param panelBuilder where last selected level challenges must be added. - * @param firstItemIndex index of first element. - */ - private void addChallenges(PanelBuilder panelBuilder, int firstItemIndex) - { - if (this.lastSelectedLevel != null) - { - List challenges = this.challengesManager.getLevelChallenges(this.lastSelectedLevel.getLevel()); - - if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) - { - challenges.removeIf(challenge -> !challenge.isRepeatable() && - this.challengesManager.isChallengeComplete(this.user, this.world, challenge)); - } - - // Remove all undeployed challenges if VisibilityMode is set to Hidden. - if (this.addon.getChallengesSettings().getVisibilityMode().equals(VisibilityMode.HIDDEN)) - { - challenges.removeIf(challenge -> !challenge.isDeployed()); - } - - final int challengesCount = challenges.size(); - - if (challengesCount > 18) - { - int index = firstItemIndex; - - if (this.pageIndex > 0) - { - panelBuilder.item(index++, new PanelItemBuilder(). - icon(Material.OAK_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() != firstItemIndex + 18 && currentIndex < challengesCount) - { - panelBuilder.item(index++, this.getChallengeButton(challenges.get(currentIndex++))); - } - - // Check if one challenge is left - if (currentIndex + 1 == challengesCount) - { - panelBuilder.item(index, this.getChallengeButton(challenges.get(currentIndex))); - } - else if (currentIndex < challengesCount) - { - panelBuilder.item(index, new PanelItemBuilder(). - icon(Material.OAK_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(firstItemIndex++, this.getChallengeButton(challenge)); - } - } - } - } - - - /** - * This method adds challenge levels to panelBuilder. - * @param panelBuilder where challenge levels must be added. - * @param firstItemIndex index of first element. - */ - private void addChallengeLevels(PanelBuilder panelBuilder, int firstItemIndex) - { - final int levelCounts = this.levelStatusList.size(); - - if (levelCounts > 9) - { - int index = firstItemIndex; - - if (this.levelIndex > 0) - { - panelBuilder.item(index++, new PanelItemBuilder(). - icon(Material.OAK_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() != firstItemIndex + 9 && currentIndex < levelCounts) - { - panelBuilder.item(index++, this.getLevelButton(this.levelStatusList.get(currentIndex++))); - } - - // Check if one challenge is left - if (currentIndex + 1 == levelCounts) - { - panelBuilder.item(index, this.getLevelButton(this.levelStatusList.get(currentIndex))); - } - else if (currentIndex < levelCounts) - { - panelBuilder.item(index, new PanelItemBuilder(). - icon(Material.OAK_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 : this.levelStatusList) - { - // there are no limitations. Just bunch insert. - panelBuilder.item(firstItemIndex++, 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() : - ChatColor.translateAlternateColorCodes('&', challenge.getFriendlyName())). - description(GuiUtils.stringSplit(this.generateChallengeDescription(challenge, this.user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength())). - clickHandler((panel, user1, clickType, slot) -> { - - // Add ability to input how many repeats player should do. - // Do not open if challenge is not repeatable. - - if (clickType.isRightClick() && - challenge.isRepeatable() && - this.user.hasPermission(this.permissionPrefix + "complete.multiple")) - { - new MultipleGUI(this.user, - this.addon.getChallengesSettings().getLoreLineLength(), - value -> { - TryToComplete.complete(this.addon, - this.user, - challenge, - this.world, - this.topLabel, - this.permissionPrefix, - value); - - this.build(); - }); - } - else - { - if (TryToComplete.complete(this.addon, - this.user, - challenge, - this.world, - this.topLabel, - this.permissionPrefix)) - { - panel.getInventory().setItem(slot, this.getChallengeButton(challenge).getItem()); - } - } - - return true; - }). - glow(this.addon.getChallengesSettings().isAddCompletedGlow() && - this.challengesManager.isChallengeComplete(this.user, this.world, challenge)). - build(); - } - - - /** - * 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 == this.lastSelectedLevel) - { - icon = level.getLevel().getIcon(); - description = GuiUtils.stringSplit( - this.generateLevelDescription(level.getLevel(), user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength()); - clickHandler = null; - glow = true; - } - else if (level.isUnlocked()) - { - icon = level.getLevel().getIcon(); - description = GuiUtils.stringSplit( - this.generateLevelDescription(level.getLevel(), user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength()); - clickHandler = (panel, user1, clickType, slot) -> { - this.lastSelectedLevel = level; - - // Reset page index for challenges. - this.pageIndex = 0; - - this.build(); - return true; - }; - glow = this.addon.getChallengesSettings().isAddCompletedGlow() && - this.challengesManager.isLevelCompleted(this.user, this.world, level.getLevel()); - } - else - { - if (level.getLevel().getLockedIcon() != null) - { - // Clone will prevent issues with description storing. - // It can be done only here as it can be null. - icon = level.getLevel().getLockedIcon().clone(); - } - else - { - icon = this.addon.getChallengesSettings().getLockedLevelIcon(); - } - - description = GuiUtils.stringSplit( - this.user.getTranslation("challenges.gui.descriptions.level-locked", - "[count]", Integer.toString(level.getNumberOfChallengesStillToDo()), - "[level]", level.getPreviousLevel().getFriendlyName()), - this.addon.getChallengesSettings().getLoreLineLength()); - - clickHandler = null; - glow = false; - } - - return new PanelItemBuilder(). - icon(icon). - name(ChatColor.translateAlternateColorCodes('&', name)). - description(description). - glow(glow). - clickHandler(clickHandler). - build(); - } - - -// --------------------------------------------------------------------- -// 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; - - /** - * This boolean indicates if in the world there exist challenges for displaying in GUI. - */ - private final boolean containsChallenges; -} diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java new file mode 100644 index 0000000..b99c44d --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java @@ -0,0 +1,818 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel.user; + + +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.TemplatedPanel; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder; +import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.config.SettingsUtils; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.tasks.TryToComplete; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.LevelStatus; +import world.bentobox.challenges.utils.Utils; + + +/** + * Main challenges panel builder. + */ +public class ChallengesPanel extends CommonPanel +{ + private ChallengesPanel(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + this.updateLevelList(); + this.containsChallenges = this.manager.hasAnyChallengeData(this.world); + } + + + /** + * Open the Challenges GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + new ChallengesPanel(addon, world, user, topLabel, permissionPrefix).build(); + } + + + protected void build() + { + // Do not open gui if there is no challenges. + if (!this.containsChallenges) + { + this.addon.logError("There are no challenges set up!"); + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-challenges")); + return; + } + + // Create lists for builder. + this.updateFreeChallengeList(); + this.updateChallengeList(); + // this.updateLevelList(); + + // Start building panel. + TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); + + // Set main template. + panelBuilder.template("main_panel", new File(this.addon.getDataFolder(), "panels")); + panelBuilder.user(this.user); + panelBuilder.world(this.user.getWorld()); + + // Register button builders + panelBuilder.registerTypeBuilder("CHALLENGE", this::createChallengeButton); + panelBuilder.registerTypeBuilder("LEVEL", this::createLevelButton); + + panelBuilder.registerTypeBuilder("UNASSIGNED_CHALLENGES", this::createFreeChallengesButton); + + panelBuilder.registerTypeBuilder("NEXT", this::createNextButton); + panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton); + + // Register unknown type builder. + panelBuilder.build(); + } + + + private void updateFreeChallengeList() + { + this.freeChallengeList = this.manager.getFreeChallenges(this.world); + + if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) + { + this.freeChallengeList.removeIf(challenge -> !challenge.isRepeatable() && + this.manager.isChallengeComplete(this.user, this.world, challenge)); + } + + // Remove all undeployed challenges if VisibilityMode is set to Hidden. + if (this.addon.getChallengesSettings().getVisibilityMode().equals(SettingsUtils.VisibilityMode.HIDDEN)) + { + this.freeChallengeList.removeIf(challenge -> !challenge.isDeployed()); + } + } + + + private void updateChallengeList() + { + if (this.lastSelectedLevel != null) + { + this.challengeList = this.manager.getLevelChallenges(this.lastSelectedLevel.getLevel()); + + if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) + { + this.challengeList.removeIf(challenge -> !challenge.isRepeatable() && + this.manager.isChallengeComplete(this.user, this.world, challenge)); + } + + // Remove all undeployed challenges if VisibilityMode is set to Hidden. + if (this.addon.getChallengesSettings().getVisibilityMode().equals(SettingsUtils.VisibilityMode.HIDDEN)) + { + this.challengeList.removeIf(challenge -> !challenge.isDeployed()); + } + } + else + { + this.challengeList = this.freeChallengeList; + } + } + + + /** + * Updates level status list and selects last unlocked level. + */ + private void updateLevelList() + { + this.levelList = this.manager.getAllChallengeLevelStatus(this.user, this.world); + + for (LevelStatus levelStatus : this.levelList) + { + if (levelStatus.isUnlocked()) + { + this.lastSelectedLevel = levelStatus; + } + else + { + break; + } + } + } + + + /** + * Updates level status list and returns if any new level has been unlocked. + * @return {code true} if a new level was unlocked, {@code false} otherwise. + */ + private boolean updateLevelListSilent() + { + Optional firstLockedLevel = + this.levelList.stream().filter(levelStatus -> !levelStatus.isUnlocked()).findFirst(); + + if (firstLockedLevel.isPresent()) + { + // If there still exist any locked level, update level status list. + this.levelList = this.manager.getAllChallengeLevelStatus(this.user, this.world); + + // Find a new first locked level. + Optional newLockedLevel = + this.levelList.stream().filter(levelStatus -> !levelStatus.isUnlocked()).findFirst(); + + return newLockedLevel.isEmpty() || + firstLockedLevel.get().getLevel() != newLockedLevel.get().getLevel(); + } + else + { + // If locked level is not present, return false. + return false; + } + } + + + @Nullable + private PanelItem createChallengeButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + if (this.challengeList.isEmpty()) + { + // Does not contain any free challenges. + return null; + } + + Challenge levelChallenge; + + // Check if that is a specific free challenge + if (template.dataMap().containsKey("id")) + { + String id = (String) template.dataMap().get("id"); + + // Find a challenge with given Id; + levelChallenge = this.challengeList.stream(). + filter(challenge -> challenge.getUniqueId().equals(id)). + findFirst(). + orElse(null); + + if (levelChallenge == null) + { + // There is no challenge in the list with specific id. + return null; + } + } + else + { + int index = this.challengeIndex * slot.amountMap().getOrDefault("CHALLENGE", 1) + slot.slot(); + + if (index >= this.challengeList.size()) + { + // Out of index. + return null; + } + + levelChallenge = this.challengeList.get(index); + } + + return this.createChallengeButton(template, levelChallenge); + } + + + @NonNull + private PanelItem createChallengeButton(ItemTemplateRecord template, @NonNull Challenge challenge) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + // Template specification are always more important than dynamic content. + builder.icon(template.icon() != null ? template.icon().clone() : challenge.getIcon()); + + // Template specific title is always more important than challenge name. + if (template.title() != null && !template.title().isBlank()) + { + builder.name(this.user.getTranslation(this.world, template.title(), + Constants.PARAMETER_CHALLENGE, challenge.getFriendlyName())); + } + else + { + builder.name(Util.translateColorCodes(challenge.getFriendlyName())); + } + + if (template.description() != null && !template.description().isBlank()) + { + // TODO: adding parameters could be useful. + builder.description(this.user.getTranslation(this.world, template.description())); + } + else + { + builder.description(this.generateChallengeDescription(challenge, this.user)); + } + + // Add Click handler + builder.clickHandler((panel, user, clickType, i) -> { + for (ItemTemplateRecord.ActionRecords action : template.actions()) + { + if (clickType == action.clickType()) + { + switch (action.actionType().toUpperCase()) + { + case "COMPLETE": + if (TryToComplete.complete(this.addon, + this.user, + challenge, + this.world, + this.topLabel, + this.permissionPrefix)) + { + if (this.updateLevelListSilent()) + { + // Need to rebuild all because completing a challenge + // may unlock a new level. #187 + this.build(); + } + else + { + // There was no unlocked levels. + panel.getInventory().setItem(i, + this.createChallengeButton(template, challenge).getItem()); + } + } + else if (challenge.isRepeatable() && challenge.getTimeout() > 0) + { + // Update timeout after clicking. + panel.getInventory().setItem(i, + this.createChallengeButton(template, challenge).getItem()); + } + break; + case "COMPLETE_MAX": + if (challenge.isRepeatable()) + { + if (TryToComplete.complete(this.addon, + this.user, + challenge, + this.world, + this.topLabel, + this.permissionPrefix, + Integer.MAX_VALUE)) + { + if (this.updateLevelListSilent()) + { + // Need to rebuild all because completing a challenge + // may unlock a new level. #187 + this.build(); + } + else + { + // There was no unlocked levels. + panel.getInventory().setItem(i, + this.createChallengeButton(template, challenge).getItem()); + } + } + else if (challenge.getTimeout() > 0) + { + // Update timeout after clicking. + panel.getInventory().setItem(i, + this.createChallengeButton(template, challenge).getItem()); + } + } + break; + case "MULTIPLE_PANEL": + if (challenge.isRepeatable()) + { + MultiplePanel.open(this.addon, this.user, value -> + { + TryToComplete.complete(this.addon, + this.user, + challenge, + this.world, + this.topLabel, + this.permissionPrefix, + value); + + this.updateLevelListSilent(); + this.build(); + }); + } + break; + } + } + } + + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + filter(action -> challenge.isRepeatable() || "COMPLETE".equalsIgnoreCase(action.actionType())). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + // Glow the icon. + builder.glow(this.addon.getChallengesSettings().isAddCompletedGlow() && + this.manager.isChallengeComplete(this.user, this.world, challenge)); + + // Click Handlers are managed by custom addon buttons. + return builder.build(); + } + + + @Nullable + private PanelItem createLevelButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + if (this.levelList.isEmpty()) + { + // Does not contain any levels. + return null; + } + + LevelStatus level; + + // Check if that is a specific level + if (template.dataMap().containsKey("id")) + { + String id = (String) template.dataMap().get("id"); + + // Find a challenge with given Id; + level = this.levelList.stream(). + filter(levelStatus -> levelStatus.getLevel().getUniqueId().equals(id)). + findFirst(). + orElse(null); + + if (level == null) + { + // There is no challenge in the list with specific id. + return null; + } + } + else + { + int index = this.levelIndex * slot.amountMap().getOrDefault("LEVEL", 1) + slot.slot(); + + if (index >= this.levelList.size()) + { + // Out of index. + return null; + } + + level = this.levelList.get(index); + } + + return this.createLevelButton(template, level); + } + + + @NonNull + private PanelItem createLevelButton(ItemTemplateRecord template, @NonNull LevelStatus level) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + // Template specification are always more important than dynamic content. + if (template.icon() != null) + { + builder.icon(template.icon().clone()); + } + else + { + if (level.isUnlocked()) + { + builder.icon(level.getLevel().getIcon()); + } + else if (level.getLevel().getLockedIcon() != null) + { + // Clone will prevent issues with description storing. + // It can be done only here as it can be null. + builder.icon(level.getLevel().getLockedIcon().clone()); + } + else + { + builder.icon(this.addon.getChallengesSettings().getLockedLevelIcon()); + } + } + + if (template.title() != null && !template.title().isBlank()) + { + builder.name(this.user.getTranslation(this.world, template.title(), + Constants.PARAMETER_LEVEL, level.getLevel().getFriendlyName())); + } + else + { + builder.name(Util.translateColorCodes(level.getLevel().getFriendlyName())); + } + + if (template.description() != null && !template.description().isBlank()) + { + // TODO: adding parameters could be useful. + builder.description(this.user.getTranslation(this.world, template.description())); + } + else + { + // TODO: Complete description generate. + builder.description(this.generateLevelDescription(level, this.user)); + } + + // Add click handler + builder.clickHandler((panel, user, clickType, i) -> { + if (level != this.lastSelectedLevel && level.isUnlocked()) + { + this.lastSelectedLevel = level; + this.challengeIndex = 0; + + this.build(); + } + + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + filter(action -> level != this.lastSelectedLevel && level.isUnlocked()). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + // Glow the icon. + builder.glow(level == this.lastSelectedLevel || + level.isUnlocked() && + this.addon.getChallengesSettings().isAddCompletedGlow() && + this.manager.isLevelCompleted(this.user, this.world, level.getLevel())); + + // Click Handlers are managed by custom addon buttons. + return builder.build(); + } + + + @Nullable + private PanelItem createFreeChallengesButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + if (this.freeChallengeList.isEmpty()) + { + // There are no free challenges for selection. + return null; + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + builder.icon(template.icon().clone()); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description())); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + if (this.lastSelectedLevel != null) + { + this.lastSelectedLevel = null; + this.build(); + } + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + filter(action -> this.lastSelectedLevel == null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @Nullable + private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + String target = template.dataMap().getOrDefault("target", "").toString().toUpperCase(); + + int nextPageIndex; + + switch (target) + { + case "CHALLENGE" -> { + int size = this.challengeList.size(); + + if (size <= slot.amountMap().getOrDefault("CHALLENGE", 1) || + 1.0 * size / slot.amountMap().getOrDefault("CHALLENGE", 1) <= this.challengeIndex + 1) + { + // There are no next elements + return null; + } + + nextPageIndex = this.challengeIndex + 2; + } + case "LEVEL" -> { + int size = this.levelList.size(); + + if (size <= slot.amountMap().getOrDefault("LEVEL", 1) || + 1.0 * size / slot.amountMap().getOrDefault("LEVEL", 1) <= this.levelIndex + 1) + { + // There are no next elements + return null; + } + + nextPageIndex = this.levelIndex + 2; + } + default -> { + // If not assigned to any type, return null. + return null; + } + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + + if ((Boolean) template.dataMap().getOrDefault("indexing", false)) + { + clone.setAmount(nextPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(nextPageIndex))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + // Next button ignores click type currently. + switch (target) + { + case "CHALLENGE" -> this.challengeIndex++; + case "LEVEL" -> this.levelIndex++; + } + + this.build(); + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @Nullable + private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + String target = template.dataMap().getOrDefault("target", "").toString().toUpperCase(); + + int previousPageIndex; + + if ("CHALLENGE".equals(target)) + { + if (this.challengeIndex == 0) + { + // There are no next elements + return null; + } + + previousPageIndex = this.challengeIndex; + } + else if ("LEVEL".equals(target)) + { + if (this.levelIndex == 0) + { + // There are no next elements + return null; + } + + previousPageIndex = this.levelIndex; + } + else + { + // If not assigned to any type, return null. + return null; + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + + if ((Boolean) template.dataMap().getOrDefault("indexing", false)) + { + clone.setAmount(previousPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(previousPageIndex))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + // Next button ignores click type currently. + switch (target) + { + case "CHALLENGE" -> this.challengeIndex--; + case "LEVEL" -> this.levelIndex--; + } + + this.build(); + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This boolean indicates if in the world there exist challenges for displaying in GUI. + */ + private final boolean containsChallenges; + + /** + * This list contains free challenges in current Panel. + */ + private List freeChallengeList; + + /** + * This will be used if levels are more than 18. + */ + private int levelIndex; + + /** + * This list contains all information about level completion in current world. + */ + private List levelList; + + /** + * This will be used if free challenges are more than 18. + */ + private int challengeIndex; + + /** + * This list contains challenges in current Panel. + */ + private List challengeList; + + /** + * This indicates last selected level. + */ + private LevelStatus lastSelectedLevel; +} diff --git a/src/main/java/world/bentobox/challenges/panel/user/GameModePanel.java b/src/main/java/world/bentobox/challenges/panel/user/GameModePanel.java new file mode 100644 index 0000000..c8ed477 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/user/GameModePanel.java @@ -0,0 +1,385 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel.user; + + +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.TemplatedPanel; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder; +import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.utils.Constants; + + +/** + * Main challenges panel builder. + */ +public class GameModePanel extends CommonPanel +{ + private GameModePanel(ChallengesAddon addon, + World world, + User user, + List addonList, + boolean adminMode) + { + super(addon, user, world, null, null); + this.addonList = addonList; + this.adminMode = adminMode; + } + + + /** + * Open the Challenges GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param addonList List of gamemode addons + * @param adminMode Indicate if admin mode. + */ + public static void open(ChallengesAddon addon, + World world, + User user, + List addonList, + boolean adminMode) + { + new GameModePanel(addon, world, user, addonList, adminMode).build(); + } + + + protected void build() + { + // Start building panel. + TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); + + // Set main template. + panelBuilder.template("gamemode_panel", new File(this.addon.getDataFolder(), "panels")); + panelBuilder.user(this.user); + panelBuilder.world(this.user.getWorld()); + + // Register button builders + panelBuilder.registerTypeBuilder("GAMEMODE", this::createGameModeButton); + + panelBuilder.registerTypeBuilder("NEXT", this::createNextButton); + panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton); + + // Register unknown type builder. + panelBuilder.build(); + } + + + @Nullable + private PanelItem createGameModeButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + if (this.addonList.isEmpty()) + { + // Does not contain any free challenges. + return null; + } + + GameModeAddon gameModeAddon; + + // Check if that is a specific free challenge + if (template.dataMap().containsKey("id")) + { + String id = (String) template.dataMap().get("id"); + + // Find a challenge with given Id; + gameModeAddon = this.addonList.stream(). + filter(gamemode -> gamemode.getDescription().getName().equals(id)). + findFirst(). + orElse(null); + + if (gameModeAddon == null) + { + // There is no gamemode in the list with specific id. + return null; + } + } + else + { + int index = this.addonIndex * slot.amountMap().getOrDefault("GAMEMODE", 1) + slot.slot(); + + if (index >= this.addonList.size()) + { + // Out of index. + return null; + } + + gameModeAddon = this.addonList.get(index); + } + + return this.createGameModeButton(template, gameModeAddon); + } + + + @NonNull + private PanelItem createGameModeButton(ItemTemplateRecord template, @NonNull GameModeAddon gameModeAddon) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + // Template specification are always more important than dynamic content. + builder.icon(template.icon() != null ? + template.icon().clone() : + new ItemStack(gameModeAddon.getDescription().getIcon())); + + // Template specific title is always more important than challenge name. + if (template.title() != null && !template.title().isBlank()) + { + builder.name(this.user.getTranslation(this.world, template.title(), + Constants.PARAMETER_GAMEMODE, gameModeAddon.getDescription().getName())); + } + else + { + builder.name(Util.translateColorCodes(gameModeAddon.getDescription().getName())); + } + + if (template.description() != null && !template.description().isBlank()) + { + // TODO: adding parameters could be useful. + builder.description(this.user.getTranslation(this.world, template.description())); + } + else + { + builder.description(gameModeAddon.getDescription().getDescription()); + } + + // Add Click handler + builder.clickHandler((panel, user, clickType, i) -> { + for (ItemTemplateRecord.ActionRecords action : template.actions()) + { + if (clickType == action.clickType()) + { + if (this.adminMode) + { + gameModeAddon.getAdminCommand().ifPresent(compositeCommand -> + user.performCommand(compositeCommand.getTopLabel() + " " + + this.addon.getChallengesSettings().getAdminMainCommand().split(" ")[0])); + } + else + { + gameModeAddon.getPlayerCommand().ifPresent(compositeCommand -> + user.performCommand(compositeCommand.getTopLabel() + " " + + this.addon.getChallengesSettings().getPlayerMainCommand().split(" ")[0])); + } + } + } + + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + // Glow the icon. + builder.glow(gameModeAddon.inWorld(this.user.getWorld())); + + // Click Handlers are managed by custom addon buttons. + return builder.build(); + } + + + @Nullable + private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + String target = template.dataMap().getOrDefault("target", "").toString().toUpperCase(); + + int nextPageIndex; + + if ("GAMEMODE".equals(target)) + { + int size = this.addonList.size(); + + if (size <= slot.amountMap().getOrDefault("GAMEMODE", 1) || + 1.0 * size / slot.amountMap().getOrDefault("GAMEMODE", 1) <= this.addonIndex + 1) + { + // There are no next elements + return null; + } + + nextPageIndex = this.addonIndex + 2; + } + else + {// If not assigned to any type, return null. + return null; + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + + if ((Boolean) template.dataMap().getOrDefault("indexing", false)) + { + clone.setAmount(nextPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description()), + Constants.PARAMETER_NUMBER, String.valueOf(nextPageIndex)); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + this.addonIndex++; + this.build(); + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @Nullable + private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + String target = template.dataMap().getOrDefault("target", "").toString().toUpperCase(); + + int previousPageIndex; + + if ("GAMEMODE".equals(target)) + { + if (this.addonIndex == 0) + { + // There are no next elements + return null; + } + + previousPageIndex = this.addonIndex; + } + else + { + // If not assigned to any type, return null. + return null; + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + + if ((Boolean) template.dataMap().getOrDefault("indexing", false)) + { + clone.setAmount(previousPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description()), + Constants.PARAMETER_NUMBER, String.valueOf(previousPageIndex)); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + // Next button ignores click type currently. + this.addonIndex--; + this.build(); + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This will be used if free challenges are more than 18. + */ + private int addonIndex; + + /** + * This list contains challenges in current Panel. + */ + private final List addonList; + + /** + * Indicate if gui is for players or admins. + */ + private final boolean adminMode; +} diff --git a/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java b/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java deleted file mode 100644 index 26a9690..0000000 --- a/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java +++ /dev/null @@ -1,215 +0,0 @@ - -package world.bentobox.challenges.panel.user; - -import java.util.function.Consumer; - -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -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.utils.GuiUtils; - - -/** - * This GUI will pop out when user uses right click on challenge. It is meant to choose - * how many times player will complete challenges. - */ -public class MultipleGUI -{ - /** - * Default constructor. - * @param user User who opens gui. - * @param lineLength Length of lore message. - * @param action Action that will be performed on value clicking. - */ - public MultipleGUI(User user, int lineLength, Consumer action) - { - this.user = user; - this.lineLength = lineLength; - this.action = action; - - this.build(); - } - - /** - * This method builds panel that allows to change given number value. - */ - private void build() - { - PanelBuilder panelBuilder = new PanelBuilder(). - user(this.user). - type(Panel.Type.HOPPER). - name(this.user.getTranslation("challenges.gui.title.multiple-complete")); - - panelBuilder.item(2, this.getButton(Button.VALUE)); - - // Reduce - panelBuilder.item(0, this.getButton(Button.REDUCE_LOT)); - panelBuilder.item(1, this.getButton(Button.REDUCE)); - - // Increase - panelBuilder.item(3, this.getButton(Button.INCREASE)); - panelBuilder.item(4, this.getButton(Button.INCREASE_LOT)); - - 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; - String description; - PanelItem.ClickHandler clickHandler; - boolean glow; - - switch (button) - { - case VALUE: - { - name = this.user.getTranslation("challenges.gui.buttons.value"); - description = this.user.getTranslation("challenges.gui.descriptions.current-value", "[value]", Integer.toString(this.value)); - icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.action.accept(this.value); - return true; - }; - glow = false; - break; - } - case INCREASE: - { - name = this.user.getTranslation("challenges.gui.buttons.increase"); - description = this.user.getTranslation("challenges.gui.descriptions.increase-by", "[value]", "1"); - icon = new ItemStack(Material.BLUE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value++; - // Necessary just to update second item - panel.getInventory().setItem(2, this.getButton(Button.VALUE).getItem()); - return true; - }; - glow = false; - break; - } - case INCREASE_LOT: - { - name = this.user.getTranslation("challenges.gui.buttons.increase"); - description = this.user.getTranslation("challenges.gui.descriptions.increase-by", "[value]", "5"); - icon = new ItemStack(Material.MAGENTA_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value += 5; - // Necessary just to update second item - panel.getInventory().setItem(2, this.getButton(Button.VALUE).getItem()); - return true; - }; - glow = false; - break; - } - case REDUCE: - { - name = this.user.getTranslation("challenges.gui.buttons.reduce"); - description = this.user.getTranslation("challenges.gui.descriptions.reduce-by", "[value]", "1"); - icon = new ItemStack(Material.ORANGE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value--; - - if (this.value < 1) - { - this.value = 1; - } - - // Necessary just to update second item - panel.getInventory().setItem(2, this.getButton(Button.VALUE).getItem()); - - return true; - }; - glow = false; - break; - } - case REDUCE_LOT: - { - name = this.user.getTranslation("challenges.gui.buttons.reduce"); - description = this.user.getTranslation("challenges.gui.descriptions.reduce-by", "[value]", "5"); - icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value -= 5; - - if (this.value < 1) - { - this.value = 1; - } - - // Necessary just to update second item - panel.getInventory().setItem(2, this.getButton(Button.VALUE).getItem()); - - return true; - }; - glow = false; - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Enums -// --------------------------------------------------------------------- - - - /** - * This enum allows to easier define available buttons. - */ - enum Button - { - VALUE, - REDUCE, - REDUCE_LOT, - INCREASE, - INCREASE_LOT - } - - -// --------------------------------------------------------------------- -// Section: Instance variables -// --------------------------------------------------------------------- - - - /** - * This variable allows to access to user object. - */ - private User user; - - /** - * This variable holds action that will be performed on accept. - */ - private Consumer action; - - /** - * This variable holds a number of characters in single line for lore message. - */ - private int lineLength; - - /** - * This integer holds current value of completion count. - */ - private int value = 1; -} diff --git a/src/main/java/world/bentobox/challenges/panel/user/MultiplePanel.java b/src/main/java/world/bentobox/challenges/panel/user/MultiplePanel.java new file mode 100644 index 0000000..011f66a --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/user/MultiplePanel.java @@ -0,0 +1,282 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel.user; + + +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.TemplatedPanel; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder; +import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; + + +public class MultiplePanel +{ + private MultiplePanel(ChallengesAddon addon, User user, Consumer action) + { + this.addon = addon; + this.user = user; + this.action = action; + } + + + public static void open(ChallengesAddon addon, User user, Consumer action) + { + new MultiplePanel(addon, user, action).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + private void build() + { + // Start building panel. + TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); + + // Set main template. + panelBuilder.template("multiple_panel", new File(this.addon.getDataFolder(), "panels")); + panelBuilder.user(this.user); + panelBuilder.world(this.user.getWorld()); + + // Register button builders + panelBuilder.registerTypeBuilder("INCREASE", this::createIncreaseButton); + panelBuilder.registerTypeBuilder("REDUCE", this::createReduceButton); + panelBuilder.registerTypeBuilder("ACCEPT", this::createValueButton); + + // Register unknown type builder. + panelBuilder.build(); + } + + + @NonNull + private PanelItem createIncreaseButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot itemSlot) + { + int increaseValue = (int) template.dataMap().getOrDefault("value", 1); + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + clone.setAmount(increaseValue); + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(increaseValue))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + this.completionValue += increaseValue; + this.build(); + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @NonNull + private PanelItem createReduceButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot itemSlot) + { + int decreaseValue = (int) template.dataMap().getOrDefault("value", 1); + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + clone.setAmount(decreaseValue); + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(decreaseValue))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + this.completionValue = Math.max(this.completionValue - decreaseValue, 1); + this.build(); + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @NonNull + private PanelItem createValueButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot itemSlot) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + clone.setAmount(this.completionValue); + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(completionValue))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + for (ItemTemplateRecord.ActionRecords actionRecords : template.actions()) + { + if (clickType == actionRecords.clickType()) + { + if (actionRecords.actionType().equalsIgnoreCase("input")) + { + // Input consumer. + Consumer numberConsumer = number -> + { + if (number != null) + { + this.completionValue = number.intValue(); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 1, + 2000); + } + else if (actionRecords.actionType().equalsIgnoreCase("accept")) + { + this.action.accept(this.completionValue); + } + } + } + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Variable stores user who created this panel. + */ + private final User user; + + /** + * This variable holds action that will be performed on accept. + */ + private final Consumer action; + + /** + * Variable stores Challenges addon. + */ + protected final ChallengesAddon addon; + + /** + * Local storing of selected value. + */ + private int completionValue = 1; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ChallengeSelector.java b/src/main/java/world/bentobox/challenges/panel/util/ChallengeSelector.java new file mode 100644 index 0000000..c24b355 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/ChallengeSelector.java @@ -0,0 +1,253 @@ +package world.bentobox.challenges.panel.util; + + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +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.util.Util; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.utils.Constants; + + +/** + * This class creates new GUI that allows to select single challenge, which is returned via consumer. + */ +public class ChallengeSelector extends PagedSelector +{ + private ChallengeSelector(User user, Material border, Map> challengesDescriptionMap, BiConsumer> consumer) + { + super(user); + this.consumer = consumer; + this.challengesDescriptionMap = challengesDescriptionMap; + this.border = border; + + this.elements = challengesDescriptionMap.keySet().stream().toList(); + this.selectedElements = new HashSet<>(this.elements.size()); + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Material border, Map> challengesDescriptionMap, BiConsumer> consumer) + { + new ChallengeSelector(user, border, challengesDescriptionMap, consumer).build(); + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "challenge-selector")); + + PanelUtils.fillBorder(panelBuilder, this.border); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(3, this.createButton(Button.ACCEPT_SELECTED)); + panelBuilder.item(5, this.createButton(Button.CANCEL)); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.getUniqueId().toLowerCase(). + contains(this.searchString.toLowerCase()) || + element.getFriendlyName().toLowerCase(). + contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case ACCEPT_SELECTED -> { + if (!this.selectedElements.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedElements.forEach(challenge -> + description.add(this.user.getTranslation(reference + "element", + "[element]", challenge.getFriendlyName()))); + } + + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(true, this.selectedElements); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); + } + case CANCEL -> { + + icon = new ItemStack(Material.IRON_DOOR); + + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for given challenge. + * @param challenge challenge which button must be created. + * @return new Button for challenge. + */ + @Override + protected PanelItem createElementButton(Challenge challenge) + { + final String reference = Constants.BUTTON + "entity."; + + List description = new ArrayList<>(this.challengesDescriptionMap.get(challenge)); + description.add(""); + + if (this.selectedElements.contains(challenge)) + { + description.add(this.user.getTranslation(reference + "selected")); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-deselect")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + } + + return new PanelItemBuilder(). + name(Util.translateColorCodes(challenge.getFriendlyName())). + icon(challenge.getIcon()). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (!this.selectedElements.add(challenge)) + { + // Remove challenge if it is already selected + this.selectedElements.remove(challenge); + } + + this.build(); + return true; + }). + glow(this.selectedElements.contains(challenge)). + build(); + } + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ACCEPT_SELECTED, + CANCEL + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * This variable stores consumer. + */ + private final BiConsumer> consumer; + + /** + * Current value. + */ + private final List elements; + + /** + * Selected challenges that will be returned to consumer. + */ + private final Set selectedElements; + + /** + * Map that contains all challenge descriptions + */ + private final Map> challengesDescriptionMap; + + /** + * Border Material. + */ + private final Material border; + + /** + * Current value. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java deleted file mode 100644 index 68bf018..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java +++ /dev/null @@ -1,146 +0,0 @@ -// -// Created by BONNe -// Copyright - 2019 -// - - -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.List; -import java.util.function.BiConsumer; - -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -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.database.object.Challenge; -import world.bentobox.challenges.database.object.requirements.InventoryRequirements; -import world.bentobox.challenges.database.object.requirements.IslandRequirements; -import world.bentobox.challenges.database.object.requirements.OtherRequirements; -import world.bentobox.challenges.database.object.requirements.Requirements; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class creates GUI that allows to select challenge type. - */ -public class ChallengeTypeGUI -{ - /** - * Default constructor that builds gui. - * @param user User who opens GUI. - * @param lineLength Lore line length - * @param consumer Consumer that allows to get clicked type. - */ - private ChallengeTypeGUI(User user, int lineLength, BiConsumer consumer) - { - this.user = user; - this.lineLength = lineLength; - this.consumer = consumer; - } - - - /** - * This method opens GUI that allows to select challenge type. - * @param user User who opens GUI. - * @param lineLength Lore line length - * @param consumer Consumer that allows to get clicked type. - */ - public static void open(User user, int lineLength, BiConsumer consumer) - { - new ChallengeTypeGUI(user, lineLength, consumer).build(); - } - - - /** - * This method builds GUI that allows to select challenge type. - */ - private void build() - { - PanelBuilder panelBuilder = new PanelBuilder(). - user(this.user). - type(Panel.Type.HOPPER). - name(this.user.getTranslation("challenges.gui.title.admin.type-select")); - - panelBuilder.item(0, this.getButton(Challenge.ChallengeType.INVENTORY)); - panelBuilder.item(1, this.getButton(Challenge.ChallengeType.ISLAND)); - panelBuilder.item(2, this.getButton(Challenge.ChallengeType.OTHER)); - - panelBuilder.build(); - } - - - /** - * Creates ChallengeType button. - * @param type Challenge type which button must be created. - * @return PanelItem button. - */ - private PanelItem getButton(Challenge.ChallengeType type) - { - ItemStack icon; - String name = this.user.getTranslation("challenges.gui.buttons.admin.type." + type.name().toLowerCase()); - List description = new ArrayList<>(); - description.add(this.user.getTranslation("challenges.gui.descriptions.type." + type.name().toLowerCase())); - PanelItem.ClickHandler clickHandler; - - switch (type) - { - case INVENTORY: - icon = new ItemStack(Material.CHEST); - clickHandler = ((panel, user1, clickType, slot) -> { - this.consumer.accept(type, new InventoryRequirements()); - return true; - }); - break; - case ISLAND: - icon = new ItemStack(Material.GRASS_BLOCK); - clickHandler = ((panel, user1, clickType, slot) -> { - this.consumer.accept(type, new IslandRequirements()); - return true; - }); - break; - case OTHER: - icon = new ItemStack(Material.EXPERIENCE_BOTTLE); - clickHandler = ((panel, user1, clickType, slot) -> { - this.consumer.accept(type, new OtherRequirements()); - return true; - }); - break; - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - clickHandler(clickHandler). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * User who runs GUI. - */ - private final User user; - - /** - * Lore line max length. - */ - private final int lineLength; - - /** - * Consumer that returns Challenge Type. - */ - private final BiConsumer consumer; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeSelector.java b/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeSelector.java new file mode 100644 index 0000000..024f441 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeSelector.java @@ -0,0 +1,133 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.challenges.panel.util; + + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +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.database.object.Challenge; +import world.bentobox.challenges.database.object.requirements.*; +import world.bentobox.challenges.utils.Constants; + + +/** + * This class creates GUI that allows to select challenge type. + */ +public record ChallengeTypeSelector(User user, BiConsumer consumer) +{ + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, BiConsumer consumer) + { + new ChallengeTypeSelector(user, consumer).build(); + } + + + /** + * This method builds GUI that allows to select challenge type. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder(). + user(this.user). + type(Panel.Type.HOPPER). + name(this.user.getTranslation(Constants.TITLE + "type-selector")); + + panelBuilder.item(0, this.getButton(Challenge.ChallengeType.INVENTORY_TYPE)); + panelBuilder.item(1, this.getButton(Challenge.ChallengeType.ISLAND_TYPE)); + panelBuilder.item(2, this.getButton(Challenge.ChallengeType.OTHER_TYPE)); + panelBuilder.item(3, this.getButton(Challenge.ChallengeType.STATISTIC_TYPE)); + + panelBuilder.build(); + } + + + /** + * Creates ChallengeType button. + * + * @param type Challenge type which button must be created. + * @return PanelItem button. + */ + private PanelItem getButton(Challenge.ChallengeType type) + { + final String reference = Constants.BUTTON + type.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (type) + { + case INVENTORY_TYPE -> { + icon = new ItemStack(Material.CHEST); + clickHandler = ( + (panel, user1, clickType, slot) -> + { + this.consumer.accept(type, new InventoryRequirements()); + return true; + }); + } + case ISLAND_TYPE -> { + icon = new ItemStack(Material.GRASS_BLOCK); + clickHandler = ( + (panel, user1, clickType, slot) -> + { + this.consumer.accept(type, new IslandRequirements()); + return true; + }); + } + case OTHER_TYPE -> { + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = ( + (panel, user1, clickType, slot) -> + { + this.consumer.accept(type, new OtherRequirements()); + return true; + }); + } + case STATISTIC_TYPE -> { + icon = new ItemStack(Material.BOOK); + clickHandler = ( + (panel, user1, clickType, slot) -> + { + this.consumer.accept(type, new StatisticRequirements()); + return true; + }); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java deleted file mode 100644 index 5a979cc..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java +++ /dev/null @@ -1,114 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.function.Consumer; - -import org.bukkit.Material; - -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.title.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.buttons.admin." + (returnValue ? "accept" : "cancel"))). - icon(returnValue ? Material.GREEN_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/EnvironmentSelector.java b/src/main/java/world/bentobox/challenges/panel/util/EnvironmentSelector.java new file mode 100644 index 0000000..23c8adf --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/EnvironmentSelector.java @@ -0,0 +1,195 @@ +package world.bentobox.challenges.panel.util; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; + +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.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class creates panel that allows to select and deselect World Environments. On save it runs + * input consumer with true and selected values. + */ +public record EnvironmentSelector(User user, Set values, BiConsumer> consumer) +{ + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Set values, BiConsumer> consumer) + { + new EnvironmentSelector(user, values, consumer).build(); + } + + + /** + * This method builds GUI that allows to select challenge type. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder(). + user(this.user). + type(Panel.Type.HOPPER). + name(this.user.getTranslation(Constants.TITLE + "environment-selector")); + + panelBuilder.item(0, this.getButton(World.Environment.NORMAL)); + panelBuilder.item(1, this.getButton(World.Environment.NETHER)); + panelBuilder.item(2, this.getButton(World.Environment.THE_END)); + panelBuilder.item(3, this.getButton(Button.ACCEPT_SELECTED)); + panelBuilder.item(4, this.getButton(Button.CANCEL)); + + panelBuilder.build(); + } + + + /** + * This method create button that does some functionality in current gui. + * + * @param environment Environment + * @return PanelItem. + */ + private PanelItem getButton(World.Environment environment) + { + final String reference = Constants.BUTTON + "environment_element."; + + String name = this.user.getTranslation(reference + "name", + "[environment]", Utils.prettifyObject(environment, this.user)); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslationOrNothing(reference + "description", + "[description]", Utils.prettifyDescription(environment, this.user))); + + if (this.values.contains(environment)) + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-deselect")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + } + + PanelItem.ClickHandler clickHandler = (panel, user, clickType, slot) -> + { + if (this.values.contains(environment)) + { + this.values.remove(environment); + } + else + { + this.values.add(environment); + } + + this.build(); + return true; + }; + + ItemStack icon; + + switch (environment) + { + case NORMAL -> icon = new ItemStack(Material.DIRT); + case NETHER -> icon = new ItemStack(Material.NETHERRACK); + case THE_END -> icon = new ItemStack(Material.END_STONE); + default -> icon = new ItemStack(Material.PAPER); + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + glow(this.values.contains(environment)). + build(); + } + + + /** + * This method create button that does some functionality in current gui. + * + * @param button Button functionality. + * @return PanelItem. + */ + private PanelItem getButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case ACCEPT_SELECTED -> { + if (!this.values.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.values.forEach(element -> + description.add(this.user.getTranslation(reference + "element", + "[element]", Utils.prettifyObject(element, this.user)))); + } + + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user, clickType, slot) -> + { + this.consumer.accept(true, this.values); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); + } + case CANCEL -> { + icon = new ItemStack(Material.IRON_DOOR); + clickHandler = (panel, user, clickType, slot) -> + { + this.consumer.accept(false, Collections.emptySet()); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This enum holds all button values in current gui. + */ + private enum Button + { + CANCEL, + ACCEPT_SELECTED + } +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ItemSelector.java similarity index 66% rename from src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java rename to src/main/java/world/bentobox/challenges/panel/util/ItemSelector.java index e7caf56..605a2fe 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/ItemSelector.java @@ -16,21 +16,23 @@ import world.bentobox.bentobox.api.panels.PanelListener; 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; +import world.bentobox.challenges.utils.Constants; /** * This class allows to change Input ItemStacks to different ItemStacks. */ -public class ItemSwitchGUI +public record ItemSelector(User user, List itemStacks, BiConsumer> consumer) { - public ItemSwitchGUI(User user, List itemStacks, int lineLength, BiConsumer> consumer) + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, List itemStacks, BiConsumer> consumer) { - this.consumer = consumer; - this.user = user; - this.itemStacks = itemStacks; - this.lineLength = lineLength; - this.build(); + new ItemSelector(user, itemStacks, consumer).build(); } @@ -39,7 +41,9 @@ public class ItemSwitchGUI */ private void build() { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.title.admin.manage-items")); + PanelBuilder panelBuilder = new PanelBuilder(). + user(this.user). + name(this.user.getTranslation(Constants.TITLE + "item-selector")); // Size of inventory that user can set via GUI. panelBuilder.size(45); @@ -66,24 +70,27 @@ public class ItemSwitchGUI /** * This method create button that does some functionality in current gui. + * * @param button Button functionality. * @return PanelItem. */ private PanelItem getButton(Button button) { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + ItemStack icon; - String name; - List description; PanelItem.ClickHandler clickHandler; switch (button) { - case SAVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.save"); - description = Collections.emptyList(); + case SAVE -> { icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { + 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); @@ -102,36 +109,37 @@ public class ItemSwitchGUI return true; }; - break; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); } - case CANCEL: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.cancel"); - description = Collections.emptyList(); + case CANCEL -> { icon = new ItemStack(Material.IRON_DOOR); - clickHandler = (panel, user, clickType, slot) -> { + clickHandler = (panel, user, clickType, slot) -> + { this.consumer.accept(false, Collections.emptyList()); return true; }; - break; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); } - case EMPTY: - { - name = ""; - description = Collections.emptyList(); + case EMPTY -> { + description.clear(); + name = "&r"; icon = new ItemStack(Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> true; - break; + clickHandler = null; + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; } - default: - return null; } return new PanelItemBuilder(). icon(icon). name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(false). + description(description). clickHandler(clickHandler). build(); } @@ -143,11 +151,10 @@ public class ItemSwitchGUI /** - * 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. + * 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 + private static class CustomPanelItem extends PanelItem { CustomPanelItem(ItemStack item) { @@ -190,7 +197,7 @@ public class ItemSwitchGUI /** * This CustomPanelListener allows to move items in current panel. */ - private class CustomPanelListener implements PanelListener + private static class CustomPanelListener implements PanelListener { @Override public void setup() @@ -227,30 +234,4 @@ public class ItemSwitchGUI 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; - - /** - * This variable stores how large line can be, before warp it. - */ - private int lineLength; } diff --git a/src/main/java/world/bentobox/challenges/panel/util/MultiBlockSelector.java b/src/main/java/world/bentobox/challenges/panel/util/MultiBlockSelector.java new file mode 100644 index 0000000..e847c97 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/MultiBlockSelector.java @@ -0,0 +1,294 @@ +package world.bentobox.challenges.panel.util; + + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +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.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * 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 MultiBlockSelector extends PagedSelector +{ + private MultiBlockSelector(User user, Mode mode, Set excluded, BiConsumer> consumer) + { + super(user); + this.consumer = consumer; + + // Current GUI cannot display air blocks. It crashes with null-pointer + excluded.add(Material.AIR); + excluded.add(Material.CAVE_AIR); + excluded.add(Material.VOID_AIR); + + // Piston head and moving piston is not necessary. useless. + excluded.add(Material.PISTON_HEAD); + excluded.add(Material.MOVING_PISTON); + + // Barrier cannot be accessible to user. + excluded.add(Material.BARRIER); + + this.selectedElements = new HashSet<>(); + + this.elements = Arrays.stream(Material.values()). + filter(material -> !excluded.contains(material)). + filter(material -> { + switch (mode) + { + case BLOCKS -> { + return material.isBlock(); + } + case ITEMS -> { + return material.isItem(); + } + default -> { + return true; + } + } + }). + // Sort by name + sorted(Comparator.comparing(Material::name)). + collect(Collectors.toList()); + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Mode mode, Set excluded, BiConsumer> consumer) + { + new MultiBlockSelector(user, mode, excluded, consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, BiConsumer> consumer) + { + new MultiBlockSelector(user, Mode.ANY, new HashSet<>(), consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "block-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(3, this.createButton(Button.ACCEPT_SELECTED)); + panelBuilder.item(5, this.createButton(Button.CANCEL)); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case ACCEPT_SELECTED -> { + if (!this.selectedElements.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedElements.forEach(material -> + description.add(this.user.getTranslation(reference + "element", + "[element]", Utils.prettifyObject(material, this.user)))); + } + + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(true, this.selectedElements); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); + } + case CANCEL -> { + + icon = new ItemStack(Material.IRON_DOOR); + + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for given material. + * @param material material which button must be created. + * @return new Button for material. + */ + @Override + protected PanelItem createElementButton(Material material) + { + final String reference = Constants.BUTTON + "material."; + + List description = new ArrayList<>(); + description.add(this.user.getTranslation(reference + "description", + "[id]", material.name())); + + if (this.selectedElements.contains(material)) + { + description.add(this.user.getTranslation(reference + "selected")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-deselect")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[material]", + Utils.prettifyObject(material, this.user))). + icon(PanelUtils.getMaterialItem(material)). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (!this.selectedElements.add(material)) + { + // Remove material if it is already selected + this.selectedElements.remove(material); + } + + this.build(); + return true; + }). + glow(this.selectedElements.contains(material)). + build(); + } + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ACCEPT_SELECTED, + CANCEL + } + + + public enum Mode + { + BLOCKS, + ITEMS, + ANY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * Set that contains selected materials. + */ + private final Set selectedElements; + + /** + * This variable stores consumer. + */ + private final BiConsumer> consumer; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/MultiEntitySelector.java b/src/main/java/world/bentobox/challenges/panel/util/MultiEntitySelector.java new file mode 100644 index 0000000..fefe992 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/MultiEntitySelector.java @@ -0,0 +1,282 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import lv.id.bonne.panelutils.PanelUtils; +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.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * 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 MultiEntitySelector extends PagedSelector +{ + private MultiEntitySelector(User user, boolean asEgg, Mode mode, Set excluded, BiConsumer> consumer) + { + super(user); + + this.consumer = consumer; + this.asEgg = asEgg; + this.selectedElements = new HashSet<>(); + + this.elements = Arrays.stream(EntityType.values()). + filter(entity -> !excluded.contains(entity)). + filter(entity -> { + if (mode == Mode.ALIVE) + { + return entity.isAlive(); + } + else + { + return true; + } + }). + // Sort by name + sorted(Comparator.comparing(EntityType::name)). + collect(Collectors.toList()); + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEgg, Mode mode, Set excluded, BiConsumer> consumer) + { + new MultiEntitySelector(user, asEgg, mode, excluded, consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEgg, BiConsumer> consumer) + { + new MultiEntitySelector(user, asEgg, Mode.ANY, new HashSet<>(), consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "entity-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(3, this.createButton(Button.ACCEPT_SELECTED)); + panelBuilder.item(5, this.createButton(Button.CANCEL)); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case ACCEPT_SELECTED -> { + if (!this.selectedElements.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedElements.forEach(material -> + description.add(this.user.getTranslation(reference + "element", + "[element]", Utils.prettifyObject(material, this.user)))); + } + + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(true, this.selectedElements); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); + } + case CANCEL -> { + + icon = new ItemStack(Material.IRON_DOOR); + + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for given entity. + * @param entity entity which button must be created. + * @return new Button for entity. + */ + @Override + protected PanelItem createElementButton(EntityType entity) + { + final String reference = Constants.BUTTON + "entity."; + + List description = new ArrayList<>(); + description.add(this.user.getTranslation(reference + "description", + "[id]", entity.name())); + + if (this.selectedElements.contains(entity)) + { + description.add(this.user.getTranslation(reference + "selected")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-deselect")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[entity]", + Utils.prettifyObject(entity, this.user))). + icon(this.asEgg ? PanelUtils.getEntityEgg(entity) : PanelUtils.getEntityHead(entity)). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (!this.selectedElements.add(entity)) + { + // Remove entity if it is already selected + this.selectedElements.remove(entity); + } + + this.build(); + return true; + }). + glow(this.selectedElements.contains(entity)). + build(); + } + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ACCEPT_SELECTED, + CANCEL + } + + + public enum Mode + { + ALIVE, + ANY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * Set that contains selected materials. + */ + private final Set selectedElements; + + /** + * This variable stores consumer. + */ + private final BiConsumer> consumer; + + /** + * Indicates that entity must be displayed as egg. + */ + private final boolean asEgg; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java b/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java deleted file mode 100644 index e58a153..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java +++ /dev/null @@ -1,559 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import org.bukkit.Material; -import org.bukkit.conversations.Conversation; -import org.bukkit.conversations.ConversationContext; -import org.bukkit.conversations.ConversationFactory; -import org.bukkit.conversations.NumericPrompt; -import org.bukkit.conversations.Prompt; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; - -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, int lineLength, BiConsumer consumer) - { - this(user, value, Integer.MIN_VALUE, Integer.MAX_VALUE, lineLength, consumer); - } - - - public NumberGUI(User user, int value, int minValue, int lineLength, BiConsumer consumer) - { - this(user, value, minValue, Integer.MAX_VALUE, lineLength, consumer); - } - - - public NumberGUI(User user, int value, int minValue, int maxValue, int lineLength, BiConsumer consumer) - { - this.user = user; - this.value = value; - this.consumer = consumer; - - this.minValue = minValue; - this.maxValue = maxValue; - - this.currentOperation = Button.SET; - - this.lineLength = lineLength; - - 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.title.admin.manage-numbers")); - - 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; - String description; - PanelItem.ClickHandler clickHandler; - boolean glow; - - switch (button) - { - case SAVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.save"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.save"); - 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.admin.cancel"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.cancel"); - 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.admin.input"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.input"); - icon = new ItemStack(Material.ANVIL); - clickHandler = (panel, user, clickType, slot) -> { - - this.getNumberInput(number -> { - if (number != null) - { - // Null value is passed if user write cancel. - this.value = number.intValue(); - } - - this.build(); - }, - this.user.getTranslation("challenges.gui.questions.admin.number")); - - return true; - }; - glow = false; - break; - } - case VALUE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.value"); - description = this.user.getTranslation("challenges.gui.descriptions.current-value", "[value]", 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.admin.set"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.set"); - 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.admin.increase"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.increase"); - 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.admin.reduce"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.reduce"); - 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.admin.multiply"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.multiply"); - 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 PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * 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("challenges.gui.buttons.admin.number","[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.errors.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.errors.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("challenges.gui.buttons.admin.number","[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.errors.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("challenges.gui.buttons.admin.number","[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.errors.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("challenges.gui.buttons.admin.number","[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.errors.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; - } - default: - break; - } - - return itemBuilder.build(); - } - - - // --------------------------------------------------------------------- - // Section: Conversation - // --------------------------------------------------------------------- - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on - * inputText in chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - */ - private void getNumberInput(Consumer consumer, @NonNull String question) - { - final User user = this.user; - - Conversation conversation = - new ConversationFactory(BentoBox.getInstance()).withFirstPrompt( - new NumericPrompt() - { - /** - * Override this method to perform some action with - * the user's integer response. - * - * @param context Context information about the - * conversation. - * @param input The user's response as a {@link - * Number}. - * @return The next {@link Prompt} in the prompt - * graph. - */ - @Override - protected Prompt acceptValidatedInput(ConversationContext context, Number input) - { - // Add answer to consumer. - consumer.accept(input); - // Reopen GUI - NumberGUI.this.build(); - // End conversation - return Prompt.END_OF_CONVERSATION; - } - - - /** - * Override this method to do further validation on - * the numeric player input after the input has been - * determined to actually be a number. - * - * @param context Context information about the - * conversation. - * @param input The number the player provided. - * @return The validity of the player's input. - */ - @Override - protected boolean isNumberValid(ConversationContext context, Number input) - { - return input.intValue() >= NumberGUI.this.minValue && - input.intValue() <= NumberGUI.this.maxValue; - } - - - /** - * Optionally override this method to display an - * additional message if the user enters an invalid - * number. - * - * @param context Context information about the - * conversation. - * @param invalidInput The invalid input provided by - * the user. - * @return A message explaining how to correct the - * input. - */ - @Override - protected String getInputNotNumericText(ConversationContext context, - String invalidInput) - { - return NumberGUI.this.user - .getTranslation("challenges.errors.not-a-integer", "[value]", invalidInput); - } - - - /** - * Optionally override this method to display an - * additional message if the user enters an invalid - * numeric input. - * - * @param context Context information about the - * conversation. - * @param invalidInput The invalid input provided by - * the user. - * @return A message explaining how to correct the - * input. - */ - @Override - protected String getFailedValidationText(ConversationContext context, - Number invalidInput) - { - return NumberGUI.this.user.getTranslation("challenges.errors.not-valid-integer", - "[value]", invalidInput.toString(), - "[min]", Integer.toString(NumberGUI.this.minValue), - "[max]", Integer.toString(NumberGUI.this.maxValue)); - } - - - /** - * @see Prompt#getPromptText(ConversationContext) - */ - @Override - public String getPromptText(ConversationContext conversationContext) - { - // Close input GUI. - user.closeInventory(); - - // There are no editable message. Just return question. - return question; - } - }). - withLocalEcho(false). - // On cancel conversation will be closed. - withEscapeSequence("cancel"). - // Use null value in consumer to detect if user has abandoned conversation. - addConversationAbandonedListener(abandonedEvent -> - { - if (!abandonedEvent.gracefulExit()) - { - consumer.accept(null); - } - }). - withPrefix(context -> - NumberGUI.this.user.getTranslation("challenges.gui.questions.prefix")). - buildConversation(user.getPlayer()); - - conversation.begin(); - } - - - // --------------------------------------------------------------------- - // 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; - - /** - * This variable stores how large line can be, before warp it. - */ - private int lineLength; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/PagedSelector.java b/src/main/java/world/bentobox/challenges/panel/util/PagedSelector.java new file mode 100644 index 0000000..290fdf2 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/PagedSelector.java @@ -0,0 +1,262 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.List; +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.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; + + +/** + * This single abstract class will manage paged selectors similar to CommonPagedPanel. + */ +public abstract class PagedSelector +{ + /** + * Instantiates a new Paged selector. + * + * @param user the user + */ + protected PagedSelector(User user) + { + this.user = user; + this.searchString = ""; + } + + + /** + * Build. + */ + protected abstract void build(); + + + /** + * Create element button panel item. + * + * @param object the object + * @return the panel item + */ + protected abstract PanelItem createElementButton(T object); + + + /** + * This method is called when filter value is updated. + */ + protected abstract void updateFilters(); + + + /** + * Populate elements. + * + * @param panelBuilder the panel builder + * @param objectList the object list + */ + protected void populateElements(PanelBuilder panelBuilder, List objectList) + { + final int MAX_ELEMENTS = 21; + final int size = objectList.size(); + + if (this.pageIndex < 0) + { + this.pageIndex = size / MAX_ELEMENTS; + } + else if (this.pageIndex > (size / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int objectIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (objectIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + objectIndex < size && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createElementButton(objectList.get(objectIndex++))); + } + + index++; + } + + // Add next page button if there are more than MAX_ELEMENTS objects and pageIndex + 1 is + // larger or equal to the max page count. + if (size > MAX_ELEMENTS && !(1.0 * size / MAX_ELEMENTS <= this.pageIndex + 1)) + { + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + + } + + // Add previous page button if pageIndex is not 0. + if (this.pageIndex > 0) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + } + + // Add search button only if there is more than MAX_ELEMENTS objects or searchString + // is not blank. + if (!this.searchString.isBlank() || objectList.size() > MAX_ELEMENTS) + { + panelBuilder.item(40, this.getButton(CommonButtons.SEARCH)); + } + } + + + /** + * 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) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + if (button == CommonButtons.NEXT) + { + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_NUMBER, String.valueOf(this.pageIndex + 2))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-next")); + + icon = new ItemStack(Material.OAK_SIGN, this.pageIndex + 2); + clickHandler = (panel, user, clickType, slot) -> + { + this.pageIndex++; + this.build(); + return true; + }; + } + else if (button == CommonButtons.PREVIOUS) + { + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_NUMBER, String.valueOf(this.pageIndex))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-previous")); + + icon = new ItemStack(Material.OAK_SIGN, Math.max(1, this.pageIndex)); + clickHandler = (panel, user, clickType, slot) -> + { + this.pageIndex--; + this.build(); + return true; + }; + } + else if (button == CommonButtons.SEARCH) + { + description.add(this.user.getTranslation(reference + "description")); + + if (this.searchString != null && !this.searchString.isEmpty()) + { + description.add(this.user.getTranslation(reference + "search", + Constants.PARAMETER_VALUE, this.searchString)); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-edit")); + + if (this.searchString != null && !this.searchString.isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-clear")); + } + + icon = new ItemStack(Material.ANVIL); + + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + // Clear string. + this.searchString = ""; + this.updateFilters(); + // Rebuild gui. + this.build(); + } + else + { + // Create consumer that process description change + Consumer consumer = value -> + { + if (value != null) + { + this.searchString = value; + this.updateFilters(); + } + + this.build(); + }; + + // start conversation + ConversationUtils.createStringInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-search"), + user.getTranslation(Constants.CONVERSATIONS + "search-updated")); + } + + return true; + }; + } + else + { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * Next and Previous Buttons. + */ + private enum CommonButtons + { + NEXT, + PREVIOUS, + SEARCH + } + + + /** + * Current page index. + */ + private int pageIndex; + + /** + * User who opens gui. + */ + protected final User user; + + /** + * Text that contains filter string. + */ + protected String searchString; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java deleted file mode 100644 index d3ffdcb..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java +++ /dev/null @@ -1,276 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -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, false, new HashSet<>(), consumer); - } - - public SelectBlocksGUI(User user, boolean singleSelect, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.singleSelect = singleSelect; - - // Current GUI cannot display air blocks. It crashes with null-pointer - Set excludedMaterial = new HashSet<>(); - - 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<>(); - this.selectedMaterials = new HashSet<>(); - - for (Material material : Material.values()) - { - if (!material.isLegacy() && !excludedMaterial.contains(material)) - { - this.elements.add(material); - } - } - - this.build(0); - } - - - public SelectBlocksGUI(User user, boolean singleSelect, Set excludedMaterial, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.singleSelect = singleSelect; - - // 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<>(); - this.selectedMaterials = new HashSet<>(); - - 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.title.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(3, - new PanelItemBuilder(). - icon(Material.RED_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.admin.cancel")). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(false, null); - return true; - }).build()); - - - List description = new ArrayList<>(); - if (!this.selectedMaterials.isEmpty()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.selected") + ":"); - this.selectedMaterials.forEach(material -> description.add(" - " + material.name())); - } - - panelBuilder.item(5, - new PanelItemBuilder(). - icon(Material.GREEN_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.admin.accept")). - description(description). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(true, this.selectedMaterials); - return true; - }).build()); - - if (this.elements.size() > MAX_ELEMENTS) - { - // Navigation buttons if necessary - - panelBuilder.item(18, - new PanelItemBuilder(). - icon(Material.OAK_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.OAK_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("_", " "))). - description(this.selectedMaterials.contains(material) ? - this.user.getTranslation("challenges.gui.descriptions.admin.selected") : ""). - icon(itemStack). - clickHandler((panel, user1, clickType, slot) -> { - if (!this.singleSelect && clickType.isRightClick()) - { - if (!this.selectedMaterials.add(material)) - { - this.selectedMaterials.remove(material); - } - - panel.getInventory().setItem(slot, this.createMaterialButton(material).getItem()); - } - else - { - this.selectedMaterials.add(material); - this.consumer.accept(true, this.selectedMaterials); - } - - return true; - }). - glow(!itemStack.getType().equals(material)). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * List with elements that will be displayed in current GUI. - */ - private List elements; - - /** - * Set that contains selected materials. - */ - private Set selectedMaterials; - - /** - * This variable stores consumer. - */ - private BiConsumer> consumer; - - /** - * User who runs GUI. - */ - private User user; - - /** - * This indicate that return set must contain only single item. - */ - private boolean singleSelect; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java deleted file mode 100644 index 16cc3b7..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java +++ /dev/null @@ -1,215 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.event.inventory.ClickType; - -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, Map> challengesDescriptionMap, int lineLength, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.challengesList = new ArrayList<>(challengesDescriptionMap.keySet()); - this.challengesDescriptionMap = challengesDescriptionMap; - this.lineLength = lineLength; - this.selectedChallenges = new HashSet<>(this.challengesList.size()); - - 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.title.admin.select-challenge")); - - 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.OAK_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.OAK_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) - { - List description; - - if (this.selectedChallenges.contains(challenge)) - { - description = new ArrayList<>(); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.selected")); - description.addAll(this.challengesDescriptionMap.get(challenge)); - } - else - { - description = this.challengesDescriptionMap.get(challenge); - } - - - return new PanelItemBuilder(). - name(ChatColor.translateAlternateColorCodes('&', challenge.getFriendlyName())). - description(GuiUtils.stringSplit(description, this.lineLength)). - icon(challenge.getIcon()). - clickHandler((panel, user1, clickType, slot) -> { - if (clickType == ClickType.RIGHT) - { - // If challenge is not selected, then select :) - if (!this.selectedChallenges.remove(challenge)) - { - this.selectedChallenges.add(challenge); - } - - // Reset button. - panel.getInventory().setItem(slot, this.createChallengeButton(challenge).getItem()); - } - else - { - this.selectedChallenges.add(challenge); - this.consumer.accept(true, this.selectedChallenges); - } - - return true; - }). - glow(this.selectedChallenges.contains(challenge)). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - - /** - * This variable stores consumer. - */ - private BiConsumer> consumer; - - /** - * User who runs GUI. - */ - private User user; - - /** - * Current value. - */ - private List challengesList; - - /** - * Selected challenges that will be returned to consumer. - */ - private Set selectedChallenges; - - /** - * Map that contains all challenge descriptions - */ - private Map> challengesDescriptionMap; - - /** - * This variable stores how large line can be, before warp it. - */ - private int lineLength; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java deleted file mode 100644 index 4104f8a..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java +++ /dev/null @@ -1,237 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Material; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; - -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); - this.selectedEntities = new HashSet<>(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.title.admin.select-entity")); - - 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(3, - new PanelItemBuilder(). - icon(Material.RED_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.admin.cancel")). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(false, null); - return true; - }).build()); - - List description = new ArrayList<>(); - if (!this.selectedEntities.isEmpty()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.selected") + ":"); - this.selectedEntities.forEach(entity -> description.add(" - " + entity.name())); - } - - panelBuilder.item(5, - new PanelItemBuilder(). - icon(Material.GREEN_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.admin.accept")). - description(description). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(true, this.selectedEntities); - return true; - }).build()); - - if (this.entities.size() > MAX_ELEMENTS) - { - // Navigation buttons if necessary - - panelBuilder.item(18, - new PanelItemBuilder(). - icon(Material.OAK_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.OAK_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). - description(this.selectedEntities.contains(entity) ? - this.user.getTranslation("challenges.gui.descriptions.admin.selected") : ""). - clickHandler((panel, user1, clickType, slot) -> { - if (clickType.isRightClick()) - { - if (!this.selectedEntities.add(entity)) - { - this.selectedEntities.remove(entity); - } - - panel.getInventory().setItem(slot, this.createEntityButton(entity).getItem()); - } - else - { - this.selectedEntities.add(entity); - this.consumer.accept(true, this.selectedEntities); - } - - return true; - }). - glow(this.selectedEntities.contains(entity)). - build(); - } - - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * This variable stores consumer. - */ - private BiConsumer> consumer; - - /** - * Set that contains selected entities. - */ - private Set selectedEntities; - - /** - * 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 deleted file mode 100644 index 928998a..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java +++ /dev/null @@ -1,148 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.Collections; -import java.util.Set; -import java.util.function.BiConsumer; - -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.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.title.admin.toggle-environment")); - - GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); - - panelBuilder.item(3, new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.admin.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.buttons.admin.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(World.Environment.NETHER.name()). - 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(World.Environment.NORMAL.name()). - 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(World.Environment.THE_END.name()). - 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.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/SingleBlockSelector.java b/src/main/java/world/bentobox/challenges/panel/util/SingleBlockSelector.java new file mode 100644 index 0000000..d9fae37 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/SingleBlockSelector.java @@ -0,0 +1,241 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import lv.id.bonne.panelutils.PanelUtils; +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.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * 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 SingleBlockSelector extends PagedSelector +{ + private SingleBlockSelector(User user, Mode mode, Set excluded, BiConsumer consumer) + { + super(user); + this.consumer = consumer; + + // Current GUI cannot display air blocks. It crashes with null-pointer + excluded.add(Material.AIR); + excluded.add(Material.CAVE_AIR); + excluded.add(Material.VOID_AIR); + + // Piston head and moving piston is not necessary. useless. + excluded.add(Material.PISTON_HEAD); + excluded.add(Material.MOVING_PISTON); + + // Barrier cannot be accessible to user. + excluded.add(Material.BARRIER); + excluded.add(Material.STRUCTURE_VOID); + + this.elements = Arrays.stream(Material.values()). + filter(material -> !excluded.contains(material)). + filter(material -> { + switch (mode) + { + case BLOCKS -> { + return material.isBlock(); + } + case ITEMS -> { + return material.isItem(); + } + default -> { + return true; + } + } + }). + // Sort by name + sorted(Comparator.comparing(Material::name)). + collect(Collectors.toList()); + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Mode mode, Set excluded, BiConsumer consumer) + { + new SingleBlockSelector(user, mode, excluded, consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, BiConsumer consumer) + { + new SingleBlockSelector(user, Mode.ANY, new HashSet<>(), consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Mode mode, BiConsumer consumer) + { + new SingleBlockSelector(user, mode, new HashSet<>(), consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "block-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(4, this.createButton()); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem button of requested type. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton() + { + final String reference = Constants.BUTTON + "cancel."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon = new ItemStack(Material.IRON_DOOR); + PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for given material. + * @param material material which button must be created. + * @return new Button for material. + */ + @Override + protected PanelItem createElementButton(Material material) + { + final String reference = Constants.BUTTON + "material."; + + List description = new ArrayList<>(); + description.add(this.user.getTranslation(reference + "description", + "[id]", material.name())); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose")); + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[material]", + Utils.prettifyObject(material, this.user))). + icon(PanelUtils.getMaterialItem(material)). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, material); + return true; + }). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Mode +// --------------------------------------------------------------------- + + + public enum Mode + { + BLOCKS, + ITEMS, + ANY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * This variable stores consumer. + */ + private final BiConsumer consumer; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SingleEntitySelector.java b/src/main/java/world/bentobox/challenges/panel/util/SingleEntitySelector.java new file mode 100644 index 0000000..b0219aa --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/SingleEntitySelector.java @@ -0,0 +1,238 @@ +package world.bentobox.challenges.panel.util; + + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +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.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This GUI allows to select single entity and return it via Consumer. + */ +public class SingleEntitySelector extends PagedSelector +{ + /** + * Instantiates a new Single entity selector. + * + * @param user the user + * @param asEggs the boolean + * @param mode the mode + * @param excluded the excluded + * @param consumer the consumer + */ + private SingleEntitySelector(User user, boolean asEggs, Mode mode, Set excluded, BiConsumer consumer) + { + super(user); + this.consumer = consumer; + this.asEggs = asEggs; + this.elements = Arrays.stream(EntityType.values()). + filter(entity -> !excluded.contains(entity)). + filter(entity -> { + if (mode == Mode.ALIVE) + { + return entity.isAlive(); + } + else + { + return true; + } + }). + // Sort by names + sorted(Comparator.comparing(EntityType::name)). + collect(Collectors.toList()); + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEggs, Mode mode, Set excluded, BiConsumer consumer) + { + new SingleEntitySelector(user, asEggs, mode, excluded, consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEggs, BiConsumer consumer) + { + new SingleEntitySelector(user, asEggs, Mode.ANY, new HashSet<>(), consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEggs, Mode mode, BiConsumer consumer) + { + new SingleEntitySelector(user, asEggs, mode, new HashSet<>(), consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "entity-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(4, this.createButton()); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method builds PanelItem for given entity. + * @param entity Entity which PanelItem must be created. + * @return new PanelItem for given Entity. + */ + @Override + protected PanelItem createElementButton(EntityType entity) + { + final String reference = Constants.BUTTON + "entity."; + + List description = new ArrayList<>(); + description.add(this.user.getTranslation(reference + "description", + "[id]", entity.name())); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose")); + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[entity]", + Utils.prettifyObject(entity, this.user))). + icon(this.asEggs ? PanelUtils.getEntityEgg(entity) : PanelUtils.getEntityHead(entity)). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, entity); + return true; + }). + build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton() + { + final String reference = Constants.BUTTON + "cancel."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon = new ItemStack(Material.IRON_DOOR); + PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Mode +// --------------------------------------------------------------------- + + + public enum Mode + { + ALIVE, + ANY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * Indicates if entities are displayed as eggs or heads. + */ + private final boolean asEggs; + + /** + * This variable stores consumer. + */ + private final BiConsumer consumer; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java b/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java new file mode 100644 index 0000000..f6d2aed --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java @@ -0,0 +1,190 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.inventory.ItemStack; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import lv.id.bonne.panelutils.PanelUtils; +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.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary things that allows to select single statistic. Selected + * stats will be returned via BiConsumer. + */ +public class StatisticSelector extends PagedSelector +{ + /** + * Instantiates a new Statistic selector. + * + * @param user the user + * @param consumer the consumer + */ + private StatisticSelector(User user, BiConsumer consumer) + { + super(user); + + this.consumer = consumer; + this.elements = new ArrayList<>(Arrays.asList(Statistic.values())); + this.elements.sort(Comparator.comparing(Statistic::name)); + + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, BiConsumer consumer) + { + new StatisticSelector(user, consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "statistic-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(4, this.createButton()); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem that represents given statistic. + * Some materials is not displayable in Inventory GUI, so they are replaced with "placeholder" items. + * @param statistic Material which icon must be created. + * @return PanelItem that represents given statistic. + */ + @Override + protected PanelItem createElementButton(Statistic statistic) + { + final String reference = Constants.BUTTON + "statistic_element."; + + List description = new ArrayList<>(); + + String descriptionText = this.user.getTranslationOrNothing(reference + description, + "[description]", Utils.prettifyDescription(statistic, user)); + + if (!descriptionText.isEmpty()) + { + description.add(descriptionText); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose")); + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[statistic]", + Utils.prettifyObject(statistic, this.user))). + icon(Material.PAPER). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, statistic); + return true; + }). + build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton() + { + final String reference = Constants.BUTTON + "cancel."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon = new ItemStack(Material.IRON_DOOR); + PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * This variable stores consumer. + */ + private final BiConsumer consumer; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java b/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java deleted file mode 100644 index 07f30a3..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java +++ /dev/null @@ -1,366 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import org.bukkit.Material; -import org.bukkit.conversations.Conversation; -import org.bukkit.conversations.ConversationContext; -import org.bukkit.conversations.ConversationFactory; -import org.bukkit.conversations.Prompt; -import org.bukkit.conversations.StringPrompt; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; - -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.TextComponent; -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, String value, int lineLength, BiConsumer> consumer) - { - this(user, Collections.singleton(value), lineLength, consumer); - } - - - public StringListGUI(User user, Collection value, int lineLength, BiConsumer> consumer) - { - this(user, new ArrayList<>(value), lineLength, consumer); - } - - - public StringListGUI(User user, List value, int lineLength, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.value = value; - this.lineLength = lineLength; - - 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.title.admin.edit-text-fields")); - - 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.admin.save"); - description = Collections.singletonList(this.user.getTranslation("challenges.gui.descriptions.admin.save")); - 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.admin.cancel"); - description = Collections.singletonList(this.user.getTranslation("challenges.gui.descriptions.admin.cancel")); - 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.admin.value"); - description = new ArrayList<>(); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", "[value]", "")); - description.addAll(this.value); - icon = new ItemStack(Material.PAPER); - clickHandler = (panel, user, clickType, slot) -> true; - break; - } - case ADD: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.add"); - description = Collections.emptyList(); - icon = new ItemStack(Material.WHITE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - - this.getStringInput(value -> { - if (value != null) - { - this.value.add(value); - } - - // Reopen GUI. - this.build(); - }, - this.user.getTranslation("challenges.gui.descriptions.admin.add-text-line")); - - return true; - }; - break; - } - case CLEAR: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.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.admin.remove-empty"); - 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 PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(false). - clickHandler(clickHandler). - build(); - } - - - /** - * 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) -> { - - this.getStringInput( - value -> { - if (value != null) - { - this.value.set(stringIndex, value); - } - - // Reopen GUI - this.build(); - }, - this.user.getTranslation("challenges.gui.descriptions.admin.edit-text-line"), - element); - - return true; - }).build(); - } - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on inputText in - * chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - */ - private void getStringInput(Consumer consumer, @NonNull String question) - { - this.getStringInput(consumer, question, null); - } - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on inputText in - * chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - * @param message Message that will be set in player text field when clicked on question. - */ - private void getStringInput(Consumer consumer, @NonNull String question, @Nullable String message) - { - final User user = this.user; - - Conversation conversation = - new ConversationFactory(BentoBox.getInstance()).withFirstPrompt( - new StringPrompt() - { - /** - * @see Prompt#getPromptText(ConversationContext) - */ - @Override - public String getPromptText(ConversationContext conversationContext) - { - // Close input GUI. - user.closeInventory(); - - if (message != null) - { - // Create Edit Text message. - TextComponent component = new TextComponent(user.getTranslation("challenges.gui.descriptions.admin.click-to-edit")); - component.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, message)); - // Send question and message to player. - user.getPlayer().spigot().sendMessage(component); - } - - // There are no editable message. Just return question. - return question; - } - - - /** - * @see Prompt#acceptInput(ConversationContext, String) - */ - @Override - public Prompt acceptInput(ConversationContext conversationContext, String answer) - { - // Add answer to consumer. - consumer.accept(answer); - // End conversation - return Prompt.END_OF_CONVERSATION; - } - }). - // On cancel conversation will be closed. - withEscapeSequence("cancel"). - // Use null value in consumer to detect if user has abandoned conversation. - addConversationAbandonedListener(abandonedEvent -> - { - if (!abandonedEvent.gracefulExit()) - { - consumer.accept(null); - } - }). - withLocalEcho(false). - withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). - buildConversation(user.getPlayer()); - - conversation.begin(); - } - - -// --------------------------------------------------------------------- -// 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; - - /** - * This variable stores how large line can be, before warp it. - */ - private int lineLength; -} diff --git a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java index 1b29ace..6b8c161 100644 --- a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java +++ b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java @@ -2,6 +2,8 @@ package world.bentobox.challenges.tasks; +import com.google.common.collect.UnmodifiableIterator; +import java.time.*; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; @@ -11,6 +13,7 @@ import java.util.Map; import java.util.Objects; import java.util.PriorityQueue; import java.util.Queue; +import java.util.UUID; import java.util.stream.Collectors; import org.bukkit.Bukkit; @@ -31,13 +34,15 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; 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.database.object.requirements.InventoryRequirements; import world.bentobox.challenges.database.object.requirements.IslandRequirements; import world.bentobox.challenges.database.object.requirements.OtherRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; @@ -55,7 +60,7 @@ public class TryToComplete /** * Challenges addon variable. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; /** * Challenges manager for addon. @@ -77,11 +82,6 @@ public class TryToComplete */ private String permissionPrefix; - /** - * Top command first label. - */ - private String topLabel; - /** * Challenge that should be completed. */ @@ -96,13 +96,6 @@ public class TryToComplete // Section: Builder // --------------------------------------------------------------------- - @Deprecated - public TryToComplete label(String label) - { - this.topLabel = label; - return this; - } - @Deprecated public TryToComplete user(User user) @@ -112,38 +105,6 @@ public class TryToComplete } - @Deprecated - public TryToComplete manager(ChallengesManager manager) - { - this.manager = manager; - return this; - } - - - @Deprecated - public TryToComplete challenge(Challenge challenge) - { - this.challenge = challenge; - return this; - } - - - @Deprecated - public TryToComplete world(World world) - { - this.world = world; - return this; - } - - - @Deprecated - public TryToComplete permPrefix(String prefix) - { - this.permissionPrefix = prefix; - return this; - } - - @Deprecated public TryToComplete(ChallengesAddon addon) { @@ -179,7 +140,6 @@ public class TryToComplete // To avoid any modifications that may occur to challenges in current completion // just clone it. this.challenge = challenge.clone(); - this.topLabel = topLabel; } @@ -224,7 +184,8 @@ public class TryToComplete int maxTimes) { return new TryToComplete(addon, user, challenge, world, topLabel, permissionPrefix). - build(maxTimes).meetsRequirements; + build(maxTimes). + meetsRequirements; } @@ -247,6 +208,13 @@ public class TryToComplete return result; } + if (this.user.getLocation() == null || this.user.getInventory() == null) + { + // This is just a cleaning check. There is no situations where location or inventory + // could be null at this point of code. + return result; + } + this.fullFillRequirements(result); // Validation to avoid rewarding if something goes wrong in removing requirements. @@ -297,32 +265,29 @@ public class TryToComplete // Send message about first completion only if it is completed only once. if (result.getFactor() == 1) { - this.user.sendMessage("challenges.messages.you-completed-challenge", "[value]", this.challenge.getFriendlyName()); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-completed-challenge", + "[value]", this.challenge.getFriendlyName())); } if (this.addon.getChallengesSettings().isBroadcastMessages()) { - for (Player player : Bukkit.getOnlinePlayers()) - { - // Only other players should see message. - if (!player.getUniqueId().equals(this.user.getUniqueId())) - { - User.getInstance(player).sendMessage("challenges.messages.name-has-completed-challenge", - "[name]", this.user.getName(), - "[value]", this.challenge.getFriendlyName()); - } - } + Bukkit.getOnlinePlayers().stream(). + map(User::getInstance). + forEach(user -> Utils.sendMessage(user, user.getTranslation( + "challenges.messages.name-has-completed-challenge", + Constants.PARAMETER_NAME, this.user.getName(), + "[value]", this.challenge.getFriendlyName()))); } // sends title to player on challenge completion if (this.addon.getChallengesSettings().isShowCompletionTitle()) { this.user.getPlayer().sendTitle( - this.parseChallenge(this.user.getTranslation("challenges.titles.challenge-title"), this.challenge), - this.parseChallenge(this.user.getTranslation("challenges.titles.challenge-subtitle"), this.challenge), - 10, - this.addon.getChallengesSettings().getTitleShowtime(), - 20); + this.parseChallenge(this.user.getTranslation("challenges.titles.challenge-title"), this.challenge), + this.parseChallenge(this.user.getTranslation("challenges.titles.challenge-subtitle"), this.challenge), + 10, + this.addon.getChallengesSettings().getTitleShowtime(), + 20); } } @@ -347,7 +312,7 @@ public class TryToComplete if (this.addon.isEconomyProvided()) { this.addon.getEconomyProvider().deposit(this.user, - (double)this.challenge.getRepeatMoneyReward() * rewardFactor); + this.challenge.getRepeatMoneyReward() * rewardFactor); } // Experience Repeat Reward @@ -362,13 +327,14 @@ public class TryToComplete if (result.getFactor() > 1) { - this.user.sendMessage("challenges.messages.you-repeated-challenge-multiple", - "[value]", this.challenge.getFriendlyName(), - "[count]", Integer.toString(result.getFactor())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-repeated-challenge-multiple", + "[value]", this.challenge.getFriendlyName(), + "[count]", Integer.toString(result.getFactor()))); } else { - this.user.sendMessage("challenges.messages.you-repeated-challenge", "[value]", this.challenge.getFriendlyName()); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-repeated-challenge", + "[value]", this.challenge.getFriendlyName())); } } @@ -406,19 +372,17 @@ public class TryToComplete // Run commands this.runCommands(level.getRewardCommands()); - this.user.sendMessage("challenges.messages.you-completed-level", "[value]", level.getFriendlyName()); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-completed-level", + "[value]", level.getFriendlyName())); if (this.addon.getChallengesSettings().isBroadcastMessages()) { - for (Player player : this.addon.getServer().getOnlinePlayers()) - { - // Only other players should see message. - if (!player.getUniqueId().equals(this.user.getUniqueId())) - { - User.getInstance(player).sendMessage("challenges.messages.name-has-completed-level", - "[name]", this.user.getName(), "[value]", level.getFriendlyName()); - } - } + Bukkit.getOnlinePlayers().stream(). + map(User::getInstance). + forEach(user -> Utils.sendMessage(user, user.getTranslation( + "challenges.messages.name-has-completed-level", + Constants.PARAMETER_NAME, this.user.getName(), + "[value]", level.getFriendlyName()))); } this.manager.setLevelComplete(this.user, this.world, level); @@ -427,11 +391,11 @@ public class TryToComplete if (this.addon.getChallengesSettings().isShowCompletionTitle()) { this.user.getPlayer().sendTitle( - this.parseLevel(this.user.getTranslation("challenges.titles.level-title"), level), - this.parseLevel(this.user.getTranslation("challenges.titles.level-subtitle"), level), - 10, - this.addon.getChallengesSettings().getTitleShowtime(), - 20); + this.parseLevel(this.user.getTranslation("challenges.titles.level-title"), level), + this.parseLevel(this.user.getTranslation("challenges.titles.level-subtitle"), level), + 10, + this.addon.getChallengesSettings().getTitleShowtime(), + 20); } } } @@ -442,68 +406,222 @@ public class TryToComplete /** - * This method full fills all challenge type requirements, that is not full filled yet. + * This method fulfills all challenge type requirements, that is not fulfilled yet. * @param result Challenge Results */ private void fullFillRequirements(ChallengeResult result) { - if (this.challenge.getChallengeType().equals(ChallengeType.ISLAND)) + switch (this.challenge.getChallengeType()) { - IslandRequirements requirements = this.challenge.getRequirements(); + case ISLAND_TYPE -> { + IslandRequirements requirements = this.challenge.getRequirements(); - if (result.meetsRequirements && + if (result.meetsRequirements && requirements.isRemoveEntities() && !requirements.getRequiredEntities().isEmpty()) - { - this.removeEntities(result.entities, result.getFactor()); - } + { + this.removeEntities(result.entities, result.getFactor()); + } - if (result.meetsRequirements && + if (result.meetsRequirements && requirements.isRemoveBlocks() && !requirements.getRequiredBlocks().isEmpty()) - { - this.removeBlocks(result.blocks, result.getFactor()); + { + this.removeBlocks(result.blocks, result.getFactor()); + } } - } - else if (this.challenge.getChallengeType().equals(ChallengeType.INVENTORY)) - { - // If remove items, then remove them - if (this.getInventoryRequirements().isTakeItems()) - { - int sumEverything = result.requiredItems.stream(). + case INVENTORY_TYPE -> { + // If remove items, then remove them + if (this.getInventoryRequirements().isTakeItems()) + { + int sumEverything = result.requiredItems.stream(). mapToInt(itemStack -> itemStack.getAmount() * result.getFactor()). sum(); - Map removedItems = + Map removedItems = this.removeItems(result.requiredItems, result.getFactor()); - int removedAmount = removedItems.values().stream().mapToInt(num -> num).sum(); + int removedAmount = removedItems.values().stream().mapToInt(num -> num).sum(); - // Something is not removed. - if (sumEverything != removedAmount) - { - this.user.sendMessage("challenges.errors.cannot-remove-items"); + // Something is not removed. + if (sumEverything != removedAmount) + { + Utils.sendMessage(this.user, + this.user.getTranslation("challenges.errors.cannot-remove-items")); - result.removedItems = removedItems; - result.meetsRequirements = false; + result.removedItems = removedItems; + result.meetsRequirements = false; + } } } - } - else if (this.challenge.getChallengeType().equals(ChallengeType.OTHER)) - { - OtherRequirements requirements = this.challenge.getRequirements(); + case OTHER_TYPE -> { + OtherRequirements requirements = this.challenge.getRequirements(); - if (this.addon.isEconomyProvided() && requirements.isTakeMoney()) - { - this.addon.getEconomyProvider().withdraw(this.user, requirements.getRequiredMoney()); - } + if (this.addon.isEconomyProvided() && requirements.isTakeMoney()) + { + this.addon.getEconomyProvider().withdraw(this.user, requirements.getRequiredMoney()); + } - if (requirements.isTakeExperience() && + if (requirements.isTakeExperience() && this.user.getPlayer().getGameMode() != GameMode.CREATIVE) - { - // Cannot take anything from creative game mode. - this.user.getPlayer().setTotalExperience( + { + // Cannot take anything from creative game mode. + this.user.getPlayer().setTotalExperience( this.user.getPlayer().getTotalExperience() - requirements.getRequiredExperience()); + } + } + case STATISTIC_TYPE -> { + StatisticRequirements requirements = this.challenge.getRequirements(); + + if (requirements.isReduceStatistic() && requirements.getStatistic() != null) + { + int removeAmount = result.getFactor() * requirements.getAmount(); + + // Start to remove from player who called the completion. + switch (requirements.getStatistic().getType()) + { + case UNTYPED -> { + int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); + + if (removeAmount >= statistic) + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), 0); + removeAmount -= statistic; + } + else + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), statistic - removeAmount); + removeAmount = 0; + } + } + case ITEM, BLOCK -> { + int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); + + if (requirements.getMaterial() == null) + { + // Just a sanity check. Material cannot be null at this point of code. + removeAmount = 0; + } + else if (removeAmount >= statistic) + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0); + removeAmount -= statistic; + } + else + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), + requirements.getMaterial(), + statistic - removeAmount); + removeAmount = 0; + } + } + case ENTITY -> { + int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); + + if (requirements.getEntity() == null) + { + // Just a sanity check. Entity cannot be null at this point of code. + removeAmount = 0; + } + else if (removeAmount >= statistic) + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getEntity(), 0); + removeAmount -= statistic; + } + else + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), + requirements.getEntity(), + statistic - removeAmount); + removeAmount = 0; + } + } + } + + // If challenges are in sync with all island members, then punish others too. + if (this.addon.getChallengesSettings().isStoreAsIslandData()) + { + Island island = this.addon.getIslands().getIsland(this.world, this.user); + + if (island == null) + { + // hmm + return; + } + + for (UnmodifiableIterator iterator = island.getMemberSet().iterator(); + iterator.hasNext() && removeAmount > 0; ) + { + Player player = Bukkit.getPlayer(iterator.next()); + + if (player == null || player == this.user.getPlayer()) + { + // cannot punish null or player who already was punished. + continue; + } + + switch (Objects.requireNonNull(requirements.getStatistic()).getType()) + { + case UNTYPED -> { + int statistic = player.getStatistic(requirements.getStatistic()); + + if (removeAmount >= statistic) + { + removeAmount -= statistic; + player.setStatistic(requirements.getStatistic(), 0); + } + else + { + player.setStatistic(requirements.getStatistic(), statistic - removeAmount); + removeAmount = 0; + } + } + case ITEM, BLOCK -> { + int statistic = player.getStatistic(requirements.getStatistic()); + + if (requirements.getMaterial() == null) + { + // Just a sanity check. Entity cannot be null at this point of code. + removeAmount = 0; + } + else if (removeAmount >= statistic) + { + removeAmount -= statistic; + player.setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0); + } + else + { + player.setStatistic(requirements.getStatistic(), + requirements.getMaterial(), + statistic - removeAmount); + removeAmount = 0; + } + } + case ENTITY -> { + int statistic = player.getStatistic(requirements.getStatistic()); + + if (requirements.getEntity() == null) + { + // Just a sanity check. Entity cannot be null at this point of code. + removeAmount = 0; + } + else if (removeAmount >= statistic) + { + removeAmount -= statistic; + player.setStatistic(requirements.getStatistic(), requirements.getEntity(), 0); + } + else + { + player.setStatistic(requirements.getStatistic(), + requirements.getEntity(), + statistic - removeAmount); + removeAmount = 0; + } + } + } + } + } + } } } } @@ -517,85 +635,101 @@ public class TryToComplete private ChallengeResult checkIfCanCompleteChallenge(int maxTimes) { ChallengeResult result; - + ChallengeType type = this.challenge.getChallengeType(); // Check the world if (!this.challenge.isDeployed()) { - this.user.sendMessage("challenges.errors.not-deployed"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-deployed")); result = EMPTY_RESULT; } else if (maxTimes < 1) { - this.user.sendMessage("challenges.errors.not-valid-integer"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-valid-integer")); result = EMPTY_RESULT; } else if (Util.getWorld(this.world) != Util.getWorld(this.user.getWorld()) || - !this.challenge.matchGameMode(Utils.getGameMode(this.world))) + !this.challenge.matchGameMode(Utils.getGameMode(this.world))) { - this.user.sendMessage("general.errors.wrong-world"); + Utils.sendMessage(this.user, this.user.getTranslation("general.errors.wrong-world")); result = EMPTY_RESULT; } // Player is not on island - else if (ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.world) && + else if (this.user.getLocation() == null || + ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.world) && !this.addon.getIslands().locationIsOnIsland(this.user.getPlayer(), this.user.getLocation())) { - this.user.sendMessage("challenges.errors.not-on-island"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-on-island")); result = EMPTY_RESULT; } // Check player permission else if (!this.addon.getIslands().getIslandAt(this.user.getLocation()). - map(i -> i.isAllowed(this.user, ChallengesAddon.CHALLENGES_ISLAND_PROTECTION)). - orElse(false)) + map(i -> i.isAllowed(this.user, ChallengesAddon.CHALLENGES_ISLAND_PROTECTION)). + orElse(false)) { - this.user.sendMessage("challenges.errors.no-rank"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.no-rank")); 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.manager.isLevelUnlocked(this.user, this.world, this.manager.getLevel(this.challenge.getLevel()))) { - this.user.sendMessage("challenges.errors.challenge-level-not-available"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.challenge-level-not-available")); result = EMPTY_RESULT; } // Check max times else if (this.challenge.isRepeatable() && this.challenge.getMaxTimes() > 0 && - this.manager.getChallengeTimes(this.user, this.world, this.challenge) >= this.challenge.getMaxTimes()) + this.manager.getChallengeTimes(this.user, this.world, this.challenge) >= this.challenge.getMaxTimes()) { - this.user.sendMessage("challenges.errors.not-repeatable"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-repeatable")); result = EMPTY_RESULT; } // Check repeatability else if (!this.challenge.isRepeatable() && this.manager.isChallengeComplete(this.user, this.world, this.challenge)) { - this.user.sendMessage("challenges.errors.not-repeatable"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-repeatable")); + result = EMPTY_RESULT; + } + // Check if timeout is not broken + else if (this.manager.isBreachingTimeOut(this.user, this.world, this.challenge)) + { + long missing = this.manager.getLastCompletionDate(this.user, this.world, challenge) + + this.challenge.getTimeout() - System.currentTimeMillis(); + + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.timeout", + "[timeout]", Utils.parseDuration(Duration.ofMillis(this.challenge.getTimeout()), this.user), + "[wait-time]", Utils.parseDuration(Duration.ofMillis(missing), this.user))); result = EMPTY_RESULT; } // Check environment else if (!this.challenge.getEnvironment().isEmpty() && - !this.challenge.getEnvironment().contains(this.user.getWorld().getEnvironment())) + !this.challenge.getEnvironment().contains(this.user.getWorld().getEnvironment())) { - this.user.sendMessage("challenges.errors.wrong-environment"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.wrong-environment")); result = EMPTY_RESULT; } // Check permission else if (!this.checkPermissions()) { - this.user.sendMessage("general.errors.no-permission"); + Utils.sendMessage(this.user, this.user.getTranslation("general.errors.no-permission")); result = EMPTY_RESULT; } - else if (type.equals(ChallengeType.INVENTORY)) + else if (type.equals(ChallengeType.INVENTORY_TYPE)) { result = this.checkInventory(this.getAvailableCompletionTimes(maxTimes)); } - else if (type.equals(ChallengeType.ISLAND)) + else if (type.equals(ChallengeType.ISLAND_TYPE)) { result = this.checkSurrounding(this.getAvailableCompletionTimes(maxTimes)); } - else if (type.equals(ChallengeType.OTHER)) + else if (type.equals(ChallengeType.OTHER_TYPE)) { result = this.checkOthers(this.getAvailableCompletionTimes(maxTimes)); } + else if (type.equals(ChallengeType.STATISTIC_TYPE)) + { + result = this.checkStatistic(this.getAvailableCompletionTimes(maxTimes)); + } else { result = EMPTY_RESULT; @@ -618,7 +752,7 @@ public class TryToComplete private boolean checkPermissions() { return this.challenge.getRequirements().getRequiredPermissions().isEmpty() || - this.challenge.getRequirements().getRequiredPermissions().stream().allMatch(s -> this.user.hasPermission(s)); + this.challenge.getRequirements().getRequiredPermissions().stream().allMatch(s -> this.user.hasPermission(s)); } @@ -630,12 +764,12 @@ public class TryToComplete */ private int getAvailableCompletionTimes(int vantedTimes) { - if (!this.challenge.isRepeatable()) + if (!this.challenge.isRepeatable() || this.challenge.getTimeout() > 0) { // Challenge is not repeatable vantedTimes = 1; } - else if (this.challenge.getMaxTimes() != 0) + else if (this.challenge.getMaxTimes() > 0) { // Challenge has limitations long availableTimes = this.challenge.getMaxTimes() - this.manager.getChallengeTimes(this.user, this.world, this.challenge); @@ -667,7 +801,7 @@ public class TryToComplete { 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(); + cmd = cmd.substring(6).replace(Constants.PARAMETER_PLAYER, this.user.getName()).trim(); try { if (!user.performCommand(cmd)) @@ -686,7 +820,7 @@ public class TryToComplete try { if (!this.addon.getServer().dispatchCommand(this.addon.getServer().getConsoleSender(), - cmd.replace("[player]", this.user.getName()))) + cmd.replace(Constants.PARAMETER_PLAYER, this.user.getName()))) { this.showError(cmd); } @@ -722,43 +856,47 @@ public class TryToComplete */ private ChallengeResult checkInventory(int maxTimes) { + if (maxTimes <= 0) + { + return EMPTY_RESULT; + } + // Run through inventory List requiredItems; // Players in creative game mode has got all items. No point to search for them. if (this.user.getPlayer().getGameMode() != GameMode.CREATIVE) { - requiredItems = Utils.groupEqualItems(this.getInventoryRequirements().getRequiredItems()); + requiredItems = Utils.groupEqualItems(this.getInventoryRequirements().getRequiredItems(), + this.getInventoryRequirements().getIgnoreMetaData()); // Check if all required items are in players inventory. for (ItemStack required : requiredItems) { int numInInventory; - if (Utils.canIgnoreMeta(required.getType())) + if (this.getInventoryRequirements().getIgnoreMetaData().contains(required.getType())) { - numInInventory = - Arrays.stream(this.user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.getType().equals(required.getType())). - mapToInt(ItemStack::getAmount). - sum(); + numInInventory = Arrays.stream(this.user.getInventory().getContents()). + filter(Objects::nonNull). + filter(i -> i.getType().equals(required.getType())). + mapToInt(ItemStack::getAmount). + sum(); } else { - numInInventory = - Arrays.stream(this.user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.isSimilar(required)). - mapToInt(ItemStack::getAmount). - sum(); + numInInventory = Arrays.stream(this.user.getInventory().getContents()). + filter(Objects::nonNull). + filter(i -> i.isSimilar(required)). + mapToInt(ItemStack::getAmount). + sum(); } if (numInInventory < required.getAmount()) { - this.user.sendMessage("challenges.errors.not-enough-items", - "[items]", - Util.prettifyText(required.getType().toString())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-items", + "[items]", + Utils.prettifyObject(required, this.user))); return EMPTY_RESULT; } @@ -792,22 +930,28 @@ public class TryToComplete int amountToBeRemoved = required.getAmount() * factor; List itemsInInventory; - if (Utils.canIgnoreMeta(required.getType())) + if (this.user.getInventory() == null) + { + // Sanity check. User always has inventory at this point of code. + itemsInInventory = Collections.emptyList(); + } + else if (this.getInventoryRequirements().getIgnoreMetaData().contains(required.getType())) { // Use collecting method that ignores item meta. itemsInInventory = Arrays.stream(user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.getType().equals(required.getType())). - collect(Collectors.toList()); + filter(Objects::nonNull). + filter(i -> i.getType().equals(required.getType())). + collect(Collectors.toList()); } else { // Use collecting method that compares item meta. itemsInInventory = Arrays.stream(user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.isSimilar(required)). - collect(Collectors.toList()); + filter(Objects::nonNull). + filter(i -> i.isSimilar(required)). + collect(Collectors.toList()); } + for (ItemStack itemStack : itemsInInventory) { if (amountToBeRemoved > 0) @@ -854,6 +998,11 @@ public class TryToComplete */ private ChallengeResult checkSurrounding(int factor) { + if (factor <= 0) + { + return EMPTY_RESULT; + } + // Init location in player position. BoundingBox boundingBox = this.user.getPlayer().getBoundingBox().clone(); @@ -905,14 +1054,14 @@ public class TryToComplete // Protection code. Do not allow to select too large region for completing challenge. if (boundingBox.getWidthX() > distance * 2 + 3 || - boundingBox.getWidthZ() > distance * 2 + 3 || - boundingBox.getHeight() > distance * 2 + 3) + boundingBox.getWidthZ() > distance * 2 + 3 || + boundingBox.getHeight() > distance * 2 + 3) { this.addon.logError("BoundingBox is larger than SearchRadius. " + - " | BoundingBox: " + boundingBox.toString() + + " | BoundingBox: " + boundingBox + " | Search Distance: " + requirements.getSearchRadius() + - " | Location: " + this.user.getLocation().toString() + - " | Center: " + island.getCenter().toString() + + " | Location: " + this.user.getLocation() + + " | Center: " + island.getCenter() + " | Range: " + range); return EMPTY_RESULT; @@ -950,10 +1099,10 @@ public class TryToComplete // This queue will contain only blocks whit required type ordered by distance till player. Queue blockFromWorld = new PriorityQueue<>((o1, o2) -> { - if (o1.getType().equals(o2.getType())) + if (o1.getType().equals(o2.getType()) && this.user.getLocation() != null) { return Double.compare(o1.getLocation().distance(this.user.getLocation()), - o2.getLocation().distance(this.user.getLocation())); + o2.getLocation().distance(this.user.getLocation())); } else { @@ -1010,13 +1159,13 @@ public class TryToComplete return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor).setBlockQueue(blockFromWorld); } - this.user.sendMessage("challenges.errors.not-close-enough", - "[number]", - String.valueOf(this.getIslandRequirements().getSearchRadius())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-close-enough", + "[number]", String.valueOf(this.getIslandRequirements().getSearchRadius()))); - blocks.forEach((k, v) -> user.sendMessage("challenges.errors.you-still-need", + blocks.forEach((k, v) -> Utils.sendMessage(this.user, + this.user.getTranslation("challenges.errors.you-still-need", "[amount]", String.valueOf(v), - "[item]", Util.prettifyText(k.toString()))); + "[item]", Utils.prettifyObject(k, this.user)))); // kick garbage collector @@ -1050,10 +1199,10 @@ public class TryToComplete // Create queue that contains all required entities ordered by distance till player. Queue entityQueue = new PriorityQueue<>((o1, o2) -> { - if (o1.getType().equals(o2.getType())) + if (o1.getType().equals(o2.getType()) && this.user.getLocation() != null) { return Double.compare(o1.getLocation().distance(this.user.getLocation()), - o2.getLocation().distance(this.user.getLocation())); + o2.getLocation().distance(this.user.getLocation())); } else { @@ -1095,9 +1244,10 @@ public class TryToComplete return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor).setEntityQueue(entityQueue); } - minimalRequirements.forEach((reqEnt, amount) -> this.user.sendMessage("challenges.errors.you-still-need", + minimalRequirements.forEach((reqEnt, amount) -> + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.you-still-need", "[amount]", String.valueOf(amount), - "[item]", Util.prettifyText(reqEnt.toString()))); + "[item]", Utils.prettifyObject(reqEnt, this.user)))); // Kick garbage collector entitiesFound.clear(); @@ -1169,48 +1319,51 @@ public class TryToComplete */ private ChallengeResult checkOthers(int factor) { + if (factor <= 0) + { + return EMPTY_RESULT; + } + OtherRequirements requirements = this.getOtherRequirements(); - if (!this.addon.isLevelProvided() && - requirements.getRequiredIslandLevel() != 0) + if (!this.addon.isLevelProvided() && requirements.getRequiredIslandLevel() != 0) { - this.user.sendMessage("challenges.errors.missing-addon"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.missing-addon")); } else if (!this.addon.isEconomyProvided() && requirements.getRequiredMoney() != 0) { - this.user.sendMessage("challenges.errors.missing-addon"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.missing-addon")); } else if (this.addon.isEconomyProvided() && requirements.getRequiredMoney() < 0) { - this.user.sendMessage("challenges.errors.incorrect"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.incorrect")); } else if (this.addon.isEconomyProvided() && - !this.addon.getEconomyProvider().has(this.user, requirements.getRequiredMoney())) + !this.addon.getEconomyProvider().has(this.user, requirements.getRequiredMoney())) { - this.user.sendMessage("challenges.errors.not-enough-money", - "[value]", - Double.toString(requirements.getRequiredMoney())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-money", + "[value]", + Double.toString(requirements.getRequiredMoney()))); } else if (requirements.getRequiredExperience() < 0) { - this.user.sendMessage("challenges.errors.incorrect"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.incorrect")); } else if (this.user.getPlayer().getTotalExperience() < requirements.getRequiredExperience() && - this.user.getPlayer().getGameMode() != GameMode.CREATIVE) + this.user.getPlayer().getGameMode() != GameMode.CREATIVE) { // Players in creative gamemode has infinite amount of EXP. - - this.user.sendMessage("challenges.errors.not-enough-experience", - "[value]", - Integer.toString(requirements.getRequiredExperience())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-experience", + "[value]", + Integer.toString(requirements.getRequiredExperience()))); } else if (this.addon.isLevelProvided() && this.addon.getLevelAddon().getIslandLevel(this.world, this.user.getUniqueId()) < requirements.getRequiredIslandLevel()) { - this.user.sendMessage("challenges.errors.island-level", - TextVariables.NUMBER, - String.valueOf(requirements.getRequiredIslandLevel())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.island-level", + TextVariables.NUMBER, + String.valueOf(requirements.getRequiredIslandLevel()))); } else { @@ -1233,6 +1386,61 @@ public class TryToComplete } + // --------------------------------------------------------------------- + // Section: Statistic Challenge + // --------------------------------------------------------------------- + + + /** + * Checks if a statistic challenge can be completed or not + * It returns ChallengeResult. + * @param factor - times that user wanted to complete + */ + private ChallengeResult checkStatistic(int factor) + { + if (factor <= 0) + { + return EMPTY_RESULT; + } + + StatisticRequirements requirements = this.challenge.getRequirements(); + + int currentValue; + + if (requirements.getStatistic() == null) + { + // Sanity check. + return EMPTY_RESULT; + } + + switch (Objects.requireNonNull(requirements.getStatistic()).getType()) + { + case UNTYPED -> currentValue = + this.manager.getStatisticData(this.user, this.world, requirements.getStatistic()); + case ITEM, BLOCK -> currentValue = + this.manager.getStatisticData(this.user, this.world, requirements.getStatistic(), requirements.getMaterial()); + case ENTITY -> currentValue = + this.manager.getStatisticData(this.user, this.world, requirements.getStatistic(), requirements.getEntity()); + default -> currentValue = 0; + } + + if (currentValue < requirements.getAmount()) + { + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.requirement-not-met", + TextVariables.NUMBER, String.valueOf(requirements.getAmount()), + "[value]", String.valueOf(currentValue))); + } + else + { + factor = requirements.getAmount() == 0 ? factor : Math.min(factor, currentValue / requirements.getAmount()); + + return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor); + } + + return EMPTY_RESULT; + } + + // --------------------------------------------------------------------- // Section: Title parsings // --------------------------------------------------------------------- @@ -1251,7 +1459,10 @@ public class TryToComplete if (inputMessage.contains("[") && inputMessage.contains("]")) { outputMessage = outputMessage.replace("[friendlyName]", challenge.getFriendlyName()); - outputMessage = outputMessage.replace("[level]", challenge.getLevel().isEmpty() ? "" : this.manager.getLevel(challenge.getLevel()).getFriendlyName()); + + ChallengeLevel level = challenge.getLevel().isEmpty() ? null : this.manager.getLevel(challenge.getLevel()); + outputMessage = outputMessage.replace("[level]", level == null ? "" : level.getFriendlyName()); + outputMessage = outputMessage.replace("[rewardText]", challenge.getRewardText()); } @@ -1324,7 +1535,7 @@ public class TryToComplete * * @author tastybento */ - class ChallengeResult + static class ChallengeResult { /** * This method sets that challenge meets all requirements at least once. diff --git a/src/main/java/world/bentobox/challenges/utils/Constants.java b/src/main/java/world/bentobox/challenges/utils/Constants.java new file mode 100644 index 0000000..bfe490a --- /dev/null +++ b/src/main/java/world/bentobox/challenges/utils/Constants.java @@ -0,0 +1,226 @@ +// +// Created by BONNe +// Copyright - 2020 +// + + +package world.bentobox.challenges.utils; + + +/** + * This class contains String constants used in messages and guis. + */ +public class Constants +{ + /** + * Reference string to ADDON_NAME in translations. + */ + public static final String ADDON_NAME = "challenges."; + +// --------------------------------------------------------------------- +// Section: Commands +// --------------------------------------------------------------------- + + + /** + * Reference string to Commands in translations. + */ + public static final String COMMANDS = ADDON_NAME + "commands."; + + /** + * Reference string to Admin in translations. + */ + public static final String ADMIN_COMMANDS = COMMANDS + "admin."; + + /** + * Reference string to Player in translations. + */ + public static final String PLAYER_COMMANDS = COMMANDS + "player."; + +// --------------------------------------------------------------------- +// Section: GUI +// --------------------------------------------------------------------- + + /** + * Reference string to GUI in translations. + */ + public static final String GUI = ADDON_NAME + "gui."; + + /** + * Reference string to TITLE in translations. + */ + public static final String TITLE = GUI + "titles."; + + /** + * Reference string to BUTTON in translations. + */ + public static final String BUTTON = GUI + "buttons."; + + /** + * Reference string to TIPS in translations. + */ + public static final String TIPS = GUI + "tips."; + + /** + * Reference string to DESCRIPTION in translations. + */ + public static final String DESCRIPTIONS = GUI + "descriptions."; + + /** + * Reference string to Messages in translations. + */ + public static final String MESSAGES = ADDON_NAME + "messages."; + + /** + * Reference string to Errors in translations. + */ + public static final String ERRORS = ADDON_NAME + "errors."; + + /** + * Reference string to Questions in translations. + */ + public static final String CONVERSATIONS = ADDON_NAME + "conversations."; + +// --------------------------------------------------------------------- +// Section: Other +// --------------------------------------------------------------------- + + /** + * Reference string to materials in translations. + */ + public static final String MATERIALS = ADDON_NAME + "materials."; + + /** + * Reference string to entities in translations. + */ + public static final String ENTITIES = ADDON_NAME + "entities."; + + /** + * Reference string to environments in translations. + */ + public static final String ENVIRONMENTS = ADDON_NAME + "environments."; + + /** + * Reference string to statistics in translations. + */ + public static final String STATISTICS = ADDON_NAME + "statistics."; + + /** + * Reference string to item stacks in translations. + */ + public static final String ITEM_STACKS = ADDON_NAME + "item-stacks."; + +// --------------------------------------------------------------------- +// Section: Parameters +// --------------------------------------------------------------------- + + /** + * Reference string to gamemode parameter in translations. + */ + public static final String PARAMETER_GAMEMODE = "[gamemode]"; + + /** + * Reference string to world parameter in translations. + */ + public static final String PARAMETER_WORLD = "[world]"; + + /** + * Reference string to value parameter in translations. + */ + public static final String PARAMETER_VALUE = "[value]"; + + /** + * Reference string to block parameter in translations. + */ + public static final String PARAMETER_MATERIAL = "[material]"; + + /** + * Reference string to entity parameter in translations. + */ + public static final String PARAMETER_ENTITY = "[entity]"; + + /** + * Reference string to environment parameter in translations. + */ + public static final String PARAMETER_ENVIRONMENT = "[environment]"; + + /** + * Reference string to file parameter in translations. + */ + public static final String PARAMETER_FILE = "[file]"; + + /** + * Reference string to id parameter in translations. + */ + public static final String PARAMETER_ID = "[id]"; + + /** + * Reference string to min parameter in translations. + */ + public static final String PARAMETER_MIN = "[min]"; + + /** + * Reference string to max parameter in translations. + */ + public static final String PARAMETER_MAX = "[max]"; + + /** + * Reference to an author parameter in translation. + */ + public static final String PARAMETER_AUTHOR = "[author]"; + + /** + * Reference to an lang parameter in translation. + */ + public static final String PARAMETER_LANG = "[lang]"; + + /** + * Reference to an version parameter in translation. + */ + public static final String PARAMETER_VERSION = "[version]"; + + /** + * Reference to island in translations. + */ + public static final String PARAMETER_ISLAND = "[island]"; + + /** + * Reference string to number parameter in translations. + */ + public static final String PARAMETER_NUMBER = "[number]"; + + /** + * Reference string to permission parameter in translations. + */ + public static final String PARAMETER_PERMISSION = "[permission]"; + + /** + * Reference string to player parameter in translations. + */ + public static final String PARAMETER_PLAYER = "[player]"; + + /** + * Reference string to owner parameter in translations. + */ + public static final String PARAMETER_OWNER = "[owner]"; + + /** + * Reference string to name parameter in translations. + */ + public static final String PARAMETER_NAME = "[name]"; + + /** + * Reference string to level parameter in translations. + */ + public static final String PARAMETER_LEVEL = "[level]"; + + /** + * Reference string to description parameter in translations. + */ + public static final String PARAMETER_DESCRIPTION = "[description]"; + + /** + * Reference string to challenge parameter in translations. + */ + public static final String PARAMETER_CHALLENGE = "[challenge]"; +} diff --git a/src/main/java/world/bentobox/challenges/utils/GuiUtils.java b/src/main/java/world/bentobox/challenges/utils/GuiUtils.java deleted file mode 100644 index 55edbb9..0000000 --- a/src/main/java/world/bentobox/challenges/utils/GuiUtils.java +++ /dev/null @@ -1,439 +0,0 @@ -package world.bentobox.challenges.utils; - - -import java.util.*; - -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 world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; - - -/** - * 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 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; - } - - if (entity.name().equals("PIG_ZOMBIE")) - { - // If pig zombie exist, then pigman spawn egg exists too. - itemStack = new ItemStack(Material.getMaterial("ZOMBIE_PIGMAN_SPAWN_EGG")); - } - - 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(new PanelItemBuilder(). - icon(icon.clone()). - name(" "). - description(Collections.emptyList()). - glow(false). - clickHandler(null)); - } - - - /** - * 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 - * @param warpLength - whn warp should be affected. - * @return list of split strings - */ - public static List stringSplit(String string, int warpLength) - { - // 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, warpLength).split(System.getProperty("line.separator")))). - forEach(result::addAll); - - // Fix colors, as splitting my lost that information. - - for (int i = 0, resultSize = result.size(); i < resultSize; i++) - { - if (i > 0) - { - String lastColor = ChatColor.getLastColors(result.get(i - 1)); - result.set(i, lastColor + result.get(i)); - } - } - - return result; - } - - - /** - * Simple splitter for all strings in list. - * @param stringList - list of string to be split - * @param warpLength - whn warp should be affected. - * @return list of split strings - */ - public static List stringSplit(List stringList, int warpLength) - { - if (stringList.isEmpty()) - { - return stringList; - } - - List newList = new ArrayList<>(stringList.size()); - stringList.stream().map(string -> GuiUtils.stringSplit(string, warpLength)).forEach(newList::addAll); - return newList; - } - - - /** - * Sanitizes the provided input. - * It replaces spaces and hyphens with underscores and lower cases the input. - * @param input input to sanitize - * @return sanitized input - */ - public static String sanitizeInput(String input) - { - return input.toLowerCase(Locale.ENGLISH).replace(" ", "_").replace("-", "_"); - } -} \ 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 deleted file mode 100644 index d08b06e..0000000 --- a/src/main/java/world/bentobox/challenges/utils/HeadLib.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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 java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; - -import world.bentobox.bentobox.BentoBox; - - -/** - * @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=="), - // Since 1.14 - CAT("f0aaa05b-0283-4663-9b57-52dbf2ca2750", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTBkYjQxMzc2Y2E1N2RmMTBmY2IxNTM5ZTg2NjU0ZWVjZmQzNmQzZmU3NWU4MTc2ODg1ZTkzMTg1ZGYyODBhNSJ9fX0="), - FOX("237a2651-7da8-457a-aaea-3714bcc196a2", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDg5NTRhNDJlNjllMDg4MWFlNmQyNGQ0MjgxNDU5YzE0NGEwZDVhOTY4YWVkMzVkNmQzZDczYTNjNjVkMjZhIn19fQ=="), - PANDA("bf7435c9-b7eb-49e9-8887-60697f8081b9", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGNhMDk2ZWVhNTA2MzAxYmVhNmQ0YjE3ZWUxNjA1NjI1YTZmNTA4MmM3MWY3NGE2MzljYzk0MDQzOWY0NzE2NiJ9fX0="), - PILLAGER("1ac9d5aa-46ef-4d71-b077-4564382c0a43", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGFlZTZiYjM3Y2JmYzkyYjBkODZkYjVhZGE0NzkwYzY0ZmY0NDY4ZDY4Yjg0OTQyZmRlMDQ0MDVlOGVmNTMzMyJ9fX0="), - RAVAGER("def81bd7-85e5-4644-b1b2-e7521e53bba8", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWNiOWYxMzlmOTQ4OWQ4NmU0MTBhMDZkOGNiYzY3MGM4MDI4MTM3NTA4ZTNlNGJlZjYxMmZlMzJlZGQ2MDE5MyJ9fX0="), - TRADER_LLAMA("47dbdab5-105f-42bc-9580-c61cee9231f3", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzA4N2E1NTZkNGZmYTk1ZWNkMjg0NGYzNTBkYzQzZTI1NGU1ZDUzNWZhNTk2ZjU0MGQ3ZTc3ZmE2N2RmNDY5NiJ9fX0="), - WANDERING_TRADER("943947ea-3e1a-4fdc-85e5-f538379f05e9", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWYxMzc5YTgyMjkwZDdhYmUxZWZhYWJiYzcwNzEwZmYyZWMwMmRkMzRhZGUzODZiYzAwYzkzMGM0NjFjZjkzMiJ9fX0="), - // Since 1.15 - BEE("77342662-8870-445a-869f-f0aef1406b3d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTlhYzE2ZjI5NmI0NjFkMDVlYTA3ODVkNDc3MDMzZTUyNzM1OGI0ZjMwYzI2NmFhMDJmMDIwMTU3ZmZjYTczNiJ9fX0="), - // Since 1.16 - PIGLIN("7b3f9b15-325b-4d6e-a184-0455e233a1cc", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2NlZDlkODAxYWE2ZjgzZjhlNDlmOTBkOWE4Yjg1YjdmOGZkYTU4M2Q4NWY3MmNmZmI2OTg2NzI1Nzg5ZjYzNiJ9fX0="), - ZOMBIFIED_PIGLIN("4f013cfb-84f8-4d80-8529-25127f6c70ee", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2VhYmFlY2M1ZmFlNWE4YTQ5Yzg4NjNmZjQ4MzFhYWEyODQxOThmMWEyMzk4ODkwYzc2NWUwYThkZTE4ZGE4YyJ9fX0="), - STRIDER("d1c2fba9-6633-4625-9cda-8528fae6fe09", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMThhOWFkZjc4MGVjN2RkNDYyNWM5YzA3NzkwNTJlNmExNWE0NTE4NjY2MjM1MTFlNGM4MmU5NjU1NzE0YjNjMSJ9fX0="), - HOGLIN("8196c240-e96a-4434-b630-6b191ceeb480", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWJiOWJjMGYwMWRiZDc2MmEwOGQ5ZTc3YzA4MDY5ZWQ3Yzk1MzY0YWEzMGNhMTA3MjIwODU2MWI3MzBlOGQ3NSJ9fX0="), - ZOGLIN("d6f4e7ce-dc71-4c81-97dc-df0d15d39a68", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWZhMGFkYTM0MTFmYmE4Yjg4NTgzZDg2NGIyNTI2MDZlOTNkZmRmNjQ3NjkwZDNjZjRjMDE3YjYzYmFiMTJiMCJ9fX0="); - - -// --------------------------------------------------------------------- -// 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); - ItemMeta meta = item.getItemMeta(); - - // Set Lora and DisplayName - if (meta != null && displayName != null) - { - 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); - } - - // Set correct Skull texture - if (meta != null && this.textureValue != null && !this.textureValue.isEmpty()) - { - GameProfile profile = new GameProfile(UUID.fromString(this.uuid), null); - profile.getProperties().put("textures", new Property("textures", this.textureValue)); - - try - { - Field profileField = meta.getClass().getDeclaredField("profile"); - profileField.setAccessible(true); - profileField.set(meta, profile); - item.setItemMeta(meta); - } - catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) - { - BentoBox.getInstance().log("Error while creating Skull Icon"); - } - } - - return item; - } - - -// --------------------------------------------------------------------- -// 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/utils/LevelStatus.java b/src/main/java/world/bentobox/challenges/utils/LevelStatus.java index 5e99cf2..12b9067 100644 --- a/src/main/java/world/bentobox/challenges/utils/LevelStatus.java +++ b/src/main/java/world/bentobox/challenges/utils/LevelStatus.java @@ -19,7 +19,7 @@ public class LevelStatus { * @param previousLevel - previous level * @param numberOfChallengesStillToDo - number of challenges still to do on this level * @param complete - whether complete or not - * @param isUnlocked + * @param isUnlocked - if level is unlocked or not. */ public LevelStatus(ChallengeLevel level, ChallengeLevel previousLevel, int numberOfChallengesStillToDo, boolean complete, boolean isUnlocked) { super(); diff --git a/src/main/java/world/bentobox/challenges/utils/Utils.java b/src/main/java/world/bentobox/challenges/utils/Utils.java index b67593a..dee956d 100644 --- a/src/main/java/world/bentobox/challenges/utils/Utils.java +++ b/src/main/java/world/bentobox/challenges/utils/Utils.java @@ -1,14 +1,25 @@ package world.bentobox.challenges.utils; +import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Locale; +import java.util.Set; -import org.bukkit.Material; -import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.*; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.hooks.LangUtilsHook; +import world.bentobox.bentobox.util.Util; /** @@ -16,13 +27,38 @@ import world.bentobox.bentobox.BentoBox; */ public class Utils { + /** + * This method checks if 2 given item stacks are similar without durability check. + * @param input First item. + * @param stack Second item. + * @return {@code true} if items are equal, {@code false} otherwise. + */ + public static boolean isSimilarNoDurability(@Nullable ItemStack input, @Nullable ItemStack stack) + { + if (stack == null || input == null) + { + return false; + } + else if (stack == input) + { + return true; + } + else + { + return input.getType() == stack.getType() && + input.hasItemMeta() == stack.hasItemMeta() && + (!input.hasItemMeta() || Bukkit.getItemFactory().equals(input.getItemMeta(), stack.getItemMeta())); + } + } + + /** * This method groups input items in single itemstack with correct amount and returns it. * Allows to remove duplicate items from list. * @param requiredItems Input item list * @return List that contains unique items that cannot be grouped. */ - public static List groupEqualItems(List requiredItems) + public static List groupEqualItems(List requiredItems, Set ignoreMetaData) { List returnItems = new ArrayList<>(requiredItems.size()); @@ -40,8 +76,8 @@ public class Utils ItemStack required = returnItems.get(i); // Merge items which meta can be ignored or is similar to item in required list. - if (Utils.canIgnoreMeta(item.getType()) && item.getType().equals(required.getType()) || - required.isSimilar(item)) + if (Utils.isSimilarNoDurability(required, item) || + ignoreMetaData.contains(item.getType()) && item.getType().equals(required.getType())) { required.setAmount(required.getAmount() + item.getAmount()); isUnique = false; @@ -50,7 +86,7 @@ public class Utils i++; } - if (isUnique && item != null) + if (isUnique) { // The same issue as in other places. Clone prevents from changing original item. returnItems.add(item.clone()); @@ -61,29 +97,6 @@ public class Utils } - /** - * This method returns if meta data of these items can be ignored. It means, that items will be searched - * and merged by they type instead of using ItemStack#isSimilar(ItemStack) method. - * - * This limits custom Challenges a lot. It comes from ASkyBlock times, and that is the reason why it is - * still here. It would be a great Challenge that could be completed by collecting 4 books, that cannot - * be crafted. Unfortunately, this prevents it. - * The same happens with firework rockets, enchanted books and filled maps. - * In future it should be able to specify, which items meta should be ignored when adding item in required - * item list. - * - * @param material Material that need to be checked. - * @return True if material meta can be ignored, otherwise false. - */ - public static boolean canIgnoreMeta(Material material) - { - return material.equals(Material.FIREWORK_ROCKET) || - material.equals(Material.ENCHANTED_BOOK) || - material.equals(Material.WRITTEN_BOOK) || - material.equals(Material.FILLED_MAP); - } - - /** * This method transforms given World into GameMode name. If world is not a GameMode * world then it returns null. @@ -105,7 +118,7 @@ public class Utils * @param Instance of given object. * @return Next value after currentValue in values array. */ - public static T getNextValue(T[] values, T currentValue) + public static T getNextValue(T[] values, T currentValue) { for (int i = 0; i < values.length; i++) { @@ -133,7 +146,7 @@ public class Utils * @param Instance of given object. * @return Previous value before currentValue in values array. */ - public static T getPreviousValue(T[] values, T currentValue) + public static T getPreviousValue(T[] values, T currentValue) { for (int i = 0; i < values.length; i++) { @@ -152,4 +165,750 @@ public class Utils return currentValue; } + + + /** + * Sanitizes the provided input. It replaces spaces and hyphens with underscores and lower cases the input. + * This code also removes all color codes from the input. + * @param input input to sanitize + * @return sanitized input + */ + public static String sanitizeInput(String input) + { + return ChatColor.stripColor( + Util.translateColorCodes(input.toLowerCase(Locale.ENGLISH). + replace(" ", "_"). + replace("-", "_"))); + } + + + /** + * Send given message to user and add prefix to the start of the message. + * + * @param user User who need to receive message. + * @param message String of message that must be send. + */ + public static void sendMessage(User user, String message) + { + user.sendMessage(user.getTranslation(Constants.CONVERSATIONS + "prefix") + message); + } + + + /** + * Prettify World.Environment object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for World.Environment. + */ + public static String prettifyObject(World.Environment object, User user) + { + // Find addon structure with: + // [addon]: + // environments: + // [environment]: + // name: [name] + String translation = user.getTranslationOrNothing(Constants.ENVIRONMENTS + object.name().toLowerCase() + ".name"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // environments: + // [environment]: [name] + + translation = user.getTranslationOrNothing(Constants.ENVIRONMENTS + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // environments: + // [environment]: [name] + + translation = user.getTranslationOrNothing("environments." + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Lang Utils do not have Environment :( + //LangUtilsHook.getEnvrionmentName(object, user); + + // Nothing was found. Use just a prettify text function. + return Util.prettifyText(object.name()); + } + + + /** + * Prettify World.Environment object description for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified description string for World.Environment. + */ + public static String prettifyDescription(World.Environment object, User user) + { + // Find addon structure with: + // [addon]: + // environments: + // [environment]: + // description: [text] + String translation = user.getTranslationOrNothing(Constants.ENVIRONMENTS + object.name().toLowerCase() + ".description"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // No text to return. + return ""; + } + + + /** + * Prettify Material object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for Material. + */ + public static String prettifyObject(@Nullable Material object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // materials: + // [material]: + // name: [name] + String translation = user.getTranslationOrNothing(Constants.MATERIALS + object.name().toLowerCase() + ".name"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // materials: + // [material]: [name] + + translation = user.getTranslationOrNothing(Constants.MATERIALS + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // materials: + // [material]: [name] + + translation = user.getTranslationOrNothing("materials." + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Use Lang Utils Hook to translate material + return LangUtilsHook.getMaterialName(object, user); + } + + + /** + * Prettify Material object description for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified description string for Material. + */ + public static String prettifyDescription(@Nullable Material object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // materials: + // [material]: + // description: [text] + String translation = user.getTranslationOrNothing(Constants.MATERIALS + object.name().toLowerCase() + ".description"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // No text to return. + return ""; + } + + + /** + * Prettify EntityType object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for EntityType. + */ + public static String prettifyObject(@Nullable EntityType object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // entities: + // [entity]: + // name: [name] + String translation = user.getTranslationOrNothing(Constants.ENTITIES + object.name().toLowerCase() + ".name"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // entities: + // [entity]: [name] + + translation = user.getTranslationOrNothing(Constants.ENTITIES + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // entities: + // [entity]: [name] + + translation = user.getTranslationOrNothing("entities." + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Use Lang Utils Hook to translate material + return LangUtilsHook.getEntityName(object, user); + } + + + /** + * Prettify EntityType object description for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified description string for EntityType. + */ + public static String prettifyDescription(@Nullable EntityType object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // entities: + // [entity]: + // description: [text] + String translation = user.getTranslationOrNothing(Constants.ENTITIES + object.name().toLowerCase() + ".description"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // No text to return. + return ""; + } + + + /** + * Prettify Statistic object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for Statistic. + */ + public static String prettifyObject(@Nullable Statistic object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // statistics: + // [statistic]: + // name: [name] + String translation = user.getTranslationOrNothing(Constants.STATISTICS + object.name().toLowerCase() + ".name"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // statistics: + // [statistic]: [name] + + translation = user.getTranslationOrNothing(Constants.STATISTICS + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // statistics: + // [statistic]: [name] + + translation = user.getTranslationOrNothing("statistics." + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Use Lang Utils Hook to translate material + //return LangUtilsHook.getStatisticName(object, user); + return Util.prettifyText(object.name()); + } + + + /** + * Prettify Statistic object description for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified description string for Statistic. + */ + public static String prettifyDescription(@Nullable Statistic object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // statistics: + // [statistic]: + // description: [text] + String translation = user.getTranslationOrNothing(Constants.STATISTICS + object.name().toLowerCase() + ".description"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // No text to return. + return ""; + } + + + /** + * Prettify ItemStack object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for ItemStack. + */ + public static String prettifyObject(@Nullable ItemStack object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // item-stacks: + // [material]: ... + // meta: + // potion-type: ... + // ... + // generic: [amount] [name] [meta] + String translation; + switch (object.getType()) + { + case POTION, SPLASH_POTION, LINGERING_POTION, TIPPED_ARROW -> + // Get Potion Meta + translation = prettifyObject(object, (PotionMeta) object.getItemMeta(), user); + case PLAYER_HEAD, PLAYER_WALL_HEAD -> + translation = prettifyObject(object, (SkullMeta) object.getItemMeta(), user); + case ENCHANTED_BOOK -> + translation = prettifyObject(object, (EnchantmentStorageMeta) object.getItemMeta(), user); + case WRITTEN_BOOK, WRITABLE_BOOK -> + translation = prettifyObject(object, (BookMeta) object.getItemMeta(), user); + case LEATHER_BOOTS,LEATHER_CHESTPLATE,LEATHER_HELMET,LEATHER_LEGGINGS,LEATHER_HORSE_ARMOR, + TRIDENT,CROSSBOW,CHAINMAIL_HELMET,CHAINMAIL_CHESTPLATE,CHAINMAIL_LEGGINGS,CHAINMAIL_BOOTS,IRON_HELMET, + IRON_CHESTPLATE,IRON_LEGGINGS,IRON_BOOTS,DIAMOND_HELMET,DIAMOND_CHESTPLATE,DIAMOND_LEGGINGS,DIAMOND_BOOTS, + GOLDEN_HELMET,GOLDEN_CHESTPLATE,GOLDEN_LEGGINGS,GOLDEN_BOOTS,NETHERITE_HELMET,NETHERITE_CHESTPLATE, + NETHERITE_LEGGINGS,NETHERITE_BOOTS,WOODEN_SWORD,WOODEN_SHOVEL,WOODEN_PICKAXE,WOODEN_AXE,WOODEN_HOE, + STONE_SWORD,STONE_SHOVEL,STONE_PICKAXE,STONE_AXE,STONE_HOE,GOLDEN_SWORD,GOLDEN_SHOVEL,GOLDEN_PICKAXE, + GOLDEN_AXE,GOLDEN_HOE,IRON_SWORD,IRON_SHOVEL,IRON_PICKAXE,IRON_AXE,IRON_HOE,DIAMOND_SWORD,DIAMOND_SHOVEL, + DIAMOND_PICKAXE,DIAMOND_AXE,DIAMOND_HOE,NETHERITE_SWORD,NETHERITE_SHOVEL,NETHERITE_PICKAXE,NETHERITE_AXE, + NETHERITE_HOE,TURTLE_HELMET,SHEARS,SHIELD,FLINT_AND_STEEL,BOW -> + translation = prettifyObject(object, object.getItemMeta(), user); + default -> + translation = ""; + } + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // materials: + // [material]: [name] + + translation = user.getTranslationOrNothing(Constants.MATERIALS + object.getType().name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // materials: + // [material]: [name] + + translation = user.getTranslationOrNothing("materials." + object.getType().name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Use Lang Utils + return LangUtilsHook.getItemDisplayName(object, user); + } + + + /** + * Prettify enchant string. + * + * @param enchantment the enchantment + * @param user the user + * @return the string + */ + public static String prettifyObject(Enchantment enchantment, User user) + { + if (enchantment == null) + { + return ""; + } + + String type = user.getTranslationOrNothing(Constants.ITEM_STACKS + "enchant." + enchantment.getKey().getKey()); + + if (type.isEmpty()) + { + type = LangUtilsHook.getEnchantName(enchantment, user); + } + + return type; + } + + + /** + * Prettify type string. + * + * @param type the potion type + * @param user the user + * @return the string + */ + public static String prettifyObject(PotionType type, User user) + { + if (type == null) + { + return ""; + } + + String text = user.getTranslationOrNothing(Constants.ITEM_STACKS + "potion-type." + type.name().toLowerCase()); + + if (text.isEmpty()) + { + text = LangUtilsHook.getPotionBaseEffectName(type, user); + } + + return text; + } + + + /** + * Prettify potion item string. + * + * @param item the item + * @param potionMeta the potion meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable PotionMeta potionMeta, User user) + { + if (potionMeta == null) + { + return ""; + } + + Material itemType = item.getType(); + + final String itemReference = Constants.ITEM_STACKS + itemType.name().toLowerCase() + "."; + final String metaReference = Constants.ITEM_STACKS + "meta."; + + PotionData potionData = potionMeta.getBasePotionData(); + + // Check custom translation for potions. + String type = user.getTranslationOrNothing(itemReference + potionData.getType().name().toLowerCase()); + + if (type.isEmpty()) + { + // Check potion types translation. + type = prettifyObject(potionData.getType(), user); + } + + String upgraded = user.getTranslationOrNothing(metaReference + "upgraded"); + String extended = user.getTranslationOrNothing(metaReference + "extended"); + + // Get item specific translation. + String specific = user.getTranslationOrNothing(itemReference + "name", + "[type]", type, + "[upgraded]", (potionData.isUpgraded() ? upgraded : ""), + "[extended]", (potionData.isExtended() ? extended : "")); + + if (specific.isEmpty()) + { + // Use generic translation. + String meta = user.getTranslationOrNothing(metaReference + "potion-meta", + "[type]", type, + "[upgraded]", (potionData.isUpgraded() ? upgraded : ""), + "[extended]", (potionData.isExtended() ? extended : "")); + + specific = user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", meta); + } + + return specific; + } + + + /** + * Prettify skull item string. + * + * @param item the item + * @param skullMeta the skull meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable SkullMeta skullMeta, User user) + { + if (skullMeta == null) + { + return ""; + } + + Material itemType = item.getType(); + final String metaReference = Constants.ITEM_STACKS + "meta."; + + String meta = user.getTranslationOrNothing(metaReference + "skull-meta", + "[player-name]", skullMeta.getDisplayName()); + + return user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", meta); + } + + + /** + * Prettify item string. + * + * @param item the item + * @param itemMeta the item meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable ItemMeta itemMeta, User user) + { + if (itemMeta == null) + { + return ""; + } + + StringBuilder builder = new StringBuilder(); + + itemMeta.getEnchants().forEach((enchantment, level) -> { + builder.append("\n"); + builder.append(user.getTranslationOrNothing(Constants.ITEM_STACKS + "meta.enchant-meta", + "[type]", prettifyObject(enchantment, user), + "[level]", String.valueOf(level))); + }); + + + Material itemType = item.getType(); + final String itemReference = Constants.ITEM_STACKS + itemType.name().toLowerCase() + "."; + + String translation = user.getTranslationOrNothing(itemReference + "name", + "[type]", prettifyObject(itemType, user), + "[enchant]", builder.toString()); + + if (translation.isEmpty()) + { + translation = user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", builder.toString()); + } + + return translation; + } + + + /** + * Prettify enchantment storage string. + * + * @param item the item + * @param enchantmentMeta the enchantment storage meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable EnchantmentStorageMeta enchantmentMeta, User user) + { + if (enchantmentMeta == null) + { + return ""; + } + + StringBuilder builder = new StringBuilder(); + + enchantmentMeta.getEnchants().forEach((enchantment, level) -> { + builder.append("\n"); + builder.append(user.getTranslationOrNothing(Constants.ITEM_STACKS + "meta.enchant-meta", + "[type]", prettifyObject(enchantment, user), + "[level]", String.valueOf(level))); + }); + + + Material itemType = item.getType(); + final String itemReference = Constants.ITEM_STACKS + itemType.name().toLowerCase() + "."; + + String translation = user.getTranslationOrNothing(itemReference + "name", + "[type]", prettifyObject(itemType, user), + "[enchant]", builder.toString()); + + if (translation.isEmpty()) + { + translation = user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", builder.toString()); + } + + return translation; + } + + + /** + * Prettify book item string. + * + * @param item the item + * @param bookMeta the book meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable BookMeta bookMeta, User user) + { + if (bookMeta == null) + { + return ""; + } + + Material itemType = item.getType(); + final String metaReference = Constants.ITEM_STACKS + "meta."; + + String meta = user.getTranslationOrNothing(metaReference + "book-meta", + "[title]", bookMeta.getTitle(), + "[author]", bookMeta.getAuthor()); + + return user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", meta); + } + + + /** + * This method parses duration to a readable format. + * @param duration that needs to be parsed. + * @return parsed duration string. + */ + public static String parseDuration(Duration duration, User user) + { + final String reference = Constants.DESCRIPTIONS + "challenge.cooldown."; + + String returnString = ""; + + if (duration.toDays() > 0) + { + returnString += user.getTranslationOrNothing(reference + "in-days", + Constants.PARAMETER_NUMBER, String.valueOf(duration.toDays())); + } + + if (duration.toHoursPart() > 0) + { + returnString += user.getTranslationOrNothing(reference + "in-hours", + Constants.PARAMETER_NUMBER, String.valueOf(duration.toHoursPart())); + } + + if (duration.toMinutesPart() > 0) + { + returnString += user.getTranslationOrNothing(reference + "in-minutes", + Constants.PARAMETER_NUMBER, String.valueOf(duration.toMinutesPart())); + } + + if (duration.toSecondsPart() > 0 || returnString.isBlank()) + { + returnString += user.getTranslationOrNothing(reference + "in-seconds", + Constants.PARAMETER_NUMBER, String.valueOf(duration.toSecondsPart())); + } + + return returnString; + } } diff --git a/src/main/java/world/bentobox/challenges/web/WebManager.java b/src/main/java/world/bentobox/challenges/web/WebManager.java index 702d7ef..23a4a01 100644 --- a/src/main/java/world/bentobox/challenges/web/WebManager.java +++ b/src/main/java/world/bentobox/challenges/web/WebManager.java @@ -115,7 +115,7 @@ public class WebManager JsonObject catalog = new JsonParser().parse(catalogContent).getAsJsonObject(); catalog.getAsJsonArray("challenges").forEach(gamemode -> - this.library.add(new LibraryEntry(gamemode.getAsJsonObject()))); + this.library.add(LibraryEntry.fromJson(gamemode.getAsJsonObject()))); } }); } @@ -142,7 +142,7 @@ public class WebManager try { challengeLibrary = gitHubWebAPI.getRepository("BentoBoxWorld", "weblink"). - getContent("challenges/library/" + entry.getRepository() + ".json"). + getContent("challenges/library/" + entry.repository() + ".json"). getContent(). replaceAll("\\n", ""); } @@ -192,7 +192,7 @@ public class WebManager public List getLibraryEntries() { List entries = new ArrayList<>(this.library); - entries.sort(Comparator.comparingInt(LibraryEntry::getSlot)); + entries.sort(Comparator.comparingInt(LibraryEntry::slot)); return entries; } @@ -216,15 +216,15 @@ public class WebManager /** * Challenges Addon variable. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; /** * BentoBox plugin variable. */ - private BentoBox plugin; + private final BentoBox plugin; /** * This list contains all entries that were downloaded from GitHub. */ - private List library; + private final List library; } diff --git a/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java b/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java index 69e6528..c31ac54 100644 --- a/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java +++ b/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java @@ -3,185 +3,47 @@ package world.bentobox.challenges.web.object; import org.bukkit.Material; import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; import com.google.gson.JsonObject; /** - * This objects allows to load each Challenges Catalog library entry. + * The type Library entry. + * @param name Name of the library entry + * @param icon Icon of the library entry + * @param description Description of the library entry + * @param repository Link to the repository + * @param language Language of the entry + * @param slot order of the entry + * @param gameMode Made primary for gamemode + * @param author Author of the entry + * @param version version of the challenges. */ -public class LibraryEntry +public record LibraryEntry(String name, Material icon, String description, String repository, + String language, int slot, String gameMode, String author, String version) { /** * Default constructor. * @param object Json Object that must be translated to LibraryEntry. */ - public LibraryEntry(@NonNull JsonObject object) + public static LibraryEntry fromJson(@NonNull JsonObject object) { - this.name = object.get("name").getAsString(); - Material material = Material.matchMaterial(object.get("icon").getAsString()); - this.icon = (material != null) ? material : Material.PAPER; - this.description = object.get("description").getAsString(); - this.repository = object.get("repository").getAsString(); - this.language = object.get("language").getAsString(); - - this.slot = object.get("slot").getAsInt(); - - this.forGameMode = object.get("for").getAsString(); - this.author = object.get("author").getAsString(); - this.version = object.get("version").getAsString(); + return new LibraryEntry(object.get("name").getAsString(), + (material != null) ? material : Material.PAPER, + object.get("description").getAsString(), + object.get("repository").getAsString(), + object.get("language").getAsString(), + object.get("slot").getAsInt(), + object.get("for").getAsString(), + object.get("author").getAsString(), + object.get("version").getAsString()); } - /** - * This method returns the name value. - * @return the value of name. - */ - @NonNull - public String getName() + public static LibraryEntry fromTemplate(String name, Material icon) { - return name; + return new LibraryEntry(name, icon, "", "", "", 0, "", "", ""); } - - - /** - * This method returns the icon value. - * @return the value of icon. - */ - @NonNull - public Material getIcon() - { - return icon; - } - - - /** - * This method returns the description value. - * @return the value of description. - */ - @NonNull - public String getDescription() - { - return description; - } - - - /** - * This method returns the repository value. - * @return the value of repository. - */ - @NonNull - public String getRepository() - { - return repository; - } - - - /** - * This method returns the language value. - * @return the value of language. - */ - @NonNull - public String getLanguage() - { - return language; - } - - - /** - * This method returns the slot value. - * @return the value of slot. - */ - @NonNull - public int getSlot() - { - return slot; - } - - - /** - * This method returns the forGameMode value. - * @return the value of forGameMode. - */ - @NonNull - public String getForGameMode() - { - return forGameMode; - } - - - /** - * This method returns the author value. - * @return the value of author. - */ - @NonNull - public String getAuthor() - { - return author; - } - - - /** - * This method returns the version value. - * @return the value of version. - */ - @NonNull - public String getVersion() - { - return version; - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - - /** - * Name of entry object - */ - private @NonNull String name; - - /** - * Defaults to {@link Material#PAPER}. - */ - private @NonNull Material icon; - - /** - * Description of entry object. - */ - private @NonNull String description; - - /** - * File name in challenges library. - */ - private @NonNull String repository; - - /** - * Language of content. - */ - private @Nullable String language; - - /** - * Desired slot number. - */ - private int slot; - - /** - * Main GameMode for which challenges were created. - */ - private @Nullable String forGameMode; - - /** - * Author (-s) who created current configuration. - */ - private @Nullable String author; - - /** - * Version of Challenges Addon, for which challenges were created. - */ - private @Nullable String version; } \ No newline at end of file diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml index c43b240..13c59e0 100755 --- a/src/main/resources/addon.yml +++ b/src/main/resources/addon.yml @@ -1,7 +1,7 @@ name: Challenges main: world.bentobox.challenges.ChallengesAddon version: ${version}${build.number} -api-version: 1.14 +api-version: 1.17 repository: 'BentoBoxWorld/Challenges' metrics: true diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 63539f1..64f9eea 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,32 +1,57 @@ -# Challenges Configuration ${project.version} +# Challenges 1.0.0-SNAPSHOT-LOCAL Configuration # 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. # commands: - # - # Allows to define common challenges command that will open User GUI - # with all GameMode selection or Challenges from user world. - # This will not affect /{gamemode_user} challenges command. - user: challenges c - # - # Allows to define common challenges command that will open Admin GUI - # with all GameMode selection. - # This will not affect /{gamemode_admin} challenges command. - admin: challengesadmin chadmin # # This enables/disables common command that will be independent from # all GameModes. For admins it will open selection with all GameModes # (unless there is one), but for users it will open GUI that corresponds # to their world (unless it is specified other way in Admin GUI). - single-gui: false + # This means that writing `/[user_global]` will open Challenges GUI's + # and `/[admin_global]` will open Admin GUI's + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + global-command: false # - # This allows for admins to define which GUI will be opened for admins - # when users calls single-gui command. + # This allows to define which GUI will be opened when `single-gui` is enabled. + # This option is ignored if `single-gui` is disabled. # Acceptable values: # - CURRENT_WORLD - will open GUI that corresponds to user location. # - GAMEMODE_LIST - will open GUI with all installed game modes. - single-gamemode: CURRENT_WORLD + global-view-mode: GAMEMODE_LIST + player: + # + # Allows to define a global challenges user command. This command will work + # only if `global-commands` is enabled. This allows to execute `/challenges` + # without referring to the gamemode. + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + global: challenges c + # + # Allows to define user command for opening challenges GUI's. + # Unlike `global` command, this requires to have gamemode player command before it. + # This will look like: `/[player_cmd] challenges` + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + main: challenges + # + # Allows to define complete command. + # This will look like: `/[player_cmd] challenges complete` + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + complete: complete + admin: + # + # Allows to define a global challenges admin command. This command will work + # only if `global-commands` is enabled. This allows to execute `/chadmin` + # without referring to the gamemode. + # Note, this must not be the same as user global command. + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + global: challengesadmin chadmin + # + # Allows to define admin command for opening challenges GUI's. + # Unlike `global` command, this requires to have gamemode admin command before it. + # This will look like: `/[admin_cmd] challenges` + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + main: challenges history: # # This indicate if player challenges data history will be stored or not. @@ -56,72 +81,11 @@ gui-settings: # their locked level icon, then it will be used, instead of this one. locked-level-icon: ==: org.bukkit.inventory.ItemStack - v: 1631 - type: BOOK - # - # This indicate if free challenges must be at the start (true) or at the end (false) of list. - free-challenges-first: true - # - # This allows to change lore description line length. By default it is 25, but some server - # owners may like it to be larger. - lore-length: 25 - # - # This string allows to change element order in Challenge description. Each letter represents - # one object from challenge description. If letter is not used, then its represented part - # will not be in description. If use any letter that is not recognized, then it will be - # ignored. Some strings can be customized via lang file under 'challenges.gui.challenge-description'. - # List of values and their meaning: - # - LEVEL - Level String: '*.level' - # - STATUS - Status String: '*.completed' - # - COUNT - Times String: '*.completed-times', '*.completed-times-of' or '*.maxed-reached' - # - DESCRIPTION - Description String: defined in challenge object - challenge.description - # - WARNINGS - Warning String: '*.warning-items-take', '*.objects-close-by', '*.warning-entities-kill', '*.warning-blocks-remove' - # - ENVIRONMENT - Environment String: defined in challenge object - challenge.environment - # - REQUIREMENTS - Requirement String: '*.required-level', '*.required-money', '*.required-experience' and items, blocks or entities - # - REWARD_TEXT - Reward String: message that is defined in challenge.rewardTest and challenge.repeatRewardText - # - REWARD_OTHER - Reward extra String: '*.experience-reward', '*.money-reward', '*.not-repeatable' - # - REWARD_ITEMS - Reward Items: List of items that will be rewarded. - # - REWARD_COMMANDS - Reward Commands: List of commands that will be rewarded. - # Requirement and reward items, blocks and entities that are defined in challenge and can be customized under 'challenges.gui.item-description.*' - challenge-lore: - - LEVEL - - STATUS - - COUNT - - DESCRIPTION - - WARNINGS - - ENVIRONMENT - - REQUIREMENTS - - REWARD_TEXT - - REWARD_OTHER - - REWARD_ITEMS - - REWARD_COMMANDS - # - # This string allows to change element order in Level description. Each letter represents - # one object from level description. If letter is not used, then its represented part - # will not be in description. If use any letter that is not recognized, then it will be - # ignored. Some strings can be customized via lang file under 'challenges.gui.level-description'. - # List of values and their meaning: - # - LEVEL_STATUS - Status String: '*.completed' - # - CHALLENGE_COUNT - Count of completed challenges String: '*.completed-challenges-of' - # - UNLOCK_MESSAGE - Description String: defined in level object - challengeLevel.unlockMessage - # - WAIVER_AMOUNT - WaiverAmount String: '*.waver-amount' - # - LEVEL_REWARD_TEXT - Reward String: message that is defined in challengeLevel.rewardText. - # - LEVEL_REWARD_OTHER - Reward extra String: '*.experience-reward', '*.money-reward' - # - LEVEL_REWARD_ITEMS - Reward Items: List of items that will be rewarded. - # - LEVEL_REWARD_COMMANDS - Reward Commands: List of commands that will be rewarded. - # Reward items that are defined in challenge level and can be customized under 'challenges.gui.item-description.*' - level-lore: - - LEVEL_STATUS - - CHALLENGE_COUNT - - UNLOCK_MESSAGE - - WAIVER_AMOUNT - - LEVEL_REWARD_TEXT - - LEVEL_REWARD_OTHER - - LEVEL_REWARD_ITEMS - - LEVEL_REWARD_COMMANDS + v: 2730 + type: BARRIER # # This indicate if challenges data will be stored per island (true) or per player (false). -store-island-data: false +store-island-data: true # # Reset Challenges - if this is true, player's challenges will reset when users # reset an island or if users are kicked or leave a team. Prevents exploiting the @@ -149,7 +113,4 @@ auto-saver: 30 # disabled-gamemodes: # - BSkyBlock disabled-gamemodes: [] -# -uniqueId: config -# configVersion: v3 diff --git a/src/main/resources/default.json b/src/main/resources/default.json index e98a54f..4e9e6a3 100644 --- a/src/main/resources/default.json +++ b/src/main/resources/default.json @@ -2,10 +2,11 @@ "challengeList": [ { "uniqueId": "cobblecollector", - "friendlyName": "Cobble Stone Exchange", + "friendlyName": "&f&l Cobble Stone Exchange", "deployed": true, "description": [ - "Exchange cobble stone to some useful materials" + "&7 Exchange cobble stone", + "&7 to some useful materials" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: COBBLESTONE\n", "order": 1, @@ -23,7 +24,7 @@ "requiredPermissions": [] } }, - "rewardText": "Small amount of gravel will always be handy", + "rewardText": "&7 Small amount of gravel \n&7 will always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GRAVEL\n amount: 4\n" ], @@ -31,7 +32,7 @@ "rewardMoney": 10, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Small amount of gravel will always be handy", + "repeatRewardText": "&7 Small amount of gravel \n&7 will always be handy", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -42,10 +43,11 @@ }, { "uniqueId": "seedcollector", - "friendlyName": "Seeds Exchange", + "friendlyName": "&f&l Seeds Exchange", "deployed": true, "description": [ - "Exchange seeds to some useful materials" + "&7 Exchange seeds to ", + "&7 some useful materials" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: WHEAT_SEEDS\n", "order": 2, @@ -63,7 +65,7 @@ "requiredPermissions": [] } }, - "rewardText": "Small amount of dirt will always be handy", + "rewardText": "&7 Small amount of dirt \n&7 will always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 4\n" ], @@ -71,7 +73,7 @@ "rewardMoney": 10, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Small amount of dirt will always be handy", + "repeatRewardText": "&7 Small amount of dirt \n&7 will always be handy", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -82,10 +84,11 @@ }, { "uniqueId": "cactuscollector", - "friendlyName": "Cactus Exchange", + "friendlyName": "&f&l Cactus Exchange", "deployed": true, "description": [ - "Exchange cactus to some useful materials" + "&7 Exchange cactus to ", + "&7 some useful materials" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CACTUS\n", "order": 3, @@ -103,7 +106,7 @@ "requiredPermissions": [] } }, - "rewardText": "Small amount of sand will always be handy", + "rewardText": "&7 Small amount of sand \n&7 will always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SAND\n amount: 4\n" ], @@ -111,7 +114,7 @@ "rewardMoney": 10, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Small amount of sand will always be handy", + "repeatRewardText": "&7 Small amount of sand will always be handy", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -122,10 +125,11 @@ }, { "uniqueId": "treecollector", - "friendlyName": "Logs Exchange", + "friendlyName": "&f&l Logs Exchange", "deployed": true, "description": [ - "Fell trees and collect some logs" + "&7 Fell trees and collect ", + "&7 some logs" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OAK_LOG\n", "order": 4, @@ -143,7 +147,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your logs for some clay", + "rewardText": "&7 Exchange your logs \n&7 for some clay", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CLAY\n amount: 4\n" ], @@ -151,7 +155,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your logs for some clay balls", + "repeatRewardText": "&7 Exchange your logs for some clay balls", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -162,10 +166,11 @@ }, { "uniqueId": "cobblegenerator", - "friendlyName": "Cobble Stone Generator", + "friendlyName": "&f&l Cobble Stone Generator", "deployed": true, "description": [ - "Everything starts with a simple cobblestone generator." + "&7 Everything starts with", + "&7 a simple cobblestone generator." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: COBBLESTONE\n", "order": 1, @@ -188,7 +193,7 @@ "requiredPermissions": [] } }, - "rewardText": "These tools will help you with some grind.", + "rewardText": "&7 These tools will help \n&7 you with some grind.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: STONE_PICKAXE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DIG_SPEED: 5\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: STONE_AXE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DIG_SPEED: 5\n", @@ -208,10 +213,11 @@ }, { "uniqueId": "cactusfarm", - "friendlyName": "Cactus farm", + "friendlyName": "&f&l Cactus farm", "deployed": true, "description": [ - "Create a simple cactus farm" + "&7 Create a simple", + "&7 cactus farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CACTUS\n", "order": 2, @@ -232,7 +238,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra sand will always be handy", + "rewardText": "&7 Extra sand will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SAND\n amount: 10\n" ], @@ -249,10 +255,11 @@ }, { "uniqueId": "melonfarm", - "friendlyName": "Melon farm", + "friendlyName": "&f&l Melon farm", "deployed": true, "description": [ - "Create a simple Melon farm" + "&7 Create a simple", + "&7 melon farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MELON\n", "order": 2, @@ -273,7 +280,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -290,10 +297,11 @@ }, { "uniqueId": "mushroomfarm", - "friendlyName": "Mushroom farm", + "friendlyName": "&f&l Mushroom farm", "deployed": true, "description": [ - "Create a simple mushroom farm" + "&7 Create a simple", + "&7 mushroom farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BROWN_MUSHROOM\n", "order": 2, @@ -315,7 +323,7 @@ "requiredPermissions": [] } }, - "rewardText": "This mycelium will help you with collecting mushrooms faster.", + "rewardText": "&7 This mycelium will \n&7 help you with collecting \n&7 mushrooms faster.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MYCELIUM\n" ], @@ -332,10 +340,11 @@ }, { "uniqueId": "pumpkinfarm", - "friendlyName": "Pumpkin farm", + "friendlyName": "&f&l Pumpkin farm", "deployed": true, "description": [ - "Create a simple Pumpkin farm" + "&7 Create a simple", + "&7 pumpkin farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PUMPKIN\n", "order": 2, @@ -356,7 +365,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -373,10 +382,11 @@ }, { "uniqueId": "sugarcanefarm", - "friendlyName": "Sugarcane farm", + "friendlyName": "&f&l Sugarcane farm", "deployed": true, "description": [ - "Create a simple sugarcane farm" + "&7 Create a simple", + "&7 sugarcane farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SUGAR_CANE\n", "order": 2, @@ -397,7 +407,7 @@ "requiredPermissions": [] } }, - "rewardText": "This sand will help you to increase your farm size.", + "rewardText": "&7 This sand will \n&7 help you to increase \n&7 your farm size.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SAND\n amount: 16\n" ], @@ -414,10 +424,11 @@ }, { "uniqueId": "treefarm", - "friendlyName": "Tree farm", + "friendlyName": "&f&l Tree farm", "deployed": true, "description": [ - "Create a simple tree farm" + "&7 Create a simple", + "&7 tree farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OAK_SAPLING\n", "order": 2, @@ -438,7 +449,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 10\n" ], @@ -455,10 +466,11 @@ }, { "uniqueId": "applecollector", - "friendlyName": "Apple Picker", + "friendlyName": "&f&l Apple Picker", "deployed": true, "description": [ - "Find and collect some apples from trees" + "&7 Find and collect some", + "&7 apples from trees" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: APPLE\n", "order": 3, @@ -476,7 +488,7 @@ "requiredPermissions": [] } }, - "rewardText": "These saplings will help you to get more trees", + "rewardText": "&7 These saplings will \n&7 help you to get more \n&7 trees", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SPRUCE_SAPLING\n amount: 4\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ACACIA_SAPLING\n amount: 4\n", @@ -488,7 +500,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "These saplings will help you to get more trees", + "repeatRewardText": "&7 These saplings will help \n&7 you to get more trees", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -503,10 +515,11 @@ }, { "uniqueId": "melonmaker", - "friendlyName": "Melon collector", + "friendlyName": "&f&l Melon collector", "deployed": true, "description": [ - "Grow, collect and craft some melons" + "&7 Grow, collect and craft", + "&7 some melons" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MELON\n", "order": 3, @@ -524,7 +537,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed collected melons into some clay blocks", + "rewardText": "&7 Magic transformed \n&7 collected melons into \n&7 some clay blocks", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CLAY\n amount: 4\n" ], @@ -532,7 +545,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed collected melons into some clay balls", + "repeatRewardText": "&7 Magic transformed collected \n&7 melons into some clay balls", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -543,10 +556,11 @@ }, { "uniqueId": "papermaker", - "friendlyName": "Paper Maker", + "friendlyName": "&f&l Paper Maker", "deployed": true, "description": [ - "Collect sugar canes and craft some paper" + "&7 Collect sugar canes and", + "&7 craft some paper" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PAPER\n", "order": 3, @@ -564,7 +578,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic will transform crafted paper into red sand", + "rewardText": "&7 Magic will transform \n&7 crafted paper into red sand", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: RED_SAND\n amount: 4\n" ], @@ -572,7 +586,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic will transform crafted paper into red sand", + "repeatRewardText": "&7 Magic will transform \n&7 crafted paper into red sand", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -583,10 +597,11 @@ }, { "uniqueId": "pumpkinmaker", - "friendlyName": "Pumpkin collector", + "friendlyName": "&f&l Pumpkin collector", "deployed": true, "description": [ - "Grow and collect some pumpkins" + "&7 Grow and collect", + "&7 some pumpkins" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PUMPKIN\n", "order": 3, @@ -604,7 +619,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed collected pumpkins into some clay blocks", + "rewardText": "&7 Magic transformed collected \n&7 pumpkins into some \n&7 clay blocks", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CLAY\n amount: 4\n" ], @@ -612,7 +627,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed collected pumpkins into some clay balls", + "repeatRewardText": "&7 Magic transformed collected \n&7 pumpkins into some clay balls", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -623,10 +638,12 @@ }, { "uniqueId": "chiseledmaker", - "friendlyName": "Chiseled stone bricks crafter", + "friendlyName": "&f&l Chiseled stone bricks crafter", "deployed": true, "description": [ - "Smelt some stone and craft some chiseled stone bricks" + "&7 Smelt some stone and", + "&7 craft some chiseled", + "&7 stone bricks" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CHISELED_STONE_BRICKS\n", "order": 4, @@ -644,7 +661,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed these bricks into andesite", + "rewardText": "&7 Magic transformed these \n&7 bricks into andesite", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ANDESITE\n amount: 4\n" ], @@ -652,7 +669,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed these bricks into andesite", + "repeatRewardText": "&7 Magic transformed these \n&7 bricks into andesite", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -663,10 +680,11 @@ }, { "uniqueId": "crackedmaker", - "friendlyName": "Cracked stone bricks crafter", + "friendlyName": "&f&l Cracked stone bricks crafter", "deployed": true, "description": [ - "Smelt some bricks to get cracked stone bricks." + "&7 Smelt some bricks to", + "&7 get cracked stone bricks." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CRACKED_STONE_BRICKS\n", "order": 4, @@ -684,7 +702,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed these bricks into diorite.", + "rewardText": "&7 Magic transformed these \n&7 bricks into bird-poop.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIORITE\n amount: 4\n" ], @@ -692,7 +710,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed these bricks into diorite.", + "repeatRewardText": "&7 Magic transformed these \n&7 bricks into diorite.", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -703,10 +721,11 @@ }, { "uniqueId": "stonesmelter", - "friendlyName": "Stone brick maker", + "friendlyName": "&f&l Stone brick maker", "deployed": true, "description": [ - "Smelt some stone and craft some stone bricks" + "&7 Smelt some stone and", + "&7 craft some stone bricks" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: STONE_BRICKS\n", "order": 4, @@ -724,7 +743,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed these bricks into granite", + "rewardText": "&7 Magic transformed these \n&7 bricks into granite", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GRANITE\n amount: 4\n" ], @@ -732,7 +751,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed these bricks into granite", + "repeatRewardText": "&7 Magic transformed these \n&7 bricks into granite", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -743,10 +762,11 @@ }, { "uniqueId": "beetrootfarm", - "friendlyName": "Beetroot farm", + "friendlyName": "&f&l Beetroot farm", "deployed": true, "description": [ - "Create a simple beetroot farm" + "&7 Create a simple", + "&7 beetroot farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BEETROOT\n", "order": 1, @@ -767,7 +787,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -784,10 +804,11 @@ }, { "uniqueId": "carrotfarm", - "friendlyName": "Carrot farm", + "friendlyName": "&f&l Carrot farm", "deployed": true, "description": [ - "Create a simple carrot farm" + "&7 Create a simple", + "&7 carrot farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CARROT\n", "order": 1, @@ -808,7 +829,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -825,10 +846,11 @@ }, { "uniqueId": "potatofarm", - "friendlyName": "Potato farm", + "friendlyName": "&f&l Potato farm", "deployed": true, "description": [ - "Create a simple potato farm" + "&7 Create a simple", + "&7 potato farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: POTATO\n", "order": 1, @@ -849,7 +871,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -866,10 +888,11 @@ }, { "uniqueId": "animalfarm", - "friendlyName": "Animal farm", + "friendlyName": "&f&l Animal farm", "deployed": true, "description": [ - "Create a grass field and wait for animals to spawn." + "&7 Create a grass field and", + "&7 wait for animals to spawn." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GRASS_BLOCK\n", "order": 2, @@ -895,7 +918,7 @@ "requiredPermissions": [] } }, - "rewardText": "Iron to craft some tools and a couple of cats.", + "rewardText": "&7 Iron to craft some \n&7 tools and a couple of \n&7 cats.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OCELOT_SPAWN_EGG\n amount: 2\n" @@ -913,10 +936,11 @@ }, { "uniqueId": "mobfarm", - "friendlyName": "Mob farm", + "friendlyName": "&f&l Mob farm", "deployed": true, "description": [ - "Create a dark place and spawn some hostile mobs." + "&7 Create a dark place and", + "&7 spawn some hostile mobs." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: STONE\n", "order": 2, @@ -940,7 +964,7 @@ "requiredPermissions": [] } }, - "rewardText": "Iron for item crafting and some dogs.", + "rewardText": "&7 Iron for item crafting \n&7 and some dogs.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: WOLF_SPAWN_EGG\n amount: 2\n" @@ -958,10 +982,12 @@ }, { "uniqueId": "visitnether", - "friendlyName": "Visit nether", + "friendlyName": "&f&l Visit nether", "deployed": true, "description": [ - "The nether is a horrible place, but it has some useful materials." + "&7 The nether is a horrible", + "&7 place, but it has some", + "&7 useful materials." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: NETHERRACK\n", "order": 3, @@ -984,7 +1010,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some iron and lapis for your efforts.", + "rewardText": "&7 Some iron and lapis \n&7 for your efforts.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: LAPIS_BLOCK\n amount: 3\n" @@ -1002,10 +1028,10 @@ }, { "uniqueId": "angler", - "friendlyName": "Angler", + "friendlyName": "&f&l Angler", "deployed": true, "description": [ - "Catch some salmon" + "&7 Catch some salmon" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SALMON\n", "order": 4, @@ -1023,7 +1049,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some prismarine crystals and kelp", + "rewardText": "&7 Some prismarine crystals \n&7 and kelp", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PRISMARINE_CRYSTALS\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: KELP\n amount: 4\n" @@ -1032,7 +1058,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some prismarine crystals", + "repeatRewardText": "&7 Some prismarine crystals", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1043,10 +1069,10 @@ }, { "uniqueId": "fisherman", - "friendlyName": "Fisherman", + "friendlyName": "&f&l Fisherman", "deployed": true, "description": [ - "Catch some cod" + "&7 Catch some cod" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: COD\n", "order": 4, @@ -1064,7 +1090,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some prismarine shards and sea pickles", + "rewardText": "&7 Some prismarine shards \n&7 and sea pickles", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PRISMARINE_SHARD\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SEA_PICKLE\n amount: 4\n" @@ -1073,7 +1099,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some prismarine shards", + "repeatRewardText": "&7 Some prismarine shards", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1084,10 +1110,11 @@ }, { "uniqueId": "grinder", - "friendlyName": "Grinder", + "friendlyName": "&f&l Grinder", "deployed": true, "description": [ - "Use your mobfarm to collect mob drops" + "&7 Use your mobfarm", + "&7 to collect mob drops" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BONE\n", "order": 4, @@ -1110,7 +1137,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchanging drops will provide some useful materials.", + "rewardText": "&7 Exchanging drops will \n&7 provide some useful \n&7 materials.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: REDSTONE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 5\n", @@ -1120,7 +1147,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some prismarine crystals", + "repeatRewardText": "&7 Some prismarine crystals", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1131,10 +1158,11 @@ }, { "uniqueId": "competent", - "friendlyName": "Competent Island", + "friendlyName": "&f&l Competent Island", "deployed": true, "description": [ - "Reach 20th level of island." + "&7 Reach 20th level", + "&7 of island." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: YELLOW_CONCRETE\n", "order": 5, @@ -1153,7 +1181,7 @@ "requiredPermissions": [] } }, - "rewardText": "Now you can create a nether portal", + "rewardText": "&7 Now you can create \n&7 a nether portal", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OBSIDIAN\n amount: 10\n" ], @@ -1170,10 +1198,12 @@ }, { "uniqueId": "irongolemfarm", - "friendlyName": "Iron Farm", + "friendlyName": "&f&l Iron Farm", "deployed": true, "description": [ - "Use your villagers to spawn free iron golems." + "&7 Use your villagers", + "&7 to spawn free iron", + "&7 golems." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_BLOCK\n", "order": 1, @@ -1194,7 +1224,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some handy mending books and redstone ore", + "rewardText": "&7 Some handy mending \n&7 books and redstone ore", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n amount: 1\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n amount: 1\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", @@ -1213,10 +1243,11 @@ }, { "uniqueId": "slimefarm", - "friendlyName": "Slime farm", + "friendlyName": "&f&l Slime farm", "deployed": true, "description": [ - "Create a slime farm to get some slime blocks" + "&7 Create a slime farm", + "&7 to get some slime blocks" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SLIME_BLOCK\n", "order": 1, @@ -1237,7 +1268,7 @@ "requiredPermissions": [] } }, - "rewardText": "Get some nether quartz ore and an extra mending book", + "rewardText": "&7 Get some nether \n&7 quartz ore and an extra \n&7 mending book", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: NETHER_QUARTZ_ORE\n amount: 16\n" @@ -1255,10 +1286,10 @@ }, { "uniqueId": "snowgolemfarm", - "friendlyName": "Snow farm", + "friendlyName": "&f&l Snow farm", "deployed": true, "description": [ - "." + "&7 Create a snow Golem" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SNOW_BLOCK\n", "order": 1, @@ -1281,7 +1312,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some mending books and lapis ore", + "rewardText": "&7 Some mending books \n&7 and lapis ore", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n amount: 1\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n amount: 1\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", @@ -1300,10 +1331,11 @@ }, { "uniqueId": "villagerbreeder", - "friendlyName": "Villager Breeder", + "friendlyName": "&f&l Villager Breeder", "deployed": true, "description": [ - "Start to create a villager army." + "&7 Start to create", + "&7 a villager army." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EMERALD_BLOCK\n", "order": 1, @@ -1324,7 +1356,7 @@ "requiredPermissions": [] } }, - "rewardText": "Iron for tools crafting and some cats.", + "rewardText": "&7 Iron for tools crafting \n&7 and some cats.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OCELOT_SPAWN_EGG\n amount: 2\n" @@ -1342,10 +1374,11 @@ }, { "uniqueId": "eggcollector", - "friendlyName": "Egg Collector", + "friendlyName": "&f&l Egg Collector", "deployed": true, "description": [ - "Create a chicken farm and collect their eggs." + "&7 Create a chicken farm", + "&7 and collect their eggs." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EGG\n", "order": 2, @@ -1363,7 +1396,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange eggs for some iron", + "rewardText": "&7 Exchange eggs for \n&7 some iron", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 24\n" ], @@ -1371,7 +1404,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange eggs for some iron", + "repeatRewardText": "&7 Exchange eggs for \n&7 some iron", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1382,10 +1415,10 @@ }, { "uniqueId": "flowercollector", - "friendlyName": "Flower gatherer", + "friendlyName": "&f&l Flower gatherer", "deployed": true, "description": [ - "Collect all small flowers." + "&7 Collect all small flowers." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DANDELION\n", "order": 2, @@ -1412,7 +1445,7 @@ "requiredPermissions": [] } }, - "rewardText": "Large flowers are always better.", + "rewardText": "&7 Large flowers are \n&7 always better.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: COCOA_BEANS\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ROSE_BUSH\n", @@ -1434,10 +1467,11 @@ }, { "uniqueId": "milkcollector", - "friendlyName": "Milk your cows", + "friendlyName": "&f&l Milk your cows", "deployed": true, "description": [ - "Create some buckets and milk your cows" + "&7 Create some buckets", + "&7 and milk your cows" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MILK_BUCKET\n", "order": 2, @@ -1455,7 +1489,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange milk to mooshrooms", + "rewardText": "&7 Exchange milk \n&7 to mooshrooms", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BUCKET\n amount: 9\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MOOSHROOM_SPAWN_EGG\n amount: 2\n" @@ -1464,7 +1498,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange milk to some iron", + "repeatRewardText": "&7 Exchange milk to \n&7 some iron", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1476,10 +1510,11 @@ }, { "uniqueId": "woolcollector", - "friendlyName": "Wool Collector", + "friendlyName": "&f&l Wool Collector", "deployed": true, "description": [ - "Collect all colors of wool." + "&7 Collect all colors", + "&7 of wool." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: WHITE_WOOL\n", "order": 2, @@ -1512,7 +1547,7 @@ "requiredPermissions": [] } }, - "rewardText": "Get some handy items for your wool", + "rewardText": "&7 Get some handy items \n&7 for your wool", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 2\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MUSIC_DISC_CAT\n", @@ -1537,10 +1572,11 @@ }, { "uniqueId": "slimeballcollector", - "friendlyName": "Slimeball collector", + "friendlyName": "&f&l Slimeball collector", "deployed": true, "description": [ - "Kill slimes and collect their slime balls" + "&7 Kill slimes and collect", + "&7 their slime balls" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SLIME_BALL\n", "order": 3, @@ -1558,7 +1594,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some redstone will always be handy!", + "rewardText": "&7 Some redstone will \n&7 always be handy!", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: REDSTONE\n amount: 27\n" ], @@ -1566,7 +1602,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some redstone will always be handy!", + "repeatRewardText": "&7 Some redstone will \n&7 always be handy!", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1577,10 +1613,11 @@ }, { "uniqueId": "ironage", - "friendlyName": "Iron Age", + "friendlyName": "&f&l Iron Age", "deployed": true, "description": [ - "Craft iron tools and armor" + "&7 Craft iron tools", + "&7 and armor" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_INGOT\n", "order": 4, @@ -1606,7 +1643,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your iron stuff to some diamonds", + "rewardText": "&7 Exchange your iron stuff \n&7 to some diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 4\n" ], @@ -1614,7 +1651,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your iron stuff to diamond", + "repeatRewardText": "&7 Exchange your iron \n&7 stuff to diamond", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1625,10 +1662,11 @@ }, { "uniqueId": "trader", - "friendlyName": "Trader", + "friendlyName": "&f&l Trader", "deployed": true, "description": [ - "Use your villagers to get some gems" + "&7 Use your villagers", + "&7 to get some gems" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EMERALD\n", "order": 4, @@ -1646,7 +1684,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your emeralds to some diamonds", + "rewardText": "&7 Exchange your emeralds \n&7 to some diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 4\n" ], @@ -1654,7 +1692,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your emeralds to diamond", + "repeatRewardText": "&7 Exchange your emeralds \n&7 to diamond", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1665,10 +1703,11 @@ }, { "uniqueId": "expert", - "friendlyName": "Expert Island", + "friendlyName": "&f&l Expert Island", "deployed": true, "description": [ - "Reach 100th level of island." + "&7 Reach 100th level of", + "&7 island." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GREEN_CONCRETE\n", "order": 6, @@ -1687,7 +1726,7 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds", + "rewardText": "&7 Get some extra \n&7 diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 10\n" ], @@ -1704,10 +1743,10 @@ }, { "uniqueId": "alchemist", - "friendlyName": "Alchemist", + "friendlyName": "&f&l Alchemist", "deployed": true, "description": [ - "Brew some potions" + "&7 Brew some potions" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BREWING_STAND\n", "order": 1, @@ -1738,7 +1777,7 @@ "requiredPermissions": [] } }, - "rewardText": "There are never too many diamonds", + "rewardText": "&7 There are never too \n&7 many diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_ORE\n amount: 4\n" ], @@ -1757,10 +1796,11 @@ }, { "uniqueId": "dj", - "friendlyName": "DJ", + "friendlyName": "&f&l DJ", "deployed": true, "description": [ - "Craft a jukebox and collect all music discs" + "&7 Craft a jukebox and", + "&7 collect all music discs" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MUSIC_DISC_CHIRP\n", "order": 2, @@ -1790,7 +1830,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some diamonds and emeralds for your disks", + "rewardText": "&7 Some diamonds and \n&7 emeralds for your disks", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 3\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EMERALD\n amount: 10\n", @@ -1800,7 +1840,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "A diamond and some emeralds for your disks", + "repeatRewardText": "&7 A diamond and some emeralds \n&7 for your disks", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1813,10 +1853,10 @@ }, { "uniqueId": "icefarmer", - "friendlyName": "Ice Farmer", + "friendlyName": "&f&l Ice Farmer", "deployed": true, "description": [ - "Collect some ice" + "&7 Collect some ice" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ICE\n", "order": 3, @@ -1835,16 +1875,17 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes|", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes|", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 16\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Get some extra diamonds|", + "repeatRewardText": "&7 Get some extra \n&7 diamonds", "maxTimes": 0, "repeatExperienceReward": 0, "repeatItemReward": [ @@ -1855,10 +1896,11 @@ }, { "uniqueId": "goldage", - "friendlyName": "Gold Age", + "friendlyName": "&f&l Gold Age", "deployed": true, "description": [ - "Craft gold tools and armor" + "&7 Craft gold tools", + "&7 and armor" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GOLD_INGOT\n", "order": 4, @@ -1884,7 +1926,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your gold stuff to some diamond", + "rewardText": "&7 Exchange your gold \n&7 stuff to some diamond", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 16\n" ], @@ -1892,7 +1934,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your gold stuff to diamond", + "repeatRewardText": "&7 Exchange your gold \n&7 stuff to diamond", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1903,10 +1945,12 @@ }, { "uniqueId": "witherkiller", - "friendlyName": "Wither Killer", + "friendlyName": "&f&l Wither Killer", "deployed": true, "description": [ - "Kill the wither and show your worthiness" + "&7 Kill the wither", + "&7 and show your", + "&7 worthiness" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: NETHER_STAR\n", "order": 5, @@ -1924,10 +1968,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_BLOCK\n amount: 5\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -1942,10 +1987,12 @@ }, { "uniqueId": "chef", - "friendlyName": "Chef", + "friendlyName": "&f&l Chef", "deployed": true, "description": [ - "Show your ability to cook all types of food" + "&7 Show your ability", + "&7 to cook all types", + "&7 of food" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CAKE\n", "order": 6, @@ -1978,16 +2025,17 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 16\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some diamond ore for your food|", + "repeatRewardText": "&7 Some diamond ore \n&7 for your food|", "maxTimes": 100, "repeatExperienceReward": 0, "repeatItemReward": [ @@ -1998,10 +2046,11 @@ }, { "uniqueId": "zoomaker", - "friendlyName": "Zoo Maker", + "friendlyName": "&f&l Zoo Maker", "deployed": true, "description": [ - "Create a peaceful mob zoo" + "&7 Create a peaceful", + "&7 mob zoo" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GLASS_PANE\n", "order": 7, @@ -2038,10 +2087,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 32\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2056,10 +2106,11 @@ }, { "uniqueId": "advanced", - "friendlyName": "Advanced Island", + "friendlyName": "&f&l Advanced Island", "deployed": true, "description": [ - "Reach 500th level of island." + "&7 Reach 500th level", + "&7 of island." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BLUE_CONCRETE\n", "order": 10, @@ -2078,7 +2129,7 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker shells", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker shells", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 20\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_SHELL\n amount: 5\n" @@ -2096,10 +2147,11 @@ }, { "uniqueId": "visitend", - "friendlyName": "Visit the End", + "friendlyName": "&f&l Visit the End", "deployed": true, "description": [ - "Is there something even after the end?" + "&7 Is there something", + "&7 even after the end?" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: END_STONE\n", "order": 3, @@ -2122,7 +2174,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some diamonds and an elytra for your efforts.", + "rewardText": "&7 Some diamonds and \n&7 an elytra for your efforts.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ELYTRA\n" @@ -2140,10 +2192,11 @@ }, { "uniqueId": "diamondage", - "friendlyName": "Diamond Age", + "friendlyName": "&f&l Diamond Age", "deployed": true, "description": [ - "Craft diamond tools and armor" + "&7 Craft diamond tools", + "&7 and armor" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n", "order": 4, @@ -2169,15 +2222,17 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your diamond stuff to some shulker boxes", + "rewardText": "&7 Exchange your diamond \n&7 stuff to some shulker boxes", "rewardItems": [ - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 3\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your diamond stuff to shulker shell", + "repeatRewardText": "&7 Exchange your diamond \n&7 stuff to shulker shell", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -2188,10 +2243,11 @@ }, { "uniqueId": "glassmaker", - "friendlyName": "Glass maker", + "friendlyName": "&f&l Glass maker", "deployed": true, "description": [ - "Craft and collect all colors of glass" + "&7 Craft and collect", + "&7 all colors of glass" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GLASS\n", "order": 4, @@ -2224,7 +2280,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your glass to a shulker box", + "rewardText": "&7 Exchange your glass \n&7 to a shulker box", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n" ], @@ -2232,7 +2288,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your glass to a shulker shell", + "repeatRewardText": "&7 Exchange your glass \n&7 to a shulker shell", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -2243,10 +2299,11 @@ }, { "uniqueId": "terracottamaker", - "friendlyName": "Terracotta maker", + "friendlyName": "&f&l Terracotta maker", "deployed": true, "description": [ - "Smelt some clay into terracotta" + "&7 Smelt some clay", + "&7 into terracotta" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: TERRACOTTA\n", "order": 4, @@ -2279,7 +2336,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your terracotta to a shulker box", + "rewardText": "&7 Exchange your terracotta \n&7 to a shulker box", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n" ], @@ -2287,7 +2344,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your terracotta to a shulker shell", + "repeatRewardText": "&7 Exchange your terracotta \n&7 to a shulker shell", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -2298,10 +2355,11 @@ }, { "uniqueId": "beaconator", - "friendlyName": "Beaconator", + "friendlyName": "&f&l Beaconator", "deployed": true, "description": [ - "Create a beacon with iron base" + "&7 Create a beacon", + "&7 with iron base" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BEACON\n", "order": 6, @@ -2323,10 +2381,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 32\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2341,10 +2400,10 @@ }, { "uniqueId": "enderkiller", - "friendlyName": "Hostile Zoo", + "friendlyName": "&f&l Hostile Zoo", "deployed": true, "description": [ - "Create hostile mob zoo" + "&7 Create hostile mob zoo" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_BARS\n", "order": 6, @@ -2380,10 +2439,14 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 32\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 5\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2398,10 +2461,11 @@ }, { "uniqueId": "engineer", - "friendlyName": "Engineer", + "friendlyName": "&f&l Engineer", "deployed": true, "description": [ - "Create some redstone machines" + "&7 Create some redstone", + "&7 machines" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OBSERVER\n", "order": 6, @@ -2429,10 +2493,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 32\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2449,10 +2514,11 @@ }, { "uniqueId": "waterzoo", - "friendlyName": "Water Zoo", + "friendlyName": "&f&l Water Zoo", "deployed": true, "description": [ - "Create a water animal zoo" + "&7 Create a water", + "&7 animal zoo" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BLUE_STAINED_GLASS_PANE\n", "order": 7, @@ -2481,10 +2547,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 16\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2499,10 +2566,11 @@ }, { "uniqueId": "elite", - "friendlyName": "Elite Island", + "friendlyName": "&f&l Elite Island", "deployed": true, "description": [ - "Reach 10 000th level of island." + "&7 Reach 10 000th level", + "&7 of island." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MAGENTA_CONCRETE\n", "order": 10, @@ -2521,10 +2589,14 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 64\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 5\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2541,14 +2613,14 @@ "challengeLevelList": [ { "uniqueId": "novice", - "friendlyName": "Novice", + "friendlyName": "&f&l Novice", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ORANGE_CONCRETE\n", "lockedIcon": null, "world": "", "order": 1, "waiverAmount": 3, "unlockMessage": "", - "rewardText": "Small reward for completing all Novice challenges.", + "rewardText": "&7 Small reward for \n&7 completing all \n&7 Novice challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GRASS_BLOCK\n" ], @@ -2576,14 +2648,14 @@ }, { "uniqueId": "competent", - "friendlyName": "Competent", + "friendlyName": "&f&l Competent", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: YELLOW_CONCRETE\n", "lockedIcon": null, "world": "", "order": 2, "waiverAmount": 2, - "unlockMessage": "Congratulations - you unlocked the &dCompetent level!", - "rewardText": "Small reward for completing all Competent challenges.", + "unlockMessage": "&7 Congratulations - \n&7 you unlocked the \n&7 &dCompetent level!", + "rewardText": "&7 Small reward for \n&7 completing all \n&7 Competent challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -2607,14 +2679,14 @@ }, { "uniqueId": "expert", - "friendlyName": "Expert", + "friendlyName": "&f&l Expert", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GREEN_CONCRETE\n", "lockedIcon": null, "world": "", "order": 3, "waiverAmount": 1, - "unlockMessage": "Congratulations - you unlocked the &dExpert level!", - "rewardText": "Small reward for completing all Expert challenges.", + "unlockMessage": "&7 Congratulations - \n&7 you unlocked the \n&7 &dExpert level!", + "rewardText": "&7 Small reward for \n&7 completing all \n&7 Expert challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_PICKAXE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DIG_SPEED: 5\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_AXE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DIG_SPEED: 5\n", @@ -2643,14 +2715,14 @@ }, { "uniqueId": "advanced", - "friendlyName": "Advanced", + "friendlyName": "&f&l Advanced", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BLUE_CONCRETE\n", "lockedIcon": null, "world": "", "order": 4, "waiverAmount": 0, - "unlockMessage": "Congratulations - you unlocked the &dAdvanced level!", - "rewardText": "Small reward for completing all Advanced challenges.", + "unlockMessage": "&7 Congratulations - \n&7 you unlocked the \n&7 &dAdvanced level!", + "rewardText": "&7 Small reward for \n&7 completing all \n&7 Advanced challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_HELMET\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n PROTECTION_ENVIRONMENTAL: 4\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_CHESTPLATE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n PROTECTION_ENVIRONMENTAL: 4\n", @@ -2676,14 +2748,14 @@ }, { "uniqueId": "elite", - "friendlyName": "Elite", + "friendlyName": "&f&l Elite", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MAGENTA_CONCRETE\n", "lockedIcon": null, "world": "", "order": 5, "waiverAmount": 0, - "unlockMessage": "Congratulations - you unlocked the &dElite level!", - "rewardText": "Small reward for completing all Elite challenges.", + "unlockMessage": "&7 Congratulations - \n&7 you unlocked the \n&7 &dElite level!", + "rewardText": "&7 Small reward for \n&7 completing all\n&7 Elite challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_SWORD\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DAMAGE_ALL: 5\n LOOT_BONUS_MOBS: 3\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BOW\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n ARROW_DAMAGE: 5\n ARROW_INFINITE: 1\n ARROW_FIRE: 1\n", diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml index 90744eb..2d65c5f 100644 --- a/src/main/resources/locales/de.yml +++ b/src/main/resources/locales/de.yml @@ -1,552 +1,1046 @@ --- +meta: + authors: + - BONNe challenges: commands: admin: main: description: Hauptadministrationsbefehl. Öffnet GUI. - import: - parameters: "[overwrite]" - description: |- - Herausforderungen aus der challenges.yml importieren - Parameter überschreiben bedeutet, dass Herausforderungen oder Level mit der gleichen ID überschrieben werden. reload: - parameters: "[hard]" description: |- Herausforderungen aus der Datenbank neu laden Parameter hard bedeutet, dass Addon die Verbindung zur Datenbank zurücksetzen wird. - defaults: - parameters: "[command]" - description: Zeigt Unterbefehle zum Importieren/Exportieren der Standardherausforderungen. - defaults-generate: - parameters: "[overwrite] - Erlaubt es eine bereits existierende Datei zu überschreiben" - description: Bestehende Herausforderungen in default.json Datei exportieren. - complete: - parameters: " " - description: Eine Herausforderung für einen Spieler abschließen. - reset: - parameters: " " - description: Eine Herausforderung für einen Spieler zurücksetzen. Wenn "challenge_id" - auf "all" gesetzt ist, werden alle Herausforderungen zurückgesetzt. - migrate: - description: Migrieren der aktuellen Spielwelt Herausforderungen Daten auf - 0.8.0 Speicherformat. show: description: Schreibt alle Herausforderungen in den Chat, die es auf dieser Welt gibt. - defaults-import: - description: Importiert die Standardherausforderungen. - user: complete: - parameters: " [count]" - description: Herausforderung abschließen. + description: Eine Herausforderung für einen Spieler abschließen. + parameters: " " + reset: + description: Eine Herausforderung für einen Spieler zurücksetzen. Wenn "challenge_id" + auf "all" gesetzt ist, werden alle Herausforderungen zurückgesetzt. + parameters: " " + migrate: + description: Migrieren der aktuellen Spielwelt Herausforderungen Daten auf + 0.8.0 Speicherformat. + user: main: description: Herausforderungen GUI öffnen. + complete: + description: Herausforderung abschließen. + parameters: " [count]" gui: - title: - admin: - gui-title: "&aHerausforderungen Admin" - edit-challenge-title: "&aBearbeite die Herausforderungen" - edit-level-title: "&aEditiere das Level" - settings-title: "&aEinstellungen bearbeiten" - choose-challenge-title: "&aHerausforderung auswählen" - choose-level-title: "&aStufe wählen" - choose-user-title: "&aSpieler auswählen" - manage-blocks: "&aBlöcke verwalten" - manage-entities: "&aObjekte verwalten" - confirm-title: "&aBestätigung" - manage-items: "&aItems verwalten" - manage-numbers: "&aNummernblock" - select-block: "&aBlock wählen" - select-challenge: "&aHerausforderung auswählen" - select-entity: "&aObjekt auswählen" - toggle-environment: "&aUmschalten der Umgebung" - edit-text-fields: "&aTextfelder bearbeiten" - library-title: "&aDownloadbare Bibliotheken" - lore-add: "&aHinzufügen von Machtelementen" - lore-remove: "&aEntfernen von Überlieferungselement" - lore-edit: "&aBearbeiten von Überlieferungen" - type-select: "&aHerausforderungstyp wählen" - challenges: "&6Herausforderungen" - game-modes: "&6Spielmodus wählen" - multiple-complete: "&6Wie oft?" + titles: + player-gui: "&0&l Herausforderungsmenü" + gamemode-gui: "&0&l Spielmodus auswählen" + multiple-gui: "&0&l Wie oft?" + admin-gui: "&0&l Herausforderung Admin Menu" + edit-challenge: "&0&l Herausforderung [challenge]" + edit-level: "&0&l Level Bearbeiten [level]" + settings: "&0&l Einstellungen" + choose-challenge: "&0&l Herausforderung wählen" + choose-level: "&0&l Level wählen" + choose-player: "&0&l Spieler wählen" + library: "&0&l Bibliothek" + manage-blocks: "&0&l Blöcke verwalten" + manage-entities: "&0&l Einheiten verwalten" + type-selector: "&0&l Wahl des Herausforderungstyps" + item-selector: "&0&l Item Auswahl" + block-selector: "&0&l Block wählen" + entity-selector: "&0&l Mob wählen" + challenge-selector: "&0&l Herausforderungsauswahl" + statistic-selector: "&0&l Statistik Auswahl" + environment-selector: "&0&l Umgebungs Auswahl" buttons: - admin: - complete: Vollständige Benutzerherausforderung - reset: Benutzer-Herausforderung zurücksetzen - create-challenge: Hinzufügen einer neuen Herausforderung - create-level: Hinzufügen eines neuen Levels - edit-challenge: 'Herausforderung bearbeiten ' - edit-level: Level bearbeiten - delete-challenge: Herausforderung entfernen - delete-level: Level entfernen - properties: Eigenschaften - requirements: Anforderungen - rewards: Belohnungen - challenges: Herausforderungen - deployment: Aufstellung - icon: Symbol - locked-icon: Gesperrtes Icon - description: Beschreibung - order: Order - environment: Umgebung - remove-on-complete: Nach Fertigstellung entfernen - required-experience: Benötigte Erfahrung - remove-experience: Erfahrung entfernen - required-level: Benötigtes Insellevel - required-money: Benötigtes Geld - remove-money: Geld entfernen - reward-text: Belohnungsnachricht - reward-items: Item Belohnung - reward-experience: Erfahrungsbelohnung - reward-money: Geld Belohnung - reward-commands: Belohnungs-Befehle - repeatable: Wiederholbar - repeat-count: Max Wiederholung - repeat-reward-text: Belohnungsnachricht wiederholen - repeat-reward-items: Item Belohnung wiederholen - repeat-reward-experience: Erfahrungsbelohnung wiederholen - repeat-reward-money: Geld Belohnung wiederholen - repeat-reward-commands: Belohnungsbefehle wiederholen - remove-completed: Nach Fertigstellung entfernen - glow: Leuchtet nach Fertigstellung - free-at-top: Freie Herausforderungen zuerst - line-length: Länge der Striche - add: Hinzufügen - accept: Akzeptieren - decline: Ablehnen - save: speichern - cancel: Abbrechen - input: Eingabe - value: Wert - set: "=" - increase: "+" - reduce: "-" - multiply: "*" - clear: Löschen - remove-empty: Leer entfernen - number: "[number]" - history-lifespan: Übersicht Lebensdauer - input-mode: Eingabemodus wechseln - title-enable: Fertigstellungstitel - library: Webbibliothek - download: Download Bibliotheken - type: - island: "&6Inseltyp" - inventory: "&6Inventartyp" - other: "&6Anderer Typ" - import: ASkyblock-Herausforderungen importieren - settings: Einstellungen bearbeiten - name: Freundliche Bezeichnung - required-entities: Erforderliche Einheiten - remove-entities: Töte Einheiten - required-blocks: Erforderliche Blöcke - remove-blocks: Blöcke entfernen - search-radius: Suchradius - required-permissions: Erforderliche Berechtigungen - required-items: Erforderliche Items - remove-items: Items entfernen - waiver-amount: Verzichtsmenge - add-challenge: Herausforderung hinzufügen - remove-challenge: Herausforderung entfernen - reset-on-new: Reset auf neuer Insel - broadcast: Fertigstellung der Übertragung - visibility-mode: Herausforderung Sichtbarkeitsmodus - toggle-user-list: Benutzerliste - remove-selected: Auswahl entfernen - show-eggs: Ansichtsmodus wechseln - level-lore: Level-Beschreibung - challenge-lore: Beschreibung der Herausforderung - gui-view-mode: Alle Spielmodi anzeigen - gui-mode: Einzelne Herausforderungen GUI - history-store: Herausforderungen Historie - island-store: Shop pro Insel - default-locked-icon: Symbol für gesperrte Level - title-showtime: Titel Anzeigezeit - default-import: Importieren von Standardherausforderungen - default-export: Bestehende Herausforderungen exportieren - complete-wipe: Addon-Datenbanken löschen - challenge-wipe: Herausforderungen Datenbank löschen - players-wipe: Benutzerdatenbank löschen - next: Nächste Seite - previous: Vorherige Seite - return: Zurück - value: Abgeschlossen - increase: Erhöhen - reduce: Reduzieren + free-challenges: + name: "&f&l Kostenlose Herausforderungen" + description: |- + &7 Zeigt eine Liste von + &7 kostenlose Herausforderungen + return: + name: "&f&l Zurück" + description: |- + &7 Zurück zum vorherigen Menü + &7 oder beenden Sie die GUI + previous: + name: "&f&l Vorherige Seite" + description: "&7 Zur Seite &e [number] &7 wechseln" + next: + name: "&f&l Nächste Seite" + description: "&7 Zur Seite &e [number] &7 wechseln" + reduce: + name: "&f&l Verringern" + description: "&7 verringern um &e [Zahl]" + increase: + name: "&f&l Erhöhen" + description: "&7 Erhöhen um &e [number]" + accept: + name: "&f&l Abgeschlossen" + description: |- + &7 Erledige Herausforderung &e [number] + &7 time(-s) + quit: + name: "&f&l Beenden" + description: "&7 Verlasse die GUI." + complete_user_challenges: + name: "&f&l Schließe die Benutzerherausforderung(en) ab" + description: |- + &7 Ermöglicht die Auswahl von einem Spieler und + &7 das Abschließen von Herausforderung(en) für + &7 ihn + reset_user_challenges: + name: "&f&l Benutzerherausforderungen zurücksetzen" + description: |- + &7 Ermöglicht die Auswahl von einem Spieler und + &7 setzt seine Herausforderungen zurück + add_challenge: + name: "&f&l Herausforderung erstellen" + description: |- + &7 Startet einen Prozess um + &7 einer neue Herausforderung ertellen. + add_level: + name: "&f&l Level erstellen" + description: |- + &7 Startet einen Prozess für + &7 Erstellen eines neuen Level. + edit_challenge: + name: "&f&l Herausforderung bearbeiten" + description: |- + &7 Ermöglicht die Auswahl und Bearbeitung + &7 eine Herausforderung. + edit_level: + name: "&f&l Level bearbeiten" + description: |- + &7 Ermöglicht die Auswahl und Bearbeitung + &7 eines Level's. + delete_challenge: + name: "&f&l Herausforderung löschen" + description: |- + &7 Ermöglicht das Auswählen und Löschen + &7 eine Herausforderung. + delete_level: + name: "&f&l Level löschen" + description: |- + &7 Ermöglicht das Auswählen und Löschen + &7 eines Level. + edit_settings: + name: "&f&l Einstellungen" + description: |- + &7 Ermöglicht das Anzeigen und Bearbeiten der + &7 Add-on-Einstellungen. + complete_wipe: + name: "&f&l Löschen abschließen" + description: |- + &7 Löscht Herausforderungen vollständig + &7 Zusatzdatenbank, einschließlich + &7 Benutzerdaten. + challenge_wipe: + name: "&f&l Herausforderung Löschen" + description: |- + &7 Löscht Herausforderungen vollständig + &7 und Level aus der Datenbank. + user_wipe: + name: "&f&l Benutzerlöschung" + description: |- + &7 Benutzer vollständig aus der + &7 Datenbank löschen. + library: + name: "&f&l Bibliothek" + description: |- + &7 Öffnet eine öffentliche + Bibliothek für &7-Herausforderungen. + import_database: + name: "&f&l Datenbank importieren" + description: |- + &7 Ermöglicht den Import von Exportierten + &7 Herausforderungen. + import_template: + name: "&f&l Vorlage importieren" + description: |- + &7 Ermöglicht das Importieren einer Vorlagen Datei + &7 mit Herausforderungen. + export_challenges: + name: "&f&l Export der Herausforderungen" + description: |- + &7 Ermöglicht den Export der Datenbank + &7 in eine lokale Datei. + properties: + name: "&f&l Eigenschaften" + description: "&7 Alle Haupteigenschaften anzeigen." + requirements: + name: "&f&l Voraussetzungen" + description: "&7 Anforderungseigenschaften anzeigen." + rewards: + name: "&f&l Belohnungen" + description: "&7 Belohnung Eigenschaften anzeigen." + deployed: + name: "&f&l Bereitstellung" + description: "&7 Umschalten, wenn Herausforderung \n&7 bereitgestellt ist + und Benutzer sie abschließen\n&7 können." + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + name: + name: "&f&l Name" + description: |- + &7 Änderung erlaubt + &7 der Anzeigename. + value: "&7 Aktuell: &r [name]" + remove_on_complete: + name: "&f&l Nach Abschluss ausblenden" + description: |- + &7 Umschalten ob Herausforderungen + &7 verborgen werden wenn diese + &7 vom Spieler abgeschlossen wurden. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + description: + name: "&f&l Beschreibung" + description: |- + &7 Die spezifische Beschreibung + &7 für die Herausforderung. Die FarbCodes + &7 müssen darauf angewendet werden. + value: "&7 Aktuelle Beschreibung:" + environment: + name: "&f&l Dimension" + description: |- + &7 Erlaubt die Limitierung + &7 in welcher Welt Sie die Herausforderung + &7 abgeschlossen werden kann. + enabled: "&2" + disabled: "&c" + order: + name: "&f&l Reihenfolge" + description: |- + &7 Ermöglicht das Ändern der Reihenfolge von + &7 Items. + &7 Items mit gleichen Nummern + &7 werden sortiert nach + &7 eindeutigem ID-Namen. + value: "&7 Aktueller Auftrag: &e [number]" + icon: + name: "&f&l Icon" + description: |- + &7 Ermöglicht das Ändern des Symbols + &7 für diese Herausforderung. + locked_icon: + name: "&f&l Sperrsymbol" + description: |- + &7 Erlaubt Änderung gesperrt + &7 des Level-Symbol. + required_permissions: + name: "&f&l Erforderliche Berechtigungen" + description: "&7 Änderungserlaubnis erforderlich,\n&7 Berechtigungen welche + benötigt werden damit die \n&7 Herausforderung abgeschlossen werden kann." + title: "&7 Berechtigungen:" + permission: " &8 - [permission]" + none: "&7 Berechtigungen sind nicht gesetzt." + remove_entities: + name: "&f&l Mobs entfernen" + description: |- + &7 Erlaubt Umschalten ob + &7 erforderliche Mobs + &7 aus der Welt entfernt werden + &7 nach Abschluss der + &7 Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_entities: + name: "&f&l Erforderliche Mobs" + description: |- + &7 Ermöglicht die Änderung der erforderlichen + &7 Mobs, damit diese + &7 Herausforderung abgeschlossen werden kann. + title: "&7 Mobs: " + list: " &8 - [number] x [entity]" + none: "&7 Mobs werden nicht hinzugefügt." + remove_blocks: + name: "&f&l Blöcke entfernen" + description: "&7 Ermöglicht das Umschalten, ob\n&7 erforderliche Blöcke\n&7 + aus der Welt entfernt werden\n&7 nach Beendigung \n&7 der Herausforderung" + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_blocks: + name: "&f&l Erforderliche Blöcke" + description: |- + &7 Ermöglicht die Änderung der erforderlichen + &7 Blöcke um die + &7 Aufgabe abschließen zu können. + title: "&7 Blöcke:" + list: " &8 - [number] x [block]" + none: "&7 Blöcke werden nicht hinzugefügt." + search_radius: + name: "&f&l Suchradius" + description: |- + &7 Ermöglicht die Änderung des Radius + &7 um den Spieler, von dem aus + &7 Blöcke und/oder Objekte + &7 erkannt werden. + value: "&7 Aktueller Abstand: &e [number]" + remove_items: + name: "&f&l Items entfernen" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 benötigte Gegenstände + &7 aus dem Inventar entfernt werden + &7 nach Beendigung der + &7 Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_items: + name: "&f&l Erforderliche Gegenstände" + description: |- + &7 Ermöglicht die Änderung der erforderlichen + &7 Elemente für diese + &7 Herausforderung abgeschlossen werden kann. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items werden nicht hinzugefügt." + add_ignored_meta: + name: "&f&l Metadaten ignorieren hinzufügen" + description: |- + &7 Ermöglicht das Hinzufügen, welche + &7 Items ignoriert werden sollen, inkl. + &7 aller Metadaten, die + &7 ihnen zugewiesen sind. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items werden nicht hinzugefügt." + remove_ignored_meta: + name: "&f&l Entfernen Metadaten ignorieren" + description: |- + &7 Ermöglicht das Entfernen der + &7 Items, die ignoriert werden sollen und + &7 allen Metadaten, die + &7 ihnen zugewiesen sind. + remove_experience: + name: "&f&l Erfahrung entfernen" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 erforderliche Erfahrung + &7 vom Spieler entfernt wird + &7 nach Beendigung der + &7 Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_experience: + name: "&f&l Erforderliche Erfahrung" + description: |- + &7 Ermöglicht die Änderung der + &7 erforderliche Erfahrung für + &7 den Spieler. + value: "&7 Aktuelle Erfahrung: &e [number]" + required_level: + name: "&f&l Erforderliche Inselstufe" + description: |- + &7 Ermöglicht die Änderung der + &7 erforderliche Inselstufe + &7 für die Herausforderung. + value: "&7 Aktuelle Ebene: &e [number]" + remove_money: + name: "&f&l Geld entfernen" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 das benötigte Geld + &7 vom Spieler entfernt wird,das + &7 Konto entfernt wird, nachdem er die + &7 die Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_money: + name: "&f&l Benötigtes Geld" + description: |- + &7 Ermöglicht die Änderung der + &7 erforderliche Geld auf dem Spieler + &7 Konto für die Herausforderung. + value: "&7 Aktueller Wert: &e [number]" + statistic: + name: "&f&l Statistik" + description: |- + &7 Ermöglicht die Änderung des + &7 Statistiktyp, der + &7 in dieser Challenge geprüft wird. + value: "&7 Aktueller Wert: &e [statistic]" + statistic_amount: + name: "&f&l Zielwert" + description: |- + &7 Ermöglicht die Änderung des + &7 statistischen Zielwert + &7, der erfüllt werden muss. + value: "&7 Aktueller Wert: &e [number]" + remove_statistic: + name: "&f&l Statistik verkleinern" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 der statistische Wert + &7 nach Beendigung der Herausforderung + &7 die Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + statistic_blocks: + name: "&f&l Zielblock" + description: |- + &7 Ermöglicht die Änderung des + &7 statistischen Zielblocks. + value: "&7 Aktueller Block: &e [block]" + statistic_items: + name: "&f&l Ziel Item" + description: |- + &7 Ermöglicht die Änderung des + &7 statistische Zielposition. + value: "&7 Aktuelles Element: &e [item]" + statistic_entities: + name: "&f&l Ziel Mob" + description: |- + &7 Ermöglicht die Änderung des + &7 statistischen Zielobjekts. + value: "&7 Aktuelle Mob: &e [entity]" + reward_text: + name: "&f&l Belohnungstext" + description: |- + &7 Der spezifische Belohnungstext. + &7 Die Farbcodes müssen + &7 darauf angewendet werden. + value: "&7 Aktueller Text:" + repeat_reward_text: + name: "&f&l Belohnungstext wiederholen" + description: |- + &7 Der spezifische Wiederholungsbelohnungstext + &7 für die Herausforderung. Die Farb + &7 Codes müssen darauf angewendet werden. + value: "&7 Aktueller Text:" + reward_items: + name: "&f&l Belohnungsgegenstände" + description: |- + &7 Ermöglicht das Ändern von Belohnungen + &7 Gegenstände. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items werden nicht hinzugefügt." + repeat_reward_items: + name: "&f&l Belohnungs Item wiederholen" + description: |- + &7 Ermöglicht die Änderung der Wiederholung + &7 Belohnungsgegenstände für diese + &7-Herausforderung. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items werden nicht hinzugefügt." + reward_experience: + name: "&f&l Belohnungserfahrung" + description: |- + &7 Ermöglicht die Änderung des + &7 Belohnungserlebnis für + &7 den Spieler. + value: "&7 Belohnungserfahrung: &e [number]" + repeat_reward_experience: + name: "&f&l Belohnungserfahrung wiederholen" + description: |- + &7 Ermöglicht die Änderung des + &7 Wiederholung der Belohnungserfahrung + &7 für den Spieler. + value: "&7 Belohnungserlebnis: &e [number]" + reward_money: + name: "&f&l Belohnungsgeld" + description: |- + &7 Ermöglicht die Änderung des + &7 Belohnungsgeld. + value: "&7 Aktueller Wert: &e [number]" + repeat_reward_money: + name: "&f&l Belohnungsgeld wiederholen" + description: |- + &7 Ermöglicht die Änderung des + &7 Wiederholung des Belohnungsgeldes + &7 für die Herausforderung. + value: "&7 Aktueller Wert: &e [number]" + reward_commands: + name: "&f&l Belohnungsbefehle" + description: |- + &7 Die spezifischen Belohnungsbefehle. + &8 Tipp: + &8 Der Befehl muss + &8 zuerst mit `/` geschrieben werden, da er + &8 automatisch angewendet wird. + &8 Standardmäßig werden die Befehle + &8 vom Server ausgeführt. Jedoch + &8 das Hinzufügen von `[SELF]` am Anfang + &8 ermöglicht es, dass der Befehl + &8 vom Spieler ausgeführt werden. Außerdem + &8 unterstützt einen Platzhalter + &8 `[player]`, der dann + &8 durch einen Spielernamen ersetzt wird + &8 der die Herausforderung abgeschlossen hat. + value: "&7 Aktuelle Befehle:" + repeat_reward_commands: + name: "&f&l Belohnungsbefehle wiederholen" + description: |- + &7 Die spezifische Wiederholungsbelohnung + &7 Befehle für die Herausforderung. + &8 Tipp: + &8 Der Befehl muss + &8 zuerst mit `/` geschrieben werden, da er + &8 automatisch angewendet wird. + &8 Standardmäßig werden die Befehle + &8 vom Server ausgeführt. Jedoch + &8 das Hinzufügen von `[SELF]` am Anfang + &8 ermöglicht es, dass der Befehl + &8 vom Spieler ausgeführt werden. Außerdem + &8 unterstützt einen Platzhalter + &8 `[player]`, der dann + &8 durch einen Spielernamen ersetzt wird + &8 der die Herausforderung abgeschlossen hat. + value: "&7 Aktuelle Befehle:" + repeatable: + name: "&f&l Wiederholbar" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 die Herausforderung + &7 wiederholbar ist. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + repeat_count: + name: "&f&l Wiederholungszähler" + description: |- + &7 Ermöglicht die Änderung der + &7 Anzahl der Wiederholungen + &7 für die Herausforderung. + value: "&7 Aktueller Wert: &e [number]" + cool_down: + name: "&f&l Abkühlen" + description: |- + &7 Ermöglicht die Änderung der + &7 Abkühlsekunden, die + &7 gewartet werden muss zwischen + &7 wiederholbaren Herausforderungen + &7 und deren Beendigung gewartet werden muss. + value: "&7 Aktueller Wert: &e [time]" + challenges: + name: "&f&l Herausforderungen" + description: |- + &7 Zugewiesene Herausforderungen anzeigen + &7 zum Level. + waiver_amount: + name: "&f&l Verzichtsbetrag" + description: |- + &7 Ermöglicht die Festlegung einer Anzahl + &7 von Herausforderungen, die + &7 unvollendet gelassen werden können, um + &7 das nächste Level freizuschalten. + value: "&7 Aktueller Wert: &e [number]" + add_challenges: + name: "&f&l Herausforderung(en) hinzufügen" + description: |- + &7 Ermöglicht die Auswahl und + &7 fügt Herausforderungen zum + &7 leveln. + remove_challenges: + name: "&f&l Herausforderung(en) entfernen" + description: |- + &7 Ermöglicht die Auswahl und + &7 Entfernen von Herausforderungen auf dem + &7 Level. + reset_on_new: + name: "&f&l Zurücksetzen auf Neu" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 Herausforderungen + &7 zurückgesetzt werden sollen, wenn der Benutzer die + &7 Insel verlässt oder eine neue + &7 Insel erstellt. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + broadcast: + name: "&f&l Broadcast" + description: |- + &7 Sendungen Herausforderung und + &7 Erstmaliger Abschluss eines Levels + &7 an alle. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + remove_completed: + name: "&f&l Ausblenden abgeschlossen" + description: |- + &7 Versteckt abgeschlossene Herausforderungen + &7 aus dem Menü. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + glow_completed: + name: "&f&l Glühen wenn abgeschlossen" + description: |- + &7 Fügt Verzauberungsglühen + &7 zu den abgeschlossenen Herausforderungen. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + store_history: + name: "&f&l Store-Verlauf" + description: |- + &7 Speichert den internen Verlauf + &7 wenn jede Herausforderung + &7 abgeschlossen ist. + &7 Derzeit nur einsehbar + &7 in der Datenbank. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + data_per_island: + name: "&f&l Speichern pro Insel" + description: |- + &7 Speichert die abgeschlossenen + &7 Herausforderungen pro Insel. + &7 Der Fortschritt wird mit allen + &7 mit allen Spielern im Team geteilt. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + show_title: + name: "&f&l Titel anzeigen" + description: |- + &7 Zeigt den Titel an, wenn eine + &7 Herausforderung oder Level + &7 abgeschlossen ist. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + gamemode_gui: + name: "&f&l GameMode-Auswahl-GUI" + description: |- + &7 Ermöglicht eine einzige GUI, die + &7 verfügbar über /challenges + &7 Befehl. + &c Erfordert einen Neustart des Servers. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + locked_level_icon: + name: "&f&l Standardsymbol für gesperrte Icon" + description: |- + &7 Standardsymbol für alle gesperrten + &7 Level. Jedes Level kann + &7 dieses Symbol ändern. + purge_history: + name: "&f&l Geschichte Lebenszeit" + description: |- + &7 Anzahl der Tage, wie lange + &7 Verlaufsdaten gespeichert werden + &7 in den Benutzerdaten. + &7 0 bedeutet, dass die Daten + &7 nicht entfernt werden. + value: "&7 Aktueller Wert: &e [number]" + title_showtime: + name: "&f&l Titel Showtime" + description: |- + &7 Anzahl der Ticks, die der Titel + &7 dem Spieler angezeigt wird. + value: "&7 Aktueller Wert: &e [number]" + active_world_list: + name: "&f&l Nur aktive Welt anzeigen" + description: |- + &7 Wenn GameMode Selection GUI + &7 aktiviert ist, kann dieser Schalter + &7 die GUI GameMode-Auswahl anzeigen + &7 oder Herausforderungen für die aktuelle Welt anzeigt. + &c Erfordert einen Server-Neustart. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + visibility_mode: + name: "&f&l Sichtbarkeitsmodus" + description: |- + &7 Sichtbarkeitsmodus für + &7 Herausforderungen, die + &7 versteckt sind. + enabled: "&2" + disabled: "&c" + visible: Zeigen Sie sichtbare Herausforderungen + hidden: Alle Herausforderungen anzeigen + toggleable: Umschalten zulassen + download: + name: "&f&l Bibliotheken herunterladen" + description: |- + &7 Manuelle Aktualisierung verfügbar für + &7 die Herausforderungs Bibliotheken. + enabled: "&2 Mit Cache löschen" + disabled: "&c Ohne Cache löschen" + player: + name: "&f&l [name]" + description: "&7 Insel Owner: [owner]" + members: "&7 Inselmitglieder:" + member: "&8 - [name]" + no-island: |- + &c Player hat + &c keine Insel. + player_list: + name: "&f&l Spielerliste wählen" + description: |- + &7 Wählen Sie, welche Benutzerliste + &7 angezeigt werden soll. + enabled: "&2" + disabled: "&c" + online: Online-Spieler + with_island: Spieler mit Inseln + in_world: Spieler in der Welt + add_block: + name: "&f&l Block hinzufügen" + description: |- + &7 Ermöglicht das Hinzufügen eines neuen + &7 Blocks zur Liste. + remove_block: + name: "&f&l Block entfernen" + description: |- + &7 Ermöglicht das Entfernen + &7 ausgewählte Blöcke + &7 aus Listen zu entfernen. + title: "&7 Ausgewählte Materialien:" + material: "&8 - [material]" + material: + name: "&f&l [material]" + description: "&7 Material ID: [id]" + selected: "&2 Ausgewählt" + add_entity: + name: "&f&l Mob hinzufügen" + description: |- + &7 Ermöglicht das Hinzufügen einer neuen + &7 Mobs zur Liste. + switch_entity: + name: "&f&l Eier wechseln" + description: |- + &7 Ermöglicht Umschalten von + &7 Eier zu Mob-Köpfe. + remove_entity: + name: "&f&l Mob entfernen" + description: |- + &7 Ermöglicht das Entfernen von + &7 ausgewählte Objekte + &7 aus Listen zu entfernen. + title: "&7 Ausgewählte Mobs:" + entity: "&8 - [entity]" + entity: + name: "&f&l [entity]" + description: "&7 Mob ID: [id]" + selected: "&2 Ausgewählt" + inventory_type: + name: "&f&l Inventartyp" + description: |- + &7 Herausforderung, die nach + &7 Gegenständen im Inventar des Spielers prüft + island_type: + name: "&f&l Inseltyp" + description: |- + &7 Herausforderung, die nach + &7 Blöcke oder Mob um den + &7 Spieler prüft. + other_type: + name: "&f&l Anderer Typ" + description: |- + &7 Challenge, die verwendet + &7 Plugins oder Addons Dinge, + &7 wie Level und Geld. + statistic_type: + name: "&f&l Statistiktyp" + description: |- + &7 Challenge, die die + &7 Spieler-Statistiken prüfen. + save: + name: "&f&l Speichern" + description: |- + &7 Speichert Änderungen und + &7 kehrt zurück. + cancel: + name: "&f&l Abbrechen" + description: |- + &7 Verwirft Änderungen und + &7 kehrt zurück. + accept_selected: + name: "&f&l Ausgewählte akzeptieren" + description: |- + &7 Gibt ausgewählte Elemente zurück + &7 und öffnet die vorherige GUI. + title: "&7 Ausgewählt:" + element: "&8 - [element]" + statistic_element: + name: "&f&l [statistic]" + description: "[description]" + environment_element: + name: "&f&l [environment]" + description: "[description]" + search: + name: "&f&l Suchen" + description: |- + &7 Ermöglicht die Suche nach einem + &7 Element mit Eingabe des + &7 Textwert. + search: "&b Wert: [value]" + tips: + click-to-select: "&e Klick &7 zu markieren." + click-to-choose: "&e Klick &7 zum wählen." + click-to-complete: "&e Klick &7 zum Abschließen." + right-click-multiple-open: "&e Klicken Sie mit der rechten Maustaste &7, um + den Fertigstellungsgrad auszuwählen." + shift-left-click-to-complete-all: "&e Klicken Sie mit gedrückter Umschalttaste + &7, um alles abzuschließen." + left-click-to-accept: "&e Klicken Sie zum Abschließen mit der &7 linken Maustaste." + right-click-to-write: "&e Rechtsklick &7 zum Schreiben." + click-to-reduce: "&e Klicken Sie zum Reduzieren &7." + click-to-increase: "&e Zum Erhöhen &7 klicken." + click-to-return: "&e Klicken Sie &7, um zurückzukehren." + click-to-quit: "&e Zum Beenden &7 klicken." + click-to-wipe: "&e Zum Löschen &7 klicken." + left-click-to-wipe: "&e Klicken Sie zum Löschen mit der linken Maustaste &7." + right-click-to-switch: "&e Rechtsklick &7 zum Umschalten." + click-to-open: "&e Klicken zum Öffnen&7." + click-to-export: "&e Zum Exportieren &7 klicken." + click-to-create: "&e Zum Exportieren &7 klicken." + left-click-to-open: "&e Klicken Sie zum Öffnen mit der linken Maustaste &7." + right-click-to-reset-all: "&e Klicken Sie mit der rechten Maustaste &7, um alles + zu löschen." + click-to-toggle: "&e Zum Umschalten &7 klicken." + click-to-change: "&e Zum Ändern &7 klicken." + shift-click-to-reset: "&e Shift Klicken Sie zum Zurücksetzen &7." + click-to-add: "&e Zum Hinzufügen &7 klicken." + click-to-remove: "&e Zum Entfernen &7 klicken." + left-click-to-cycle: "&e Klicken Sie mit der linken Maustaste &7, um nach unten + zu blättern." + right-click-to-cycle: "&e Klicken Sie mit der rechten Maustaste &7, um nach + oben zu wechseln." + click-to-edit: "&e Zum Bearbeiten &7 klicken." + left-click-to-download: "&e Klicken Sie zum Herunterladen mit der linken Maustaste + &7." + right-click-to-toggle: "&e Klicken Sie mit der rechten Maustaste &7, um umzuschalten." + click-to-install: "&e Zum Installieren &7 klicken." + click-to-reset-all: "&e Klicken Sie &7, um alle zurückzusetzen." + right-click-to-select: "&e Klicken Sie zum Auswählen mit der rechten Maustaste + &7." + right-click-to-deselect: "&e Klicken Sie mit der rechten Maustaste &7, um die + Auswahl aufzuheben." + left-click-to-choose: "&e Klicken Sie mit der linken Maustaste &7, um auszuwählen." + click-to-cancel: "&e Zum Abbrechen &7 klicken." + click-to-save: "&e Zum Speichern &7 klicken." + click-to-deselect: "&e Klicken Sie &7, um die Auswahl aufzuheben." + click-on-item: |- + &e Klicken Sie &7 auf ein Item im + &7 Inventar. + left-click-to-edit: "&e Klicken Sie zum Bearbeiten mit der linken Maustaste + &7." + right-click-to-clear: "&e Klicken Sie zum Löschen mit der rechten Maustaste + &7." + click-to-previous: "&e Klicken Sie &7, um die vorherige Seite anzuzeigen." + click-to-next: "&e Klicken Sie &7, um die nächste Seite anzuzeigen." descriptions: - admin: - input: Öffne Textfeldeingabe. - deployment: Ermöglicht es den Benutzern, die Herausforderung abzuschließen - (anzusehen). - icon-challenge: Symbol, das in GUI-Panels für diese Herausforderung angezeigt - wird. - icon-level: Symbol, das in den GUI-Panels für dieses Level angezeigt wird. - remove-completed: Aktiviert/deaktiviert das Ausblenden von Herausforderungen, - die abgeschlossen sind und nicht wiederholt werden können. - toggle-user-list: Zu einer anderen Spielerliste wechseln. - selected: Ausgewählt - show-eggs: Wechselt die Ansicht der Objekte zwischen Eimodus oder Kopfmodus. - click-to-edit: "&4Hier klicken, um Eingaben zu bearbeiten." - input-mode: Wechsel zwischen Chat- und Amboss-Eingabemodus. - library-author: by &e[author] - library-lang: "&aSprache: [lang]" - library-gamemode: "&aPrimär für [gamemode]" - download-disabled: GitHub-Daten-Downloader ist in BentoBox deaktiviert. Ohne - ihn können keine Bibliotheken verwendet werden! - create-level: Neues Level hinzufügen. - edit-challenge: Herausforderung-Einstellungen bearbeiten. - edit-level: Level-Einstellungen bearbeiten. - delete-challenge: Eine Herausforderung entfernen. - delete-level: Ein Level entfernen. - settings: Einstellungen ändern. - properties: Allgemeine Eigenschaften ändern - requirements: Anforderungen verwalten - rewards: Belohnungen verwalten - description: Beschreibung bearbeiten. - order: Auftragsnummer ändern. - environment: Herausforderung Umfeld ändern. - name-challenge: Herausforderung Anzeigename ändern. - name-level: Level Anzeigename ändern. - remove-entities: Entferne (töte) Entitäten nach Abschluss der Herausforderung. - remove-blocks: Entferne ( ersetze durch Luft ) Blöcke nach Abschluss der Herausforderung. - search-radius: Radius um den Standort des Spielers, in dem die benötigten - Einheiten und Blöcke gesucht werden. - reward-text: Ändere die Nachricht, die dem Spieler nach Abschluss der Herausforderung - geschickt wird. - repeatable: Definiert, ob die Herausforderung wiederholbar ist oder nicht. - free-at-top: Freie Herausforderungen Standort wechseln. True bedeutet, diese - Herausforderungen werden die Ersten sein, sonst die Letzten. - line-length: Ändern der maximalen Zeilenlänge in der Lore Box. Hat keinen - Einfluss auf gespeicherte Objekte. - level-lore: Ändern welche Elemente der Levelbeschreibung sichtbar sein sollen. - challenge-lore: Ändern welche Elemente der Herausforderungsbeschreibung sichtbar - sein sollen. - gui-view-mode: Festlegen, ob /challenges GUI die Spielmodi oder Herausforderungen - in der Welt des Spielers anzeigen soll. - history-store: Aktivieren/Deaktivieren der Historienspeicherung von Herausforderungen. - default-import: Importieren von Standardherausforderungen. - default-export: Bestehende Herausforderungen in die Datei defaults.json exportieren. - complete-wipe: Vollständig alle Herausforderungen Addon-Datenbanken löschen. - Inklusive Spielerdaten! - challenge-wipe: Vollständig Herausforderungen und deren Leveldatenbanken löschen! - players-wipe: Vollständige Spielerdatenbank löschen! - library: Öffnet ein GUI, das alle verfügbaren öffentlichen Herausforderungen-Bibliotheken - anzeigt. - library-version: "&9Gemacht in Herausforderungen [version]" - save: Speichern und zur vorherigen GUI zurückkehren. - cancel: Rückkehr zur vorherigen GUI. Änderungen werden nicht gesichert. - set: Betrieb einstellen. Durch Anklicken der Zahlen wird der Wert auf die - ausgewählte Zahl geändert. - increase: Betrieb erhöhen. Durch Anklicken der Zahlen wird der Wert um die - gewählte Zahl erhöht. - reduce: Betrieb reduzieren. Durch Anklicken der Zahlen wird der Wert um die - gewählte Zahl reduziert. - multiply: Betrieb multiplizieren. Durch Anklicken der Zahlen wird der Wert - mit der ausgewählten Zahl multipliziert. - challenges: Verwalten von Level-Herausforderungen (hinzufügen/entfernen). - locked-icon: Symbol, das in GUI-Panels angezeigt wird, wenn das Level gesperrt - ist. - remove-on-complete: Entfernen einer Herausforderung aus der GUI eines Spielers, - nachdem sie abgeschlossen ist. - remove-items: Entfernen von Items aus dem Inventar des Spielers nach Abschluss - der Herausforderung. - required-experience: Die erforderliche Erfahrung für einen Benutzer festlegen, - um die Herausforderung abzuschließen. - remove-experience: Erforderliche Erfahrung entfernen. - reward-experience: Ändern der Erfahrungsbelohnung für den ersten Abschluss. - repeat-count: Maximale Anzahl der Wiederholungen festlegen. Wird der Wert - auf 0 gesetzt, gibt es keine Einschränkungen. - repeat-reward-text: Ändern der Nachricht, die dem Spieler nach dem wiederholten - Abschluss der Herausforderung geschickt wird. - repeat-reward-experience: Ändern der Erfahrungsbelohnung für den ersten Abschluss. - waiver-amount: Legt die Anzahl der Herausforderungen fest, die ein Spieler - auslassen kann, um das nächste Level freizuschalten. - reward-text-level: Ändere die Nachricht, die dem Spieler nach Abschluss aller - Herausforderungen in einem Level geschickt wird. - add-challenge: Fügt dem aktuellen Level eine bestehende Herausforderung hinzu. - remove-challenge: Eine Herausforderung aus dem aktuellen Level entfernen. - reset-on-new: Aktiviert/deaktiviert die Resets aller Herausforderungen für - einen Spieler, wenn er neu startet, die Insel verlässt oder von ihr gekickt - wird. - broadcast: Aktiviert/Deaktiviert die Übertragung über den Abschluss der ersten - Herausforderung für alle Spieler die online sind. - glow: Aktiviert/deaktiviert den Leuchteffekt für abgeschlossene Herausforderungen. - mode-online: Spieler, die gerade online sind. - mode-in-world: Spieler in einer Spielmodus-Welt. - mode-with-island: Spieler, die eine Insel in einer Spielmodus-Welt haben. - visibility-mode: Ein-/ausblenden von nicht umgesetzten Herausforderungen. - edit-text-line: "&6Textnachricht bearbeiten!" - add-text-line: "&6Neue Textnachricht hinzufügen!" - title-enable: Aktivieren/deaktivieren der Titelnachricht, die den Spielern - angezeigt wird, wenn sie eine Herausforderung abschließen. - title-showtime: Ändern wie lange Titelmeldungen für den Spieler sichtbar sein - sollen. - import: |- - ASkyblock-Herausforderungen importieren. - Bei Rechtsklick wird der Überschreibmodus aktiviert/deaktiviert. - Platziere die challenges.yml im Ordner ./BentoBox/addons/Challenges. - complete: |- - Herausforderung für jeden Benutzer abschließen. - Der Benutzer erhält keine Belohnung für den Abschluss. - reset: |- - Abgeschlossene Benutzerherausforderungen zurücksetzen. - Rechtsklick aktiviert/deaktiviert zurücksetzen aller Funktionen. - create-challenge: |- - Neue Herausforderung hinzufügen. - Wird standardmäßig in der Liste der freien Herausforderungen stehen. - required-entities: |- - Hinzufügen/Bearbeiten/Entfernen von erforderlichen Einheiten. - Einheiten: - required-blocks: |- - Erforderliche Blöcke hinzufügen/bearbeiten/entfernen. - Blöcke: - required-permissions: |- - Benötigte Berechtigungen für den Spieler, um diese Herausforderung abzuschließen. - Berechtigung: - required-items: |- - Benötigte Items im Inventar des Spielers. - Items: - required-level: |- - Legt das für diese Herausforderung erforderliche Insellevel fest. - &cBenötigt Level-Addon.' - required-money: |- - Legt das erforderliche Geld auf dem Spielerkonto fest. - &cErfordert Vault und ein Economy-Plugin.' - remove-money: |- - Entfernt das erforderliche Geld vom Spielerkonto. - &cErfordert Vault und ein Economy-Plugin.' - reward-items: |- - Ändere die Itembelohnungen für den erstmaligen Abschluss. - Items: - reward-money: |- - Ändere die Geldbelohnung für den ersten Abschluss. - &cErfordert Vault und Economy-Plugin. - reward-commands: |- - Legt Belohnungsbefehle fest, die nach der ersten Ausführung aufgerufen werden. - ***Das Hinzufügen von "[SELF]" am Anfang bedeutet, dass der Befehl vom Spieler ausgeführt wird, z.B. "/kill". - ***Zeichenfolge "[player]" wird durch den Spielernamen ersetzt, z.B. wird "/kill [player]" in "/kill BONNe1704" umgewandelt - Befehle: - repeat-reward-items: |- - Ändert die Itembelohungen für den wiederholten Abschluss. - Items: - repeat-reward-money: |- - Ändert die Geldbelohnung für den wiederholten Abschluss. - &cErfordert Vault und ein Economy-Plugin. - repeat-reward-commands: |- - Legt Belohnungsbefehle fest, die nach wiederholter Ausführung der Herausforderung ausgeführt werden. - ***Hinzufügen von "[SELF]" am Anfang bedeutet, dass der Befehl vom Spieler ausgeführt wird, z.B. "/kill". - ***Zeichenfolge "[player]" wird durch den Spielernamen ersetzt, z.B. wird "/kill [player]" in "/kill BONNe1704" umgewandelt - Befehle: - remove-selected: |- - Ausgewählte Elemente entfernen. - Elemente mit der rechten Maustaste auswählen. - history-lifespan: |- - Ändern für wie viele Tage die Daten der Historie gespeichert werden sollen. - 0 bedeutet für immer. - island-store: |- - Aktivieren/Deaktivieren der Herausforderungen Datenspeicherung pro Insel. Das bedeutet, dass die Herausforderungen für das gesamte Team gleich sind, wenn dies aktiviert ist. - &cWird NICHT bei Klick Daten konvertieren. DER FORTSCHRITT GEHT VERLOREN. - default-locked-icon: |- - Ändert das Standardsymbol des gesperrten Levels. - Diese Option kann von jedem Level überschrieben werden.' - gui-mode: |- - Aktivieren/Deaktivieren einzelner Herausforderungen GUI. - &2Erfordert einen Server-Neustart.' - download: |- - Manuelle Aktualisierung der verfügbaren Herausforderungen-Bibliotheken. - Rechtsklick zum Aktivieren der Cache-Leerung.' - lore: - level: |- - Level-Zeichenfolge. - Stellt die Übersetzung challenge.gui.challenge-description.level dar. - status: |- - Status-Zeichenfolge. - Stellt die Übersetzung challenge.gui.challenge-description.completed dar. - count: |- - Zeichenfolge für den Abschluss der Zählung. - Stellt die Übersetzung für challenges.gui.challenge-description.completed-times dar. - challenges.gui.challenge-description.completed-times-of - und challenges.gui.challenge-description.maxed-reached - description: |- - Beschreibung Zeichenfolge. - Festgelegt in Challenge-Objekt - challenge.description. - warnings: |- - Warnzeichenfolge. - Stellt die Übersetzung dar für: - challenges.gui.challenge-description.warning-items-take - challenges.gui.challenge-description.objects-close-by - challenges.gui.challenge-description.warning-entities-kill - challenges.gui.challenge-description.warning-blocks-remove - environment: |- - Umgebung Zeichenkette. - Festgelegt in Challenges Objekt - challenge.environment. - requirements: |- - Anforderungszeichenfolge. - Stellt die Übersetzung dar von: - challenges.gui.challenge-description.required-level - challenges.gui.challenge-description.required-money - challenges.gui.challenge-description.required-experience - challenge.requiredItems' - challenge.requiredBlocks' - or challenge.requiredEntities. - reward_text: |- - Belohnungs-Zeichenfolge. - Festgelegt in challenge.rewardText und challenge.repeatRewardText - reward_other: |- - Belohnung andere Zeichenfolge. - Stellt die Übersetzung dar für: - challenges.gui.challenge-description.experience-reward - challenges.gui.challenge-description.money-reward - challenges.gui.challenge-description.not-repeatable - reward_items: |- - Itembelohnungen. - Liste der Items, die zu Belohnungen werden, festgelegt in challenge.rewardItems und challenge.repeatRewardItems. - reward_commands: |- - Belohnungsbefehle. - Liste der Befehle, die Belohnen sollen, festgelegt in challenge.rewardCommands und challenge.repeatRewardCommands. - level_status: |- - Status-Zeichenfolge. - Stellt die Übersetzung von challenges.gui.level-description.completed dar. - challenge_count: |- - Abgeschlossener Herausforderung Zähl Zeichenfolge - Stellt die Übersetzung für challenges.gui.level-description.completed-challenges-of dar. - unlock_message: |- - Entsperren der Nachricht Zeichenfolge. - Festgelegt in Herausforderungen Levelobjekt - challengeLevel.unlockMessage - waiver_amount: |- - Die Anzahl der versetzbaren Herausforderungen, um die nächste Level-Zeichenkette freizuschalten. - Stellt die Übersetzung für challenges.gui.level-description.waver-amount dar - level_reward_text: |- - Belohnungs-Zeichenfolge. - Festgelegt in challengeLevel.rewardText - level_reward_other: |- - Belohnung anderer Zeichenfolge. - Stellt die Übersetzung dar für: - challenges.gui. level-description. experience-reward. - challenges.gui. level-description. money-reward - level_reward_items: |- - Itembelohnungen. - Liste der Items, die zu Belohnungen werden, festgelegt in challengeLevel.rewardItems - level_reward_commands: |- - Belohnungsbefehle. - Liste der Befehle, die zu Belohnungen führen, festgelegt in challengeLevel.rewardCommands - enabled: Aktiv - disabled: Deaktiviert - the-end: "- End" - nether: "- Nether" - normal: "- Oberwelt" - entity: "- [entity] : [count]" - block: "- [block] : [count]" - permission: "- [permission]" - item: "- [count] x [item]" - item-meta: "([meta])" - item-enchant: "- [enchant] [level]" - command: "- [command]" - level-unlocked: Klicke, um [level] Herausforderungen zu sehen! - level-locked: Schließe [count] weitere [level] Herausforderungen ab, um dieses - Level freizuschalten! - increase-by: "&aErhöhung der Fertigstellung um [value]" - reduce-by: "&cReduzierung der Fertigstellungen um [value]" - visibility: - hidden: Nur eingesetzte Herausforderungen sind sichtbar. - visible: Alle Herausforderungen sind für jeden sichtbar - toggleable: Umschalten, ob nicht verteilte Herausforderungen angezeigt werden - sollen - type: - island: "&aErfordert Blöcke oder Mobs um Spieler" - other: "&aErfordert Dinge von anderen Plugins/Addons" - inventory: "&aErforderliche Items im Inventar des Spielers" - current-value: "&6Aktueller Wert: [value]." - challenge-description: - completed-times-of: "[donetimes] erledigt aus [maxtimes]" - maxed-reached: "[donetimes] erledigt aus [maxtimes]" - completed-times: "[donetimes] erledigt" - objects-close-by: "&cAlle benötigten Blöcke und Objekte müssen sich in deiner - Nähe auf deiner Insel befinden!" - warning-entities-kill: "&cAlle erforderlichen Einheiten werden getötet, wenn - du diese Herausforderung abschließt!" - warning-blocks-remove: "&cAlle benötigten Blöcke werden entfernt, wenn du diese - Herausforderung abschließt!" - not-repeatable: "&cDiese Herausforderung ist nicht wiederholbar!" - experience-reward: "&6Exp Belohnung: [value]" - money-reward: "&6Geld Belohnung: $[value]" - required-experience: "&6Erforderliche Exp: [value]" - required-money: "&6Erforderliches Geld: $[value]" - required-island-level: "&6Erforderliches Insellevel: [value]" - environment: 'Erforderliche Umgebungen:' - reward-items: "&6Item Belohnungen:" - reward-commands: "& 6Belohnungsbefehle:" - required-items: 'Erforderliche Items:' - required-entities: 'Erforderliche Einheiten:' - required-blocks: 'Erforderliche Blöcke:' - level: "&fLevel: [level]" - completed: "&bAbgeschlossen" - warning-items-take: "&cAlle erforderlichen Items werden aus deinem Inventar - genommen, wenn du diese Herausforderung abschließt!" - rewards-title: "& a Belohnungen:" - level-description: - experience-reward: "&6Exp Belohnung: [value]" - money-reward: "&6Geldbelohnung: $[value]" - reward-items: "&6Item Belohnungen:" - reward-commands: "&6Belohnungsbefehle:" - waver-amount: "&6[value] Herausforderungen können übersprungen werden, um das - nächste Level freizuschalten." - completed: "&bAbgeschlossen" - completed-challenges-of: "&3Du hast [number] aus [max] Herausforderungen in - diesem Level abgeschlossen." - item-description: - item: "- [count] x [item]" - item-meta: "([meta])" - item-enchant: "- [enchant] [level]" - item-name: "[name]" - item-lore: 'Item Lore:' - book-meta: "[title] von [author]" - recipe-count: "[count] Rezepte" - armor-color: "[color]" - potion-type-extended-upgraded: Erweitert und verbessert [name] - potion-type-upgraded: Aktualisiert [name] - potion-type-extended: Erweitert [name] - potion-type: "[name]" - custom-effects: 'Benutzerdefinierte Effekte:' - potion-effect: "[effect] x [amplifier] für [duration]t" - skull-owner: "[owner]" - egg-meta: "[mob]" - fish-meta: "[body-color] mit [pattern-color] [pattern]" - questions: - prefix: "&2[SERVER]:" - admin: - unique-id: Schreiben Sie die eindeutige ID eines Objekts und drücken Sie die - Eingabetaste. - number: Schreibe eine Zahl in den Chat und drücke die Eingabetaste. - challenge-name: Schreibe den Anzeigenamen für die aktuelle Herausforderung - in den Chat. - level-name: Schreibe den Anzeigenamen für das aktuelle Level in den Chat. + challenge: + lore: |- + [description] + [status] + [cooldown] + [requirements] + [rewards] + status: + completed: "&2&l Abgeschlossen" + completed-times: "&2 Abgeschlossen &7&l [number] &r&2 time(-s)" + completed-times-of: "&2 &7&l [number] &r&2 von &7&l [max] &r&2 mal abgeschlossen" + completed-times-reached: "&2&l Alle Abschließen &7 [max] &2 times" + cooldown: + lore: |- + [timeout] + [wait-time] + timeout: "&7&l Cool down: &r&7 [time]" + wait-time: "&c&l Verfügbar nach: &r&c [time]" + in-days: "[number] d " + in-hours: "[number] H" + in-minutes: "[number] min " + in-seconds: "[number] s" + requirements: + lore: |- + [environment] + [type-requirement] + [permissions] + environment-single: "&7 Limitiert auf [environment]" + environment-title: "&7 Limitiert auf:" + environment-list: " &7 - &e [environment]" + permission-single: "&c Requires [permissions] permission" + permissions-title: "&c Erfordert Berechtigungen:" + permissions-list: " &c - [permission]" + island: + lore: |- + [blocks] + [entities] + [search-radius] + [warning-block] + [warning-entity] + blocks-title: "&7&l Erforderliche Blöcke:" + block-value: " &7 - &e [material]" + blocks-value: " &7 - &e [number] x [material]" + entities-title: "&7&l Erforderliche Entitäten:" + entity-value: " &7 - &e [entity]" + entities-value: " &7 - &e [number] x [entity]" + search-radius: "&7 Nicht weiter als &e [number] &7 Meter" + warning-block: "&e Blöcke werden &c entfernt" + warning-entity: "&e Mobs werden &c entfernt" + inventory: + lore: |- + [items] + [warning] + item-title: "&7&l Erforderliche Items:" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + warning: "&e Item(-s) wird &c entfernt werden" + other: + lore: |- + [experience] + [experience-warning] + [money] + [money-warning] + [level] + experience: "&7&l Erforderliche Erfahrung: &r&e [number]" + experience-warning: "&e Erfahrung wird &c entfernt" + money: "&7&l Benötigtes Geld: &r&e [number]" + money-warning: "&e Geld wird &c entfernt" + level: "&7&l Erforderliches Insellevel: &r&e [number]" + statistic: + lore: |- + [statistic] + [warning] + multiple-target: "&7&l [statistic]: &r&e [number] x [target]" + single-target: "&7&l [statistic]: &r&e [target]" + statistic: "&7&l [statistic] &r&e [number]" + warning: "&e Statistikdaten werden &c reduziert" + rewards: + lore: |- + &7&l Rewards: + [text] + [items] + [experience] + [money] + [commands] + item-title: "&7 Items:" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + experience: "&7 Erfahrung: &r&e [number]" + money: "&7 Geld: &r&e [number]" + commands-title: "&7 Befehle:" + command: " &7 - &e [command]" + level: + lore: |- + [text] + [status] + [waiver] + [rewards] + status: + completed: "&2&l Abgeschlossen" + completed-challenges-of: |- + &2 Abgeschlossen &7&l [number] &r&2 aus + &7&l [max] &r&2 Herausforderungen. + locked: "&c&l Gesperrt" + missing-challenges: "&7 [number] weitere Herausforderungen müssen \n&7 abgeschlossen + sein, um diese Stufe freizuschalten." + waiver: |- + &7&l [number] Herausforderung(en) &r&7 können + &7 übersprungen werden, um die nächste Stufe freizuschalten. + rewards: + lore: |- + &7&l Rewards: + [text] + [items] + [experience] + [money] + [commands] + item-title: "&7 Items:" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + experience: "&7 Erfahrung: &r&e [number]" + money: "&7 Geld: &r&e [number]" + commands-title: "&7 Befehle:" + command: " &7 - &e [command]" + library: + author: "&7 by &e [author]" + version: "&7 Made with Challenges &e [version]" + lang: "&7 Sprache: &e [lang]" + gamemode: "&7 Primär für &e [gamemode]" + conversations: + prefix: "&l&6 [BentoBox]: &r" + confirm-string: zustimmen, an, ja, bestätigen, ja, gültig, richtig + deny-string: falsch, aus, nein, verweigern, n, ungültig, falsch + cancel-string: Abbrechen + exit-string: abbrechen, beenden, beenden + cancelled: "&c Gespräch abgebrochen!" + input-number: "&e Bitte geben Sie im Chat eine Nummer ein." + input-seconds: "&e Bitte geben Sie im Chat eine Sekunde ein." + numeric-only: "&c Der angegebene [value] ist keine Zahl!" + not-valid-value: "&c Die angegebene Zahl [value] ist ungültig. Er muss größer + als [min] und kleiner als [max] sein!" + user-data-removed: "&a Alle Benutzerdaten für [gamemode] werden aus der Datenbank + gelöscht." + confirm-user-data-deletion: "&e Bitte bestätigen Sie, dass Sie die Benutzerdatenbank + für [gamemode] löschen möchten." + challenge-data-removed: "&a Alle Herausforderungsdaten für [gamemode] werden aus + der Datenbank gelöscht." + confirm-challenge-data-deletion: "&e Bitte bestätigen Sie, dass Sie die Herausforderungsdatenbank + für [gamemode] löschen möchten." + all-data-removed: "&a Alle Addon-Daten für [gamemode] werden aus der Datenbank + gelöscht." + confirm-all-data-deletion: "&e Bitte bestätigen Sie, dass Sie Addon-Daten für + [gamemode] löschen möchten." + write-name: "&e Bitte schreiben Sie einen Namen in den Chat." + new-object-created: "&ein neues Objekt für [gamemode] wird erstellt." + object-already-exists: "&c Objekt &7 [id] &c existiert bereits. Wählen Sie einen + anderen Namen." + invalid-challenge: "&c Challenge [challenge] enthält ungültige Daten. Es kann + nicht eingesetzt werden!" + name-changed: "&a Erfolg, der Name wurde aktualisiert." + write-description: "&e Bitte geben Sie eine neue Beschreibung im Chat ein und + beenden Sie den Chat in einer eigenen Zeile." + description-changed: "&a Erfolg, die Beschreibung wurde aktualisiert." + write-permissions: "&e Bitte geben Sie die erforderlichen Berechtigungen ein, + eine pro Zeile im Chat, und 'beenden' in einer eigenen Zeile, um den Chat zu + beenden." + permissions-changed: "&a Erfolg, Challenge-Berechtigungen wurden aktualisiert." + write-reward-text: "&e Bitte geben Sie einen neuen Belohnungstext im Chat ein + und beenden Sie den Chat in einer eigenen Zeile." + reward-text-changed: "&a Erfolg, der Belohnungstext wurde aktualisiert." + write-repeat-reward-text: "&e Bitte geben Sie einen neuen Wiederholungsbelohnungstext + im Chat ein und beenden Sie den Chat in einer eigenen Zeile." + repeat-reward-text-changed: "&a Erfolg, der Wiederholungsbelohnungstext wurde + aktualisiert." + write-reward-commands: "&e Bitte geben Sie pro Zeile im Chat einen neuen Belohnungsbefehl + ein und beenden Sie den Chat in einer eigenen Zeile." + reward-commands-changed: "&a Erfolg, die Belohnungsbefehle wurden aktualisiert." + write-repeat-reward-commands: "&e Bitte geben Sie pro Zeile im Chat einen neuen + Belohnungswiederholungsbefehl ein und beenden Sie den Chat in einer eigenen + Zeile." + repeat-reward-commands-changed: "&a Erfolg, die Wiederholungsbelohnungsbefehle + wurden aktualisiert." + challenge-removed: "&a Herausforderungen [challende] für [gamemode] wird aus der + Datenbank entfernt." + confirm-challenge-deletion: "&e Bitte bestätigen Sie, dass Sie [challenge] für + [gamemode] aus der Datenbank entfernen möchten." + level-removed: "&a Level [level] für [gamemode] wird aus der Datenbank entfernt." + confirm-level-deletion: "&e Bitte bestätigen Sie, dass Sie [level] für [gamemode] + aus der Datenbank entfernen möchten." + start-downloading: "&a Herunterladen und Importieren der Challenges-Bibliothek + beginnen." + written-text: "&a Eingabetext:" + confirm-data-replacement: "&e Bitte bestätigen Sie, dass Sie Ihre aktuellen Herausforderungen + durch neue ersetzen möchten." + new-challenges-imported: "&a Erfolg, neue Herausforderungen für [gamemode] wurden + importiert." + exported-file-name: "&e Bitte geben Sie einen Dateinamen für die exportierte Datenbankdatei + ein. (zum Beenden 'cancel' schreiben)" + database-export-completed: "&a Erfolg, der Datenbankexport für [world] ist abgeschlossen. + Datei wurde [file] generiert." + file-name-exist: "&c Datei mit dem Namen '[id]' existiert. Kann nicht überschrieben + werden." + write-search: "&e Bitte geben Sie einen Suchwert ein. (zum Beenden 'cancel' schreiben)" + search-updated: "&a Suchwert aktualisiert." titles: challenge-title: Erfolgreich abgeschlossen challenge-subtitle: "[friendlyName]" level-title: Erfolgreich abgeschlossen level-subtitle: "[friendlyName]" messages: - admin: - you-added: Du hast der Herausforderung eine [thing] hinzugefügt - challenge-created: "[challenge] &r erstellt!" - completed: Du hast Herausforderung [name] für [player] abgeschlossen! - already-completed: "&2Diese Herausforderung wurde bereits abgeschlossen!" - reset: "&2Du setzt Herausforderung [name] für [player] zurück!" - reset-all: "&2Alle [Player] Herausforderungen wurden zurückgesetzt!" - not-completed: "&2Diese Herausforderung ist noch nicht abgeschlossen!" - migrate-start: "&2Beginne Migration von Herausforderungen Addon-Daten." - migrate-not: "&2Alle Daten sind gültig." - start-downloading: "&5Starten des Downloads und Imports der Herausforderungen-Bibliothek." - migrate-end: "&2Herausforderungen Addon-Daten auf neues Format aktualisiert." - hit-things: Anklicken der Dinge, um sie zur Liste der benötigten Dinge hinzuzufügen. - Rechtsklick, wenn du fertig bist. - complete-wipe: "&cHoffentlich hast du Backups, denn du hast gerade alle Datenbanken - des Challenges Addons gelöscht!" - challenge-wipe: "&cHoffentlich hast du Backups, denn du hast gerade alle Herausforderungen - und ihre Level gelöscht!" - players-wipe: "&cHoffentlich hast du Backups, denn du löschst einfach alle abgeschlossenen - Herausforderungen des Spielers!" + completed: "&2 Sie haben die Herausforderung [name] für [player] abgeschlossen!" + already-completed: "&2 Diese Herausforderung wurde bereits abgeschlossen!" + reset: "&2 Sie haben die Herausforderung [name] für [player] zurückgesetzt!" + reset-all: "&2 Alle [player]-Herausforderungen wurden zurückgesetzt!" + not-completed: "&2 Diese Herausforderung ist noch nicht abgeschlossen!" + migrate-start: "&2 Starten Sie die Migration von Challenges-Addon-Daten." + migrate-end: "&2 Challenges-Addon-Daten auf neues Format aktualisiert." + migrate-not: "&2 Alle Daten sind gültig." + start-downloading: "&5 Herunterladen und Importieren der Challenges-Bibliothek + beginnen." you-completed-challenge: "&2Du hast die [value] &r&2Herausforderungen abgeschlossen!" you-repeated-challenge: "&2Du hast die [value] &r&2Herausforderung wiederholt!" you-repeated-challenge-multiple: "&2Du hast die [value] &r&2Herausforderungen @@ -554,81 +1048,64 @@ challenges: you-completed-level: "&2Du hast den [value] &r&2level abgeschlossen!" name-has-completed-challenge: "&5[name] hat die [value] &r&5-Herausforderung abgeschlossen!" name-has-completed-level: "&5[name] hat den [value] &r&5Level abgeschlossen!" - import-levels: Startet Importieren von Leveln - import-challenges: Startet Importieren von Herausforderungen - no-levels: 'Warnung: Keine Level in der challenges.yml definiert' - import-number: "[number] Herausforderungen importiert" load-skipping: '"[value]" existiert bereits - überspringen' load-overwriting: Überschreibt "[value]" load-add: 'Neues Objekt hinzufügen: [value]' - defaults-file-overwrite: defaults.json existiert. Sie wird überschrieben. - defaults-file-completed: defaults.json Datei ist mit Herausforderungen von [world] - belegt! errors: no-name: "&cFehlender Herausforderungsname" unknown-challenge: "&cUnbekannte Herausforderung" - unique-id: '&cEindeutige ID "[id]" ist nicht gültig.' - wrong-icon: '&cGegebenes Material "[value]" ist nicht gültig und kann nicht als - Symbol verwendet werden.' + not-valid-integer: |- + &cDie Angabe der ganzen Zahl "[value]" ist nicht gültig! + Der Wert sollte zwischen [min] und [max] liegen. not-deployed: "&cHerausforderung wird nicht eingesetzt!" not-on-island: "&cDu musst auf deiner Insel sein, um das zu tun!" + challenge-level-not-available: "&cDu hast das erforderliche Level nicht freigeschaltet, + um diese Herausforderung abzuschließen." not-repeatable: "&cDiese Herausforderung ist nicht wiederholbar!" + wrong-environment: "&cDu bist in der falschen Umgebung!" not-enough-items: "&cDu hast nicht genug [items], um diese Herausforderung abzuschließen!" not-close-enough: "&cDu musst innerhalb von [number] Blöcken aller benötigten Positionen stehen." you-still-need: "&cDu brauchst noch [amount] x [item]" - not-enough-money: "&cEs ist notwendig, [value] auf deinem Konto zu haben, um die - Herausforderung abzuschließen." - import-no-file: "&cEs konnte keine challenges.yml Datei zum Importieren gefunden - werden!" - no-load: "&cFehler: Die challenges.yml konnte nicht geladen werden.[message]" - load-error: "&cFehler: [value] kann nicht geladen werden." - defaults-file-exist: "&cdefaults.json existiert bereits. Benutze den Überschreibungsmodus, - um sie zu ersetzen!" - defaults-file-error: "&cBeim Erstellen der Datei defaults.json ist ein Fehler - aufgetreten! Konsole überprüfen!" - missing-arguments: "&cDem Befehl fehlen Argumente." - wrong-environment: "&cDu bist in der falschen Umgebung!" missing-addon: "&cKann die Herausforderung nicht vollenden: Benötigtes Addon oder Plugin fehlt." + incorrect: "&cKann die Herausforderung nicht abschließen: Anforderungen sind falsch." + not-enough-money: "&cEs ist notwendig, [value] auf deinem Konto zu haben, um die + Herausforderung abzuschließen." + not-enough-experience: "&cEs ist notwendig [value] EXP zu haben, um diese Herausforderung + abzuschließen." + island-level: "&cDeine Insel muss mindestens Level [number] oder höher sein, um + diese Herausforderung abzuschließen!" + no-load: "&cFehler: Die challenges.yml konnte nicht geladen werden.[message]" + load-error: "&cFehler: [value] kann nicht geladen werden." + no-rank: "&cDu hast keinen Rang, der hoch genug ist, um das zu tun." + cannot-remove-items: "&cEinige Items können nicht aus deinem Inventar entfernt + werden!" exist-challenges-or-levels: "&cDie Herausforderung existiert bereits in deiner Welt. Kann nicht fortfahren!" no-challenges: "&cDie Herausforderungen sind in dieser Welt noch nicht implementiert!" no-challenges-admin: "&cDie Herausforderungen sind in dieser Welt noch nicht implementiert! Verwende &5/[command] &cum sie hinzuzufügen!" - missing-level: "&cHerausforderung Level [level] ist in der Datenbank nicht festgelegt. - Dies kann Fehler verursachen!" + missing-arguments: "&cDem Befehl fehlen Argumente." no-multiple-permission: "&cDu hast keine Berechtigung, diese Herausforderung mehrmals hintereinander auszuführen." - not-a-integer: '&cDer angegebene Wert "[value]" ist keine ganze Zahl!' - challenge-level-not-available: "&cDu hast das erforderliche Level nicht freigeschaltet, - um diese Herausforderung abzuschließen." - incorrect: "&cKann die Herausforderung nicht abschließen: Anforderungen sind falsch." - not-enough-experience: "&cEs ist notwendig [value] EXP zu haben, um diese Herausforderung - abzuschließen." - island-level: "&cDeine Insel muss mindestens Level [number] oder höher sein, um - diese Herausforderung abzuschließen!" - no-rank: "&cDu hast keinen Rang, der hoch genug ist, um das zu tun." - cannot-remove-items: "&cEinige Items können nicht aus deinem Inventar entfernt - werden!" - not-valid-integer: |- - &cDie Angabe der ganzen Zahl "[value]" ist nicht gültig! - Der Wert sollte zwischen [min] und [max] liegen. invalid-level: "& c Level [Level] enthält ungültige Daten. Es wird nicht aus der Datenbank geladen!" invalid-challenge: "& c Challenge [Challenge] enthält ungültige Daten. Sie wird nicht aus der Datenbank geladen!" + no-library-entries: "&c Es können keine Bibliothekseinträge gefunden werden. Nichts + zu zeigen." + not-hooked: "&c Challenges Addon konnte keinen GameMode finden." + timeout: "&c Diese Abfrage erfordert eine Wartezeit von [timeout] zwischen den + Vervollständigungen. Sie müssen [wait-time] warten, bis Sie es erneut abschließen." protection: flags: CHALLENGES_ISLAND_PROTECTION: - description: "&5&Umschalten, wer &5&Herausforderungen erledigen kann" + description: "&5Umschalten, wer &5Herausforderungen erledigen kann" name: Herausforderungen Schutz CHALLENGES_WORLD_PROTECTION: - name: Herausforderungen Inselbegrenzung - hint: Keine Herausforderungen außerhalb der Insel description: "&5&oAktivieren/Deaktivieren von \n&5&oAnforderung für Spieler,\n&5&oauf ihrer Insel zu sein, um \n&5&oeine Herausforderung abzuschließen." -version: 11 -meta: - authors: - '0': xXjojoXx + name: Herausforderungen Inselbegrenzung + hint: Keine Herausforderungen außerhalb der Insel +version: 12 diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index d7a812a..bc03183 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -13,28 +13,12 @@ challenges: main: parameters: '' description: 'Main admin command. Opens GUI.' - import: - description: |- - Import challenges from challenges.yml - Parameter overwrite means that challenges or levels with the same ID will be overwritten. - parameters: '[overwrite]' reload: - description: |- - Reload challenges from the database - Parameter hard means that addon will reset the connection to the database. - parameters: '[hard]' + description: "Reload challenges from the database" + parameters: '' show: description: 'Prints all challenges in the chat which exist in this world.' parameters: '' - defaults: - description: 'Shows subcommands to import/export the default challenges.' - parameters: '[command]' - defaults-import: - description: 'Import the default challenges.' - parameters: '' - defaults-generate: - description: 'Export existing challenges to default.json file.' - parameters: '[overwrite] - allows to overwrite existing file.' complete: description: 'Complete a challenge for a player.' parameters: ' ' @@ -52,523 +36,1146 @@ challenges: description: 'Complete challenge.' parameters: ' [count]' gui: - title: - admin: - gui-title: '&a Challenges Admin' - edit-challenge-title: '&a Edit Challenge' - edit-level-title: '&a Edit Level' - settings-title: '&a Edit Settings' - choose-challenge-title: '&a Choose Challenge' - choose-level-title: '&a Choose Level' - choose-user-title: '&a Choose Player' - manage-blocks: '&a Manage Blocks' - manage-entities: '&a Manage Entities' - confirm-title: '&a Confirmation' - manage-items: '&a Manage Items' - manage-numbers: '&a Number Pad' - select-block: '&a Select Block' - select-challenge: '&a Select Challenge' - select-entity: '&a Select Entity' - toggle-environment: '&a Toggle Environment' - edit-text-fields: '&a Edit Text Fields' - - library-title: '&a Downloadable Libraries' - - lore-add: '&a Add Lore Element' - lore-remove: '&a Remove Lore Element' - lore-edit: '&a Edit Lore' - - type-select: "&a Choose Challenge Type" - challenges: '&6 Challenges' - game-modes: '&6 Choose GameMode' - - multiple-complete: '&6 How many times?' + titles: + # The title for the Main GUI + player-gui: "&0&l Challenges Menu" + # The title for the Main GUI + gamemode-gui: "&0&l Select GameMode" + # The title for the Multiple Completion GUI + multiple-gui: "&0&l How many times?" + # GUI titles below is visible just for Admins. + admin-gui: "&0&l Challenges Admin Menu" + edit-challenge: "&0&l Edit [challenge]" + edit-level: "&0&l Edit [level]" + settings: "&0&l Settings" + choose-challenge: "&0&l Choose Challenge" + choose-level: "&0&l Choose Level" + choose-player: "&0&l Choose Player" + library: "&0&l Library" + manage-blocks: "&0&l Manage Blocks" + manage-entities: "&0&l Manage Entities" + type-selector: "&0&l Challenge Type Selector" + item-selector: "&0&l Item Selector" + block-selector: "&0&l Block Selector" + entity-selector: "&0&l Entity Selector" + challenge-selector: "&0&l Challenge Selector" + statistic-selector: "&0&l Statistic Selector" + environment-selector: "&0&l Environment Selector" buttons: - admin: - complete: 'Complete user challenge' - reset: 'Reset user challenge' - create-challenge: 'Add new challenge' - create-level: 'Add new level' - edit-challenge: 'Edit challenge' - edit-level: 'Edit level' - delete-challenge: 'Remove challenge' - delete-level: 'Remove level' - import: 'Import ASkyblock challenges' - settings: 'Edit settings' - properties: 'Properties' - requirements: 'Requirements' - rewards: 'Rewards' - challenges: 'Challenges' - deployment: 'Deployment' - icon: 'Icon' - locked-icon: 'Locked Icon' - description: 'Description' - order: 'Order' - environment: 'Environment' - remove-on-complete: 'Remove after completion' - name: 'Friendly name' - required-entities: 'Required entities' - remove-entities: 'Kill entities' - required-blocks: 'Required blocks' - remove-blocks: 'Remove blocks' - search-radius: 'Search radius' - required-permissions: 'Required permissions' - required-items: 'Required items' - remove-items: 'Remove items' - required-experience: 'Required experience' - remove-experience: 'Remove experience' - required-level: 'Required island level' - required-money: 'Required money' - remove-money: 'Remove money' - reward-text: 'Reward message' - reward-items: 'Reward items' - reward-experience: 'Reward experience' - reward-money: 'Reward money' - reward-commands: 'Reward commands' - repeatable: 'Repeatable' - repeat-count: 'Max Times' - repeat-reward-text: 'Repeat reward message' - repeat-reward-items: 'Repeat reward items' - repeat-reward-experience: 'Repeat reward experience' - repeat-reward-money: 'Repeat reward money' - repeat-reward-commands: 'Repeat reward commands' - waiver-amount: 'Waiver amount' - add-challenge: 'Add challenge' - remove-challenge: 'Remove challenge' - reset-on-new: 'Reset on new island' - broadcast: 'Broadcast completion' - remove-completed: 'Remove after complete' - glow: 'Glow when completed' - free-at-top: 'Free challenges first' - line-length: 'Lore line length' - visibility-mode: 'Challenge visibility mode' - toggle-user-list: 'User list' - remove-selected: 'Remove selected' - add: 'Add' - show-eggs: 'Switch view mode' - accept: 'Accept' - decline: 'Decline' - save: 'Save' - cancel: 'Cancel' - input: 'Input' - value: 'Value' - set: '=' - increase: '+' - reduce: '-' - multiply: '*' - clear: 'Clear' - remove-empty: 'Remove empty' - number: '[number]' - level-lore: 'Level description' - challenge-lore: 'Challenge description' - gui-view-mode: 'Display all GameModes' - gui-mode: 'Single challenges GUI' - history-store: 'Challenges history' - history-lifespan: 'History LifeSpan' - island-store: 'Store per island' - default-locked-icon: 'Locked level icon' - input-mode: 'Switch input mode' - title-enable: 'Completion title' - title-showtime: 'Title show time' - default-import: 'Import default challenges' - default-export: 'Export existing challenges' - complete-wipe: 'Wipe addon databases' - challenge-wipe: 'Wipe challenges database' - players-wipe: 'Wipe user database' - - library: 'Web Library' - download: 'Download Libraries' - - type: - island: '&6 Island Type' - inventory: '&6 Inventory Type' - other: '&6 Other Type' - next: 'Next' - previous: 'Previous' - return: 'Return' - - value: "Complete" - increase: "Increase" - reduce: "Reduce" + # Button in the Challenges GUI that allows to select free challenges. + free-challenges: + name: "&f&l Free Challenges" + description: |- + &7 Displays a list of + &7 free challenges + # Button that is used to return to previous GUI or exit it completely. + return: + name: "&f&l Return" + description: |- + &7 Return to previous menu + &7 or exit GUI + # Button that is used in multi-page GUIs which allows to return to previous page. + previous: + name: "&f&l Previous Page" + description: |- + &7 Switch to &e [number] &7 page + # Button that is used in multi-page GUIs which allows to go to next page. + next: + name: "&f&l Next Page" + description: |- + &7 Switch to &e [number] &7 page + # Button that allows to reduce number + reduce: + name: "&f&l Reduce" + description: |- + &7 Reduce by &e [number] + # Button that allows to increase number + increase: + name: "&f&l Increase" + description: |- + &7 Increase by &e [number] + # Button that displays and allows completing challenge + accept: + name: "&f&l Complete" + description: |- + &7 Complete challenge &e [number] + &7 time(-s) + # Button that allows to quit the current gui. + quit: + name: "&f&l Quit" + description: |- + &7 Exit from the GUI. + # All following buttons are mainly for Admin GUI. + complete_user_challenges: + name: "&f&l Complete User Challenge(-s)" + description: |- + &7 Allows to choose user and + &7 complete challenge(-s) for + &7 him + reset_user_challenges: + name: "&f&l Reset User Challenges" + description: |- + &7 Allows to choose user and + &7 reset his challenges + add_challenge: + name: "&f&l Create Challenge" + description: |- + &7 Starts a process for + &7 creating a new challenge. + add_level: + name: "&f&l Create Level" + description: |- + &7 Starts a process for + &7 creating a new level. + edit_challenge: + name: "&f&l Edit Challenge" + description: |- + &7 Allows to choose and edit + &7 a challenge. + edit_level: + name: "&f&l Edit Level" + description: |- + &7 Allows to choose and edit + &7 a level. + delete_challenge: + name: "&f&l Delete Challenge" + description: |- + &7 Allows to choose and delete + &7 a challenge. + delete_level: + name: "&f&l Delete Level" + description: |- + &7 Allows to choose and delete + &7 a level. + edit_settings: + name: "&f&l Settings" + description: |- + &7 Allows to view and edit + &7 an addon settings. + complete_wipe: + name: "&f&l Complete Wipe" + description: |- + &7 Completely clears challenges + &7 addon database, including + &7 user data. + challenge_wipe: + name: "&f&l Challenge Wipe" + description: |- + &7 Completely clears challenges + &7 and levels from database. + user_wipe: + name: "&f&l User Wipe" + description: |- + &7 Completely clears user + &7 data from database. + library: + name: "&f&l Library" + description: |- + &7 Opens a public + &7 challenges library. + import_database: + name: "&f&l Import Database" + description: |- + &7 Allows to import exported + &7 challenges database. + import_template: + name: "&f&l Import Template" + description: |- + &7 Allows to import template + &7 file with challenges. + export_challenges: + name: "&f&l Export Challenges" + description: |- + &7 Allows to export database + &7 to a local file. + properties: + name: "&f&l Properties" + description: |- + &7 View all main properties. + requirements: + name: "&f&l Requirements" + description: |- + &7 View requirements properties. + rewards: + name: "&f&l Rewards" + description: |- + &7 View rewards properties. + deployed: + name: "&f&l Deployment" + description: |- + &7 Toggle if challenge is + &7 deployed and users can + &7 complete it. + enabled: "&2 Enabled" + disabled: "&c Disabled" + name: + name: "&f&l Name" + description: |- + &7 Allows to change + &7 the display name. + value: "&7 Currently: &r [name]" + remove_on_complete: + name: "&f&l Hide After Completion" + description: |- + &7 Toggle if challenge should + &7 hidden from player after + &7 it is completed. + enabled: "&2 Enabled" + disabled: "&c Disabled" + description: + name: "&f&l Description" + description: |- + &7 The specific description + &7 for the challenge. The color + &7 codes must be applied to it. + value: "&7 Current description:" + environment: + name: "&f&l Dimension" + description: |- + &7 Allows to limit in which + &7 dimension the challenge + &7 can be completed. + enabled: "&2" + disabled: "&c" + order: + name: "&f&l Order" + description: |- + &7 Allows to change order of + &7 objects. + &7 Objects with equal numbers + &7 will be ordered by their + &7 unique id names. + value: "&7 Current order: &e [number]" + icon: + name: "&f&l Icon" + description: |- + &7 Allows to change icon + &7 for this challenge. + locked_icon: + name: "&f&l Locked Icon" + description: |- + &7 Allows to change locked + &7 level icon. + required_permissions: + name: "&f&l Required Permissions" + description: |- + &7 Allows to change required + &7 permissions for this + &7 challenge to be completable. + title: "&7 Permissions: " + permission: " &8 - [permission]" + none: "&7 Permissions are not set." + remove_entities: + name: "&f&l Remove Entities" + description: |- + &7 Allows to toggle if + &7 required entities will + &7 be removed from world + &7 after completing the + &7 challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_entities: + name: "&f&l Required Entities" + description: |- + &7 Allows to change required + &7 entities for this + &7 challenge to be completable. + title: "&7 Entities: " + list: " &8 - [number] x [entity]" + none: "&7 Entities are not added." + remove_blocks: + name: "&f&l Remove Blocks" + description: |- + &7 Allows to toggle if + &7 required blocks will + &7 be removed from world + &7 after completing the + &7 challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_blocks: + name: "&f&l Required Blocks" + description: |- + &7 Allows to change required + &7 blocks for this + &7 challenge to be completable. + title: "&7 Blocks: " + list: " &8 - [number] x [block]" + none: "&7 Blocks are not added." + search_radius: + name: "&f&l Search Radius" + description: |- + &7 Allows to change the radius + &7 around player from which + &7 blocks and/or entities are + &7 detected. + value: "&7 Current distance: &e [number]" + remove_items: + name: "&f&l Remove Items" + description: |- + &7 Allows to toggle if + &7 required items will + &7 be removed from inventory + &7 after completing the + &7 challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_items: + name: "&f&l Required Items" + description: |- + &7 Allows to change required + &7 items for this + &7 challenge to be completable. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items are not added." + add_ignored_meta: + name: "&f&l Add Ignore Metadata" + description: |- + &7 Allows to add which + &7 items should ignore + &7 any meta-data that + &7 is assigned to them. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items are not added." + remove_ignored_meta: + name: "&f&l Remove Ignore Metadata" + description: |- + &7 Allows to remove which + &7 items should ignore + &7 any meta-data that + &7 is assigned to them. + remove_experience: + name: "&f&l Remove Experience" + description: |- + &7 Allows to toggle if + &7 required experience will + &7 be removed from player + &7 after completing the + &7 challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_experience: + name: "&f&l Required Experience" + description: |- + &7 Allows to change the + &7 required experience for + &7 the player. + value: "&7 Current experience: &e [number]" + required_level: + name: "&f&l Required Island Level" + description: |- + &7 Allows to change the + &7 required island level + &7 for the challenge. + value: "&7 Current level: &e [number]" + remove_money: + name: "&f&l Remove Money" + description: |- + &7 Allows to toggle if + &7 required money will + &7 be removed from player + &7 account after completing + &7 the challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_money: + name: "&f&l Required Money" + description: |- + &7 Allows to change the + &7 required money on player + &7 account for the challenge. + value: "&7 Current value: &e [number]" + statistic: + name: "&f&l Statistic" + description: |- + &7 Allows to change the + &7 statistic type that is + &7 checked in this challenge. + value: "&7 Current value: &e [statistic]" + statistic_amount: + name: "&f&l Target Value" + description: |- + &7 Allows to change the + &7 statistic target value + &7 that must be met. + value: "&7 Current value: &e [number]" + remove_statistic: + name: "&f&l Reduce Statistic" + description: |- + &7 Allows to toggle if + &7 statistic value will + &7 be reduced after completing + &7 the challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + statistic_blocks: + name: "&f&l Target Block" + description: |- + &7 Allows to change the + &7 statistic target block. + value: "&7 Current block: &e [block]" + statistic_items: + name: "&f&l Target Item" + description: |- + &7 Allows to change the + &7 statistic target item. + value: "&7 Current item: &e [item]" + statistic_entities: + name: "&f&l Target Entity" + description: |- + &7 Allows to change the + &7 statistic target entity. + value: "&7 Current entity: &e [entity]" + reward_text: + name: "&f&l Reward Text" + description: |- + &7 The specific reward text. + &7 The color codes must be + &7 applied to it. + value: "&7 Current text:" + repeat_reward_text: + name: "&f&l Repeat Reward Text" + description: |- + &7 The specific repeat reward text + &7 for the challenge. The color + &7 codes must be applied to it. + value: "&7 Current text:" + reward_items: + name: "&f&l Reward Items" + description: |- + &7 Allows to change reward + &7 items. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items are not added." + repeat_reward_items: + name: "&f&l Repeat Reward Items" + description: |- + &7 Allows to change repeat + &7 reward items for this + &7 challenge. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items are not added." + reward_experience: + name: "&f&l Reward Experience" + description: |- + &7 Allows to change the + &7 reward experience for + &7 the player. + value: "&7 Reward experience: &e [number]" + repeat_reward_experience: + name: "&f&l Repeat Reward Experience" + description: |- + &7 Allows to change the + &7 repeat reward experience + &7 for the player. + value: "&7 Reward experience: &e [number]" + reward_money: + name: "&f&l Reward Money" + description: |- + &7 Allows to change the + &7 reward money. + value: "&7 Current value: &e [number]" + repeat_reward_money: + name: "&f&l Repeat Reward Money" + description: |- + &7 Allows to change the + &7 repeat reward money + &7 for the challenge. + value: "&7 Current value: &e [number]" + reward_commands: + name: "&f&l Reward Commands" + description: |- + &7 The specific reward commands. + &8 Tip: + &8 The command does not requires + &8 writing first `/` as it will + &8 be applied automatically. + &8 By default commands will be + &8 executed by server. However + &8 adding `[SELF]` at the start + &8 will allow command to be + &8 executed by player. It also + &8 supports one placeholder + &8 `[player]` that will be + &8 replaced with a player name + &8 who completed challenge. + value: "&7 Current commands:" + repeat_reward_commands: + name: "&f&l Repeat Reward Commands" + description: |- + &7 The specific repeat reward + &7 commands for the challenge. + &8 Tip: + &8 The command does not requires + &8 writing first `/` as it will + &8 be applied automatically. + &8 By default commands will be + &8 executed by server. However + &8 adding `[SELF]` at the start + &8 will allow command to be + &8 executed by player. It also + &8 supports one placeholder + &8 `[player]` that will be + &8 replaced with a player name + &8 who completed challenge. + value: "&7 Current commands:" + repeatable: + name: "&f&l Repeatable" + description: |- + &7 Allows to toggle if + &7 the challenge is + &7 repeatable. + enabled: "&2 Enabled" + disabled: "&c Disabled" + repeat_count: + name: "&f&l Repeat Count" + description: |- + &7 Allows to change the + &7 number of repeats + &7 for the challenge. + value: "&7 Current value: &e [number]" + cool_down: + name: "&f&l Cool Down" + description: |- + &7 Allows to change the + &7 cool down seconds that + &7 must be waited between + &7 repeatable challenge + &7 completions. + value: "&7 Current value: &e [time]" + challenges: + name: "&f&l Challenges" + description: |- + &7 View challenges assigned + &7 to the level. + waiver_amount: + name: "&f&l Waiver Amount" + description: |- + &7 Allows to set a number + &7 of challenges that can + &7 be left uncompleted for + &7 unlocking next level. + value: "&7 Current value: &e [number]" + add_challenges: + name: "&f&l Add Challenge(-s)" + description: |- + &7 Allows to select and + &7 adds challenges to the + &7 level. + remove_challenges: + name: "&f&l Remove Challenge(-s)" + description: |- + &7 Allows to select and + &7 remove challenges to the + &7 level. + reset_on_new: + name: "&f&l Reset On New" + description: |- + &7 Allows to toggle if + &7 challenges should be + &7 reset when user leaves + &7 island or creates a new + &7 island. + enabled: "&2 Enabled" + disabled: "&c Disabled" + broadcast: + name: "&f&l Broadcast" + description: |- + &7 Broadcasts challenge and + &7 level first time completion + &7 to everyone. + enabled: "&2 Enabled" + disabled: "&c Disabled" + remove_completed: + name: "&f&l Hide Completed" + description: |- + &7 Hides compleated challenges + &7 from the menu. + enabled: "&2 Enabled" + disabled: "&c Disabled" + glow_completed: + name: "&f&l Glow Completed" + description: |- + &7 Adds enchantment glow + &7 to the completed challenges. + enabled: "&2 Enabled" + disabled: "&c Disabled" + store_history: + name: "&f&l Store History" + description: |- + &7 Stores internal history + &7 when each challenge is + &7 completed. + &7 Currently viewable only + &7 in the database. + enabled: "&2 Enabled" + disabled: "&c Disabled" + data_per_island: + name: "&f&l Store Per Island" + description: |- + &7 Stores the completed + &7 challenges per island. + &7 Progress will be shared + &7 with all players in team. + enabled: "&2 Enabled" + disabled: "&c Disabled" + show_title: + name: "&f&l Show Title" + description: |- + &7 Shows title when a + &7 challenge or level + &7 is completed. + enabled: "&2 Enabled" + disabled: "&c Disabled" + gamemode_gui: + name: "&f&l GameMode Selection GUI" + description: |- + &7 Enables single GUI that + &7 is available via /challenges + &7 command. + &c Requires server restart. + enabled: "&2 Enabled" + disabled: "&c Disabled" + locked_level_icon: + name: "&f&l Default Locked Level Icon" + description: |- + &7 Default icon for all locked + &7 levels. Each level can change + &7 this icon. + purge_history: + name: "&f&l History Lifetime" + description: |- + &7 Number of days how long + &7 history data is stored + &7 in user data. + &7 0 means that data will + &7 not be removed. + value: "&7 Current value: &e [number]" + title_showtime: + name: "&f&l Title Showtime" + description: |- + &7 Number of ticks that title + &7 will be showed to the player. + value: "&7 Current value: &e [number]" + active_world_list: + name: "&f&l Show Only Active World" + description: |- + &7 If GameMode Selection GUI + &7 is enabled, this can switch + &7 if GUI shows GameMode selection + &7 or challenges for current world. + &c Requires server restart. + enabled: "&2 Enabled" + disabled: "&c Disabled" + visibility_mode: + name: "&f&l Visibility Mode" + description: |- + &7 Visibility Mode for + &7 challenges that are + &7 hiddend. + enabled: "&2" + disabled: "&c" + visible: "Show visible challenges" + hidden: "Show all challenges" + toggleable: "Allow toggling" + download: + name: "&f&l Download Libraries" + description: |- + &7 Manually update available + &7 challenges libraries. + enabled: "&2 With cache clear" + disabled: "&c Without cache clear" + player: + name: "&f&l [name]" + description: |- + &7 Island Owner: [owner] + members: "&7 Island Members:" + member: "&8 - [name]" + no-island: |- + &c Player does not have + &c an island. + player_list: + name: "&f&l Choose User List" + description: |- + &7 Choose which user list + &7 should be showed. + enabled: "&2" + disabled: "&c" + online: "Online Players" + with_island: "Players With Islands" + in_world: "Players In World" + add_block: + name: "&f&l Add Block" + description: |- + &7 Allows to add a new + &7 block to the list. + remove_block: + name: "&f&l Remove Block" + description: |- + &7 Allows to remove + &7 selected blocks + &7 from lists. + title: "&7 Selected Materials:" + material: "&8 - [material]" + material: + name: "&f&l [material]" + description: |- + &7 Material ID: [id] + selected: "&2 Selected" + add_entity: + name: "&f&l Add Entity" + description: |- + &7 Allows to add a new + &7 entity to the list. + switch_entity: + name: "&f&l Switch Eggs" + description: |- + &7 Allows to switch from + &7 eggs to the mob heads. + remove_entity: + name: "&f&l Remove Entity" + description: |- + &7 Allows to remove + &7 selected entities + &7 from lists. + title: "&7 Selected Entities:" + entity: "&8 - [entity]" + entity: + name: "&f&l [entity]" + description: |- + &7 Entity ID: [id] + selected: "&2 Selected" + inventory_type: + name: "&f&l Inventory Type" + description: |- + &7 Challenge that checks + &7 items in player inventory + island_type: + name: "&f&l Island Type" + description: |- + &7 Challenge that checks + &7 blocks or entities around + &7 player. + other_type: + name: "&f&l Other Type" + description: |- + &7 Challenge that uses + &7 plugins or addons things, + &7 like level and money. + statistic_type: + name: "&f&l Statistic Type" + description: |- + &7 Challenge that checks + &7 player statistic data. + save: + name: "&f&l Save" + description: |- + &7 Saves changes and + &7 returns. + cancel: + name: "&f&l Cancel" + description: |- + &7 Discards changes and + &7 returns. + accept_selected: + name: "&f&l Accept Selected" + description: |- + &7 Returns selected elements + &7 and opens previous GUI. + title: "&7 Selected: " + element: "&8 - [element]" + statistic_element: + name: "&f&l [statistic]" + description: "[description]" + environment_element: + name: "&f&l [environment]" + description: "[description]" + search: + name: "&f&l Search" + description: |- + &7 Allows to search an + &7 element with input + &7 text value. + search: "&b Value: [value]" + tips: + click-to-select: "&e Click &7 to select." + click-to-choose: "&e Click &7 to choose." + click-to-complete: "&e Click &7 to complete." + right-click-multiple-open: "&e Right Click &7 to chose completion count." + shift-left-click-to-complete-all: "&e Shift Click &7 to complete all." + left-click-to-accept: "&e Left Click &7 to complete." + right-click-to-write: "&e Right Click &7 to write." + click-to-reduce: "&e Click &7 to reduce." + click-to-increase: "&e Click &7 to increase." + click-to-return: "&e Click &7 to return." + click-to-quit: "&e Click &7 to quit." + click-to-wipe: "&e Click &7 to wipe." + left-click-to-wipe: "&e Left Click &7 to wipe." + right-click-to-switch: "&e Right Click &7 to switch." + click-to-open: "&e Click &7 to open." + click-to-export: "&e Click &7 to export." + click-to-create: "&e Click &7 to create." + left-click-to-open: "&e Left Click &7 to open." + right-click-to-reset-all: "&e Right Click &7 to wipe all." + click-to-toggle: "&e Click &7 to toggle." + click-to-change: "&e Click &7 to change." + shift-click-to-reset: "&e Shift Click &7 to reset." + click-to-add: "&e Click &7 to add." + click-to-remove: "&e Click &7 to remove." + left-click-to-cycle: "&e Left Click &7 to cycle down." + right-click-to-cycle: "&e Right Click &7 to cycle up." + click-to-edit: "&e Click &7 to edit." + left-click-to-download: "&e Left Click &7 to download." + right-click-to-toggle: "&e Right Click &7 to toggle." + click-to-install: "&e Click &7 to install." + click-to-reset-all: "&e Click &7 to reset all." + right-click-to-select: "&e Right Click &7 to select." + right-click-to-deselect: "&e Right Click &7 to deselect." + left-click-to-choose: "&e Left Click &7 to choose." + click-to-cancel: "&e Click &7 to cancel." + click-to-save: "&e Click &7 to save." + click-to-deselect: "&e Click &7 to deselect." + click-on-item: |- + &e Click &7 on item in + &7 your inventory. + left-click-to-edit: "&e Left Click &7 to edit." + right-click-to-clear: "&e Right Click &7 to clear." + click-to-previous: "&e Click &7 to view previous page." + click-to-next: "&e Click &7 to view next page." descriptions: - admin: - save: 'Save and return to the previous GUI.' - cancel: 'Return to the previous GUI. Changes will not be saved.' - input: 'Open text field input.' - set: 'Set operation. Clicking on the numbers will change the value to the selected number.' - increase: 'Increase operation. Clicking on the numbers will increase the value by the selected number.' - reduce: 'Reduce operation. Clicking on the numbers will reduce the value by the selected number.' - multiply: 'Multiply operation. Clicking on the numbers will multiply the value by the selected number.' - import: |- - Import ASkyblock challenges. - On right click it enables/disables overwrite mode. - Place challenges.yml inside the ./BentoBox/addons/Challenges folder. - complete: |- - Complete challenges for any user. - The user will not get any reward for completion. - reset: |- - Reset completed user challenges. - Right click enables/disables Reset all functionality. - create-challenge: |- - Add new challenge. - Will be in free challenges list by default. - create-level: 'Add new Level.' - edit-challenge: 'Edit Challenge settings.' - edit-level: 'Edit Level settings.' - delete-challenge: 'Remove a Challenge.' - delete-level: 'Remove a Level.' - settings: 'Change settings.' - properties: 'Change general properties' - requirements: 'Manage requirements' - rewards: 'Manage rewards' - challenges: 'Manage level challenges (add / remove).' - deployment: 'Allows users to complete (view) challenge.' - icon-challenge: 'Icon that will be displayed in GUI panels for this challenge.' - icon-level: 'Icon that will be displayed in GUI panels for this level.' - locked-icon: 'Icon that will be displayed in GUI panels if the level is locked.' - description: 'Edit description.' - order: 'Change order number.' - environment: 'Change challenge environment.' - remove-on-complete: 'Remove a challenge from a player"s GUI after it is completed.' - name-challenge: 'Change challenge display name.' - name-level: 'Change level display name.' - required-entities: |- - Add/edit/remove required entities. - Entities: - remove-entities: 'Remove (kill) entities on challenge completion.' - required-blocks: |- - Add/edit/remove required blocks. - Blocks: - remove-blocks: 'Remove (replace with air) blocks on challenge completion.' - search-radius: "Radius around player's location where required entities and blocks will be searched." - required-permissions: |- - Required permissions a for player to be able to complete this challenge. - Permission: - required-items: |- - Required items in player"s inventory. - Items: - remove-items: 'Remove items from player"s inventory after challenge completion.' - required-experience: 'Define required experience for a user to complete the challenge.' - remove-experience: 'Remove required experience.' - required-level: |- - Define the required island level for this challenge. - &c Requires Level addon.' - required-money: |- - Define the required money in player"s account. - &c Requires Vault and an Economy plugin.' - remove-money: |- - Remove required money from player"s account. - &c Requires Vault and an Economy plugin.' - reward-text: 'Change message that will be sent to player after challenges completion.' - reward-items: |- - Change first time completion reward items. - Items: - reward-experience: 'Change first time completion reward experience.' - reward-money: |- - Change first time completion reward money. - &c Requires Vault and Economy plugin. - reward-commands: |- - Define reward commands that will be called after first time completion. - ***Adding "[SELF]" at the start means that command will be run by player, e.g. "/kill" - ***String "[player]" will be replaced with player name, e.g. "/kill [player]" will be transformed to "/kill BONNe1704" - Commands: - repeatable: 'Define if challenge is repeatable or not.' - repeat-count: 'Define maximal repeat count. If the value is set 0, there are no limitations.' - repeat-reward-text: 'Change message that will be sent to the player after challenge repeated completion.' - repeat-reward-items: |- - Change repeated completion reward items. - Items: - repeat-reward-experience: 'Change repeated completion reward experience.' - repeat-reward-money: |- - Change repeated completion reward money. - &c Requires Vault and an Economy plugin. - repeat-reward-commands: |- - Define reward commands that will be executed after challenge repeated completion. - ***Adding "[SELF]" at the start means that command will be run by player, e.g. "/kill" - ***String "[player]" will be replaced with player name, e.g. "/kill [player]" will be transformed to "/kill BONNe1704" - Commands: - waiver-amount: 'Set the amount of challenges a player can leave out to unlock the next level.' - reward-text-level: 'Change the message that will be sent to the player after completing all challenges in a level.' - add-challenge: 'Add an existing challenge to the current level.' - remove-challenge: 'Remove a challenge from the current level.' - reset-on-new: 'Enables/Disables resets of all challenges for a player if they restart, leave or get kicked from an island.' - broadcast: 'Enables/Disables the broadcast about the first time challenge completion to all online players.' - remove-completed: 'Enables/Disables hiding challenges that are completed and cannot be repeated.' - glow: 'Enables/Disables the glowing effect for completed challenges.' - free-at-top: 'Change free challenges location. True means that challenges will be first, otherwise they will be last.' - line-length: 'Modify the maximum line length in lore box. Will not affect stored objects.' - toggle-user-list: 'Switch to different player list.' - mode-online: 'Players which are currently online.' - mode-in-world: 'Players in a GameMode world.' - mode-with-island: 'Players that have an island in a GameMode world.' - selected: 'Selected' - remove-selected: |- - Remove selected elements. - Select elements with the right mouse button. - show-eggs: 'Switch entity view between Egg mode or Head mode.' - level-lore: 'Modify which level description elements should be visible.' - challenge-lore: 'Modify which challenge description elements should be visible.' - gui-view-mode: "Set if /challenges GUI should show GameModes or challenges in player's world." - history-store: 'Enable/disable challenges history storage.' - history-lifespan: |- - Modify how many days history data should be stored. - 0 means forever. - island-store: |- - Enable/disable challenges data storing per island. This means that challenges will be the same for the whole team if this is enabled. - &c Will NOT convert data on click. PROGRESS WILL BE LOST.' - default-locked-icon: |- - Change default locked level icon. - This option can be overwritten by each level.' - gui-mode: |- - Enable/disable single challenges GUI. - &2 Requires a server restart.' - visibility-mode: 'Show/hide undeployed challenges.' - - click-to-edit: '&4 Click here to edit input.' - edit-text-line: '&6 Edit text message!' - add-text-line: '&6 Add new text message!' - input-mode: 'Switch between chat and anvil input modes.' - title-enable: 'Enable/disable the title message that will be shown to player"s when they complete a challenge.' - title-showtime: 'Modify how long title messages will be visible to the player.' - default-import: 'Import default challenges.' - default-export: 'Export existing challenges to defaults.json file.' - complete-wipe: 'Completely clear all challenges addon databases. Includes player data!' - - challenge-wipe: 'Completely clear challenges and their level databases!' - players-wipe: 'Completely clear player database!' - - library: 'Open GUI that shows all available public Challenges Libraries.' - - library-author: 'by &e [author]' - library-version: '&9 Made in Challenges [version]' - library-lang: '&a Language: [lang]' - library-gamemode: '&a Primary for [gamemode]' - - download: |- - Manually update available challenges libraries. - Right click to enable cache clearing.' - download-disabled: 'GitHub data downloader is disabled in BentoBox. Without it, you cannot use Libraries!' - - lore: - level: |- - Level string. - Represents translation challenges.gui.challenge-description.level - status: |- - Status string. - Represents translation challenges.gui.challenge-description.completed - count: |- - Completion count string. - Represents translation for challenges.gui.challenge-description.completed-times - challenges.gui.challenge-description.completed-times-of - and challenges.gui.challenge-description.maxed-reached - description: |- - Description string. - Defined in challenges object - challenge.description. - warnings: |- - Warning string. - Represents translation for: - challenges.gui.challenge-description.warning-items-take - challenges.gui.challenge-description.objects-close-by - challenges.gui.challenge-description.warning-entities-kill - challenges.gui.challenge-description.warning-blocks-remove - environment: |- - Environment string. - Defined in challenges object - challenge.environment. - requirements: |- - Requirement string. - Represents translation for: - challenges.gui.challenge-description.required-level - challenges.gui.challenge-description.required-money - challenges.gui.challenge-description.required-experience - challenge.requiredItems' - challenge.requiredBlocks' - or challenge.requiredEntities. - reward_text: |- - Reward string. - Defined in challenge.rewardText and challenge.repeatRewardText - reward_other: |- - Reward other string. - Represents translation for: - challenges.gui.challenge-description.experience-reward - challenges.gui.challenge-description.money-reward - challenges.gui.challenge-description.not-repeatable - reward_items: |- - Reward items. - List of items that will be rewarded defined in challenge.rewardItems and challenge.repeatRewardItems. - reward_commands: |- - Reward commands. - List of commands that will be rewarded defined in challenge.rewardCommands and challenge.repeatRewardCommands. - level_status: |- - Status string. - Represents translation challenges.gui.level-description.completed - challenge_count: |- - Completed challenge count string. - Represents translation for challenges.gui.level-description.completed-challenges-of - unlock_message: |- - Unlock message string. - Defined in challenges Level object - challengeLevel.unlockMessage - waiver_amount: |- - Shippable challenge count to unlock next level string. - Represents translation for challenges.gui.level-description.waver-amount - level_reward_text: |- - Reward string. - Defined in challengeLevel.rewardText - level_reward_other: |- - Reward other string. - Represents translation for: - challenges.gui.level-description.experience-reward - challenges.gui.level-description.money-reward - level_reward_items: |- - Reward items. - List of items that will be rewarded defined in challengeLevel.rewardItems - level_reward_commands: |- - Reward commands. - List of commands that will be rewarded defined in challengeLevel.rewardCommands - current-value: |- - &6 Current value: [value]. - enabled: 'Active' - disabled: 'Disabled' - type: - island: '&a require blocks or mobs around player' - inventory: '&a require items in the player"s inventory' - other: '&a require things from other plugins/addons' - the-end: '- The End' - nether: '- Nether' - normal: '- Overworld' - entity: '- [entity] : [count]' - block: '- [block] : [count]' - permission: '- [permission]' - item: '- [count] x [item]' - item-meta: ' ([meta])' - item-enchant: ' - [enchant] [level]' - command: '- [command]' - level-unlocked: 'Click to see [level] challenges!' - level-locked: 'Complete [count] more [level] challenges to unlock this level!' - - increase-by: "&a Increase completion count by [value]" - reduce-by: "&c Reduce completion count by [value]" - - visibility: - visible: "All challenges are visible to everyone" - hidden: "Only Deployed challenges are visible." - toggleable: "Toggle if undeployed challenges should be displayed" - - challenge-description: - level: '&f Level: [level]' - completed: '&b Completed' - completed-times-of: 'Completed [donetimes] out of [maxtimes]' - maxed-reached: 'Completed [donetimes] out of [maxtimes]' - completed-times: 'Completed [donetimes]' - warning-items-take: '&c All required items are taken from your inventory when you complete this challenge!' - objects-close-by: '&c All required blocks and entities must be close to you on your island!' - warning-entities-kill: '&c All required entities will be killed when you complete this challenge!' - warning-blocks-remove: '&c All required blocks will be removed when you complete this challenge!' - not-repeatable: '&c This challenge is not repeatable!' - experience-reward: '&6 Exp reward: [value]' - money-reward: '&6 Money reward: $[value]' - required-experience: '&6 Required exp: [value]' - required-money: '&6 Required money: $[value]' - required-island-level: '&6 Required island level: [value]' - environment: 'Required Environments:' - rewards-title: '&a Rewards:' - reward-items: '&6 Reward Items:' - reward-commands: '&6 Reward Commands:' - required-items: 'Required Items:' - required-entities: 'Required Entities:' - required-blocks: 'Required Blocks:' - level-description: - completed: '&b Completed' - completed-challenges-of: '&3 You have completed [number] out of [max] challenges in this level.' - waver-amount: '&6 [value] challenges can be skipped to unlock next level.' - experience-reward: '&6 Exp reward: [value]' - money-reward: '&6 Money reward: $[value]' - reward-items: '&6 Reward Items:' - reward-commands: '&6 Reward Commands:' - item-description: - item: '- [count] x [item]' - item-meta: ' ([meta])' - item-enchant: ' - [enchant] [level]' - item-name: ' [name]' - item-lore: ' Item Lore:' - book-meta: ' [title] by [author]' - recipe-count: ' [count] recipes' - armor-color: ' [color]' - potion-type-extended-upgraded: ' Extended and upgraded [name]' - potion-type-upgraded: ' Upgraded [name]' - potion-type-extended: ' Extended [name]' - potion-type: ' [name]' - custom-effects: ' Custom Effects:' - potion-effect: ' [effect] x [amplifier] for [duration]t' - skull-owner: ' [owner]' - egg-meta: ' [mob]' - fish-meta: ' [body-color] with [pattern-color] [pattern]' - - questions: - prefix: "&2 [SERVER]: " - - admin: - number: "Write a number in the chat and press enter." - unique-id: "Write the object's unique id and press enter." - challenge-name: "Write the display name in the chat for the current challenge." - level-name: "Write the display name in chat for the current level." - + # This part generates description text for challenges object in all GUI's. + challenge: + # The main part that generates description text. + # [description] comes from challenge.description + lore: |- + [description] + [status] + [cooldown] + [requirements] + [rewards] + # Contains a text generated inside [status] lore + status: + # Status message for completed unrepeatable challenge + completed: "&2&l Completed" + # Status message that contains number of completions for unlimited repeatable challenge + completed-times: "&2 Completed &7&l [number] &r&2 time(-s)" + # Status message that contains number of completions from max available for repeatable challenge + completed-times-of: "&2 Completed &7&l [number] &r&2 out of &7&l [max] &r&2 times" + # Status message that indicates that max completion count reached for repeatable challenge + completed-times-reached: "&2&l Completed all &7 [max] &2 times" + # Contains a text generated inside [cooldown] lore + cooldown: + lore: |- + [timeout] + [wait-time] + # Text message that shows challenges timeout. + timeout: "&7&l Cool down: &r&7 [time]" + # Text message that shows challenges wait time if it is larger than 0. + wait-time: "&c&l Available after: &r&c [time]" + # Text message that replaces days if number > 1 + in-days: "[number] d " + # Text message that replaces hours if number > 1 + in-hours: "[number] h " + # Text message that replaces minutes if number > 1 + in-minutes: "[number] min " + # Text message that replaces seconds if number > 1 + in-seconds: "[number] s" + # Contains a text generated inside [requirements] lore + requirements: + lore: |- + [environment] + [type-requirement] + [permissions] + # Message that will replace [environment] placeholder if there is just a single environment. + environment-single: "&7 Limited to [environment]" + # Message that will replace [environment] placeholder if there are multiple environments. + environment-title: "&7 Limited to: " + # Message that will be added after environment-title-multiple. + environment-list: " &7 - &e [environment]" + # Message that will replace [permissions] placeholder if there is just a single permission. + permission-single: "&c Requires [permissions] permission" + # Message that will replace [permissions] placeholder if there are multiple permissions. + permissions-title: "&c Requires permissions: " + # Message that will be added after permissions-title-multiple. + permissions-list: " &c - [permission]" + # Message that will generate for island type requirements and replace [type-requirements] + island: + lore: |- + [blocks] + [entities] + [search-radius] + [warning-block] + [warning-entity] + # Title that will be used if there are defined blocks in island challenge + blocks-title: "&7&l Required Blocks:" + # Listing of blocks that are required on the island. + block-value: " &7 - &e [material]" + blocks-value: " &7 - &e [number] x [material]" + # Title that will be used if there are defined entities in island challenge + entities-title: "&7&l Required Entities:" + # Listing of entities that are required on the island. + entity-value: " &7 - &e [entity]" + entities-value: " &7 - &e [number] x [entity]" + # Search radius for the blocks/entities + search-radius: "&7 Not further than &e [number] &7 meters" + # Waning about block/entity removing + warning-block: "&e Blocks will be &c removed" + warning-entity: "&e Entities will be &c removed" + # Message that will generate for inventory type requirements and replace [type-requirements] + inventory: + lore: |- + [items] + [warning] + # Title that will be used if there are list of items for challenge + item-title: "&7&l Required Items:" + # Listing of an item that are required multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Warning that items will be removed + warning: "&e Item(-s) will be &c removed" + # Message that will generate for other type requirements and replace [type-requirements] + other: + lore: |- + [experience] + [experience-warning] + [money] + [money-warning] + [level] + # Text for required experience + experience: "&7&l Required Experience: &r&e [number]" + # Warning that experience will be reduced + experience-warning: "&e Experience will be &c removed" + # Text for required money + money: "&7&l Required money: &r&e [number]" + # Warning that money will be reduced + money-warning: "&e Money will be &c removed" + # Text for required island level + level: "&7&l Required island level: &r&e [number]" + # Message that will generate for statistic type requirements and replace [type-requirements] + statistic: + lore: |- + [statistic] + [warning] + # Type of statistic for multiple target counter. Target may be entity or material/block + multiple-target: "&7&l [statistic]: &r&e [number] x [target]" + # Type of statistic for single target. Target may be entity or material/block + single-target: "&7&l [statistic]: &r&e [target]" + # Type of statistic without entity/block target + statistic: "&7&l [statistic] &r&e [number]" + # Warning that statistic will be removed + warning: "&e Statistic data will be &c reduced" + # Contains a text generated inside [rewards] lore + rewards: + # [text] comes from challenge.rewardText and challenge.repeatRewardText + lore: |- + &7&l Rewards: + [text] + [items] + [experience] + [money] + [commands] + # Title that will be used if there are list of items for rewards + item-title: "&7 Items:" + # Listing of an item that are rewards multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Text for reward experience + experience: "&7 Experience: &r&e [number]" + # Text for reward money + money: "&7 Money: &r&e [number]" + # Title for commands listing: + commands-title: "&7 Commands:" + # Command listing element + command: " &7 - &e [command]" + # This part generates description text for levels object in all GUI's. + level: + lore: |- + [text] + [status] + [waiver] + [rewards] + # Status is either challengeLevel.unlockMessage or current status of the level + status: + # Status message for completed unrepeatable challenge + completed: "&2&l Completed" + # Status message that contains number of completed challenges from all challenges + completed-challenges-of: |- + &2 Completed &7&l [number] &r&2 out of + &7&l [max] &r&2 challenges. + # Status message for locked level + locked: "&c&l Locked" + # Status message for locked level that will show missing challenge count. + missing-challenges: |- + &7 [number] more challenges must be + &7 completed to unlock this level. + # Contains a text for waiver amount to unlock next level + waiver: |- + &7&l [number] challenge(-s) &r&7 can be + &7 skipped to unlock next level. + # Contains a text generated inside [rewards] lore + rewards: + # [text] comes from challengeLevel.rewardText + lore: |- + &7&l Rewards: + [text] + [items] + [experience] + [money] + [commands] + # Title that will be used if there are list of items for rewards + item-title: "&7 Items:" + # Listing of an item that are rewards single time. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Text for reward experience + experience: "&7 Experience: &r&e [number]" + # Text for reward money + money: "&7 Money: &r&e [number]" + # Title for commands listing: + commands-title: "&7 Commands:" + # Command listing element + command: " &7 - &e [command]" + # This part generates description for the Library Entry + library: + author: '&7 by &e [author]' + version: '&7 Made with Challenges &e [version]' + lang: '&7 Language: &e [lang]' + gamemode: '&7 Primary for &e [gamemode]' + conversations: + # Prefix for messages that are send from server. + prefix: "&l&6 [BentoBox]: &r" + # List of strings that are valid for confirming input. (separated with ,) + confirm-string: "true, on, yes, confirm, y, valid, correct" + # List of strings that are valid for denying input. (separated with ,) + deny-string: "false, off, no, deny, n, invalid, incorrect" + # String that allows to cancel conversation. (can be only one) + cancel-string: "cancel" + # List of strings that allows to exit conversation. (separated with ,) + exit-string: "cancel, exit, quit" + # Message that is send to user when conversation is cancelled. + cancelled: "&c Conversation cancelled!" + # Message that appears when admin clicks on number editing button. + input-number: "&e Please enter a number in chat." + # Message that appears when admin clicks on seconds editing button. + input-seconds: "&e Please enter a seconds in chat." + # Error message that is showed if user input a value that is not a number. + numeric-only: "&c The given [value] is not a number!" + # Error message that is showed if user input a number that is smaller or larger that allowed. + not-valid-value: "&c The given number [value] is not valid. It must be larger than [min] and smaller than [max]!" + # Message that confirms user data removing. + user-data-removed: "&a All user data for [gamemode] is cleared from the database." + # Message that asks confirmation for user data removing. + confirm-user-data-deletion: "&e Please confirm that you want to clear user database for [gamemode]." + # Message that confirms user data removing. + challenge-data-removed: "&a All challenges data for [gamemode] is cleared from the database." + # Message that asks confirmation for user data removing. + confirm-challenge-data-deletion: "&e Please confirm that you want to clear challenges database for [gamemode]." + # Message that confirms user data removing. + all-data-removed: "&a All addon data for [gamemode] is cleared from the database." + # Message that asks confirmation for user data removing. + confirm-all-data-deletion: "&e Please confirm that you want to clear addon data for [gamemode]." + # Message that asks user to write a name + write-name: "&e Please write a name in the chat." + # Message that confirms new object creation. + new-object-created: "&a New object for [gamemode] is created." + # Error message that sends that object cannot be created with a given name + object-already-exists: "&c Object &7 [id] &c already exists. Choose different name." + # Error message that sends information that challenge cannot be deployed. + invalid-challenge: "&c Challenge [challenge] contains invalid data. It cannot be deployed!" + # Message that confirms name changing + name-changed: "&a Success, the name was updated." + # Message that appears after clicking + write-description: "&e Please enter a new description in chat and 'quit' on a line by itself to finish." + # Message that appears after successful description change. + description-changed: "&a Success, the description was updated." + # Message that appears when admin clicks on permission editing button. + write-permissions: "&e Please enter the required permissions, one per line in chat, and 'quit' on a line by itself to finish." + # Message that appears after successful permission updating. + permissions-changed: "&a Success, challenge permissions were updated." + # Message that appears after clicking + write-reward-text: "&e Please enter a new reward text in chat and 'quit' on a line by itself to finish." + # Message that appears after successful reward-text change. + reward-text-changed: "&a Success, the reward text was updated." + # Message that appears after clicking + write-repeat-reward-text: "&e Please enter a new repeat reward text in chat and 'quit' on a line by itself to finish." + # Message that appears after successful repeat-reward-text change. + repeat-reward-text-changed: "&a Success, the repeat reward text was updated." + # Message that appears after clicking + write-reward-commands: "&e Please enter a new reward command per line in chat and 'quit' on a line by itself to finish." + # Message that appears after successful commands-text change. + reward-commands-changed: "&a Success, the reward commands was updated." + # Message that appears after clicking + write-repeat-reward-commands: "&e Please enter a new repeat reward command per line in chat and 'quit' on a line by itself to finish." + # Message that appears after successful repeat-commands-text change. + repeat-reward-commands-changed: "&a Success, the repeat reward commands was updated." + # Message that confirms user data removing. + challenge-removed: "&a Challenges [challenge] for [gamemode] is removed from the database." + # Message that asks confirmation for user data removing. + confirm-challenge-deletion: "&e Please confirm that you want to remove [challenge] for [gamemode] from database." + # Message that confirms user data removing. + level-removed: "&a Level [level] for [gamemode] is removed from the database." + # Message that asks confirmation for user data removing. + confirm-level-deletion: "&e Please confirm that you want to remove [level] for [gamemode] from database." + # Message that appears when user clicks on library installation. + start-downloading: "&a Starting to download and import Challenges Library." + # Message that appears when writing multiline text. + written-text: "&a Input Text:" + # Message that appears after importing library data into database. + confirm-data-replacement: "&e Please confirm that you want to replace your current challenges with new one." + # Message that appears after successful data importing + new-challenges-imported: "&a Success, new Challenges for [gamemode] were imported." + # Message that appears after admin clicks on database exporting button. + exported-file-name: "&e Please enter a file name for the exported database file. (write 'cancel' to exit)" + # Message that appears after successful database exporting to file. + database-export-completed: "&a Success, the database export for [world] is completed. File [file] generated." + # Message that appears if input file name is already taken. + file-name-exist: "&c File with name '[id]' exists. Cannot overwrite." + # Message that asks for search value input. + write-search: "&e Please write a search value. (write 'cancel' to exit)" + # Message that appears after updating search value. + search-updated: "&a Search value updated." titles: -# Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. -# [friendlyName] will be replaced with challenge friendly name. -# [level] will be replaced with level friendly name. -# [rewardText] will be replaced with the challenge reward text. + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. + # [friendlyName] will be replaced with challenge friendly name. + # [level] will be replaced with level friendly name. + # [rewardText] will be replaced with the challenge reward text. challenge-title: 'Successfully completed' challenge-subtitle: '[friendlyName]' -# Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. -# [friendlyName] will be replaced with level friendly name. -# [rewardText] will be replaced with the level reward text. + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. + # [friendlyName] will be replaced with level friendly name. + # [rewardText] will be replaced with the level reward text. level-title: 'Successfully completed' level-subtitle: '[friendlyName]' messages: - admin: - hit-things: 'Click the things to add them to the list of required things. Right click when done.' - you-added: 'You added one [thing] to the challenge' - challenge-created: '[challenge]&r created!' - complete-wipe: '&c Hopefully you have backups, because you just erased all the Challenges Addon databases!' - - challenge-wipe: '&c Hopefully you have backups, because you just erased all the Challenges and their levels!' - players-wipe: '&c Hopefully you have backups, because you just erase all the player completed challenges!' - - completed: '&2 You completed challenge [name] for [player]!' - already-completed: '&2 This challenge was already completed!' - reset: '&2 You reset challenge [name] for [player]!' - reset-all: '&2 All [player] challenges were reset!' - not-completed: '&2 This challenge is not completed yet!' - - migrate-start: '&2 Start migrating challenges addon data.' - migrate-end: '&2 Challenges addon data updated to new format.' - migrate-not: '&2 All data is valid.' - - start-downloading: '&5 Starting to download and import Challenges Library.' + completed: '&2 You completed challenge [name] for [player]!' + already-completed: '&2 This challenge was already completed!' + reset: '&2 You reset challenge [name] for [player]!' + reset-all: '&2 All [player] challenges were reset!' + not-completed: '&2 This challenge is not completed yet!' + migrate-start: '&2 Start migrating challenges addon data.' + migrate-end: '&2 Challenges addon data updated to new format.' + migrate-not: '&2 All data is valid.' + start-downloading: '&5 Starting to download and import Challenges Library.' you-completed-challenge: '&2 You completed the [value] &r &2 challenge!' you-repeated-challenge: '&2 You repeated the [value] &r &2 challenge!' you-repeated-challenge-multiple: '&2 You repeated the [value] &r &2 challenge [count] times!' you-completed-level: '&2 You completed the [value] &r &2 level!' name-has-completed-challenge: '&5 [name] has completed the [value] &r &5 challenge!' name-has-completed-level: '&5 [name] has completed the [value] &r &5 level!' - import-levels: 'Start importing Levels' - import-challenges: 'Start importing Challenges' - no-levels: 'Warning: No levels defined in challenges.yml' - import-number: 'Imported [number] challenges' load-skipping: '"[value]" already exists - skipping' load-overwriting: 'Overwriting "[value]"' load-add: 'Adding new object: [value]' - defaults-file-overwrite: 'defaults.json exists. It will be overwritten.' - defaults-file-completed: 'defaults.json file is populated with challenges from [world]!' errors: no-name: '&c Missing challenge name' unknown-challenge: '&c Unknown challenge' - unique-id: '&c UniqueID "[id]" is not valid.' - wrong-icon: '&c Given material "[value]" is not valid and cannot be used as icon.' not-valid-integer: |- &c Given integer "[value]" is not valid! Value should be between [min] and [max]. - not-a-integer: '&c Given value "[value]" is not an integer!' not-deployed: '&c Challenge is not deployed!' not-on-island: '&c You must be on your island to do that!' challenge-level-not-available: '&c You have not unlocked the required level to complete this challenge.' @@ -582,21 +1189,85 @@ challenges: not-enough-money: '&c It is necessary to have [value] on your account to complete the challenge.' not-enough-experience: '&c It is necessary to have [value] EXP to complete this challenge.' island-level: '&c Your island must be level [number] or greater to complete this challenge!' - import-no-file: '&c Could not find challenges.yml file to import!' - no-load: '&c Error: Could not load challenges.yml. [message]' + no-load: '&c Error: Could not load file. [message]' load-error: '&c Error: Cannot load [value].' no-rank: "&c You do not have rank that is high enough to do that." cannot-remove-items: '&c Some items cannot be removed from your inventory!' exist-challenges-or-levels: '&c Challenges already exist in your world. Cannot proceed!' - defaults-file-exist: '&c defaults.json already exists. Use overwrite mode to replace it!' - defaults-file-error: '&c There was an error while creating defaults.json file! Check console!' no-challenges: '&c Challenges are not implemented in this world yet!' no-challenges-admin: '&c Challenges are not implemented in this world yet! Use &5 /[command] &c to add them!' - missing-level: '&c Challenge Level [level] is not defined in the database. It may cause errors!' missing-arguments: '&c Command is missing arguments.' no-multiple-permission: "&c You do not have permission to complete this challenge multiple times at once." invalid-level: "&c Level [level] contains invalid data. It will not be loaded from database!" invalid-challenge: "&c Challenge [challenge] contains invalid data. It will not be loaded from database!" + no-library-entries: "&c Cannot find any library entries. Nothing to show." + not-hooked: "&c Challenges Addon could not find any GameMode." + timeout: "&c This challenge requires to wait [timeout] between completions. You must wait [wait-time] till complete it again." +# # Showcase for manual material translation +# materials: +# # Names should be lowercase. +# cobblestone: "Cobblestone" +# # Also supports descriptions. +# stone: +# name: "Stone" +# description: "" +# item-stacks: +# # Non-specific item meta translations. +# # TYPE is the item type +# # META is a content of item meta. +# generic: "[type] [meta]" +# # Non-specific meta translations. Will replace [meta] +# meta: +# upgraded: "Upgraded" +# extended: "Extended" +# potion-meta: "&e [type] [upgraded] [extended]" +# # Be aware, enchants are always listed below item in separate line. +# enchant-meta: " &7 - &e [type] [level]" +# skull-meta: ": &e [player-name]" +# book-meta: "&e [title] [author]" +# # Custom Enchantment Translation. +# enchant: +# menting: "Mending" +# unbreaking: "Unbreaking" +# # Custom Potion Translation. +# potion-type: +# water_breathing: "Water Breathing" +# # You can also create specific item translations +# # Like translate all potions. +# potion: +# # This will overwrite generic translation. +# name: "[type] [upgraded] [extended]" +# # Type is either specific translation or potion effect. +# water_breathing: "Potion of Water Breathing" +# stone_shovel: +# # This will mean that only stone shovels will not show +# # meta information. +# name: "[type]" +# +# # Showcase how to support multi-linguistic challenges +# challenges: +# # Database ID name. +# example_challenge_id: +# name: "&2 Translated Name" +# description: |- +# &7 Translated Custom +# &7 description +# reward-text: |- +# &7 Translated Reward +# &7 text +# repeat-reward-text: |- +# &7 Translated Repeat +# &7 Reward text +# levels: +# # Database ID name. +# example_level_id: +# name: "&2 Translated Name" +# description: |- +# &7 Translated Custom +# &7 description +# reward-text: |- +# &7 Translated Reward +# &7 text protection: flags: CHALLENGES_ISLAND_PROTECTION: @@ -606,4 +1277,4 @@ protection: description: "&5 &o Enable/disable\n&5 &o requirement for players to\n&5 &o be on their island to\n&5 &o complete a challenge." name: "Challenges Island limitation" hint: "No challenges outside island" -version: 11 +version: 12 diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml index c8dc9fe..43c8d69 100644 --- a/src/main/resources/locales/lv.yml +++ b/src/main/resources/locales/lv.yml @@ -1,537 +1,1285 @@ ---- +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +meta: + authors: + - BONNe + challenges: commands: admin: - complete: - description: Šī komanda ļauj pabeigt uzdevumu spēlētājam. - parameters: " " - defaults: - description: Šī komanda izvada apakškomandas, kas pārvalda sākotnējos uzdevumus/līmeņus. - parameters: "[komanda]" - defaults-generate: - description: Šī metode izveidos failu defaults.json, kas saturēs šīs pasaules - uzdevumus un līmeņus.|Parametrs overwrite ļauj pārrakstīt pāri esošajam - failam. - parameters: "[overwrite]" - defaults-import: - description: Šī metode ļauj uzstādīt sākotnējos uzdevumus un to līmeņus. - import: - description: Šī metode importē Uzdevumus no challenges.yml faila.|Parametrs - [overwrite] nozīmē, ka esošie uzdevumi vai līmeņi ar tādu pašu ID tiks pārrakstīti. - parameters: "[overwrite]" main: - description: Administratora komanda, kura atver izvēlnes logu. - migrate: - description: Šī metode ļauj migrēt saglabātos datus uz jauno formātu no versijas - 0.7.5 uz 0.8.0. + parameters: '' + description: 'Galvenā administratora komanda. Atver administratora izvēlni.' reload: - description: Šī metode pārlādē Challenges papildinājumu.|Parametrs [hard] - nozīmē, ka tiks pārlādēts arī savienojums ar datubāzi. - parameters: "[hard]" - reset: - description: Šī komanda ļauj atiestatīt uzdevumu spēlētājam. Ja "challenge_id" - aizstāj ar "all", tad tiek atiestatīti visi uzdevumi. - parameters: " " + description: "Atkārtoti ielādē uzdevumus no datubāzes." + parameters: '' show: - description: Šī komanda izvada visus uzdevumu nosaukumus sarakstē. - user: + description: 'Izdrukā visus uzdevumus sarakstē.' + parameters: '' complete: - description: Šī metode ļauj izpildīt uzdevumus neatverot Uzdevumu logu.|Beigās - pierakstot skaitli, kas lielāks par 0 ļauj izpildīt uzdevumus vairākas reizes. - parameters: " [reižu skaits]" + description: 'Pabeidz uzdevumu priekš spēlētāja.' + parameters: ' ' + reset: + description: 'Atjauno uzdevumu priekš spēlētāja. Ja "uzdevums" parametrs ir "all", tad tiks atjaunoti visi uzdevumi.' + parameters: ' ' + migrate: + description: 'Migrē datus uz jaunāko formātu.' + parameters: '' + user: main: - description: Šī metode atver Uzdevumu logu. - errors: - cannot-remove-items: "&cDažas lietas nevarēja izņemt no inventāra!" - challenge-level-not-available: "&cŠī uzdevuma līmenis nav atklāts. Tu nevari to - pildīt." - defaults-file-error: "&cRadās kļūda veidojot defaults.json failu! Pārbaudi konsoli!" - defaults-file-exist: "&cdefaults.json jau eksistē. Lieto overwrite, lai to pārrakstītu!" - exist-challenges-or-levels: "&cŠajā pasaulē jau eksistē Uzdevumi. Nevar turpināt!" - import-no-file: "&cNevarēja atrast challenges.yml failu, no kura importēt!" - incorrect: "&cNevar izpildīt uzdevumu. Prasības nav korektas." - island-level: "&cSalas līmenim ir nepieciešams būt lielākam par [number]!" - load-error: "&cKļūda: Nevarēja ielādēt [value]." - missing-addon: "&cNevar izpildīt uzdevumu. Serverī nav uzstādīti nepieciešamie - papildinājumi." - missing-arguments: "&cKomandai trūkst parametri." - missing-level: "&cLīmenis [level] nav definēts datubāzē. Tas var radīt problēmas!" - no-challenges: "&cŠajā pasaulē nav izveidoti uzdevumi!" - no-challenges-admin: "&cŠajā pasaulē nav izveidoti uzdevumi! Izmanot komandu &5/[command]&c, - lai tos pievienotu!" - no-load: "&cKļūda: Nevarēja ielādēt challenges.yml. [message]" - no-name: "&cTrūkst Uzdevuma nosaukums" - no-rank: "&cTev nav nepieciešamais rangs, lai šo darītu." - not-a-integer: '&c"[value]" nav skaitlis!' - not-close-enough: "&c[number] bloku rādiusā ap tevi nav nepieciešamais bloku daudzums." - not-deployed: "&cUzdevums nav ieslēgts! Tā izpilde ir bloķēta." - not-enough-experience: "&cNepietiek pieredzes. Tev jābūt [value] pieredzei, lai - izpildītu Uzdevumu." - not-enough-items: "&cTev nav pietiekoši daudz [items], lai pabeigtu šo uzdevumu!" - not-enough-money: "&cNepietiek naudas. Tavā kontā vajag būt [value] vienībām, - lai izpildītu Uzdevumu." - not-on-island: "&cTev nepieciešams būt uz savas salas, lai izpildītu uzdevumu!" - not-repeatable: "&cŠis uzdevums nav atkārtojams!" - not-valid-integer: '&cDotais cipars "[value]" nav derīgs!|Skaitlim jābūt no [min] - līdz [max].' - unique-id: '&cUnikālais ID "[id]" nav derīgs.' - unknown-challenge: "&cNezināms uzdevums" - wrong-environment: "&cTu neesi pareizajā pasaulē!" - wrong-icon: '&cDotais materiāls "[value]" neeksistē vai arī nevar tikt izmantots - kā ikona.' - you-still-need: "&cTev vēl nepieciešams [amount] x [item]" - no-multiple-permission: "&cTev nav atļaujas pildīt uzdevumu vairākas reizes vienlaicīgi." + description: 'Atver Uzdevumu Izvēlni' + parameters: '' + complete: + description: 'Pabeidz uzdevumu.' + parameters: ' ' gui: + titles: + # The title for the Main GUI + player-gui: "&0&l Uzdevumu Izvēlne" + # The title for the Main GUI + gamemode-gui: "&0&l Izvēlies Spēles Režīmu" + # The title for the Multiple Completion GUI + multiple-gui: "&0&l Cik daudz reižu?" + # GUI titles below is visible just for Admins. + admin-gui: "&0&l Administratora Izvēlne" + edit-challenge: "&0&l Mainīt [challenge]" + edit-level: "&0&l Mainīt [level]" + settings: "&0&l Iestatījumi" + choose-challenge: "&0&l Izvēlies Uzdevumu" + choose-level: "&0&l Izvēlies Līmeni" + choose-player: "&0&l Izvēlies Spēlētāju" + library: "&0&l Bibliotēka" + manage-blocks: "&0&l Pārvaldīt Blokus" + manage-entities: "&0&l Pārvaldīt Radības" + type-selector: "&0&l Uzdevumu Tipu Izvēlne" + item-selector: "&0&l Lietu Izvēlne" + block-selector: "&0&l Bloku Izvēlne" + entity-selector: "&0&l Radību Izvēlne" + challenge-selector: "&0&l Uzdevumu Izvēlne" + statistic-selector: "&0&l Statistikas Izvēlne" + environment-selector: "&0&l Vides Izvēlne" buttons: - admin: - accept: Apstiprināt - add: Pievienot - add-challenge: Pievienot Uzdevumu - broadcast: Paziņot par izpildi - cancel: Pārtraukt - challenge-lore: Uzdevuma Apraksts - challenges: Uzdevumi - clear: Notīrīt - complete: Pabeigt uzdevumu spēlētājam - complete-wipe: Iztīrīt datubāzes - create-challenge: Izveidot jaunu uzdevumu - create-level: Izveidot jaunu līmeni - decline: Noraidīt - default-export: Exportēt esošos Uzdevumus - default-import: Importēt standarta Uzdevumus - default-locked-icon: Slēgta līmeņa ikona - delete-challenge: Izmest uzdevumu - delete-level: Izmest līmeni - deployment: Pieejams - description: Apraksts - edit-challenge: Labot uzdevumu - edit-level: Labot līmeni - environment: Vide - free-at-top: Brīvie uzdevumi augšā - glow: Mirdzēt pēc izpildes - gui-mode: Rādīt esošās pasaules uzdevumus - gui-view-mode: Rādīt visus spēles režīmus - history-lifespan: Vēstures Dzīvesilgums - history-store: Uzdevumu Vēsture - icon: Ikona - import: Importēt ASkyblock Uzdevumus - increase: "+" - input: Ievads - input-mode: Pārslēgt ievades metodi - island-store: Kopīgi salas uzdevumi - level-lore: Līmeņa Apraksts - line-length: Apraksta līnijas garums - locked-icon: Aizvērtā ikona - multiply: "*" - name: Nosaukums - number: "[number]" - order: Secība - properties: Rekvizīti - reduce: "-" - remove-blocks: Iznīcināt blokus - remove-challenge: Noņemt Uzdevumu - remove-completed: Noņemt pēc izpildes - remove-empty: Noņemt tukšos - remove-entities: Nogalināt radības - remove-experience: Noņemt pieredzi - remove-items: Noņemt priekšmetus - remove-money: Noņemt naudu - remove-on-complete: Noņemt pēc izpildes - remove-selected: Noņemt iezīmētos - repeatable: Atkārtojamība - repeat-count: Maksimālais skaits - repeat-reward-commands: Atkārtotās atlīdzības komandas - repeat-reward-experience: Atkārtotās atlīdzības pieredze - repeat-reward-items: Atkārtotās atlīdzības priekšmeti - repeat-reward-money: Atkārtotās atlīdzības nauda - repeat-reward-text: Atkārtotās atlīdzības ziņa - required-blocks: Nepieciešamie bloki - required-entities: Nepieciešamās radības - required-experience: Nepieciešamā pieredze - required-items: Nepieciešamie priekšmeti - required-level: Nepieciešamais salas līmenis - required-money: Nepieciešamā nauda - required-permissions: Nepieciešamās atļaujas - requirements: Prasības - reset: Noņemt uzdevuma izpildi spēlētājam - reset-on-new: Iztīrīt veidojot jaunu salu - reward-commands: Atlīdzības komandas - reward-experience: Atlīdzības pieredze - reward-items: Atlīdzības priekšmeti - reward-money: Atlīdzības nauda - rewards: Atlīdzības - reward-text: Atlīdzības ziņa - save: Saglabāt - search-radius: Meklēšanas distnace - set: "=" - settings: Pārvaldīt Iestatījumus - show-eggs: Pārslēgt attēlošanas režīmu - title-enable: Izpildes virsraksts - title-showtime: Virsrakta rādīšanas ilgums - toggle-user-list: Spēlētāju saraksts - value: Vērtība - waiver-amount: Neizpildāmo skaits - library: Interneta Bibliotēka - download: Lejupielādēt Bibliotēkas - challenge-wipe: Notīrīt Uzdevumu Datubāzi - players-wipe: Notīrīt Lietotāju Datubāzi - visibility-mode: Uzdevumu Redzamības mode - type: - island: "&6Salas tips" - inventory: "&6Inventāra tips" - other: "&6Cita veida tips" - next: Nākošā - previous: Iepriekšējā - return: Atgriezties - value: Pabeigt - increase: Palielināt - reduce: Samazināt - challenge-description: - completed: "&BIzpildīts" - completed-times: Izpildīts [donetimes] reizes - completed-times-of: Izpildīts [donetimes] no [maxtimes] reizēm - environment: 'Nepieciešamā pasaule:' - experience-reward: "&6Pieredzes atlīdzība: [value]" - level: "&FLīmenis: [level]" - maxed-reached: Izpildīts [donetimes] no [maxtimes] reizēm - money-reward: "&6Naudas atlīdzība: $[value]" - not-repeatable: "&cŠis Uzdevums nav atkārtojams!" - objects-close-by: "&cVisiem nepieciešamajiem blokie, un radībām nepieciešams - būt tuvu tev un uz tavas salas!" - required-blocks: 'Nepieciešamās bloki:' - required-entities: 'Nepieciešamās radības:' - required-experience: "&6Nepieciešamā pieredze: [value]" - required-island-level: "&6Nepieciešamais salas līmenis: [value]" - required-items: 'Nepieciešamās lietas:' - required-money: "&6Nepieciešamā nauda: $[value]" - reward-commands: "&6Atlīdzības komandas:" - reward-items: "&6Atlīdzības lietas:" - warning-blocks-remove: "&cVisi nepieciešamie bloki tiks noņemti (aizstāti ar - gaisu) pēc Uzdevuma izpildes!" - warning-entities-kill: "&cVisas pieprasītās radības tiks noņemtas (nogalinātas) - pēc Uzdevuma izpildes!" - warning-items-take: "&cVisas pieprasītas lietas tiks izņemtas no tava inventāra!" + # Button in the Challenges GUI that allows to select free challenges. + free-challenges: + name: "&f&l Brīvie Uzdevumi" + description: |- + &7 Attainos visus brīvos + &7 uzdevumus + # Button that is used to return to previous GUI or exit it completely. + return: + name: "&f&l Atgriezties" + description: |- + &7 Atgriežas uz iepriekšējo + &7 izvēlni vai iziet no + &7 izvēlnes. + # Button that is used in multi-page GUIs which allows to return to previous page. + previous: + name: "&f&l Iepriekšējā Lapa" + description: |- + &7 Pārslēgties uz &e [number] &7 lapu + # Button that is used in multi-page GUIs which allows to go to next page. + next: + name: "&f&l Nākošā Lapa" + description: |- + &7 Pārslēgties uz &e [number] &7 lapu + # Button that allows to reduce number + reduce: + name: "&f&l Samazināt" + description: |- + &7 Samazināt par &e [number] + # Button that allows to increase number + increase: + name: "&f&l Palielināt" + description: |- + &7 Palielināt par &e [number] + # Button that displays and allows completing challenge + accept: + name: "&f&l Pabeigt" + description: |- + &7 Pabeigt uzdevumu &e [number] + &7 reizi(-es) + # Button that allows to quit the current gui. + quit: + name: "&f&l Iziet" + description: |- + &7 Iziet no izvēlnes. + # All following buttons are mainly for Admin GUI. + complete_user_challenges: + name: "&f&l Pabeigt Spēlētāja Uzdevumu(-s)" + description: |- + &7 Ļauj pabeigt uzdevumu(-s) + &7 spēlētāja vietā. + reset_user_challenges: + name: "&f&l Atjaunot Spēlētāja Uzdevumu(-s)" + description: |- + &7 Ļauj atjaunot uzdevumu(-s) + &7 priekš spēlētāja. + add_challenge: + name: "&f&l Izveidot Uzdevumu" + description: |- + &7 Sāk uzdevuma izveides + &7 procesu. + add_level: + name: "&f&l Izveidot Līmeni" + description: |- + &7 Sāk līmeņa izveides + &7 procesu. + edit_challenge: + name: "&f&l Labot Uzdevumu" + description: |- + &7 Ļauj izvēlēties un + &7 labot uzdevumu. + edit_level: + name: "&f&l Labot Līmeni" + description: |- + &7 Ļauj izvēlēties un + &7 labot līmeni. + delete_challenge: + name: "&f&l Dzēst Uzdevumu" + description: |- + &7 Ļauj izvēlēties un + &7 dzēst uzdevumu. + delete_level: + name: "&f&l Dzēst Līmeni" + description: |- + &7 Ļauj izvēlēties un + &7 dzēst līmeni. + edit_settings: + name: "&f&l Iestatījumi" + description: |- + &7 Ļauj skatīt un mainīt + &7 papildinājuma iestatījumus. + complete_wipe: + name: "&f&l Pilnīga Datu Dzēšana" + description: |- + &7 Pilnībā dzēš visus + &7 datus no papildinājuma + &7 datubāzes. + challenge_wipe: + name: "&f&l Uzdevumu Dzēšana" + description: |- + &7 Pilnībā dzēš uzdevumu un + &7 līmeņu datus no papildinājuma + &7 datubāzes. + user_wipe: + name: "&f&l Lietotāju Datu Dzēšana" + description: |- + &7 Pilnībā dzēš lietotāju + &7 datus no papildinājuma + &7 datubāzes. + library: + name: "&f&l Bibliotēka" + description: |- + &7 Atver publisko + &7 uzdevumu datubāzi. + import_database: + name: "&f&l Ielādēt Datubāzi" + description: |- + &7 Ļauj ielādēt eksportētu + &7 datubāzes failu. + import_template: + name: "&f&l Ielādēt Paraugu" + description: |- + &7 Ļauj ielādēt parauga + &7 failu. + export_challenges: + name: "&f&l Eksportēt Uzdevumus" + description: |- + &7 Ļauj eksportēt datubāzi + &7 uz failu. + properties: + name: "&f&l Iestatījumi" + description: |- + &7 Skatīt galvenos iestatījumus. + requirements: + name: "&f&l Prasības" + description: |- + &7 Skatīt prasības. + rewards: + name: "&f&l Atlīdzības" + description: |- + &7 Skatīt atlīdzības. + deployed: + name: "&f&l Strādājošs" + description: |- + &7 Ļauj pārslēgt vai + &7 uzdevums ir pieejams + &7 priekš spēlētājiem. + enabled: "&2 Pieejams" + disabled: "&c Nepieejams" + name: + name: "&f&l Nosaukums" + description: |- + &7 Ļauj mainīt uzdevuma + &7 nosaukumu. + value: "&7 Šobrīd: &r [name]" + remove_on_complete: + name: "&f&l Slēpt pēc Pabeigšanas" + description: |- + &7 Ļauj pārslēgt uzdevumu + &7 paslēpšanu pēc pilnīga + &7 uzdevuma izpildes. + enabled: "&2 Ieslēgs" + disabled: "&c Izslēgs" + description: + name: "&f&l Apraksts" + description: |- + &7 Ļauj uzstādīt un mainīt + &7 uzdevumam specifisku + &7 aprakstu. + value: "&7 Esošais apraksts:" + environment: + name: "&f&l Dimensija" + description: |- + &7 Ļauj limitēt uzdevuma + &7 izpildes vidi. + enabled: "&2" + disabled: "&c" + order: + name: "&f&l Prioritāte" + description: |- + &7 Ļauj uzstādīt prioritātes + &7 numuru. + &7 Objekti tiks sakārtoti augošā + &7 secībā. Ja prioritātes numuri + &7 ir vienādi, tad objekti tiks + &7 kārtoti pēc to `id` vārdiem.. + value: "&7 Esošais numurs: &e [number]" + icon: + name: "&f&l Ikona" + description: |- + &7 Ļauj mainīt ikonu. + locked_icon: + name: "&f&l Slēgtā Ikona" + description: |- + &7 Ļauj mainīt slēgta + &7 līmeņa ikonu. + required_permissions: + name: "&f&l Nepieciešamās Atļaujas" + description: |- + &7 Ļauj uzstādīt un mainīt + &7 atļaujas, lai uzdevumu + &7 varētu izpildīt. + title: "&7 Atļaujas: " + permission: " &8 - [permission]" + none: "&7 Atļaujas nav uzstādītas." + remove_entities: + name: "&f&l Izņemt Radības" + description: |- + &7 Ļauj pārslēgt vai + &7 radības tiks izņemtas + &7 no pasaules pēc + &7 uzdevuma izpildes. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_entities: + name: "&f&l Nepieciešamās Radības" + description: |- + &7 Ļauj mainīt uzdevuma + &7 radības, kas nepieciešamas + &7 uzdevuma izpildei. + title: "&7 Radības: " + list: " &8 - [number] x [entity]" + none: "&7 Radības nav uzstādītas." + remove_blocks: + name: "&f&l Izņemt Blokus" + description: |- + &7 Ļauj pārslēgt vai + &7 bloki tiks izņemti + &7 no pasaules pēc + &7 uzdevuma izpildes. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_blocks: + name: "&f&l Nepieciešamie Bloki" + description: |- + &7 Ļauj mainīt uzdevuma + &7 blokus, kas nepieciešamas + &7 uzdevuma izpildei. + title: "&7 Bloki: " + list: " &8 - [number] x [block]" + none: "&7 Bloki nav uzstādīti." + search_radius: + name: "&f&l Meklēšanas Rādiuss" + description: |- + &7 Ļauj mainīt bloku un radību + &7 meklēšanas rādiusu. + value: "&7 Esošā distance: &e [number]" + remove_items: + name: "&f&l Izņemt Priekšmetus" + description: |- + &7 Ļauj pārslēgt vai + &7 priekšmetus tiks izņemtas + &7 no spēlētāja inventāra + &7 pēc uzdevuma izpildes. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_items: + name: "&f&l Priekšmeti" + description: |- + &7 Ļauj mainīt uzdevuma + &7 priekšmeti, kas nepieciešamas + &7 uzdevuma izpildei. + title: "&7 Priekšmeti: " + list: " &8 - [number] x [item]" + none: "&7 Priekšmeti nav uzstādīti." + add_ignored_meta: + name: "&f&l Pievienot Meta-datu Ignorēšanu" + description: |- + &7 Ļauj uzstādīt, kuriem + &7 priekšmetiem drīkst + &7 ignorēt meta-datus, kā + &7 bojājumi, vārdi, uzlabojumi. + title: "&7 Priekšmeti: " + list: " &8 - [item]" + none: "&7 Priekšmeti nav uzstādīti." + remove_ignored_meta: + name: "&f&l Noņemt Meta-datu Ignorēšanu" + description: |- + &7 Ļauj noņemt, kuriem + &7 priekšmetiem drīkst + &7 ignorēt meta-datus, kā + &7 bojājumi, vārdi, uzlabojumi. + remove_experience: + name: "&f&l Noņemt Pieredzi" + description: |- + &7 Ļauj pārslēgt vai pēc + &7 uzdevuma izpildes + &7 pieredzes punkti + &7 spēlētājam tiks noņemti. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_experience: + name: "&f&l Nepieciešamā Pieredze" + description: |- + &7 Ļauj uzstādīt nepieciešamo + &7 pieredzi uzdevuma izpildei. + value: "&7 Pieredze: &e [number]" + required_level: + name: "&f&l Salas Līmenis" + description: |- + &7 Ļauj uzstādīt nepieciešamo + &7 salas līmeni uzdevuma + 77 izpildei. + value: "&7 Līmenis: &e [number]" + remove_money: + name: "&f&l Noskaitīt Naudu" + description: |- + &7 Ļauj pārslēgt vai pēc + &7 uzdevuma izpildes + &7 nauda tiks noskaitīta + &7 spēlētāja maka. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_money: + name: "&f&l Nepieciešamā Nauda" + description: |- + &7 Ļauj uzstādīt nepieciešamo + &7 naudas daudzumu spēlētāja + &7 makā. + value: "&7 Naudas Daudzums: &e [number]" + statistic: + name: "&f&l Statistika" + description: |- + &7 Ļauj mainīt, kura + &7 statistikas vērtība + &7 tiks skatīta. + value: "&7 Vērtība: &e [statistic]" + statistic_amount: + name: "&f&l Nepieciešamā Vērtība" + description: |- + &7 Ļauj uzstādīt kādu + &7 vērtību statistikas + &7 rādītājam ir jāsasniedz. + value: "&7 Vērtība: &e [number]" + remove_statistic: + name: "&f&l Samazināt Statistiku" + description: |- + &7 Ļauj pārslēgt vai pēc + &7 uzdevuma izpildes + &7 no statistikas datiem + &7 nepieciešamā vērtība + &7 tiks noskaitīta. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + statistic_blocks: + name: "&f&l Bloks" + description: |- + &7 Ļauj uzstādīt bloku, + &7 ko skatīs statistika. + value: "&7 Bloks: &e [block]" + statistic_items: + name: "&f&l Priekšmets" + description: |- + &7 Ļauj uzstādīt priekšmetu, + &7 ko skatīs statistika. + value: "&7 Priekšmets: &e [item]" + statistic_entities: + name: "&f&l Radība" + description: |- + &7 Ļauj uzstādīt radību, + &7 ko skatīs statistika. + value: "&7 Radība: &e [entity]" + reward_text: + name: "&f&l Atlīdzības Teksts" + description: |- + &7 Ļauj uzrakstīt uzdevumam + &7 specifisku atlīdzības + &7 tekstu. + value: "&7 Teksts:" + repeat_reward_text: + name: "&f&l Atkārtotas Atlīdzības Teksts" + description: |- + &7 Ļauj uzrakstīt uzdevumam + &7 specifisku atkārtotas + &7 atlīdzības tekstu. + value: "&7 Teksts:" + reward_items: + name: "&f&l Atlīdzības Priekšmeti" + description: |- + &7 Ļauj uzstādīt atlīdzības + &7 priekšmetus. + title: "&7 Priekšmeti: " + list: " &8 - [number] x [item]" + none: "&7 Priekšmeti nav uzstādīti." + repeat_reward_items: + name: "&f&l Atkārtotas Atlīdzības Priekšmeti" + description: |- + &7 Ļauj uzstādīt atkārtotās + &7 atlīdzības priekšmetus. + title: "&7 Priekšmeti: " + list: " &8 - [number] x [item]" + none: "&7 Priekšmeti nav uzstādīti." + reward_experience: + name: "&f&l Atlīdzības Pieredze" + description: |- + &7 Ļauj uzstādīt atlīdzības + &7 pieredzi spēlētājam. + value: "&7 Pieredze: &e [number]" + repeat_reward_experience: + name: "&f&l Atkārtotas Atlīdzības Pieredze" + description: |- + &7 Ļauj uzstādīt atkārtotas + &7 atlīdzības pieredzi + &7 spēlētājam. + value: "&7 Pieredze: &e [number]" + reward_money: + name: "&f&l Atlīdzības Nauda" + description: |- + &7 Ļauj uzstādīt atlīdzības + &7 naudu spēlētājam. + value: "&7 Vērtība: &e [number]" + repeat_reward_money: + name: "&f&l Atkārtotas Atlīdzības Nauda" + description: |- + &7 Ļauj uzstādīt atkārtotas + &7 atlīdzības naudu spēlētājam. + value: "&7 Vērtība: &e [number]" + reward_commands: + name: "&f&l Atlīdzības Komandas" + description: |- + &7 Ļauj uzstādīt atlīdzības + &7 komandas. + &8 Padomi: + &8 Komandām nav nepieciešams + &8 pirmais `/` to sākumā. + &8 Pēc noklusējuma komandas + &8 izpildīs serveris. Taču + &8 pievienojot `[SELF]` sākumā + &8 komanda tiks izpildīta no + &8 spēlētāja konta. + &8 Komanda atbalsta arī + &8 `[player]` aizvietotāju, + &8 kurš tiks pārvidots par + &8 spēlētāja vārdu. + value: "&7 Komandas:" + repeat_reward_commands: + name: "&f&l Atkārtotas Atlīdzības Komandas" + description: |- + &7 Ļauj uzstādīt atkārtotas + &7 atlīdzības komandas. + &8 Padomi: + &8 Komandām nav nepieciešams + &8 pirmais `/` to sākumā. + &8 Pēc noklusējuma komandas + &8 izpildīs serveris. Taču + &8 pievienojot `[SELF]` sākumā + &8 komanda tiks izpildīta no + &8 spēlētāja konta. + &8 Komanda atbalsta arī + &8 `[player]` aizvietotāju, + &8 kurš tiks pārvidots par + &8 spēlētāja vārdu. + value: "&7 Komandas:" + repeatable: + name: "&f&l Atkārtojams" + description: |- + &7 Ļauj pārslēgt vai + &7 uzdevums ir atkārtojams. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + repeat_count: + name: "&f&l Atkārtojumu Daudzums" + description: |- + &7 Ļauj uzstādīt cik + &7 daudz reižu uzdevumu + &7 varēs izpildīt. + value: "&7 Vērtība: &e [number]" + cool_down: + name: "&f&l Taimouts" + description: |- + &7 Ļauj uzstādīt sekunžu + &7 daudzumu, kas jānogaida + &7 starp sekunžu izpildīšanu + &7 starp atkārtojamajiem + &7 uzdevumiem. + value: "&7 Current value: &e [time]" + challenges: + name: "&f&l Uzdevumi" + description: |- + &7 Skatīt uzdevumus, kas + &7 piesaistīti līmenim. + waiver_amount: + name: "&f&l Atbrīvojuma Daudzums" + description: |- + &7 Ļauj uzstādīt cik daudz + &7 uzdevumu var palikt + &7 neizpildīti iekš līmeņa, + &7 lai atvērtu nākošo līmeni. + value: "&7 Vērtība: &e [number]" + add_challenges: + name: "&f&l Pievienot Uzdevumu(-us)" + description: |- + &7 Ļauj pievienot uzdevumus + &7 līmenim. + remove_challenges: + name: "&f&l Noņemt Uzdevumu(-us)" + description: |- + &7 Ļauj noņemt uzdevumus + &7 no līmeņa. + reset_on_new: + name: "&f&l Atjaunot pēc Iziešanas" + description: |- + &7 Ļauj ieslēgt uzdevumu + &7 progresa atjaunošanu + &7 pēc iziešanas no komandas + &7 vai jaunas salas izveidi. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + broadcast: + name: "&f&l Izziņot" + description: |- + &7 Paziņot uzdevuma un līmeņa + &7 pirmās reizes pabeigšanu + &7 visiem spēlētājiem. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + remove_completed: + name: "&f&l Slēpt Izpildītos" + description: |- + &7 Ļauj slēpt izpildītos + &7 uzdevumus no spēlētāju + &7 izvēlnēm. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + glow_completed: + name: "&f&l Atzīmēt Izpildītos" + description: |- + &7 Pievienot uzlabojuma + &7 mirdzēšanu izpildītajiem + &7 uzdevumiem. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + store_history: + name: "&f&l Saglabāt Vēsturi" + description: |- + &7 Saglabā vēsturi par + &7 uzdevumu izpildīšanu. + &7 Šobrīd skatāms tikai + &7 datubāzē. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + data_per_island: + name: "&f&l Kopīgot Salām" + description: |- + &7 Uzdevumi tiek kopīgoti + &7 visiem salas biedriem, + &7 ja ieslēgts, vai katram + &7 atsevišķi, ja izslēgts. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + show_title: + name: "&f&l Rādīt Virsrakstu" + description: |- + &7 Rādīt virsrakstu par + &7 uzdevuma izpildīšanu. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + gamemode_gui: + name: "&f&l Globālā Komanda" + description: |- + &7 Ļauj ieslēgt vienotu globālo + &7 komandu priekš šī papildinājuma. + &7 Pēc noklusējuma: /challenges + &c Nepieciešams restarts. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + locked_level_icon: + name: "&f&l Noklusējuma Slēgta Līmeņa Ikona" + description: |- + &7 Noklusējuma ikona priekš visiem + &7 slēgtajiem līmeņiem, kam nav + &7 sava specificējuma. + purge_history: + name: "&f&l Vēstures Ilgums" + description: |- + &7 Dienu skaits cik ilgi + &7 vēsture tiks glabāta. + value: "&7 Dienas: &e [number]" + title_showtime: + name: "&f&l Virsraksta Ilgums" + description: |- + &7 Virsraksta rādīšanas ilgums. + value: "&7 Vērtība: &e [number]" + active_world_list: + name: "&f&l Spēlētāja Pasaules Izvēlne" + description: |- + &7 Ļauj pārslēgt vai globālā + &7 komanda atvērs spēles režīmu + &7 izvēlni vai uzdevumu izvēlni + &7 no spēlētāja pasaules. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + visibility_mode: + name: "&f&l Redzamība" + description: |- + &7 Redzamība priekš + &7 uzdevumiem spēlētāju + &7 izvēlnēs. + enabled: "&2" + disabled: "&c" + visible: "Rādīt Pieejamos Uzdevumus" + hidden: "Rādīt Visus Uzdevumus" + toggleable: "Pārslēdzams" + download: + name: "&f&l Lejupielādēt Bibliotēku" + description: |- + &7 Manuāli lejupielādēt + &7 bibliotētku. + enabled: "&2 Notīrīt iekšējo atmiņu" + disabled: "&c Netīrīt iekšējo atmiņu" + player: + name: "&f&l [name]" + description: |- + &7 Salas īpašnieks: [owner] + members: "&7 Salas biedri:" + member: "&8 - [name]" + no-island: |- + &c Spēlētājam nav salas. + player_list: + name: "&f&l Filtrēt Spēlētājus" + description: |- + &7 Ļauj filtrēt spēlētājus. + enabled: "&2" + disabled: "&c" + online: "Spēlētāji Tiešsaitē" + with_island: "Spēlētāji ar Salām" + in_world: "Spēlētāji šajā Pasaulē" + add_block: + name: "&f&l Pievienot Bloku" + description: |- + &7 Ļauj pievienot bloku + &7 sarakstam. + remove_block: + name: "&f&l Noņemt Bloku(-us)" + description: |- + &7 Ļauj noņemt iezīmētos + &7 blokus no saraksta. + title: "&7 Iezīmētie Bloki:" + material: "&8 - [material]" + material: + name: "&f&l [material]" + description: |- + &7 Materiāla ID: [id] + selected: "&2 Iezīmēts" + add_entity: + name: "&f&l Pievienot Radību" + description: |- + &7 Ļauj pievienot jaunu + &7 radību sarakstam. + switch_entity: + name: "&f&l Pārslēgt Ikonas" + description: |- + &7 Ļauj pārslēgt radību + &7 olas uz radību galvām. + remove_entity: + name: "&f&l Dzēst Radības" + description: |- + &7 Ļauj noņemt iezīmētās + &7 radības no saraksta. + title: "&7 Iezīmētās Radības:" + entity: "&8 - [entity]" + entity: + name: "&f&l [entity]" + description: |- + &7 Radības ID: [id] + selected: "&2 Iezīmēts" + inventory_type: + name: "&f&l Inventāra Tips" + description: |- + &7 Uzdevums, kas skatās + &7 priekšmetus spēlētāja + &7 inventārā. + island_type: + name: "&f&l Salas Tips" + description: |- + &7 Uzdevums, kas skatās + &7 lietas uz spēlētāja salas. + other_type: + name: "&f&l Cits Tips" + description: |- + &7 Uzdevums, kas skatās + &7 pieredzi, naudu un salas + &7 līmeni. + statistic_type: + name: "&f&l Statistikas Tips" + description: |- + &7 Uzdevums, kas skatās + &7 spēlētāja statistiku. + save: + name: "&f&l Saglabāt" + description: |- + &7 Saglabāt izmaiņas un + &7 atgriezties. + cancel: + name: "&f&l Atcelt" + description: |- + &7 Atcelt izmaiņas un + &7 atgriezties iepriekšējā + &7 izvēlnē. + accept_selected: + name: "&f&l Akceptēt Atzīmētos" + description: |- + &7 Akceptēt atzīmētos un + &7 atgriezties iepriekšējā + &7 izvēlnē. + title: "&7 Atzīmēti: " + element: "&8 - [element]" + statistic_element: + name: "&f&l [statistic]" + description: "[description]" + environment_element: + name: "&f&l [environment]" + description: "[description]" + search: + name: "&f&l Meklēt" + description: |- + &7 Ļauj ievadīt meklēšanas + &7 tekstu. + search: "&b Vērtība: [value]" + tips: + click-to-select: "&e Klikšķini, &7 lai atlasītu." + click-to-choose: "&e Klikšķini, &7 lai izvēlētos." + click-to-complete: "&e Klikšķini, &7 lai pabeigtu." + right-click-multiple-open: |- + &e Labais klikšķis, + &7 lai izvēlētos daudzumu. + shift-left-click-to-complete-all: |- + &e Shift un klikšķis, + &7 lai pabeigtu visus. + left-click-to-accept: |- + &e Kreisais klikšķis, + &7 lai pabeigtu. + right-click-to-write: |- + &e Labais klikšķis, + &7 lai rakstītu. + click-to-reduce: "&e Klikšķini, &7 lai samazinātu." + click-to-increase: "&e Klikšķini, &7 lai palielinātu." + click-to-return: "&e Klikšķini, &7 lai atgrieztos." + click-to-quit: "&e Klikšķini, &7 lai izietu." + click-to-wipe: "&e Klikšķini, &7 lai dzēstu." + left-click-to-wipe: |- + &e Kreisais klikšķis, + &7 lai dzēstu. + right-click-to-switch: |- + &e Labais klikšķis, + &7 lai mainītu. + click-to-open: "&e Klikšķini, &7 lai atvērtu." + click-to-export: "&e Klikšķini, &7 lai eksportētu." + click-to-create: "&e Klikšķini, &7 lai izveidotu." + left-click-to-open: |- + &e Kreisais klikšķis, + &7 lai atvērtu. + right-click-to-reset-all: |- + &e Labais klikšķis, + &7 lai dzēstu visu. + click-to-toggle: "&e Klikšķini, &7 lai pārslēgtu." + click-to-change: "&e Klikšķini, &7 lai mainītu." + shift-click-to-reset: |- + &e Shift un klikšķis, + &7 lai dzēstu. + click-to-add: "&e Klikšķini, &7 lai pievienotu." + click-to-remove: "&e Klikšķini, &7 lai noņemtu." + left-click-to-cycle: |- + &e Kreisais klikšķis, + &7 pārslēgtu uz leju. + right-click-to-cycle: |- + &e Labais klikšķis, + &7 pārslēgtu uz augšu. + click-to-edit: "&e Klikšķini, &7 lai labotu." + left-click-to-download: |- + &e Kreisais klikšķis, + &7 lai lejupielādētu + right-click-to-toggle: |- + &e Labais klikšķis, + &7 lai pārslēgtu. + click-to-install: "&e Klikšķini, &7 lai instalētu." + click-to-reset-all: "&e Klikšķini, &7 lai dzēstu visu." + right-click-to-select: |- + &e Labais klikšķis, + &7 lai atzīmētu. + right-click-to-deselect: |- + &e Labais klikšķis, + &7 lai atceltu atzīmi. + left-click-to-choose: |- + &e Kreisais klikšķis, + &7 lai izvēlētos. + click-to-cancel: "&e Klikšķini, &7 lai atceltu." + click-to-save: "&e Klikšķini, &7 lai saglabātu." + click-to-deselect: |- + &e Klikšķini, &7 lai + &7 atceltu atzīmi. + click-on-item: |- + &e Klikšķini, &7 uz priekšmeta + &7 tavā inventārā. + left-click-to-edit: |- + &e Kreisais klikšķis, + &7 lai labotu. + right-click-to-clear: |- + &e Labais klikšķis, + &7 lai notīrītu. + click-to-previous: |- + &e Klikšķini, &7 lai + &7 skatītu iepriekšējo. + click-to-next: |- + &e Klikšķini, &7 lai + &7 skatītu nākošo. descriptions: - admin: - add-challenge: Ļauj pievienot jebkuru Uzdevumu šim Līmenim. - add-text-line: "&6 Pievieno jaunu teksta ziņu!" - broadcast: Ļauj ieslēgt/izslēgt paziņojumu visiem tiešsaitē esošajiem spēlētājiem, - ka spēlētājs izpildījis Uzdevumu vai Līmeni pirmo reizi. - cancel: Atgriezties iepriekšējā izvēlnē. &cDati netiks saglabāti. - challenge-lore: Ļauj definēt kādus elementus rādīs Uzdevumu aprakstos. - challenges: Ļauj pārvaldīt Līmeņa uzdevumus (pievienot / noņemt). - click-to-edit: "&4Uzspied šeit, lai labotu." - complete: Ļauj pabeigt uzdevumus jebkuram spēlētājam.|Spēlētājs nedabūs izpildes - atlīdzības. - complete-wipe: Ļauj pilnībā iztīrīt papildinājuma datubāzes. Ieskaitot spēlētāju - datus! - create-challenge: Ļauj izveidot jaunu Uzdevumu. - create-level: Ļauj izveidot jaunu Līmeni. - default-export: Ļauj eksportēt uzdevumus uz defaults.json failu. - default-import: Ļauj ielādēt sākotnējos uzdevumus. - default-locked-icon: Ļauj mainīt neatklāto līmeņu ikonu.|Katrs līmenis šo - opciju var pārrakstīt. - delete-challenge: Ļauj izdzēst Uzdevumu. - delete-level: Ļauj izdzēst Līmeni. - deployment: Ļauj nobloķēt uzdevuma izpildi.|Uzdevumu nevarēs izpildīt, ja - opcija nav aktīva. - description: Ļauj labot aprakstu. - edit-challenge: Ļauj labot Uzdevuma opcijas. - edit-level: Ļauj labot Līmeņa opcijas. - edit-text-line: "&6 Labo teksta ziņu" - environment: Ļauj izvēlēties, kurās vidēs Uzdevumu var pildīt. - free-at-top: Ļauj definēt kurā rindā būs brīvie uzdevumi (bez līmeņa). Pirmā - vai pēdējā rinda. - glow: Ļauj ieslēgt/izslēgt opciju, kas liks mirdzēt izpildītajiem Uzdevumiem/Līmeņiem. - gui-mode: Ļauj ieslēgt/izslēgt vienas komandas opciju. Komandu var mainīt - konfigurācijas failā.|&2Nepieciešama servera restartēšana. - gui-view-mode: Ļauj definēt vai /challenges logs rādīs visus spēles režīmus - vai uzdevumus no spēlētāja pasaules. - history-lifespan: Ļauj mainīt cik pēdējās dienas dati tiks glabāti.|0 nozīmē, - ka dati netiks dzēsti. - history-store: Ļauj ieslēgt/izslēgt uzdevumu izpildes vēstures saglabāšanu. - icon-challenge: Uzdevuma ikona, kas parādīsies lietotāja izvēlnē. - icon-level: Līmeņa ikona, kas parādīsies lietotāja izvēlnē. - import: Ļauj importēt ASkyblock Uzdevumus.|Ar labo peles klikšķi tas ļauj - pārslēgt pārrakstīšanas modi.|Nepieciešams Challenges.yml fails ./BentoBox/addons/Challenges - mapītē. - increase: Saskaitīšanas operācija. - input: Atvērt teksta lauka ievadi. - input-mode: Pārslēgties starp sarakstes un lakts ievades veidu. - island-store: Ļauj mainīt datu saglabāšanas režīmu. Dati var tikt glabāti - katrai salai, vai katram spēlētājam.|&cMAINOT REŽĪMUS DATI NETIKS PĀRVEIDOTI. - level-lore: Ļauj definēt kādus elementus rādīs Līmeņu aprakstos. - line-length: Ļauj definēt cik simbolus gara rindiņa būs redzama lietu aprakstos. - locked-icon: Neatklāta (neatvērta) Līmeņa ikona, kas parādīsies lietotāja - izvēlnē. - mode-in-world: Spēlētāji, kas ir spēles režīma pasaulē. - mode-online: Spēlētāji, kas ir tiešsaitē. - mode-with-island: Spēlētāji, kam ir sala šajā režīmā. - multiply: Reizināšanas operācija. - name-challenge: Ļauj labot Uzdevuma nosaukumu. - name-level: Ļauj labot Līmeņa nosaukumu. - order: Ļauj labot secību. - properties: Ļauj labot rekvizītus. - reduce: Atņemšanas operācija. - remove-blocks: Ļauj noņemt (aizstāt ar gaisu) blokus pēc Uzdevuma izpildes. - remove-challenge: Ļauj noņemt Uzdevumu no šī Līmeņa. - remove-completed: Ļauj ieslēgt/izslēgt opciju, kas paslēps visus izpildītos - uzdevumus, kurus vairs nevar atkārtot. - remove-entities: Ļauj noņemt (nogalināt) radības pēc Uzdevuma izpildes. - remove-experience: Ļauj noņemt pieprasīto pieredzes daudzumu pēc Uzdevuma - izpildes. - remove-items: Ļauj izņemt lietas no spēlētāja inventāra pēc Uzdevuma izpildes. - remove-money: Ļauj noņemt naudu no spēlētāja konta pēc Uzdevuma izpildes.|&cNepieciešams - Vault un ekonomikas papildinājumi. - remove-on-complete: Šīs opcijas ieslēgšana nozīmē, ka Uzdevumu nerādīs lietotāja - logos, ja tas ir izpildīts. - remove-selected: Dzēst iezīmētos elementus.|Iezīmēt elementus var ar labo - peles klikšķi. - repeatable: Ļauj definēt vai uzdevumu var pildīt atkārtoti. - repeat-count: Ļauj definēt cik reizes uzdevumu var pildīt atkārtoti. Cipars - 0 nozīmē, ka uzdevumu var pildīt neierobežotu daudzumu reižu. - repeat-reward-commands: 'Ļauj definēt komandas, kuras tiks izpildītas pēc - Uzdevuma atkārtotas izpildes.|&c***Pievienojot "[SELF]" sākumā nozīmē, ka - komandu izpildīs spēlētājs, piemēram. "/kill"|&c***Teksts "[player]" tiks - pārtaisīts par izpildītāja lietotājvārdu, piemēram "/kill [player]" tiks - pārveidots par "/kill BONNe1704"|Komandas:' - repeat-reward-experience: Ļauj mainīt atkārtotas izpildes atlīdzības pieredzes - daudzmumu. - repeat-reward-items: 'Ļauj pārvaldīt atkārtotas izpildes atlīdzības lietas.|Lietas:' - repeat-reward-money: Ļauj definēt atkārtotas izpildes naudas atlīdzības lielumu.|&cNepieciešams - Vault un ekonomikas papildinājumi. - repeat-reward-text: Ļauj definēt ziņu, kuru rādīs spēlētājam pēc atkārtotas - Uzdevuma izpldes. - required-blocks: Ļauj pārvaldīt nepieciešamos blokus.|Bloki:| - required-entities: Ļauj pārvaldīt nepieciešamās radības.|Radības:| - required-experience: Ļauj nodefinēt nepieciešamo spēlētāja pieredzes daudzumu, - lai izpildītu uzdevumu. - required-items: 'Nepieciešamās lietas spēlētāja inventārā.|Lietas:' - required-level: Ļauj definēt nepieciešamo salas līmeni Uzdevuma izpildei.|&cNepieciešams - Levels papildinājums. - required-money: Ļauj definēt nepieciešamo naudas daudzumu spēlētāja kontā.|&cNepieciešams - Vault un ekonomikas papildinājumi. - required-permissions: 'Nepieciešamās atļaujas, lai izpildītu Uzdevumu.|Atļaujas:' - requirements: Ļauj labot prasības - reset: Ļauj noņemt uzdevumu izpildi spēlētājam.|Ar labo peles klikšķi tas - ļauj pārslēgdz iespēju nodzēst visus izpildes datus. - reset-on-new: Ļauj pārslēgt opciju, kas notīra spēlētāja izpildītos Uzdevumus - un Līmeņus, ja tas atsāk no sākuma, pamet vai tiek izmests no salas. - reward-commands: 'Ļauj definēt komandas, kuras tiks izpildītas pēc Uzdevuma - izpildes.|&c***Pievienojot "[SELF]" sākumā nozīmē, ka komandu izpildīs spēlētājs, - piemēram. "/kill"|&c***Teksts "[player]" tiks pārtaisīts par izpildītāja - lietotājvārdu, piemēram "/kill [player]" tiks pārveidots par "/kill BONNe1704"|Komandas:' - reward-experience: Ļauj mainīt pieredzi, ko iegūs spēlētājs pēc Uzdevuma izpildes. - reward-items: 'Ļauj pārvaldīt lietas, kuras iegūs spēlētājs pēc Uzdevuma izpildes.|Lietas:' - reward-money: Ļauj mainīt naudas daudzumu, ko iegūs spēlētājs pēc Uzdevuma - izpildes.|&cNepieciešams Vault un ekonomikas papildinājumi. - rewards: Ļauj pārvaldīt atlīdzības - reward-text: Ļauj mainīt ziņu, kas parādīsies spēlētājam pēc Uzdevuma izpildes. - reward-text-level: Ļauj definēt ziņu, kas tiks nosūtīta spēlētājiem pēc Līmeņa - izpildes. - save: Saglabāt un atgriezties iepriekšējā izvēlnē. - search-radius: Distance no spēlētāja, kādā prasītie bloki un radības tiks - meklētas. - selected: Iezīmēts - set: Uzstādīšanas operācija. - settings: Ļauj labot Papildinājuma iestatījumus. - show-eggs: Ļauj pārslēgt radību attainošanas režīmu no olām uz galvu ikonām. - title-enable: Ļauj ieslēgt/izlēgt virsraksta parādīšanos pēc pirmās Uzdevuma/Līmeņa - izpildes. - title-showtime: Ļauj mainīt cik ilgi virsraksts spēlētājam būs redzams. - toggle-user-list: Ļauj pārslēgt spēlētāju saraksta režīmu. - waiver-amount: Ļauj definēt cik daudz uzdevumi var tikt atstāti neizpildīti, - lai atvērtu nākošo līmeni. - library: Atver izvēlni, kurā ir saraksts ar publiski pieejamajām Uzdevumu - Bibliotēkām - library-author: veidoja &e[author] - library-version: "&9Veidots ar Challenges-[version] versiju" - library-lang: "&aValoda: [lang]" - library-gamemode: "&aPrimāri priekš [gamemode]" - lore: - level: Līmeņa rindas. | Definētas iekš 'challenges.gui.challenge-description.level'. - status: Statusa rindas. |Definētas iekš 'challenges.gui.challenge-description.completed'. - count: Pabeigtības rindas. | Definētas iekš 'challenges.gui.challenge-description.completed-times', 'challenges.gui.challenge-description.completed-times-of' - un 'challenges.gui.challenge-description.maxed-reached'. - description: Apraksta rindas. | Definētas iekš challenges objekta- challenge.description. - warnings: 'Brīdinājumu rindas. | Definētas iekš: | ''challenges.gui.challenge-description.warning-items-take'' - | ''challenges.gui.challenge-description.objects-close-by'' | ''challenges.gui.challenge-description.warning-entities-kill'' - | ''challenges.gui.challenge-description.warning-blocks-remove''.' - environment: Apkārtnes rindas. | Definētas iekš challenges objektā- challenge.environment. - requirements: 'Prasību rinas. | Definēts iekš: | ''challenges.gui.challenge-description.required-level'' - | ''challenges.gui.challenge-description.required-money'' | ''challenges.gui.challenge-description.required-experience'' - | and challenge.requiredItems, challenge.requiredBlocks vai challenge.requiredEntities.' - reward_text: Atlīdzības rindas. |Definētas iekš challenge.rewardText un - challenge.repeatRewardText - reward_other: 'Citas atlīdzību rinas. | Definēti iekš : | ''challenges.gui.challenge-description.experience-reward'' - | ''challenges.gui.challenge-description.money-reward'' | ''challenges.gui.challenge-description.not-repeatable''.' - reward_items: Atlīdzības priekšmeti. | Definēti iekš challenge.rewardItems - un challenge.repeatRewardItems. - reward_commands: Atlīdzības komandas. | Definētas iekš challenge.rewardCommands - un challenge.repeatRewardCommands. - level_status: Statusa rindas. | Definēts iekš 'challenges.gui.level-description.completed'. - challenge_count: Pabeigto uzdevumu skaits. | Definēts iekš 'challenges.gui.level-description.completed-challenges-of' - unlock_message: Atvēršanas rindas. | Definēts iekš - challengeLevel.unlockMessage. - waiver_amount: Nepieciešamo uzdevumu skaits, kas nepieceišams līmeņa atvēršanai. - | Representē rindas 'challenges.gui.level-description.waver-amount' - level_reward_text: Atlīdzības teksts. | Definēts iekš challengeLevel.rewardText - level_reward_other: 'Atlīdzības rindiņas. | Representē rinas: | ''challenges.gui.level-description.experience-reward'' - | ''challenges.gui.level-description.money-reward''.' - level_reward_items: |- - Atlīdzības priekšmeti. | Saraksts ar priekšmetiem, kas definētas iekš - challengeLevel.rewardItems. - level_reward_commands: Atlīdzības komandas. | Saraksts ar komandām, kas - ir definētas iekš challengeLevel.rewardCommands. - download: Ļauj pašrocīgi lejupielādēt uzdevumu bibliotēkas. |Labais klikšķis - pārslēdz lokālās atmiņas dzēšanu. - download-disabled: GitHub datu lejupielāde ir izslēgta iekš BentoBox. Bez - tā nav iespējams lietot bibliotēkas! - challenge-wipe: Ļauj pilnībā notīrīt uzdevumu un to līmeņu datus! - players-wipe: Ļauj pilnībā notīrīt spēlētāju datus! - visibility-mode: Ļauj pārslēgt vai neizlaistie uzdevumi ir redzami, vai nē - block: "- [block] : [count]" - command: "- [command]" - current-value: "|&6Šī brīža vērtība: [value]." - disabled: Neaktīvs - enabled: Aktīvs - entity: "- [entity] : [count]" - item: "- [count] x [item]" - item-enchant: " - [enchant] [level]" - item-meta: " ([meta])" - level-locked: Jāpabeidz vēl [count] uzdevumus no [level] līmeņa, lai atklātu - šo līmeni! - level-unlocked: Uzspied, lai redzētu [level] uzdevumus! - nether: "- Elle" - normal: "- Virszeme" - permission: "- [permission]" - the-end: "- Beigas" - increase-by: "&aPalielināt pabeigšanas skaitu par [value]" - reduce-by: "&cSamazināt pabeigšanas skaitu par [value]" - visibility: - visible: Spēlētāji redz visus uzdevumus - hidden: Spēlētāji redz tikai izlaistos uzdevumus - toggleable: Ļauj lietotājiem pārslēgt kādus uzdevumus viņi redzēs - type: - island: "&aļauj definēt nepieciešamos blokus un radības apkārt spēlētājam" - inventory: "&aļauj definēt nepieciešamos priekšmetus spēlētāja invertārā" - other: "&aļauj definēt citas nepieciešamās lietas no citiem papildinājumiem" - item-description: - armor-color: " [color]" - book-meta: " [title] no [author]" - custom-effects: " Speciālie effekti:" - egg-meta: " [mob]" - fish-meta: " [body-color] ar [pattern-color] [pattern]" - item: "- [count] x [item]" - item-enchant: " - [enchant] [level]" - item-lore: " Lietas apraksts:" - item-meta: " ([meta])" - item-name: " [name]" - potion-effect: " [effect] x [amplifier] for [duration]t" - potion-type: " [name]" - potion-type-extended: " Pagarināts [name]" - potion-type-extended-upgraded: " Uzlabots un pagarintāts [name]" - potion-type-upgraded: " Uzlabots [name]" - recipe-count: " [count] receptes" - skull-owner: " [owner]" - level-description: - completed: "&BIzpildīts" - completed-challenges-of: "&3Tu esi izpildījis [number] no [max] uzdevumiem šajā - līmenī." - experience-reward: "&6Pieredzes atlīdzība: [value]" - money-reward: "&6Naudas atlīdzība: $[value]" - reward-commands: "&6Atlīdzības komandas:" - reward-items: "&6Atlīdzības lietas:" - waver-amount: "&6Tu vari izlaist [value] uzdevumus, lai atvērtu nākošo līmeni." - questions: - admin: - challenge-name: Ieraksti uzdevuma nosaukumu sarakstē. - level-name: Ieraksti uzdevuma līmeņa nosaukumu sarakstē. - number: Ieraksti nummuru sarakstē. - unique-id: Ieraksti objekta unikālo nosaukumu sarakstē. - prefix: "&2[SERVERIS]: " - title: - admin: - choose-challenge-title: "&aIzvēlēties Uzdevumu" - choose-level-title: "&aIzvēlēties Līmeni" - choose-user-title: "&aIzvēlēties Spēlētāju" - confirm-title: "&aApstiprināt" - edit-challenge-title: "&aLabot Uzdevumu" - edit-level-title: "&aLabot Līmeni" - edit-text-fields: "&aLabot Teksta Laukus" - gui-title: "&aUzdevumu administrēšana" - manage-blocks: "&aPārvaldīt Blokus" - manage-entities: "&aPārvaldīt Radības" - manage-items: "&aPārvaldīt Lietas" - manage-numbers: "&aSkaitļu Panelis" - select-block: "&aIzvēlēties Bloku" - select-challenge: "&aIzvēlēties Uzdevumu" - select-entity: "&aIzvēlēties Radību" - settings-title: "&aLabot Iestatījumus" - toggle-environment: "&aPārvaldīt Vidi" - library-title: "&aLejupielādējamās bibliotēkas" - lore-add: "&aPievienot apraksta elementu" - lore-remove: "&aNoņemt apraksta elementu" - lore-edit: "&aLabot aprakstu" - type-select: "&aIzvēlies uzdevumu tipu" - challenges: "&6Uzdevumi" - game-modes: "&6Izvēlies spēles režīmu" - multiple-complete: "&6Cik daudz reizes?" - messages: - admin: - already-completed: "&2Šīs uzdevums jau bija izpildīts!" - challenge-created: "[challenge]&r izveidots!" - completed: "&2Tu pabeidzi uzdevumu [name] [player] vietā!" - complete-wipe: "&cCerams, ka tev ir saglabātas rezerves kopijas, jo tu tikko - iztīrīji visas šī papildinājuma datubāzes!" - hit-things: Nospiediet lietas, lai pievienotu tās nepieciešamo lietu sarakstam. - Kad izdarīts, noklikšķiniet ar peles labo pogu. - migrate-end: "&2Uzdevumu papildinājums veiksmīgi migrēts uz jauno formātu.\n" - migrate-not: "&2Visi dati ir korekti." - migrate-start: "&2Uzsāk uzdevumu papildinājuma migrāciju uz jauno formātu." - not-completed: "&2Šis uzdevums vēl nav izpildīts!" - reset: "&2Tu atiestatīji uzdevumu [name] priekš [player]!" - reset-all: "&2Visi [player] uzdevumi ir atiesatīti!" - you-added: Tu uzdevumam pievienoji [thing] - start-downloading: "&5Uzsāk Uzdevumu bibliotēkas lejupielādēšanu un uzstādīšanu." - challenge-wipe: "&cCerams, ka tev ir rezerves kopijas, jo uzdevumi un to līmeņi - ir dzēsti!" - players-wipe: "&cCerams, ka tev ir rezerves kopijas, jo papildinājuma datubāzes - ir pilnībā notīrītas!" - defaults-file-completed: defaults.json failā ir sarakstīti uzdevumi un līmeņi - no [world]! - defaults-file-overwrite: defaults.json jau existē. Tas tiek pārrakstīts. - import-challenges: Sāk importēt uzdevumus - import-levels: Sāk importēt līmeņus - import-number: Importēti [number] uzdevumi - load-add: 'Pievieno jaunu: [value]' - load-overwriting: Pārraksta "[value]" - load-skipping: '"[value]" jau ekistē - izlaiž' - name-has-completed-challenge: "&5[name] izpildīja [value] &r&5uzdevumu!" - name-has-completed-level: "&5[name] izpildīja visus uzdevumus no [value] &r&5līmeņa!" - no-levels: "&cUzmanību, nav definēti līmeņi iekš challenges.yml faila" - you-completed-challenge: "&2Tu izpildīji [value] &r&2uzdevumu!" - you-completed-level: "&2Tu izpildīji [value] &r&2līmeni!" - you-repeated-challenge: "&2Tu atkārtoji [value] &r&2uzdevumu!" - you-repeated-challenge-multiple: "&2Tu atkārtoji [value] &r&2uzdevumu [count] - reizes!" + # This part generates description text for challenges object in all GUI's. + challenge: + # The main part that generates description text. + # [description] comes from challenge.description + lore: |- + [description] + [status] + [cooldown] + [requirements] + [rewards] + # Contains a text generated inside [status] lore + status: + # Status message for completed unrepeatable challenge + completed: "&2&l Izpildīts" + # Status message that contains number of completions for unlimited repeatable challenge + completed-times: "&2 Izpildīts &7&l [number] &r&2 reizi(-es)" + # Status message that contains number of completions from max available for repeatable challenge + completed-times-of: "&2 Izpildīts &7&l [number] &r&2 no &7&l [max] &r&2 reizēm" + # Status message that indicates that max completion count reached for repeatable challenge + completed-times-reached: "&2&l Izpildīts visas &7 [max] &2 reizes" + # Contains a text generated inside [cooldown] lore + cooldown: + lore: |- + [timeout] + [wait-time] + # Text message that shows challenges timeout. + timeout: "&7&l Taimauts: &r&7 [time]" + # Text message that shows challenges wait time if it is larger than 0. + wait-time: "&c&l Jānogaida: &r&c [time]" + # Text message that replaces days if number > 1 + in-days: "[number] d " + # Text message that replaces hours if number > 1 + in-hours: "[number] h " + # Text message that replaces minutes if number > 1 + in-minutes: "[number] min " + # Text message that replaces seconds if number > 1 + in-seconds: "[number] s" + # Contains a text generated inside [requirements] lore + requirements: + lore: |- + [environment] + [type-requirement] + [permissions] + # Message that will replace [environment] placeholder if there is just a single environment. + environment-single: "&7 Limitēts iekš [environment]" + # Message that will replace [environment] placeholder if there are multiple environments. + environment-title: "&7 Limitēts iekš: " + # Message that will be added after environment-title-multiple. + environment-list: " &7 - &e [environment]" + # Message that will replace [permissions] placeholder if there is just a single permission. + permission-single: "&c Nepieciešama [permissions] atļauja" + # Message that will replace [permissions] placeholder if there are multiple permissions. + permissions-title: "&c Nepieciešamās atļaujas: " + # Message that will be added after permissions-title-multiple. + permissions-list: " &c - [permission]" + # Message that will generate for island type requirements and replace [type-requirements] + island: + lore: |- + [blocks] + [entities] + [search-radius] + [warning-block] + [warning-entity] + # Title that will be used if there are defined blocks in island challenge + blocks-title: "&7&l Nepieciešamie bloki:" + # Listing of blocks that are required on the island. + block-value: " &7 - &e [material]" + blocks-value: " &7 - &e [number] x [material]" + # Title that will be used if there are defined entities in island challenge + entities-title: "&7&l Nepieciešamās radības:" + # Listing of entities that are required on the island. + entity-value: " &7 - &e [entity]" + entities-value: " &7 - &e [number] x [entity]" + # Search radius for the blocks/entities + search-radius: |- + &7 Ne tālāk kā &e [number] + &7 metru attālumā + # Waning about block/entity removing + warning-block: "&e Bloks(-i) tiks &c dzēsti" + warning-entity: "&e Radība(-as) tiks &c dzēstas" + # Message that will generate for inventory type requirements and replace [type-requirements] + inventory: + lore: |- + [items] + [warning] + # Title that will be used if there are list of items for challenge + item-title: "&7&l Nepieciešamie Priekšmeti:" + # Listing of an item that are required multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Warning that items will be removed + warning: "&e Priekšmets(-i) tiks &c dzēsti" + # Message that will generate for other type requirements and replace [type-requirements] + other: + lore: |- + [experience] + [experience-warning] + [money] + [money-warning] + [level] + # Text for required experience + experience: "&7&l Nepieciešamā pieredze: &r&e [number]" + # Warning that experience will be reduced + experience-warning: "&e Pieredze tiks &c noņemta" + # Text for required money + money: "&7&l Nepieciešamā nauda: &r&e [number]" + # Warning that money will be reduced + money-warning: "&e Nauda tiks &c noskaitīta" + # Text for required island level + level: "&7&l Nepieciešamais salas līmenis: &r&e [number]" + # Message that will generate for statistic type requirements and replace [type-requirements] + statistic: + lore: |- + [statistic] + [warning] + # Type of statistic for multiple target counter. Target may be entity or material/block + multiple-target: "&7&l [statistic]: &r&e [number] x [target]" + # Type of statistic for single target. Target may be entity or material/block + single-target: "&7&l [statistic]: &r&e [target]" + # Type of statistic without entity/block target + statistic: "&7&l [statistic] &r&e [number]" + # Warning that statistic will be removed + warning: "&e Statistikas dati tiks &c samazināti" + # Contains a text generated inside [rewards] lore + rewards: + # [text] comes from challenge.rewardText and challenge.repeatRewardText + lore: |- + &7&l Atlīdzības: + [text] + [items] + [experience] + [money] + [commands] + # Title that will be used if there are list of items for rewards + item-title: "&7 Priekšmeti:" + # Listing of an item that are rewards multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Text for reward experience + experience: "&7 Pieredze: &r&e [number]" + # Text for reward money + money: "&7 Nauda: &r&e [number]" + # Title for commands listing: + commands-title: "&7 Komandas:" + # Command listing element + command: " &7 - &e [command]" + # This part generates description text for levels object in all GUI's. + level: + lore: |- + [text] + [status] + [waiver] + [rewards] + # Status is either challengeLevel.unlockMessage or current status of the level + status: + # Status message for completed unrepeatable challenge + completed: "&2&l Izpildīts" + # Status message that contains number of completed challenges from all challenges + completed-challenges-of: |- + &2 Izpildīti &7&l [number] &r&2 no + &7&l [max] &r&2 uzdevumiem. + # Status message for locked level + locked: "&c&l Slēgts" + # Status message for locked level that will show missing challenge count. + missing-challenges: |- + &7 Nepieciešams izpildīt vēl [number] + &7 uzdevumus, lai atvērtu šo līmeni. + # Contains a text for waiver amount to unlock next level + waiver: |- + &7&l [number] uzdevums(-i) &r&7 var + &7 palikt neizpildīti, lai atvērtu + &7 nākošo līmeni. + # Contains a text generated inside [rewards] lore + rewards: + # [text] comes from challengeLevel.rewardText + lore: |- + &7&l Atlīdzības: + [text] + [items] + [experience] + [money] + [commands] + # Title that will be used if there are list of items for rewards + item-title: "&7 Priekšmeti:" + # Listing of an item that are rewards multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Text for reward experience + experience: "&7 Pieredze: &r&e [number]" + # Text for reward money + money: "&7 Nauda: &r&e [number]" + # Title for commands listing: + commands-title: "&7 Komandas:" + # Command listing element + command: " &7 - &e [command]" + # This part generates description for the Library Entry + library: + author: '&7 veidoja &e [author]' + version: '&7 Uzdevuma Versija: &e [version]' + lang: '&7 Valoda: &e [lang]' + gamemode: '&7 Priekš &e [gamemode]' + conversations: + # Prefix for messages that are send from server. + prefix: "&l&6 [BentoBox]: &r" + # List of strings that are valid for confirming input. (separated with ,) + confirm-string: "true, on, yes, confirm, y, valid, correct, jā, ja, apstiprinu, pareizi" + # List of strings that are valid for denying input. (separated with ,) + deny-string: "false, off, no, deny, n, invalid, incorrect, nē, ne, neapstiprinu, nepareizi" + # String that allows to cancel conversation. (can be only one) + cancel-string: "atcelt" + # List of strings that allows to exit conversation. (separated with ,) + exit-string: "cancel, exit, quit, atcelt, iziet" + # Message that is send to user when conversation is cancelled. + cancelled: "&c Saruna pārtraukta!" + # Message that appears when admin clicks on number editing button. + input-number: "&e Lūdzu ieraksti skaitli sarakstē!" + # Message that appears when admin clicks on seconds editing button. + input-seconds: "&e Lūdzu ieraksti sekunžu skaitu sarakstē!" + # Error message that is showed if user input a value that is not a number. + numeric-only: "&c Šis `[value]` nav skaitlis!" + # Error message that is showed if user input a number that is smaller or larger that allowed. + not-valid-value: "&c Ierakstītais skaitlis [value] nav derīgs. Skaitlis nedrīkst būt mazāks par [min] un lielāks par [max]!" + # Message that confirms user data removing. + user-data-removed: "&a Visi lietotāju dati priekš [gamemode] ir dzēsti." + # Message that asks confirmation for user data removing. + confirm-user-data-deletion: "&e Lūdzu apstiprini sarakstē (jā), ka vēlies dzēst lietotāju datus par [gamemode]." + # Message that confirms user data removing. + challenge-data-removed: "&a Visu uzdevumu dati priekš [gamemode] ir dzēsti." + # Message that asks confirmation for user data removing. + confirm-challenge-data-deletion: "&e Lūdzu apstiprini sarakstē (jā), ka vēlies dzēst uzdevumu datus par [gamemode]." + # Message that confirms user data removing. + all-data-removed: "&a Visi dati priekš [gamemode] ir dzēsti." + # Message that asks confirmation for user data removing. + confirm-all-data-deletion: "&e Lūdzu apstiprini sarakstē (jā), ka vēlies dzēst visus datus par [gamemode]." + # Message that asks user to write a name + write-name: "&e Lūdzu ieraksti sarakstē vārdu." + # Message that confirms new object creation. + new-object-created: "&a Jauns objekts priekš [gamemode] ir uztaisīts." + # Error message that sends that object cannot be created with a given name + object-already-exists: "&c Objekts ar &7 [id] &c jau eksistē. Izvēlies citu vārdu." + # Error message that sends information that challenge cannot be deployed. + invalid-challenge: "&c Uzdevums [challenge] satur nekorektus datus. Tas nevar tikt izlaists!" + # Message that confirms name changing + name-changed: "&a Nosaukums veiksmīgi mainīts." + # Message that appears after clicking + write-description: "&e Lūdzu raksti jauno aprakstu sarakstē un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful description change. + description-changed: "&a Apraksts vaiksmīgi mainīts." + # Message that appears when admin clicks on permission editing button. + write-permissions: "&e Lūdzu ieraksti nepieciešamās atļaujas sarakstē, katru savā rindā, and 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful permission updating. + permissions-changed: "&a Atļaujas veiksmīgi mainītas." + # Message that appears after clicking + write-reward-text: "&e Lūdzu ieraksti apbalvojuma tekstu un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful reward-text change. + reward-text-changed: "&a Apbalvojuma paraksts veiksmīgi mainīts." + # Message that appears after clicking + write-repeat-reward-text: "&e Lūdzu ieraksti atkārtotā apbalvojuma tekstu un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful repeat-reward-text change. + repeat-reward-text-changed: "&a Atkārtotā apbalvojuma paraksts veiksmīgi mainīts." + # Message that appears after clicking + write-reward-commands: "&e Lūdzu ieraksti apbalvojuma komandas katru savā rindā, un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful commands-text change. + reward-commands-changed: "&a Apbalvojuma komandas veiksmīgi mainīts." + # Message that appears after clicking + write-repeat-reward-commands: "&e Lūdzu ieraksti atkārtotā apbalvojuma komandas katru savā rindā, un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful repeat-commands-text change. + repeat-reward-commands-changed: "&a Atkārtotā apbalvojuma komandas veiksmīgi mainīts." + # Message that confirms user data removing. + challenge-removed: "&a Uzdevums [challenge] priekš [gamemode] ir dzēsts no datubāzes." + # Message that asks confirmation for user data removing. + confirm-challenge-deletion: "&e Lūdzu apstiprini (jā), ka vēlies dzēst [challenge] priekš [gamemode] no datubāzes." + # Message that confirms user data removing. + level-removed: "&a Līmenis [level] priekš [gamemode] ir dzēsts no datubāzes." + # Message that asks confirmation for user data removing. + confirm-level-deletion: "&e Lūdzu apstiprini (jā), ka vēlies dzēst [level] priekš [gamemode] no datubāzes." + # Message that appears when user clicks on library installation. + start-downloading: "&a Sāk lejupielādēt Uzdevumu Bibliotēku." + # Message that appears when writing multiline text. + written-text: "&a Ievadītais teksts:" + # Message that appears after importing library data into database. + confirm-data-replacement: "&e Lūdzu apstiprini (jā), ka vēlies dzēst esošos uzdevumus un aizstāt ar jauniem." + # Message that appears after successful data importing + new-challenges-imported: "&a Jaunie Uzdevumi ir veiksmīgi ielādēti priekš [gamemode]." + # Message that appears after admin clicks on database exporting button. + exported-file-name: "&e Lūdzu ieraksti eksportētā faila nosaukumu. (ieraksti 'atcelt', lai izietu)" + # Message that appears after successful database exporting to file. + database-export-completed: "&a Veiksmīgi eksportēti uzdevumi no [world]. Fails [file] ir izveidots." + # Message that appears if input file name is already taken. + file-name-exist: "&c Faila nosaukums '[id]' jau eksistē. Nevar pārrakstīt." + # Message that asks for search value input. + write-search: "&e Lūdzu ievadi meklēšanas vērtību. (vai 'atcelt', lai izietu)" + # Message that appears after updating search value. + search-updated: "&a Meklēšanas vērtība atjaunota." + titles: - challenge-subtitle: "[friendlyName]" - challenge-title: Veiksmīgi izpildīts uzdevums - level-subtitle: "[friendlyName]" - level-title: Veiksmīgi pabeigts līmenis -meta: - authors: - - BONNe + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. + # [friendlyName] will be replaced with challenge friendly name. + # [level] will be replaced with level friendly name. + # [rewardText] will be replaced with the challenge reward text. + challenge-title: 'Veiksmīgi Izpildīts' + challenge-subtitle: '[friendlyName]' + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. + # [friendlyName] will be replaced with level friendly name. + # [rewardText] will be replaced with the level reward text. + level-title: 'Veiksmīgi Izpildīts' + level-subtitle: '[friendlyName]' + + messages: + completed: '&2 Tu izpildīji uzdevumu [name] priekš [player]!' + already-completed: '&2 Šis uzdevums jau bija izpildīts!' + reset: '&2 Tu atjaunoji uzdevumu [name] priekš [player]!' + reset-all: '&2 Visi spēlētāja [player] uzdevumi ir atjaunoti!' + not-completed: '&2 Šis uzdevums vēl nav izpildīts!' + migrate-start: '&2 Sāk migrēt papildinājuma datus.' + migrate-end: '&2 Papildinājuma dati ir migrēti.' + migrate-not: '&2 Visi dati ir derīgi.' + start-downloading: '&5 Sāk lejupielādēt un importēt uzdevumus.' + you-completed-challenge: '&2 Tu izpildīji [value] &r&2 uzdevumu!' + you-repeated-challenge: '&2 Tu atkārtoti izpildīji [value] &r&2 uzdevumu!' + you-repeated-challenge-multiple: '&2 Tu atkārtoji [value] &r&2 uzdevumu [count] reizes!' + you-completed-level: '&2 Tu pabeidzi [value] &r&2 līmeni!' + name-has-completed-challenge: '&5 [name] pabeidza [value] &r&5 uzdevumu!' + name-has-completed-level: '&5 [name] pabeidza [value] &r&5 līmeni!' + load-skipping: '"[value]" jau eksistē - izlaiž' + load-overwriting: 'Pārraksta "[value]"' + load-add: 'Pievieno jaunu objektu: [value]' + errors: + no-name: '&c Trūkst uzdevuma nosaukums' + unknown-challenge: '&c Nezināms uzdevums' + not-valid-integer: |- + &c Ievadītais skaitlis "[value]" nau darīgs! Vērtībai jābūt no [min] līdz [max]. + not-deployed: '&c Uzdevums nav pieejams!' + not-on-island: '&c Tev jābūt uz salas, lai veiktu šo darbību!' + challenge-level-not-available: '&c Tu neesi atvēriz nepieciešamo līmeni, lai izpildītu šo uzdevumu.' + not-repeatable: '&c Šis uzdevums nav atkārtojams!' + wrong-environment: '&c Tu neesi pareizajā vidē!' + not-enough-items: '&c Tev nav pietiekoši daudz [items], lai pabeigtu uzdevumu!' + not-close-enough: '&c Tev tuvumā nav pietiekoši daudz nepieciešamie bloki vai radības.' + you-still-need: '&c Tev vēl nepieciešams [amount] x [item] lietas' + missing-addon: '&c Nav iespējams izpildīt šo uzdevumu: trūkst nepieciešamais papildinājums vai plugins.' + incorrect: '&c Nav iespējams izpildīt uzdevumu. Prasības nav korektas.' + not-enough-money: '&c Tev ir nepieciešama [value] nauda kontā, lai izpildītu uzdevumu!' + not-enough-experience: '&c Tev ir nepieciešama [value] pieredze, lai izpildītu uzdevumu!' + island-level: '&c Tavai salai jāsasniedz [number] līmeni, lai izpildītu uzdevumu!' + no-load: '&c Kļūda: Nevar ielādēt failu. [message]' + load-error: '&c Kļūda: Nevar ielādēt [value].' + no-rank: "&c Tavs ranks nav pietiekoši augsts, lai veiktu šo darbību." + cannot-remove-items: '&c Dažus priekšmetus neizdevās izņemt no tava inventāra!' + exist-challenges-or-levels: '&c Šajā pasaulē jau existē uzdevumi, nevar turpināt!' + no-challenges: '&c Uzdevumi nav izveidoti šajā pasaulē!' + no-challenges-admin: '&c Uzdevumi nav izveidoti šajā pasaulē! Izmanto &5 /[command], &c lai tos pievienotu!' + missing-arguments: '&c Pietrūkst komandas parametri.' + no-multiple-permission: "&c Tev nav atļaujas, lai pildītu uzdevumu vairākas reizes." + invalid-level: "&c Līmenis [level] satur nekorektus datus. Tas var tikt ielādēts nekorekti!" + invalid-challenge: "&c Uzdevums [challenge] satur nekorektus datus. Tas var tikt ielādēts nekorekti!" + no-library-entries: "&c Nevar atrast bibliotēkas ierakstus. Nav ko rādīt." + not-hooked: "&c Uzdevumu Papildinājumam neizdevās atrast Spēles Režīmu." + timeout: "&c Šim uzdevumam ir uzstādīts [timeout] taimauts starp izpildēm. Tev vēl ir jāgaida [wait-time], lai varētu pildīt uzdevumu." +# # Showcase for manual material translation +# materials: +# # Names should be lowercase. +# cobblestone: "Mūrakmens" +# # Also supports descriptions. +# stone: +# name: "Akmens" +# description: "" +# item-stacks: +# # Non-specific item meta translations. +# # TYPE is the item type +# # META is a content of item meta. +# generic: "[type] [meta]" +# # Non-specific meta translations. Will replace [meta] +# meta: +# upgraded: "Uzlabots" +# extended: "Pagarināts" +# potion-meta: "&e [type] [upgraded] [extended]" +# # Be aware, enchants are always listed below item in separate line. +# enchant-meta: " &7 - &e [type] [level]" +# skull-meta: ": &e [player-name]" +# book-meta: "&e [title] [author]" +# # Custom Enchantment Translation. +# enchant: +# menting: "Lāpīšana" +# unbreaking: "Nelūztošs" +# # Custom Potion Translation. +# potion-effect: +# water_breathing: "Zemūdens Elpošanas" +# # You can also create specific item translations +# # Like translate all potions. +# potion: +# # This will overwrite generic translation. +# name: "[type] [upgraded] [extended]" +# # Type is either specific translation or potion effect. +# water_breathing: "Zemūdens Elpošanas Dzira" +# stone_shovel: +# # This will mean that only stone shovels will not show +# # meta information. +# name: "[type]" protection: flags: CHALLENGES_ISLAND_PROTECTION: - description: "&5&oPārslēdz kurš var|&5&opildīt uzdevumus" + description: |- + &5&o Pārslēdz kurš var + &5&o pildīt uzdevumus name: Uzdevumu izpildes aizsardzība CHALLENGES_WORLD_PROTECTION: description: |- - &5&oĻauj pārslēgt vai|&5&ospēlētājam ir nepieciešams - &5&obūt uz salais, - &5&olai pildītu uzdevumus. + &5&o Ļauj pārslēgt vai + &5&o spēlētājam ir nepieciešams + &5&o būt uz salais, + &5&o lai pildītu uzdevumus. hint: Uzdevumus nevar pildīt ārpus salas name: Uzdevumu salas ierobežosāna -version: 11 +version: 12 diff --git a/src/main/resources/locales/pl.yml b/src/main/resources/locales/pl.yml index 46a7590..d4b829c 100644 --- a/src/main/resources/locales/pl.yml +++ b/src/main/resources/locales/pl.yml @@ -45,10 +45,10 @@ challenges: gui: title: admin: - gui-title: "& aChallenges Administrator" + gui-title: "&a Challenges Administrator" edit-challenge-title: " edytuj wyzwanie" edit-level-title: Poziom edycji - settings-title: "&aEdytuj ustawienia" + settings-title: "&a Edytuj ustawienia" choose-challenge-title: " Wybierz wyzwanie" choose-level-title: i a Wybierz poziom choose-user-title: Wybierz odtwarzacz @@ -64,11 +64,11 @@ challenges: edit-text-fields: i edytuj pola tekstowe library-title: i a Biblioteki do pobrania lore-add: i Dodaj element wiedzy - lore-remove: "& aUsuń element wiedzy" + lore-remove: "&a Usuń element wiedzy" lore-edit: i edytuj Lore type-select: i a Wybierz typ wyzwania challenges: I 6 Wyzwania - game-modes: "& 6 Wybierz GameMode" + game-modes: "&6 Wybierz GameMode" multiple-complete: I 6 Ile razy? buttons: admin: @@ -132,8 +132,8 @@ challenges: library: Biblioteka internetowa download: Pobierz biblioteki type: - island: "& 6 Typ wyspy" - inventory: "& 6 Typ zapasów" + island: "&6 Typ wyspy" + inventory: "&6 Typ zapasów" other: I 6 Inne typy import: Importuj wyzwania ASkyblock settings: Edytuj ustawienia @@ -270,8 +270,8 @@ challenges: mode-in-world: Gracze w świecie GameMode. mode-with-island: Gracze, którzy mają wyspę w świecie GameMode. visibility-mode: Pokaż / ukryj niewykorzystane wyzwania. - edit-text-line: "& 6 Edytuj wiadomość tekstową!" - add-text-line: "& 6 Dodaj nową wiadomość tekstową!" + edit-text-line: "&6 Edytuj wiadomość tekstową!" + add-text-line: "&6 Dodaj nową wiadomość tekstową!" title-enable: Włącz / wyłącz wiadomość tytułową, która będzie wyświetlana graczom po ukończeniu wyzwania. title-showtime: Zmień, jak długo wiadomości tytułowe będą widoczne dla odtwarzacza. @@ -450,17 +450,17 @@ challenges: the-end: "- Koniec" nether: "- Nether" normal: "- Overworld" - entity: "- [podmiot]: [liczba]" - block: "- [blok]: [liczba]" - permission: "- [pozwolenie]" - item: "- [liczba] x [pozycja]" + entity: "- [entity]: [count]" + block: "- [block]: [count]" + permission: "- [permission]" + item: "- [count] x [item]" item-meta: "([meta])" - item-enchant: "- [enchant] [poziom]" - command: "- [Komenda]" - level-unlocked: Kliknij, aby zobaczyć wyzwania [poziomu]! - level-locked: Ukończ [liczyć] więcej [poziomów] wyzwań, aby odblokować ten poziom! - increase-by: "& aZwiększ liczbę ukończeń o [wartość]" - reduce-by: "& c Zmniejsz liczbę ukończeń o [wartość]" + item-enchant: "- [enchant] [level]" + command: "- [command]" + level-unlocked: Kliknij, aby zobaczyć wyzwania [level]! + level-locked: Ukończ [count] więcej [level] wyzwań, aby odblokować ten poziom! + increase-by: "&a Zwiększ liczbę ukończeń o [value]" + reduce-by: "&c Zmniejsz liczbę ukończeń o [value]" visibility: hidden: Widoczne są tylko wdrożone wyzwania. visible: Wszystkie wyzwania są widoczne dla wszystkich @@ -469,63 +469,63 @@ challenges: island: i zdobywaj bloki lub moby wokół gracza other: i pytaj o rzeczy z innych wtyczek / dodatków inventory: i zdobywaj przedmioty w ekwipunku gracza - current-value: "&6Aktualna wartość e: [value]." + current-value: "&6 Aktualna wartość e: [value]." challenge-description: completed-times-of: Ukończone [donetimes] z [maxtimes] maxed-reached: Ukończone [donetimes] z [maxtimes] completed-times: Ukończone [donetimes] - objects-close-by: "& cWszystkie wymagane bloki i byty muszą znajdować się + objects-close-by: "&c Wszystkie wymagane bloki i byty muszą znajdować się blisko ciebie na twojej wyspie!" - warning-entities-kill: "& c Wszystkie wymagane jednostki zostaną zabite + warning-entities-kill: "&c Wszystkie wymagane jednostki zostaną zabite po ukończeniu tego wyzwania!" - warning-blocks-remove: "& cWszystkie wymagane bloki zostaną usunięte po + warning-blocks-remove: "&c Wszystkie wymagane bloki zostaną usunięte po ukończeniu tego wyzwania!" - not-repeatable: "& c To wyzwanie nie jest powtarzalne!" - experience-reward: "& 6Exp nagroda: [wartość]" - money-reward: "& Nagroda pieniężna: $ [wartość]" - required-experience: "& 6 Wymagany exp: [wartość]" - required-money: 'I 6 Wymagane pieniądze: [wartość]' - required-island-level: "& 6 Wymagany poziom wyspy: [wartość]" + not-repeatable: "&c To wyzwanie nie jest powtarzalne!" + experience-reward: "&6 Exp nagroda: [value]" + money-reward: "& Nagroda pieniężna: $[value]" + required-experience: "&6 Wymagany exp: [value]" + required-money: 'I 6 Wymagane pieniądze: [value]' + required-island-level: "&6 Wymagany poziom wyspy: [value]" environment: 'Wymagane środowiska:' reward-items: 'I 6 przedmiotów dodatkowych:' - reward-commands: "& 6 Polecenia dodatkowe:" + reward-commands: "&6 Polecenia dodatkowe:" required-items: 'Wymagane rzeczy:' required-entities: 'Wymagane podmioty:' required-blocks: 'Wymagane bloki:' - level: "& fLevel: [poziom]" + level: "&f Level: [level]" completed: i b Ukończone - warning-items-take: "& c Wszystkie wymagane przedmioty są pobierane z ekwipunku + warning-items-take: "&c Wszystkie wymagane przedmioty są pobierane z ekwipunku po ukończeniu tego wyzwania!" rewards-title: "& Nagrody:" level-description: - experience-reward: "& 6Exp nagroda: [wartość]" - money-reward: "& Nagroda pieniężna: $ [wartość]" + experience-reward: "&6 Exp nagroda: [value]" + money-reward: "& Nagroda pieniężna: $ [value]" reward-items: 'I 6 przedmiotów dodatkowych:' - reward-commands: "& 6 Polecenia dodatkowe:" - waver-amount: I 6 wyzwań [wartość] można pominąć, aby odblokować następny poziom. + reward-commands: "&6 Polecenia dodatkowe:" + waver-amount: I 6 wyzwań [value] można pominąć, aby odblokować następny poziom. completed: i b Ukończone - completed-challenges-of: "& 3 Ukończyłeś [liczbę] spośród [maks.] Wyzwań + completed-challenges-of: "&3 Ukończyłeś [number] spośród [max] Wyzwań na tym poziomie." item-description: - item: "- [liczba] x [pozycja]" + item: "- [count] x [item]" item-meta: "([meta])" - item-enchant: "- [enchant] [poziom]" - item-name: "[imię]" + item-enchant: "- [enchant] [level]" + item-name: "[name]" item-lore: 'Przedmiot:' - book-meta: "[tytuł] autor: [autor]" - recipe-count: "[liczba] przepisów" - armor-color: "[kolor]" - potion-type-extended-upgraded: Rozszerzone i zaktualizowane [nazwa] - potion-type-upgraded: Ulepszony [nazwa] - potion-type-extended: Rozszerzone [nazwa] - potion-type: "[imię]" + book-meta: "[title] autor: [author]" + recipe-count: "[count] przepisów" + armor-color: "[color]" + potion-type-extended-upgraded: Rozszerzone i zaktualizowane [name] + potion-type-upgraded: Ulepszony [name] + potion-type-extended: Rozszerzone [name] + potion-type: "[name]" custom-effects: 'Efekty niestandardowe:' - potion-effect: "[efekt] x [wzmacniacz] dla [czas trwania] t" - skull-owner: "[właściciel]" - egg-meta: "[tłum]" + potion-effect: "[effect] x [amplifier] dla [duration] t" + skull-owner: "[owner]" + egg-meta: "[mob]" fish-meta: "[body-color] with [pattern-color] [pattern]" questions: - prefix: "& 2 [SERWER]:" + prefix: "&2 [SERWER]:" admin: number: Wpisz liczbę na czacie i naciśnij enter. challenge-name: Wpisz nazwę wyświetlaną na czacie dla bieżącego wyzwania. @@ -533,104 +533,101 @@ challenges: unique-id: Wpisz unikalny identyfikator obiektu i naciśnij klawisz Enter. titles: challenge-title: Zakończone sukcesem - challenge-subtitle: "[przyjazne imię]" + challenge-subtitle: "[friendlyName]" level-title: Zakończone sukcesem - level-subtitle: "[przyjazne imię]" + level-subtitle: "[friendlyName]" messages: admin: you-added: 'Dodałeś do wyzwań ' - challenge-created: "[wyzwanie]&r stwórz" - completed: "&2Ukończył wyzwanie [name] [player]!" + challenge-created: "[challenge]&r stwórz" + completed: "&2 Ukończył wyzwanie [name] [player]!" already-completed: "&2 Ukończyłeś to wyzwanie!" - reset: "&2Zresetowałeś wyzwanie [name] dla [player]!" - reset-all: "& 2 Wszystkie wyzwania [gracza] zostały zresetowane!" + reset: "&2 Zresetowałeś wyzwanie [name] dla [player]!" + reset-all: "&2 Wszystkie wyzwania [player] zostały zresetowane!" not-completed: |- - &2To wyzwanie nie zostało ukończone! + &2 To wyzwanie nie zostało ukończone! Sprawdz czy wszystko wykonałeś poprawnie - migrate-start: "&2Rozpoczęto migrację wyzwań addon data." - migrate-not: "&2Wszystkie dane są poprawne." - start-downloading: "& 5 Rozpoczęcie pobierania i importowania biblioteki + migrate-start: "&2 Rozpoczęto migrację wyzwań addon data." + migrate-not: "&2 Wszystkie dane są poprawne." + start-downloading: "&5 Rozpoczęcie pobierania i importowania biblioteki wyzwań." - migrate-end: "& 2 Wyzwania dotyczące danych dodatkowych zaktualizowano do + migrate-end: "&2 Wyzwania dotyczące danych dodatkowych zaktualizowano do nowego formatu." hit-things: Kliknij rzeczy, aby dodać je do listy wymaganych rzeczy. Po zakończeniu kliknij prawym przyciskiem myszy. - complete-wipe: "& c Mam nadzieję, że masz kopie zapasowe, ponieważ właśnie + complete-wipe: "&c Mam nadzieję, że masz kopie zapasowe, ponieważ właśnie skasowałeś wszystkie bazy danych Wyzwań Addon!" challenge-wipe: I c Mam nadzieję, że masz kopie zapasowe, ponieważ właśnie skasowałeś wszystkie Wyzwania i ich poziomy! - players-wipe: "& c Mam nadzieję, że masz kopie zapasowe, ponieważ po prostu + players-wipe: "&c Mam nadzieję, że masz kopie zapasowe, ponieważ po prostu usuwasz wszystkie ukończone wyzwania gracza!" - you-completed-challenge: "& 2Ukończono [wartość] & r & 2 wyzwanie!" - you-repeated-challenge: "& 2 Powtórzyłeś [wartość] & r & 2 wyzwanie!" - you-repeated-challenge-multiple: "& 2 Powtórzyłeś [wartość] & r & - 2challenge [liczba] razy!" - you-completed-level: "& 2Ukończono [wartość] & r & 2 poziom!" - name-has-completed-challenge: "& 5 [nazwa] zakończyła [wartość] & r & - 5 wyzwanie!" - name-has-completed-level: "& 5 [nazwa] uzupełniła [wartość] & r & - 5 poziom!" + you-completed-challenge: "&2 Ukończono [value] &r&2 wyzwanie!" + you-repeated-challenge: "&2 Powtórzyłeś [value] &r&2 wyzwanie!" + you-repeated-challenge-multiple: "&2 Powtórzyłeś [value] &r&2 challenge [count] razy!" + you-completed-level: "&2 Ukończono [value] &r&2 poziom!" + name-has-completed-challenge: "&5 [name] zakończyła [value] &r&5 wyzwanie!" + name-has-completed-level: "&5 [name] uzupełniła [value] &r&5 poziom!" import-levels: Rozpocznij importowanie poziomów import-challenges: Rozpocznij importowanie wyzwań no-levels: 'Ostrzeżenie: Brak poziomów zdefiniowanych w challenge.yml' - import-number: Zaimportowane [liczba] wyzwań + import-number: Zaimportowane [count] wyzwań load-skipping: '"[value]" Istnieje -' - load-overwriting: Nadpisywanie „[wartość]” - load-add: 'Dodanie nowego obiektu: [wartość]' + load-overwriting: Nadpisywanie „[value]” + load-add: 'Dodanie nowego obiektu: [value]' defaults-file-overwrite: defaults.json istnieje. zostanie nadpisany. - defaults-file-completed: Plik defaults.json jest wypełniony wyzwaniami z [świata]! + defaults-file-completed: Plik defaults.json jest wypełniony wyzwaniami z [world]! errors: no-name: "&cNieprawidłowa nazwa wyzwania" unknown-challenge: "&cNieznane wyzwanie" - unique-id: "& cUniqueID „[id]” jest nieprawidłowy." - wrong-icon: "& cDany materiał „[wartość]” jest nieprawidłowy i nie może być + unique-id: "&c UniqueID „[id]” jest nieprawidłowy." + wrong-icon: "&c Dany materiał „[value]” jest nieprawidłowy i nie może być używany jako ikona." - not-deployed: "& cChallenge nie został wdrożony!" + not-deployed: "&c Challenge nie został wdrożony!" not-on-island: "&cMusisz być na swojej wyspie by to zrobic!" not-repeatable: "&cTo wyzwanie możesz wykonać tylko raz!" - not-enough-items: "&cNie posiadasz wystarczająco [przedmiotów] do zakończenia + not-enough-items: "&cNie posiadasz wystarczająco [items] do zakończenia tego wyzwania!" not-close-enough: "&cmusisz stać w środku [number] przy wszystkich wymaganych blokach." you-still-need: "&cPotrzebujesz nadal [amount] x [item]" - not-enough-money: "&cAby ukończyć wyzwanie, musisz mieć [walutę] na swoim koncie." - import-no-file: "& cNie mogłem znaleźć pliku challenge.yml do importowania!" - no-load: "&cBłąd: Nie można załadować challenges.yml. [wiadomość]" - load-error: "& cError: Nie można załadować [wartość]." - defaults-file-exist: "& cdefaults.json już istnieje. Użyj trybu zastępowania, + not-enough-money: "&cAby ukończyć wyzwanie, musisz mieć [value] na swoim koncie." + import-no-file: "&c Nie mogłem znaleźć pliku challenge.yml do importowania!" + no-load: "&cBłąd: Nie można załadować challenges.yml. [message]" + load-error: "&c Error: Nie można załadować [value]." + defaults-file-exist: "&c defaults.json już istnieje. Użyj trybu zastępowania, aby go zastąpić!" - defaults-file-error: "& c Wystąpił błąd podczas tworzenia pliku defaults.json! + defaults-file-error: "&c Wystąpił błąd podczas tworzenia pliku defaults.json! Sprawdź konsolę!" - missing-arguments: "& cCommand brakuje argumentów." - wrong-environment: "& c Jesteś w złym środowisku!" - missing-addon: "& cNie można ukończyć wyzwania: brakuje wymaganego dodatku + missing-arguments: "&c Command brakuje argumentów." + wrong-environment: "&c Jesteś w złym środowisku!" + missing-addon: "&c Nie można ukończyć wyzwania: brakuje wymaganego dodatku lub wtyczki." - exist-challenges-or-levels: "& cChallenges już istnieją w twoim świecie. Nie + exist-challenges-or-levels: "&c Challenges już istnieją w twoim świecie. Nie można kontynuować!" - no-challenges: "& cChallenges nie są jeszcze zaimplementowane na tym świecie!" - no-challenges-admin: "& cChallenges nie są jeszcze zaimplementowane na tym - świecie! Użyj & 5 / [polecenie] i c, aby je dodać!" - missing-level: "& cChallenge Poziom [poziom] nie jest zdefiniowany w bazie + no-challenges: "&c Challenges nie są jeszcze zaimplementowane na tym świecie!" + no-challenges-admin: "&c Challenges nie są jeszcze zaimplementowane na tym + świecie! Użyj &5 /[command] i c, aby je dodać!" + missing-level: "&c Challenge Poziom [level] nie jest zdefiniowany w bazie danych. Może to powodować błędy!" - no-multiple-permission: "& c Nie masz uprawnień do wielokrotnego wykonania + no-multiple-permission: "&c Nie masz uprawnień do wielokrotnego wykonania tego wyzwania jednocześnie." - not-a-integer: "& c Podana wartość „[wartość]” nie jest liczbą całkowitą!" - challenge-level-not-available: "& c Nie odblokowałeś wymaganego poziomu, aby + not-a-integer: "&c Podana wartość „[value]” nie jest liczbą całkowitą!" + challenge-level-not-available: "&c Nie odblokowałeś wymaganego poziomu, aby ukończyć to wyzwanie." - incorrect: "& cNie można ukończyć wyzwania: Wymagania są niepoprawne." - not-enough-experience: "& c Konieczne jest posiadanie [wartość] EXP, aby ukończyć + incorrect: "&c Nie można ukończyć wyzwania: Wymagania są niepoprawne." + not-enough-experience: "&c Konieczne jest posiadanie [value] EXP, aby ukończyć to wyzwanie." - island-level: "& cTa wyspa musi mieć poziom [liczba] lub wyższy, aby ukończyć + island-level: "&c Ta wyspa musi mieć poziom [count] lub wyższy, aby ukończyć to wyzwanie!" - no-rank: "& c Nie masz wystarczająco wysokiej rangi, aby to zrobić." - cannot-remove-items: "& c Niektórych przedmiotów nie można usunąć z ekwipunku!" + no-rank: "&c Nie masz wystarczająco wysokiej rangi, aby to zrobić." + cannot-remove-items: "&c Niektórych przedmiotów nie można usunąć z ekwipunku!" not-valid-integer: |- &c Podana liczba całkowita "[value]"jest nieprawidłowa wartość powinna być między [min] i [max]. - invalid-level: "& c Poziom [poziom] zawiera nieprawidłowe dane. Nie zostanie załadowany + invalid-level: "&c Poziom [level] zawiera nieprawidłowe dane. Nie zostanie załadowany z bazy danych!" - invalid-challenge: "& c Wyzwanie [wyzwanie] zawiera nieprawidłowe dane. Nie zostanie + invalid-challenge: "&c Wyzwanie [challenge] zawiera nieprawidłowe dane. Nie zostanie załadowany z bazy danych!" protection: flags: @@ -643,11 +640,10 @@ protection: name: Wyzwania Ograniczenia wyspy hint: Żadnych wyzwań poza wyspą description: |- - & 5 i o Włącz / wyłącz + &5 i o Włącz / wyłącz Oraz 5 i wymagania dla graczy do I 5 i przestrzegaj ich wyspy I 5 i ukończ wyzwanie. version: 11 meta: - authors: - - BONNe + authors: [] diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index 690e0e9..0c2d673 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -1,471 +1,582 @@ ---- -challenges: - commands: - admin: - complete: - parameters: " " - description: 通过指令完成挑战 - defaults: - description: 显示导入/导出插件自带挑战的子指令 - parameters: "[command]" - import: - parameters: "[overwrite]" - description: 从 challenges.yml 文件中导入挑战|参数覆盖意味着具有相同 ID 的挑战或等级将被覆盖。 - main: - description: 打开管理员菜单 - reload: - description: 重载挑战组件 - parameters: "[hard]" - show: - description: 在聊天中列出当前世界的所有挑战 - defaults-import: - description: 导入系统自带挑战 - defaults-generate: - description: 将现有的挑战导出到default.json文件中 - parameters: "[overwrite] - 允许覆盖已有文件" - reset: - description: 通过指令重置玩家挑战。若 "challenge_id" 参数设为 all,则将重置该玩家的所有挑战。 - parameters: " " - migrate: - description: 将参考当前游戏模式世界的挑战数据迁移到新的0.8.0存储格式。 - user: - complete: - description: 通过指令完成挑战 - parameters: " [count]" - main: - description: 打开挑战菜单 - errors: - cannot-remove-items: "&c有些物品无法从你的背包中删除!" - challenge-level-not-available: "&c你没有解锁挑战级别来完成这个挑战。" - defaults-file-error: "&c创建defaults.json文件时出错! 检查控制台!" - defaults-file-exist: "&cdefaults.json已经存在。 使用覆盖模式替换它!" - exist-challenges-or-levels: "&c在你的世界里已经存在挑战。 无法继续!" - import-no-file: "&c找不到challenge.yml文件导入!" - incorrect: "&c无法完成挑战。 要求不正确。" - island-level: "&c你的岛屿等级需要达到[number]才能完成挑战" - load-error: "&c错误:无法加载 [value]." - missing-addon: "&c无法完成挑战。 缺少必需的插件或组件。" - missing-arguments: "&c命令缺少参数." - missing-level: "&c挑战级别[level]未在数据库中定义. 可能会出现错误!" - no-challenges: "&c当前世界没有实施挑战!" - no-challenges-admin: "&c当前世界没有实施挑战!你应该使用 &5/[label] challenges &c添加他们!" - no-load: "&c错误:无法加载challenge.yml。 [message]" - no-name: "&c缺少挑战名称" - no-rank: "&c你没有等级可以做到这一点." - not-a-integer: "&c给定值“[value]”不是整数!" - not-close-enough: "&c你必须站在拥有[number]个任务需求方块的旁边." - not-deployed: "&c未开启挑战!" - not-enough-items: "&c你没有足够的[items]来完成这个挑战!" - not-enough-money: "&c您的帐户必须有[value]金钱才能完成挑战。." - not-on-island: "&c你必须在你的岛上做到这一点!" - not-repeatable: "&c这个挑战是不可重复的!" - not-valid-integer: "&c给定整数“[value]”无效!|值应介于[min]和[max]之间。" - unique-id: '&cUniqueID "[id]" 无效.' - unknown-challenge: "&c未知挑战" - wrong-environment: "&c你在错误的环境中!" - wrong-icon: "&c给定材料“[value]”无效且不能用作图标。" - you-still-need: "&c你还需要 [amount] x [item]" - not-enough-experience: "&c必须有[value]经验才能完成挑战" - no-multiple-permission: "&c你无权一次完成挑战多次" - gui: - buttons: - admin: - accept: 接受 - add: 添加 - add-challenge: 添加挑战 - broadcast: 成功任务后发出广播 - cancel: 取消 - challenge-lore: 挑战介绍 - challenges: 挑战 - clear: 清空 - complete: 完成某个玩家的挑战 - complete-wipe: 抹除插件数据库! - create-challenge: 创建新的挑战 - create-level: 创建新的挑战等级 - decline: 拒绝 - default-export: 导出现有挑战 - default-import: 导入默认挑战 - default-locked-icon: 等级图标锁 - delete-challenge: 删除挑战 - delete-level: 删除挑战等级 - deployment: 是否开启该挑战 - description: 介绍 - edit-challenge: 编辑挑战 - edit-level: 编辑挑战等级 - environment: 环境 - free-at-top: 免费挑战优先排列 - glow: 完成任务后发光 - gui-mode: 挑战GUI - gui-view-mode: 显示所有GameModes - history-store: 挑战历史 - icon: 图标 - increase: "+" - input: 输入 - input-mode: 切换输入模式 - level-lore: 等级介绍 - locked-icon: 图标锁 - multiply: "*" - name: 挑战名称 - number: "[number]" - order: 顺序 - properties: 属性 - reduce: "-" - remove-blocks: 完成任务后是否删除方块 - remove-challenge: 删除挑战 - remove-completed: 完成任务后删除图标 - remove-empty: 删除空栏 - remove-entities: 完成任务后是否杀死实体 - remove-experience: 完成任务后是否删除经验值 - remove-items: 完成任务后是否删除物品 - remove-money: 完成任务后是否删除金钱 - remove-on-complete: 完成后删除该挑战 - remove-selected: 删除选定 - repeatable: 是否可重复挑战 - repeat-count: 最大时间 - repeat-reward-commands: 重复指令奖励 - repeat-reward-experience: 重复经验奖励 - repeat-reward-items: 重复物品奖励 - repeat-reward-money: 重复金钱奖励 - repeat-reward-text: 重复奖励信息 - required-blocks: 检测的方块 - required-entities: 检测的实体 - required-experience: 需要的经验值 - required-items: 需要的物品 - required-level: 需要的岛屿等级 - required-money: 需要的金钱 - required-permissions: 需要的权限 - requirements: 要求 - reset: 重置某个玩家的挑战 - reset-on-new: 在新的岛屿上重置 - reward-commands: 指令奖励 - reward-experience: 经验奖励 - reward-items: 物品奖励 - reward-money: 金钱奖励 - rewards: 奖励 - reward-text: 奖励信息 - save: 保存 - search-radius: 搜索半径 - set: "=" - settings: 编辑设定 - show-eggs: 切换视图模式 - title-enable: 显示标题 - title-showtime: 标题显示时间 - toggle-user-list: 用户列表 - value: 值 - waiver-amount: 豁免金额 - import: 导入ASkyBlock挑战 - line-length: 物品Lore行长度 - history-lifespan: 历史生命周期 - island-store: 逐岛存储 - library: 网络库 - download: 已下载的挑战库 - challenge-wipe: 清空挑战数据库 - players-wipe: 清空用户数据库 - visibility-mode: 挑战可见性模式 - type: - island: "&6岛屿类型" - inventory: "&6物品栏类型" - other: "&6其他类型" - next: 下一页 - previous: 上一页 - return: 返回上一级 - value: 完成 - increase: 增加 - reduce: 减少 - challenge-description: - completed: "&B已完成" - completed-times: 已完成 [donetimes] - completed-times-of: '完成次数: [donetimes] 上限: [maxtimes]' - environment: '所需实体:' - experience-reward: "&6经验奖励: [value]" - level: "&F挑战级别: [level]" - maxed-reached: '完成次数: [donetimes] 上限: [maxtimes]' - money-reward: "&6金钱奖励: $[value]" - not-repeatable: "&c该挑战不可重复!" - objects-close-by: "&c任务需求的方块/生物不能离你太远!(超出侦测范围)" - required-blocks: '所需方块:' - required-entities: '所需实体:' - required-experience: "&6所需经验: [value]" - required-island-level: "&6所需岛屿等级: [value]" - required-items: '所需物品:' - required-money: "&6所需金钱: $[value]" - reward-commands: "&6指令奖励:" - reward-items: "&6物品奖励:" - warning-blocks-remove: "&c完成此挑战后,该挑战需要的方块将会被清空" - warning-entities-kill: "&c完成此挑战后,该挑战需要的生物将会被清空" - warning-items-take: "&c完成此挑战后,该挑战的需要物品将会被清空" - descriptions: - admin: - add-challenge: 将现有挑战添加到当前挑战级别 - add-text-line: "&6 添加新的文本!" - broadcast: 允许/禁止,当玩家完成第一次挑战后向全服玩家广播 - cancel: 取消任何操作并返回上一级菜单 - challenge-lore: 修改挑战介绍的哪些目标是可见的 - challenges: 管理挑战级别 (添加/删除). - click-to-edit: "&4点击此处编辑输入." - complete: 为某个玩家完成某个挑战|玩家无法获得完成奖励。 - complete-wipe: 清空插件数据库中的所有挑战,包括玩家的挑战数据! - create-challenge: 添加新挑战。|默认情况下,它将在免费挑战列表中。 - create-level: 添加新的挑战等级。 - default-export: 将现有的挑战导出至 defaults.json 文件中. - default-import: 导入插件自带挑战 - default-locked-icon: 更改默认锁定级别图标。|此级别可以覆盖此选项。 - delete-challenge: 删除某个挑战 - delete-level: 删除某个挑战等级 - deployment: 查看某玩家已完成的挑战 - description: 编辑介绍 - edit-challenge: 编辑某个挑战设定 - edit-level: 编辑某个挑战等级设定 - edit-text-line: "&6编辑文本!" - environment: 改变挑战运作的环境。 - free-at-top: 改变免费挑战的位置,如果为true免费挑战会放在前排,如果为false免费挑战将放在后排 - glow: 允许/禁用,在已完成的挑战中加上附魔效果 - gui-mode: 启用/禁用单一挑战GUI。|&2要求服务器重启。 - gui-view-mode: 如果玩家输入/challenges,菜单应该显示GameModes还是挑战 - icon-challenge: 将在此挑战的GUI面板中显示的图标。 - icon-level: 将在此级别的GUI面板中显示的图标。 - import: 导入ASkyblock挑战。|右键单击它启用/禁用覆盖模式。|将Challenges.yml放在./BentoBox/addons/Challenges文件夹中。 - increase: 增加操作。 单击数字将增加所选数字的值。 - input: 打开文本字段输入 - input-mode: 在聊天和铁砧输入模式之间切换。 - island-store: 启用/禁用挑战每个岛的数据存储。如果启用此选项,整个岛屿团队的挑战进度将是相同的。|不会在点击时转换数据。进展将会失败。 - level-lore: 修改挑战级别介绍的哪些目标是可见的 - locked-icon: 如果级别被锁定,将在GUI面板中显示的图标。 - mode-in-world: 属于游戏模式中的世界的玩家. - mode-online: 目前在线的玩家 - mode-with-island: 属于游戏模式中的岛屿的玩家. - multiply: 乘法运算。 单击数字会将值乘以所选数字。 - name-challenge: 修改挑战名称 - name-level: 修改挑战等级名称 - order: 更改挑战顺序 - properties: 修改常规属性 - reduce: 减少操作。 单击数字将减少所选数字的值。 - remove-blocks: 是否在挑战结束后删除任务需求方块(替换为空气) - remove-challenge: 从当前级别删除挑战 - remove-completed: 允许/禁止,在玩家挑战列表中隐藏已完成的挑战 - remove-entities: 是否在挑战结束后删除挑战需要的实体 - remove-experience: 是否在玩家完成挑战后删除任务需求的经验值 - remove-items: 是否在完成挑战后删除玩家背包中的挑战需求物品 - remove-money: 是否在玩家完成挑战后删除任务需求的金钱.|&c需要经济前置. - remove-on-complete: 在玩家完成挑战后是否删除该挑战图标于任务面板中 - remove-selected: 删除所选目标|您可以使用鼠标右键选择目标 - repeatable: 挑战是否可以重复 - repeat-count: 重复挑战的次数,如果设置为0则为无限制 - repeat-reward-experience: 重复挑战完成后的经验奖励. - repeat-reward-items: '重复挑战完成后的物品奖励.|物品:' - repeat-reward-money: 重复挑战完成后的金钱奖励.|&c需要经济前置. - repeat-reward-text: 重复挑战完成后的信息 - required-blocks: 修改挑战需要的方块.|方块:| - required-entities: 修改挑战需要的实体.|实体:| - required-experience: 玩家需要有该项目所设置的经验值才可以完成挑战 - required-items: '玩家背包中需要有以下物品才能完成挑战|物品:' - required-level: 玩家需要岛屿等级达到该项目所设置的等级才能完成挑战.|&c需要 Level 组件. - required-money: 玩家需要有该项目所设置的金钱才能完成挑战.|&c需要经济前置. - required-permissions: '玩家需要具有以下权限才能完成挑战|权限:' - requirements: 修改挑战需求 - reset: 重置已完成的玩家挑战。|右键单击启用/禁用重置所有功能。 - reset-on-new: 允许/禁止,当玩家重置/踢出岛屿后都会重置挑战 - reward-commands: '设置完成任务后的指令奖励.|指令:' - reward-experience: 设置完成任务后的经验奖励. - reward-items: '设置完成任务后的物品奖励.|物品:' - rewards: 修改挑战奖励 - reward-text: 设置完成任务后的奖励信息 - reward-text-level: 完成某挑战级别所有挑战后发送给玩家的信息 - save: 保存并返回上一级菜单 - search-radius: 玩家完成挑战时检测实体/方块的范围(半径) - selected: 已选中 - set: 设置操作,击数字会将值更改为所选数字。 - settings: 修改组件设定 - show-eggs: 在Egg模式或Head模式之间切换实体视图 - title-enable: 启用/禁用玩家完成挑战时显示的标题消息。 - title-showtime: 修改标题消息对玩家可见的时间。 - toggle-user-list: 切换到不同的玩家列表 - waiver-amount: 完成该数量的挑战玩家方能解锁下一挑战级别的挑战 - reward-money: 设置完成任务后的金钱奖励.|&c需要经济前置(Vault插件和Economy插件). - repeat-reward-commands: 定义挑战完成后执行的自定义命令。| ***在开始处添加 [SELF] 表示命令将由玩家执行,例如 /kill - 命令等,否则将被服务器执行。|***字符串 [player] 将被系统替换为完成挑战的玩家名称,例如 /kill [player] 将变成 /kill - BONNe1704 等。|命令: - line-length: 修改每条lore的最大长度。不会影响已有物品。 - history-store: 启用/禁用挑战历史存储 - history-lifespan: 修改保存历史数据的天数|0表示永久 - library: 打开 GUI 显示所有可用的公开挑战库 - library-author: 由 &e[author] 创作 - library-version: "&9创作于 [version] 版本" - library-lang: "&a语言: [lang]" - library-gamemode: "&a用于 [gamemode] 游戏模式" - lore: - level: 等级字符串 | 表示翻译 'challenges.gui.challenge-description.level'. - status: 状态字符串 | 表示翻译 'challenges.gui.challenge-description.completed'. - count: 完成计数字符串 | 表示翻译 'challenges.gui.challenge-description.completed-times', 'challenges.gui.challenge-description.completed-times-of' - 和 'challenges.gui.challenge-description.maxed-reached'. - description: 描述字符串 | 在挑战对象的此处定义 - challenge.description. - warnings: '警告字符串 | 表示下列翻译: | ''challenges.gui.challenge-description.warning-items-take'' - | ''challenges.gui.challenge-description.objects-close-by'' | ''challenges.gui.challenge-description.warning-entities-kill'' - | ''challenges.gui.challenge-description.warning-blocks-remove''.' - environment: 环境字符串 | 于挑战对象此处定义 - challenge.environment. - requirements: '需求字符串 | 表示下列翻译: | ''challenges.gui.challenge-description.required-level'' - | ''challenges.gui.challenge-description.required-money'' | ''challenges.gui.challenge-description.required-experience'' - | 以及 challenge.requiredItems, challenge.requiredBlocks 或 challenge.requiredEntities.' - reward_text: 奖励文本 | 在 challenge.rewardText 和 challenge.repeatRewardText - 中定义 - reward_other: '其他奖励字符串 | 表示下列翻译: | ''challenges.gui.challenge-description.experience-reward'' - | ''challenges.gui.challenge-description.money-reward'' | ''challenges.gui.challenge-description.not-repeatable''.' - reward_items: 奖励物品 | 在 challenge.rewardItems 和 challenge.repeatRewardItems - 中定义的奖励物品 - reward_commands: 奖励命令 | 在 challenge.rewardCommands 和 challenge.repeatRewardCommands - 中定义的奖励命令 - level_status: 状态字符串 | 表示翻译 'challenges.gui.level-description.completed'. - challenge_count: 成就完成计数器字符串. | 表示 'challenges.gui.level-description.completed-challenges-of' - 的翻译 - unlock_message: 解锁信息文本 | 在挑战等级对象中定义 - challengeLevel.unlockMessage. - waiver_amount: 解锁下一等级字符串的可继承的挑战计数器 | 表示翻译 'challenges.gui.level-description.waver-amount' - level_reward_text: 奖励文本 | 在 challengeLevel.rewardText 中定义 - level_reward_other: '其他奖励字符串 | 表示翻译: | ''challenges.gui.level-description.experience-reward'' - | ''challenges.gui.level-description.money-reward''.' - level_reward_items: 奖励物品 | 在 challengeLevel.rewardItems 中定义的物品 - level_reward_commands: 奖励命令 | 在 challengeLevel.rewardCommands 中定义的完成挑战将会奖励玩家的命令 - download: 允许手动升级可用的挑战库 | 右击以启用缓存清理 - download-disabled: GitHub 数据下载器已在 BentoBox 中被禁用。没有它,你不能使用库! - challenge-wipe: 完全清空挑战及等级数据库! - players-wipe: 完全清空玩家数据库! - visibility-mode: 切换未发布的挑战是否应当可见 - block: "- [block] : [count]" - command: "- [command]" - current-value: "|&6当前值: [value]." - disabled: 禁用 - enabled: 有效 - entity: "- [entity] : [count]" - item: "- [count] x [item]" - item-enchant: " - [enchant] [level]" - item-meta: " ([meta])" - level-locked: 请完成 [count] 个 [level] 级别的挑战来解锁这个挑战级别! - level-unlocked: 点击查看 [level] 级别的挑战! - nether: "- 地狱" - normal: "- 主世界" - permission: "- [permission]" - the-end: "- 末地" - increase-by: "&a为完成计数器增加 [value]" - reduce-by: "&c为完成计数器减少 [value]" - visibility: - visible: 所有挑战对任何人可见 - hidden: 仅发布的挑战可见 - toggleable: 切换未发布的挑战的可见性 - type: - island: "&a允许获取玩家附近的方块或怪物" - inventory: "&a允许获取玩家物品栏中的物品" - other: "&a允许获取其他插件/扩展中的物品" - item-description: - armor-color: " [color]" - book-meta: " [title] by [author]" - custom-effects: " 自定义效果:" - egg-meta: " [mob]" - item: "- [count] x [item]" - item-enchant: " - [enchant] [level]" - item-lore: " 物品Lore:" - item-meta: " ([meta])" - item-name: " [name]" - potion-effect: " [effect] x [amplifier] for [duration]t" - potion-type: " [name]" - potion-type-extended: " Extended [name]" - potion-type-extended-upgraded: " [name]" - potion-type-upgraded: " Upgraded [name]" - recipe-count: " [count] recipes" - skull-owner: " [owner]" - fish-meta: "[body-color] 以 [pattern-color] [pattern]" - level-description: - completed: "&B已完成" - completed-challenges-of: "&你已经完成 [number] 个该级别的挑战,达到了 [max]个挑战." - experience-reward: "&6经验奖励: [value]" - money-reward: "&6金钱奖励: $[value]" - reward-commands: "&6指令奖励:" - reward-items: "&6物品奖励:" - waver-amount: "&6可以跳过[value] 个挑战来解锁下一个挑战级别." - questions: - prefix: "&2[SERVER]: " - admin: - number: 输入一个数字,然后按两次回车。 - unique-id: 输入不重复的对象名,然后按回车。 - challenge-name: 输入当前挑战的显示名称,然后按回车。 - level-name: 输入当前等级的显示名称,然后按回车。 - title: - admin: - choose-challenge-title: "&a选择挑战" - choose-level-title: "&a选择挑战等级" - choose-user-title: "&a选择玩家" - confirm-title: "&a确认" - edit-challenge-title: "&a编辑挑战" - edit-level-title: "&a编辑挑战等级" - edit-text-fields: "&a编辑文本字段" - manage-blocks: "&a管理方块" - manage-entities: "&a管理实体" - manage-items: "&a管理物品" - manage-numbers: "&a数字垫" - select-block: "&a选择方块" - select-challenge: "&a选择挑战" - select-entity: "&a选择实体" - settings-title: "&a编辑设定" - toggle-environment: "&a切换环境" - gui-title: "&a挑战管理" - library-title: "&a可下载的库" - lore-add: "&a添加物品Lore" - lore-remove: "&a移除物品Lore" - lore-edit: "&a编辑物品Lore" - type-select: "&a选择挑战类型" - challenges: "&6挑战" - game-modes: "&6选择游戏模式" - multiple-complete: "&6多少次?" - messages: - admin: - already-completed: "&2这个挑战已经完成" - challenge-created: "[challenge]&r created!" - completed: "&2已为[player]完成挑战[name]!" - complete-wipe: "&c希望你有备份,因为你已经创建了所有Challenges Addon数据库!" - hit-things: 点击物品将它们添加到所需的事物列表中。 完成后右键单击。 - you-added: 你在挑战中添加了一个[thing] - reset: "&2你刚重设了 [player] 的 [name] 挑战!" - reset-all: "&2[player] 的所有挑战都被你重设了!" - not-completed: "&2这个挑战还没完成呢!" - migrate-start: "&2开始迁移挑战扩展数据." - migrate-end: "&2挑战扩展数据已迁移到新格式." - migrate-not: "&2数据全部有效." - start-downloading: "&5开始下载并导入挑战库" - challenge-wipe: "&c希望你已做好备份,因你刚刚从数据库中删除了所有的挑战和等级!" - players-wipe: "&c希望你已做好备份,因你刚刚从数据库中删除了所有玩家已完成的挑战!" - defaults-file-completed: defaults.json文件填充了来自[world]的挑战! - defaults-file-overwrite: defaults.json存在。 它将被覆盖。 - import-challenges: 开始导入挑战 - import-levels: 开始导入挑战级别 - import-number: 导入 [number] 个挑战 - load-add: '添加新的对象: [value]' - load-overwriting: 覆盖 "[value]" - load-skipping: '"[value]" 已存在 - 跳过' - name-has-completed-challenge: "&5[name] 已完成 [value] &r&5挑战!" - name-has-completed-level: "&5[name] 已完成 [value] &r&5挑战级别!" - no-levels: '警告: challenges.yml文件中没有定义挑战级别' - you-completed-challenge: "&2你已经完成了 [value] &r&2挑战!" - you-completed-level: "&2你完成了 [value] &r&2级别!" - you-repeated-challenge: "&2你已经重复完成了 [value] &r&2挑战!" - you-repeated-challenge-multiple: "&2你重复完成了 [value] &r&2挑战 [count] 次!" - titles: - challenge-subtitle: "[friendlyName]" - challenge-title: 完成挑战 - level-subtitle: "[friendlyName]" - level-title: 成功挑战级别 -protection: - flags: - CHALLENGES_ISLAND_PROTECTION: - name: 挑战保护 - description: |- - &5&o切换谁可以 - &5&o完成挑战 - CHALLENGES_WORLD_PROTECTION: - description: |- - &5&o为玩家启用/禁用 - &5&o要求他们在他们的岛屿上 - &5&o才能完成挑战. - hint: 请在自己的岛屿完成挑战! - name: 挑战岛屿限制 -version: 11 +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + meta: - authors: - - BONNe + authors: + - ApacheZy + +challenges: + commands: + admin: + main: + parameters: '' + description: '打开管理员面板。' + import: + description: |- + 从 challenges.yml 中导入挑战 + 如果使用了 overwrite 参数,将覆盖数据库中具有相同ID的挑战。 + parameters: '[overwrite]' + reload: + description: |- + 从数据库中重载挑战 + 如果使用了 hard 参数,将重置与数据库的连接。 + parameters: '[hard]' + show: + description: '在聊天框中列出这个世界适用的所有挑战。' + parameters: '' + defaults: + description: '显示用于导入/导出默认挑战的子命令。' + parameters: '[command]' + defaults-import: + description: '导入默认挑战。' + parameters: '' + defaults-generate: + description: '将现有挑战导出到 default.json 文件。' + parameters: '[overwrite] - 允许覆盖现有文件。' + complete: + description: '将玩家的某个设置为完成为一个玩家完成挑战。' + parameters: ' ' + reset: + description: '重设玩家的挑战。如果将参数 设置为 "all", 则将重置该玩家所有挑战。' + parameters: ' ' + migrate: + description: '将当前的游戏世界挑战数据迁移到 0.8.0 存储格式。' + parameters: '' + user: + main: + description: '打开挑战界面。' + parameters: '' + complete: + description: '尝试完成挑战。' + parameters: ' [count]' + gui: + title: + admin: + gui-title: '&3&l挑战管理' + edit-challenge-title: '&3&l编辑挑战' + edit-level-title: '&3&l编辑挑战等级' + settings-title: '&3&l插件设置' + choose-challenge-title: '&5&l选择挑战' + choose-level-title: '&3&l挑战等级' + choose-user-title: '&5&l选择玩家' + manage-blocks: '&3&l管理方块' + manage-entities: '&3&l管理实体' + confirm-title: '&4&l确认' + manage-items: '&3&l管理物品' + manage-numbers: '&9&l数字输入' + select-block: '&5&l选择方块' + select-challenge: '&5&l选择挑战' + select-entity: '&5&l选择实体' + toggle-environment: '&3&l更改环境' + edit-text-fields: '&3&l编辑多行文本' + + library-title: '&3&l网络库' + + lore-add: '&3&l增加描述内容' + lore-remove: '&3&l删除描述内容' + lore-edit: '&3&l编辑描述内容' + + type-select: "&3&l选择挑战类型" + challenges: '&3&l挑战' + game-modes: '&3&l选择游戏模式' + + multiple-complete: '&6&l多少次?' + buttons: + admin: + complete: '&f&l完成玩家挑战' + reset: '&6&l重置玩家挑战' + create-challenge: '&f&l创建挑战项' + create-level: '&f&l创建挑战等级' + edit-challenge: '&f&l编辑挑战项' + edit-level: '&f&l编辑挑战等级' + delete-challenge: '&c&l删除挑战' + delete-level: '&c&l删除挑战等级' + import: '&f&l导入 ASkyblock 的挑战' + settings: '&f&l插件设置' + properties: '&f&l一般属性' + requirements: '&b&l必要条件' + rewards: '&a&l奖励内容' + challenges: '&f&l挑战' + deployment: '&f&l部署' + icon: '&f&l图标' + locked-icon: '&f&l未解锁图标' + description: '&f&l描述' + order: '&f&l顺序' + environment: '&f&l环境' + remove-on-complete: '&7&l完成后删除' + name: '&f&l友好名称' + required-entities: '&f&l需要的实体' + remove-entities: '&c&l消灭实体' + required-blocks: '&f&l需要的方块' + remove-blocks: '&c&l拿掉方块' + search-radius: '&f&l探测范围' + required-permissions: '&f&l权限' + required-items: '&f&l需要的物品' + remove-items: '&c&l删除物品' + required-experience: '&f&l经验值' + remove-experience: '&f&l扣除经验' + required-level: '&f&l岛屿等级' + required-money: '&f&l游戏币' + remove-money: '&f&l扣除游戏币' + reward-text: '&f&l反馈消息' + reward-items: '&f&l奖励物品' + reward-experience: '&f&l奖励经验' + reward-money: '&f&l奖励游戏币' + reward-commands: '&f&l命令奖励' + repeatable: '&f&l可重复' + repeat-count: '&f&l最大重复次数' + repeat-reward-text: '&f&l重复奖励消息' + repeat-reward-items: '&f&l重复奖励物品' + repeat-reward-experience: '&f&l重复奖励经验' + repeat-reward-money: '&f&l重复奖励游戏币' + repeat-reward-commands: '&f&l重复命令奖励' + waiver-amount: '&f&l豁免挑战数' + add-challenge: '&f&l添加挑战' + remove-challenge: '&f&l删除挑战' + reset-on-new: '&f&l重玩时重置' + broadcast: '&f&l完成后广播消息' + remove-completed: '&f&l完成后删除' + glow: '&f&l完成后发光' + free-at-top: '&f&l前置独立挑战' + line-length: '&f&l描述行长度' + visibility-mode: '&f&l挑战可见模式' + toggle-user-list: '&f&l玩家过滤' + remove-selected: '&f&l删除' + add: '&f&l添加' + show-eggs: '&f&l切换显示模式' + accept: '&c&l接受' + decline: 'Decline' # 待确认 + save: '&f&l保存' + cancel: '&f&l取消' + input: '&f&l键盘输入' + value: '&f&l结果' + set: '&f&l=' + increase: '&f&l+' + reduce: '&f&l-' + multiply: '&f&l*' + clear: '&f&l清除' + remove-empty: '&f&l删除空行' + number: '[number]' + level-lore: '&f&l等级描述元素' + challenge-lore: '&f&l挑战描述元素' + gui-view-mode: '&f&l独立命令用途' + gui-mode: '&f&l独立命令' + history-store: '&f&l挑战历史' + history-lifespan: '&f&l保存期限' + island-store: '&6&l按岛屿存储' + default-locked-icon: '&f&l未解锁等级图标' + input-mode: '&f&l切换输入模式' + title-enable: '&f&l标题消息' + title-showtime: '&f&l标题显示时间' + default-import: '&f&l导入默认挑战' + default-export: '&f&l导出现有挑战' + complete-wipe: '&c&l清空插件数据库' + challenge-wipe: '&c&l清空挑战数据库' + players-wipe: '&c&l清空玩家数据库' + + library: '&f&l网络库' + download: '&f&l下载网络库' + + type: + island: '&6&l岛屿类' + inventory: '&6&l物品类' + other: '&6&l其它类型' + next: '&f&l下一页' + previous: '&f&l上一页' + return: '&f&l返回' + + value: "&f&l完全" # 待确认 + increase: "&f&l增加" + reduce: "&f&l减少" + descriptions: + admin: + save: '&7保存并返回上级' + cancel: '&7取消保存并返回上级' + input: '&7请打开聊天框并手动输入数值' + set: '&f&l设置模式。|&7单击数字将值直接设置为所选数值。' + increase: '&f&l累加模式。|&7单击数字将当前值加上所选数值。' + reduce: '&f&l递减模式。|&7点击数字将当前值减去所选数值。' + multiply: '&f&l累乘模式。|&7单击数字将当前值乘以所选数值。' + import: |- + &7导入 ASkyblock(旧版空岛插件) 的挑战。 + &f右键点击 启用/禁用 覆盖模式。 + &7请将要导入的 challenges.yml 放置在: + &7./BentoBox/addons/Challenges 目录。 + complete: |- + &7直接将玩家的挑战状态设置为完成。 + &7这样做玩家不会获得任何奖励。 + reset: |- + &7重置玩家已完成的挑战。 + &f右键点击 启用/禁用 全部重置功能。 + create-challenge: |- + &7创建一个新的挑战。 + &7默认情况下新挑战将出现在独立挑战列表中。 + create-level: '&7创建一个新的挑战等级。' + edit-challenge: '&7修改现有的挑战。' + edit-level: '&7修改现有的挑战等级。' + delete-challenge: '&7删除某项挑战。' + delete-level: '&7删除某个挑战等级。' + settings: '&7修改挑战组件配置。' + properties: '&7修改这项挑战的常规属性。' + requirements: '&7设置要完成这项挑战的必要条件。' + rewards: '&7设置完成这项挑战后获得的奖励。' + challenges: '&7管理该等级的所有挑战(增加/删除)。' + deployment: '&7允许玩家查看和完成这项挑战。' + icon-challenge: '&7设置这项挑战将显示在|&7挑战面板中的图标。' + icon-level: '&7设置这项挑战等级将显示在|&7挑战面板中的图标。' + locked-icon: '&7设置这个挑战等级未解锁时|&7在面板中显示的图标。' + description: '&7修改挑战描述文本。' + order: '&7修改顺序号。|&7顺序号越大,显示在面板中的位置越靠后。' + environment: '&7设置要完成这项挑战应所处的环境。' + remove-on-complete: '&7设置玩家完成挑战后,挑战面板|&7中是否不再显示这项挑战。' + name-challenge: '&7设置这项挑战在面板中的显示名称。|&7如果是新建的挑战项,显示名称是挑战项ID。' + name-level: '&7设置这个挑战等级在面板中的显示名称。|&7如果是新' + required-entities: |- + &7添加/修改/删除 + &7要完成这项挑战应在指定范围内存在的实体。 + &6所需实体: + remove-entities: '&7设置当玩家完成挑战后,|&7是否删除(杀死)所需实体。' + required-blocks: |- + &7添加/修改/删除 + &7要完成这项挑战应在指定范围内存在的方块。 + &6所需方块: + remove-blocks: '&7设置当玩家完成挑战后,|&7是否删除(替换成空气)所需方块。' + search-radius: "&7玩家所在位置周围的半径,|&7将在其范围内探测所需的实体和方块。" + required-permissions: |- + &7设置玩家要完成挑战必须具有的权限。 + &6所需权限: + required-items: |- + &7设置玩家要完成挑战物品栏中必须有的物品。 + &6所需物品: + remove-items: '&7设置当玩家完成挑战后,是否|&7从物品栏中删除所需物品。' + required-experience: '&7设置玩家要完成挑战所|&7需要的经验值。' + remove-experience: '&7设置玩家完成挑战后,是否扣除所需经验值。' + required-level: |- + &7设置要完成此挑战所需的岛屿等级。 + &c需要安装 Level 组件。 + required-money: |- + &7设置要完成此挑战所需的游戏币数量。 + &c需要安装 Vault 和兼容的经济插件。 + remove-money: |- + &7完成挑战后,是否扣除玩家所需数量的游戏币。 + &c需要安装 Vault 和兼容的经济插件。 + reward-text: '&7设置玩家完成挑战后发送给玩家的聊天消息。' + reward-items: |- + &7设置首次完成挑战获得的物品奖励。 + &a奖励物品: + reward-experience: '&7设置首次完成挑战后获得的经验值奖励。' + reward-money: |- + &7设置首次完成挑战获得的游戏币奖励。 + &c需要安装 Vault 和兼容的经济插件。 + reward-commands: |- + &7设置首次完成挑战后将执行的命令。 + &3无需在命令行首加斜杠 “/”。 + &3命令行首加 “[SELF]” 将由玩家执行。 + &3例如 “&f[SELF] heal&3”。 + &3文字 “[player]” 将替换为玩家名称。 + &3例如 “&fkill [player]&3”。 + &a奖励命令: + repeatable: '&7设置这项挑战是否可重复进行。' + repeat-count: '&7设置最大完成次数,设置为 0 表示不限次数。' + repeat-reward-text: '&7设置重复完成挑战后发送给玩家的聊天消息。' + repeat-reward-items: |- + &7设置重复完成挑战获得的奖励物品。 + &a奖励物品: + repeat-reward-experience: '&7设置重复完成挑战后获得的经验值奖励。' + repeat-reward-money: |- + &7设置重复完成挑战获得的游戏币奖励。 + &c需要安装 Vault 和兼容的经济插件。 + repeat-reward-commands: |- + &7设置首次完成挑战后将执行的命令。 + &3无需在命令行首加斜杠 “/”。 + &3命令行首加 “[SELF]” 将由玩家执行。 + &3例如 “&f[SELF] heal&3”。 + &3文字 “[player]” 将替换为玩家名称。 + &3例如 “&fkill [player]&3”。 + &a奖励命令: + waiver-amount: '&7设置玩家解锁下一级需要|&7完成当前等级的数量。' + reward-text-level: '&7完成当前等级所有挑战后|&7发送给玩家的聊天消息。' + add-challenge: '&7将现有挑战添加到当前挑战等级。' + remove-challenge: '&7从当前挑战等级中移除挑战。' + reset-on-new: '&7当玩家重置、离开或被踢出岛屿时|&7是否重置他的所有挑战。' + broadcast: '&7设置玩家首次完成挑战后是否|&7向所有在线玩家发送广播。' + remove-completed: '&7设置完成挑战后是否从面板|&7中隐藏且无法重复。' + glow: '&7设置' + free-at-top: '&7设置是否将独立挑战放在面板前排。' + line-length: '&7设置面板图标描述行显示的最大长度。|&7这个设定只影响显示效果,不会修改存储数据。' + toggle-user-list: '&7按所选模式过滤玩家。' + mode-online: '当前所有在线玩家。' + mode-in-world: '当前游戏模式中的玩家。' + mode-with-island: '在当前游戏模式中有归属岛屿的玩家。' + selected: '&5已选定' + remove-selected: |- + &7删除所有选定的内容。 + &7右键单击来选定内容。 + show-eggs: '&7切换使用&3生成蛋&7或&3带纹理的玩家头&7来显示实体。' + level-lore: '&7设置挑战等级描述中哪些元素是可见的。' + challenge-lore: '&7设置挑战描述中哪些元素是可见的。' + gui-view-mode: |- + &7设置通过独立命令是否打开游戏模 + &7式选择器。 + &7当 &a开启 &7时,通过独立命令可以打开 + &7游戏模式选择器以进行适用挑战。 + &7当 &c关闭 &7时,将直接打开适用于当前 + &7游戏模式的挑战面板。 + &e只在安装了多个游戏模式时有用。 + history-store: '&7设置是存储挑战历史记录。' + history-lifespan: |- + &7设置历史记录数据可以保存的天数。 + &7设置为 0 将永久保存. + island-store: |- + &7设置是否按岛屿为单位来存储数据。 + &7如果开启此选项,则整个岛屿所有 + &7成员的挑战将是相同的。 + &c点击切换时不会立即转换数据。 + + default-locked-icon: |- + &7设置未解锁的挑战等级默认图标. + &7为挑战等级单独设定未解锁图标可以覆盖此设置。 + gui-mode: |- + &7设置是否可以用单独的命令打开面板, + &7例如使用 &f/c &7打开面板。 + &c&l更改设置后重启服务器才能生效。 + visibility-mode: '&7选择未部署的挑战可见模式。| ' + + click-to-edit: '&4点击此处编辑输入。' + edit-text-line: '&6编辑文本消息!' + add-text-line: '&6增加新的文本消息!' + input-mode: '&7选择是在聊天中还是铁砧上输入文本。' + title-enable: '&7设置是否在完成挑战后|&7向玩家发送标题消息。' + title-showtime: '&7设置标题消息显示时长。|&7单位为 &f游戏刻 (Ticks)' + default-import: '&7导入默认挑战。' + default-export: '&7将现有挑战导出到 defaults.json 文件。' + complete-wipe: '&c彻底清空所有挑战组件数据库,|&c包括玩家数据!' + + challenge-wipe: '&c彻底清空挑战和挑战等级数据库!|&c清空后玩家不能再进行任何挑战。' + players-wipe: '&c彻底清空玩家数据库!|&c清空后所有玩家的挑战进度将丢失。' + + library: '&c从网络上下载共享挑战库。' + + library-author: '&7由 &e[author] &7创作。' + library-version: '&9兼容 Challenges [version]' + library-lang: '&7语言: [lang]' + library-gamemode: '&7适用于 [gamemode]' + + download: |- + &7从共享网络库上下载可用的挑战。 + &7右键点击 开启/关闭 缓存清理。 + download-disabled: '&cGitHub 下载器已在 BentoBox 中禁用。|&c没有它,您将无法使用共享库!' + + lore: + level: '&7所属挑战等级名称' + status: '&7完成状态' + count: '&7完成次数' + description: '&7描述文字' + warnings: '&7警告文字' + environment: '&7环境需求' + requirements: '&7需求' + reward_text: '&7奖励描述' + reward_other: |- + &7其他奖励描述 + &7包含经验奖励、游戏币奖励以及不可重复完成的提示 + reward_items: '&7奖励物品' + reward_commands: '&7命令奖励' + level_status: '&7等级完成状态' + challenge_count: '&7完成的挑战计数字符串。' + unlock_message: '&7解锁状态字符串' + waiver_amount: '&7豁免数的说明' + level_reward_text: '&7等级奖励描述文本' + level_reward_other: '&7挑战等级其他奖励内容|&7包含经验值奖励和游戏币奖励' + level_reward_items: '&7奖励物品内容' + level_reward_commands: '&7命令奖励' + current-value: |- + &6当前值: &f[value] + enabled: '&a已开启' + disabled: '&c已关闭' + type: + island: '&a挑战内容为建造和驯养类。|&7要完成该类挑战,玩家周围|&7必须有指定数量的方块或实体。' + inventory: '&a挑战内容为物品收集。|&7要完成该类挑战,玩家物品栏中|&7必须有指定数量的物品。' + other: '&a挑战内容为数据条件类。|&7要完成该类挑战,玩家必须有设定的|&7游戏币/经验值/岛屿等级/权限。' + the-end: '- 末地' + nether: '- 下界' + normal: '- 主世界' + entity: '&7- [entity] x [count]' + block: '&7- [block] x [count]' + item: '&7- [item] x [count]' + item-meta: '&7 ([meta])' + item-enchant: '&7 - [enchant] [level]' + permission: '&7- [permission]' + command: '- [command]' + + level-unlocked: '&f点击查看 &r[level] &f的所有挑战!' + level-locked: '&7再完成 &f[count] &7个 &r[level] &7的挑战项目即可解锁此等级。' + + increase-by: "&a将完成计数增加 &f[value]" + reduce-by: "&c将完成计数减少 &f[value]" + + visibility: + visible: "所有挑战项都可见" + hidden: "仅显示已部署的挑战项" + toggleable: "玩家可以自行从面板中切换显示模式|&r" + + challenge-description: + level: '&f等级: [level]' + completed: '&b已完成' + completed-times-of: '&3可挑战 [maxtimes] 次, 完成了 [donetimes] 次。' + maxed-reached: '&b完成了 [donetimes] 次, 可挑战 [maxtimes] 次。' + completed-times: '&b完成了 [donetimes] 次。' + warning-items-take: '&c完成后,所需物品将被删除。' + objects-close-by: '&c周围必须有所需方块和实体。' + warning-entities-kill: '&c完成后,所需实体将被消灭。' + warning-blocks-remove: '&c完成后,所需方块将被摧毁。' + not-repeatable: '&c这项挑战不可重复!' + experience-reward: '&2经验值奖励: &7[value]' + money-reward: '&2游戏币奖励: &7[value]' + required-experience: '&6需要经验值: &7[value]' + required-money: '&6需要游戏币: - &7[value]' + required-island-level: '&6需要岛屿等级: - &7[value]' + environment: '&6需要环境:' + rewards-title: '&a&l奖励:' + reward-items: '&2物品奖励:' + reward-commands: '&2命令奖励:' + required-items: '&6需要物品:' + required-entities: '&6需要实体:' + required-blocks: '&6需要方块:' + level-description: + completed: '&b已全部完成' + completed-challenges-of: |- + &3该等级共有 &f[max] &3项挑战, + &3你已完成了 &f[number] &3项。 + waver-amount: '&6你可以跳过 &f[value] &6项挑战来解锁下一级。' + experience-reward: '&2经验值奖励: - &f[value]' + money-reward: '&2游戏币奖励: - &f[value]' + reward-items: '&2物品奖励:' + reward-commands: '&2命令奖励:' + item-description: + item: '&7- [item] x [count]' + item-meta: '&8 ([meta])' + item-enchant: '&8 [enchant] [level]' + item-name: '&7 [name]' + item-lore: '&7 物品描述:' + book-meta: '&7 《[title]》 - [author] 著' + recipe-count: '&7 [count] 个配方' + armor-color: '&8&o [color]' + potion-type-extended-upgraded: '&7 [name] II (Extended)' + potion-type-upgraded: '&7 [name] II' + potion-type-extended: '&7 [name] Extended ' + potion-type: '&7 [name]' + custom-effects: '&7 自定义效果:' + potion-effect: '&8 [effect] [amplifier] ([duration])' + skull-owner: '&7 [owner]' + egg-meta: '&7 [mob]' + fish-meta: '&8&o [body-color]-[pattern-color] [pattern]' + + questions: + prefix: "&7[&e服务器&7]: &r" + + admin: + number: "请通过聊天栏输入数值:" + unique-id: "请通过聊天栏输入对象的唯一ID:" + challenge-name: "请通过聊天栏输入这项挑战的名称:" + level-name: "请通过聊天栏输入这个挑战等级的名称:" + + titles: + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. + # [friendlyName] will be replaced with challenge friendly name. + # [level] will be replaced with level friendly name. + # [rewardText] will be replaced with the challenge reward text. + challenge-title: '&a已完成' + challenge-subtitle: '[friendlyName]' + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. + # [friendlyName] will be replaced with level friendly name. + # [rewardText] will be replaced with the level reward text. + level-title: '&a已完成' + level-subtitle: '[friendlyName]' + messages: + admin: + # 鬼知道你说的 Thing 是什么!况且这些翻译内容从来没用到过。 + hit-things: '&7单击以将其添加到所需列表中。完成后右键单击。' + you-added: '&a您向挑战添加了一个 &2r[thing]' # ??? + challenge-created: '&a挑战项 &r[challenge] &a已创建!' + complete-wipe: '&c希望您有备份,因为您刚刚删除了挑战组件的全部数据库!' + + challenge-wipe: '&c希望您有备份,因为您刚刚删除了所有挑战项和挑战等级!' + players-wipe: '&c希望您有备份,因为您刚刚删除了所有玩家已完成的挑战!' + + completed: '&2你将玩家 &r[player] &2的挑战项 &r[name] &2设置为已完成!' + already-completed: '&2这项挑战已经完成过了!' + reset: '&2你为玩家 &r[player] &2重置了挑战项 &r[name]&2!' + reset-all: '&2玩家 &r[player] &2的所有挑战项已被重置!' + not-completed: '&2这项挑战尚未完成!' + + migrate-start: '&2开始迁移挑战组件数据库。' + migrate-end: '&2挑战组件数据库已更新为新格式。' + migrate-not: '&2所有数据均有效。' + + start-downloading: '&5开始下载网络库并导入。' + you-completed-challenge: '&2你完成了挑战 &r[value] &2!' + you-repeated-challenge: '&2你再次完成了挑战 &r[value] &2!' + you-repeated-challenge-multiple: '&2你完成挑战 &r[value] &r[count] &2次了!' + you-completed-level: '&2恭喜,你的挑战等级 &r[value] &2已完成!' + name-has-completed-challenge: '&a恭喜! &r[name] &a完成了挑战 [value] &a!' + name-has-completed-level: '&a恭喜! &r[name] &a的挑战等级 [value] &a已全部完成!' + import-levels: '&a开始导入挑战等级' + import-challenges: '&a开始导入挑战' + no-levels: '&e警告: 文件 &fchallenges.yml &e中没有定义任何挑战等级!' + import-number: '&a导入了 &f[number] &a个挑战项目' + load-skipping: '&c挑战项 "[value]" &c已存在 - 将跳过' + load-overwriting: '&6覆盖了已载入的挑战: "[value]"' + load-add: '&a新增了挑战: [value]' + defaults-file-overwrite: '&cdefaults.json 已被覆盖。' + defaults-file-completed: 'defaults.json 已经保存了 [world] 中的所有挑战。' + errors: + no-name: '&c缺少挑战名称。' + unknown-challenge: '&c未知的挑战。' + unique-id: '&c唯一ID "[id]" 无效。' + wrong-icon: '&c给定的材料 "[value]" 无效,不能用作图标。' + not-valid-integer: '&c给定的整数值 "[value]" 无效!它只能在 [min] 到 [max] 取值。' + not-a-integer: '&c给定的值 "[value]" 不是有效整数!' + not-deployed: '&c这项挑战尚未部署!' + not-on-island: '&c您必须在您的岛上才能完成挑战!' + challenge-level-not-available: '&c您尚未解锁这项挑战的等级。' + not-repeatable: '&c这项挑战不可重复进行!' + wrong-environment: '&c您在错误的环境中!' + not-enough-items: '&c你没有足够的 &r[items] &c来完成这项挑战。' + not-close-enough: '&c要完成挑战,你必须站在所需项目 &f[number] &c格范围内。' + you-still-need: '&c你还差 &f[amount] &c个 &f[item] &c才能完成挑战。' + missing-addon: '&c无法完成挑战:缺少必需的组件或插件。' + incorrect: '&c无法完成挑战:必要条件设定错误。' + not-enough-money: '&c你必须有 &f[value] &c游戏币才能完成任务。' + not-enough-experience: '&c你必须有 &f[value] &c经验值才能完成任务。' + island-level: '&c你的岛屿等级必须达到 &flv[number] &c才能完成任务!' + import-no-file: '&c未找到要导入的 &fchallenges.yml &c文件!' + no-load: '&c错误: 无法载入 &fchallenges.yml&c. [message]' + load-error: '&c错误: 无法载入 &r[value]&c。' + no-rank: "&c你的阶衔不能进行这项挑战。" + cannot-remove-items: '&c有些物品无法从你的物品栏中删除!' + exist-challenges-or-levels: '&c这项挑战或这个等级已存在!' + defaults-file-exist: '&c文件 &fdefaults.json &c已存在,要将其替换请开启覆盖模式。' + defaults-file-error: '&c创建文件 defaults.json 发生错误,请查阅控制台消息!' + no-challenges: '&c这个游戏模式没有可进行的挑战!' + no-challenges-admin: '&c这个游戏模式还没有可进行的挑战!请使用 &f/[command] &c来添加挑战。' + missing-level: '&c数据库中未定义挑战等级 [level]&c, 使用它可能发生错误!' + missing-arguments: '&c命令缺少参数。' + no-multiple-permission: "&c你没有权限多次完成这项挑战。" + invalid-level: "&c挑战等级 [level] &c包含错误,它不会从数据库中加载!" + invalid-challenge: "&c挑战项 [challenge] &c包含错误,它不会从数据库中加载!" +protection: + flags: + CHALLENGES_ISLAND_PROTECTION: + description: "允许/禁止 在岛屿上完成挑战" + name: "挑战权限" + CHALLENGES_WORLD_PROTECTION: + description: |- + &7允许/禁止 限制玩家只能在自己岛 + &7上才能完成挑战。 + &c允许时,玩家只能在自己岛上进行 + &c和完成挑战。 + name: "挑战岛屿限制" + hint: "&c已被禁止在岛屿范围外进行挑战" +version: 11 diff --git a/src/main/resources/panels/gamemode_panel.yml b/src/main/resources/panels/gamemode_panel.yml new file mode 100644 index 0000000..7634b0c --- /dev/null +++ b/src/main/resources/panels/gamemode_panel.yml @@ -0,0 +1,53 @@ +# Panels are read once upon first GUI open. +# Changes in configuration will be taken only if server is restarted or panels are reloaded. +# Information about setup for the Panels are available at: +# https://docs.bentobox.world/en/latest/addons/Challenges/ +gamemode_panel: + title: challenges.gui.titles.gamemode-gui + type: INVENTORY + background: + icon: BLACK_STAINED_GLASS_PANE + title: "&b&r" # Empty text + border: + icon: BLACK_STAINED_GLASS_PANE + title: "&b&r" # Empty text + force-shown: [] + content: + 2: + 1: + icon: TIPPED_ARROW:INSTANT_HEAL::::1 + title: challenges.gui.buttons.previous.name + description: challenges.gui.buttons.previous.description + data: + type: PREVIOUS + target: GAMEMODE + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-previous + 2: gamemode + 3: gamemode + 4: gamemode + 5: gamemode + 6: gamemode + 7: gamemode + 8: gamemode + 9: + icon: TIPPED_ARROW:JUMP::::1 + title: challenges.gui.buttons.next.name + description: challenges.gui.buttons.next.description + data: + type: NEXT + target: GAMEMODE + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-next + reusable: + gamemode: + data: + type: GAMEMODE + actions: + left: + type: SELECT + tooltip: challenges.gui.tips.click-to-select \ No newline at end of file diff --git a/src/main/resources/panels/main_panel.yml b/src/main/resources/panels/main_panel.yml new file mode 100644 index 0000000..78171b6 --- /dev/null +++ b/src/main/resources/panels/main_panel.yml @@ -0,0 +1,114 @@ +# Panels are read once upon first GUI open. +# Changes in configuration will be taken only if server is restarted or panels are reloaded. +# Information about setup for the Panels are available at: +# https://docs.bentobox.world/en/latest/addons/Challenges/ +main_panel: + title: challenges.gui.titles.player-gui + type: INVENTORY + background: + icon: BLACK_STAINED_GLASS_PANE + title: "&b&r" # Empty text + border: + icon: BLACK_STAINED_GLASS_PANE + title: "&b&r" # Empty text + force-shown: [4] + content: + 2: + 2: challenge_button + 3: challenge_button + 4: challenge_button + 5: challenge_button + 6: challenge_button + 7: challenge_button + 8: challenge_button + 3: + 1: + icon: TIPPED_ARROW:INSTANT_HEAL::::1 + title: challenges.gui.buttons.previous.name + description: challenges.gui.buttons.previous.description + data: + type: PREVIOUS + target: CHALLENGE + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-previous + 2: challenge_button + 3: challenge_button + 4: challenge_button + 5: challenge_button + 6: challenge_button + 7: challenge_button + 8: challenge_button + 9: + icon: TIPPED_ARROW:JUMP::::1 + title: challenges.gui.buttons.next.name + description: challenges.gui.buttons.next.description + data: + type: NEXT + target: CHALLENGE + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-next + 5: + 1: + icon: TIPPED_ARROW:INSTANT_HEAL::::1 + title: challenges.gui.buttons.previous.name + description: challenges.gui.buttons.previous.description + data: + type: PREVIOUS + target: LEVEL + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-previous + 2: level_button + 3: level_button + 4: level_button + 5: level_button + 6: level_button + 7: level_button + 8: level_button + 9: + icon: TIPPED_ARROW:JUMP::::1 + title: challenges.gui.buttons.next.name + description: challenges.gui.buttons.next.description + data: + type: NEXT + target: LEVEL + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-next + 6: + 5: + icon: IRON_BARS + title: challenges.gui.buttons.free-challenges.name + description: challenges.gui.buttons.free-challenges.description + data: + type: UNASSIGNED_CHALLENGES + action: + left: + tooltip: challenges.gui.tips.click-to-select + reusable: + challenge_button: + data: + type: CHALLENGE + actions: + left: + type: COMPLETE + tooltip: challenges.gui.tips.click-to-complete + right: + type: MULTIPLE_PANEL + tooltip: challenges.gui.tips.right-click-multiple-open + shift_left: + type: COMPLETE_MAX + tooltip: challenges.gui.tips.shift-left-click-to-complete-all + level_button: + data: + type: LEVEL + actions: + left: + type: SELECT + tooltip: challenges.gui.tips.click-to-select \ No newline at end of file diff --git a/src/main/resources/panels/multiple_panel.yml b/src/main/resources/panels/multiple_panel.yml new file mode 100644 index 0000000..ae6144d --- /dev/null +++ b/src/main/resources/panels/multiple_panel.yml @@ -0,0 +1,62 @@ +# Panels are read once upon first GUI open. +# Changes in configuration will be taken only if server is restarted or panels are reloaded. +# Information about setup for the Panels are available at: +# https://docs.bentobox.world/en/latest/addons/Challenges/ +multiple_panel: + title: challenges.gui.titles.multiple-gui + type: HOPPER + content: + 1: + 1: + icon: RED_STAINED_GLASS_PANE + title: challenges.gui.buttons.reduce.name + description: challenges.gui.buttons.reduce.description + data: + type: REDUCE + value: 5 + actions: + left: + tooltip: challenges.gui.tips.click-to-reduce + 2: + icon: ORANGE_STAINED_GLASS_PANE + title: challenges.gui.buttons.reduce.name + description: challenges.gui.buttons.reduce.description + data: + type: REDUCE + value: 1 + actions: + left: + tooltip: challenges.gui.tips.click-to-reduce + 3: + icon: GREEN_STAINED_GLASS_PANE + title: challenges.gui.buttons.accept.name + description: challenges.gui.buttons.accept.description + data: + type: ACCEPT + actions: + left: + type: ACCEPT + tooltip: challenges.gui.tips.left-click-to-accept + right: + type: INPUT + tooltip: challenges.gui.tips.right-click-to-write + 4: + icon: BLUE_STAINED_GLASS_PANE + title: challenges.gui.buttons.increase.name + description: challenges.gui.buttons.increase.description + data: + type: INCREASE + value: 1 + actions: + left: + tooltip: challenges.gui.tips.click-to-increase + 5: + icon: MAGENTA_STAINED_GLASS_PANE + title: challenges.gui.buttons.increase.name + description: challenges.gui.buttons.increase.description + data: + type: INCREASE + value: 5 + actions: + left: + tooltip: challenges.gui.tips.click-to-increase \ No newline at end of file diff --git a/src/main/resources/template.yml b/src/main/resources/template.yml new file mode 100644 index 0000000..f0f0910 --- /dev/null +++ b/src/main/resources/template.yml @@ -0,0 +1,562 @@ +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### +# This is a template file that allows to create challenges in YAML format. +# Be aware, some features are not supported in YAML so some things may not be able to do with this +# file. +# Note that this is just a template. All challenges are stored and taken from the database. +# Template is used just for importing challenges in gamemode. +# Information about setup for the Template File are available at: +# https://docs.bentobox.world/en/latest/addons/Challenges/ +challenges: + # Each challenge starts withs it ID. + # Everything for challenge must be inside it. + example_inventory_challenge: + # Name of the Challenge. If it is not present, name will be set to the challenge id. + # Supports ColorCodes. + name: "&2 Example Inventory Challenge" + # Icon for the Challenge + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: CHEST + # Each challenge can have an extra text in their lore that is only for this challenge. + # Text has its own Color Codes. + description: |- + &7 Description of inventory + &7 challenge + # This allows to set that challenge is completable by players. If challenge is not + # deployed, then players cannot complete it until deployed status is set to true. + deployed: true + # This allows to set the ordering of the challenges inside same level. + # If 2 challenges has the same order number, they will be ordered by their unique_id. + order: 0 + # You can set that challenge is limited to a specific environment. Or leave it empty + # as it will indicate that challenge can be completed in every dimension. + # Supported values: NORMAL, NETHER, THE_END. + environments: + - NORMAL + - NETHER + - THE_END + # This option allows to auto-hide challenge after player completes it. + # It does not work for infinitely repeatable challenges. + remove-completed: false + # Type of the Challenge allows defining which requirements will be used. + # Challenge cannot exist without type or requirements. + # Each requirement has its own set of rules. + # Currently, addon has 4 types: + # INVENTORY_TYPE - checks items in player inventory. + # ISLAND_TYPE - checks for blocks or entities on player island. + # OTHER_TYPE - checks different things like, experience, island level, balance. + # STATISTIC_TYPE - checks specific player statistic value. + type: INVENTORY_TYPE + # Requirements are the section that defines what challenge will require to do. + requirements: + # All requirements supports to define a list of permissions that player must have. + # If permission is not set for the player, he will not be able to compete the + # challenge. + permissions: + - permission.value.1 + - permission.value.2 + # Take items allows to set that items will be removed from player inventory + # after challenge completion. + take-items: true + items: + - DIRT:220 + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Repeatable allows to define if challenge is repeatable or not. + # Repeat Rewards are stored only if repeatable is set to true. + repeatable: true + # Repeat times allow defining how many times challenge can be repeated. + # If the value is 0 or smaller, it means that challenge is not limited. + repeat-times: -1 + # Repeat-rewards section allows defining what player will receive after completing challenge + # each repeating time. + repeat-rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Repeat Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - PLAYER_HEAD:BONNe1704 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # There are no specific requirements for challenge ID format. + example_island_challenge: + # Name of the Challenge. If it is not present, name will be set to the challenge id. + # Supports ColorCodes. + name: "&2 Example Island Challenge" + # Icon for the Challenge + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: GRASS_BLOCK:3 + # Each challenge can have an extra text in their lore that is only for this challenge. + # Text has its own Color Codes. + description: |- + &7 Description of island + &7 challenge + # This allows to set that challenge is completable by players. If challenge is not + # deployed, then players cannot complete it until deployed status is set to true. + deployed: true + # This allows to set the ordering of the challenges inside same level. + # If 2 challenges has the same order number, they will be ordered by their unique_id. + order: 0 + # You can set that challenge is limited to a specific environment. Or leave it empty + # as it will indicate that challenge can be completed in every dimension. + # Supported values: NORMAL, NETHER, THE_END. + environments: + - NORMAL + - NETHER + - THE_END + # This option allows to auto-hide challenge after player completes it. + # It does not work for infinitely repeatable challenges. + remove-completed: false + # Type of the Challenge allows defining which requirements will be used. + # Challenge cannot exist without type or requirements. + # Each requirement has its own set of rules. + # Currently, addon has 4 types: + # INVENTORY_TYPE - checks items in player inventory. + # ISLAND_TYPE - checks for blocks or entities on player island. + # OTHER_TYPE - checks different things like, experience, island level, balance. + # STATISTIC_TYPE - checks specific player statistic value. + type: ISLAND_TYPE + # Requirements are the section that defines what challenge will require to do. + requirements: + # All requirements supports to define a list of permissions that player must have. + # If permission is not set for the player, he will not be able to compete the + # challenge. + permissions: + - permission.value.1 + - permission.value.2 + # Remove Blocks indicate that all required blocks will be removed from world + # after challenge is completed. + remove-blocks: true + # Section `blocks` follows format: `MATERIAL_NAME: ` That indicate which block and + # how many of them must be checked. + # You can find all material names in https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html + blocks: + DIRT: 220 + STONE: 2 + # Remove Entities indicate that all required entities will be removed from world + # after challenge is completed. + remove-entities: true + # Section `entities` follows format: `ENTITY_NAME: ` That indicate which entities and + # how many of them must be checked. + # You can find all entity names in https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html + entities: + CAT: 1 + BAT: 2 + # With search distance you can define how far player should stand to detect blocks. + # Be aware that setting it too large will take a time to detect. + # Also, by default this will be capped at island border. So if player stands next to a border, + # only blocks inside his island will be taken into account. + search-distance: 10 + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Repeatable allows to define if challenge is repeatable or not. + # Repeat Rewards are stored only if repeatable is set to true. + repeatable: true + # Repeat times allow defining how many times challenge can be repeated. + # If the value is 0 or smaller, it means that challenge is not limited. + repeat-times: -1 + # Repeat-rewards section allows defining what player will receive after completing challenge + # each repeating time. + repeat-rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Repeat Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - PLAYER_HEAD:BONNe1704 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # There are no specific requirements for challenge ID format. + example_other_challenge: + # Name of the Challenge. If it is not present, name will be set to the challenge id. + # Supports ColorCodes. + name: "&2 Example Other Challenge" + # Icon for the Challenge + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: EXPERIENCE_BOTTLE + # Each challenge can have an extra text in their lore that is only for this challenge. + # Text has its own Color Codes. + description: |- + &7 Description of other + &7 challenge + # This allows to set that challenge is completable by players. If challenge is not + # deployed, then players cannot complete it until deployed status is set to true. + deployed: true + # This allows to set the ordering of the challenges inside same level. + # If 2 challenges has the same order number, they will be ordered by their unique_id. + order: 0 + # You can set that challenge is limited to a specific environment. Or leave it empty + # as it will indicate that challenge can be completed in every dimension. + # Supported values: NORMAL, NETHER, THE_END. + environments: + - NORMAL + - NETHER + - THE_END + # This option allows to auto-hide challenge after player completes it. + # It does not work for infinitely repeatable challenges. + remove-completed: false + # Type of the Challenge allows defining which requirements will be used. + # Challenge cannot exist without type or requirements. + # Each requirement has its own set of rules. + # Currently, addon has 4 types: + # INVENTORY_TYPE - checks items in player inventory. + # ISLAND_TYPE - checks for blocks or entities on player island. + # OTHER_TYPE - checks different things like, experience, island level, balance. + # STATISTIC_TYPE - checks specific player statistic value. + type: OTHER_TYPE + # Requirements are the section that defines what challenge will require to do. + requirements: + # All requirements supports to define a list of permissions that player must have. + # If permission is not set for the player, he will not be able to compete the + # challenge. + permissions: + - permission.value.1 + - permission.value.2 + # Indicate that experience will be removed after challenge completion. + take-experience: true + # How much experience points player must have. It is not an experience level, but a points. + experience: 10 + # Indicate that money will be removed after challenge completion. + take-money: true + # How much money player must have. + money: 10.0 + # Allows requesting specific minimal island level for challenge completion. + level: 10 + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Repeatable allows to define if challenge is repeatable or not. + # Repeat Rewards are stored only if repeatable is set to true. + repeatable: true + # Repeat times allow defining how many times challenge can be repeated. + # If the value is 0 or smaller, it means that challenge is not limited. + repeat-times: -1 + # Repeat-rewards section allows defining what player will receive after completing challenge + # each repeating time. + repeat-rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Repeat Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - PLAYER_HEAD:BONNe1704 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # There are no specific requirements for challenge ID format. + example_statistic_challenge: + # Name of the Challenge. If it is not present, name will be set to the challenge id. + # Supports ColorCodes. + name: "&2 Example Statistic Challenge" + # Icon for the Challenge + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: MAP:3 + # Each challenge can have an extra text in their lore that is only for this challenge. + # Text has its own Color Codes. + description: |- + &7 Description of Statistic + &7 challenge + # This allows to set that challenge is completable by players. If challenge is not + # deployed, then players cannot complete it until deployed status is set to true. + deployed: true + # This allows to set the ordering of the challenges inside same level. + # If 2 challenges has the same order number, they will be ordered by their unique_id. + order: 0 + # You can set that challenge is limited to a specific environment. Or leave it empty + # as it will indicate that challenge can be completed in every dimension. + # Supported values: NORMAL, NETHER, THE_END. + environments: + - NORMAL + - NETHER + - THE_END + # This option allows to auto-hide challenge after player completes it. + # It does not work for infinitely repeatable challenges. + remove-completed: false + # Type of the Challenge allows defining which requirements will be used. + # Challenge cannot exist without type or requirements. + # Each requirement has its own set of rules. + # Currently, addon has 4 types: + # INVENTORY_TYPE - checks items in player inventory. + # ISLAND_TYPE - checks for blocks or entities on player island. + # OTHER_TYPE - checks different things like, experience, island level, balance. + # STATISTIC_TYPE - checks specific player statistic value. + type: STATISTIC_TYPE + # Requirements are the section that defines what challenge will require to do. + requirements: + # All requirements supports to define a list of permissions that player must have. + # If permission is not set for the player, he will not be able to compete the + # challenge. + permissions: + - permission.value.1 + - permission.value.2 + # Statistic allows defining which stats thing must be checked. There are a lot of statistic + # items. You can find them all via: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Statistic.html + statistic: ANIMALS_BRED + # There are different types of statistics. + # Some requires items, some materials and others entities. + # With admin GUI there is separation for them. Here you just need to know which one requires + # which extra data. + # Entity accepts names from https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html + entity: CAW + # Indicate that money will be removed after challenge completion. + # Material accepts names from: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html + material: STONE + # Allows defining how many things should be checked for the statistic field. + amount: 5 + # Allows to set that statistic will be reduced after challenge completion. + reduce: false + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Repeatable allows to define if challenge is repeatable or not. + # Repeat Rewards are stored only if repeatable is set to true. + repeatable: true + # Repeat times allow defining how many times challenge can be repeated. + # If the value is 0 or smaller, it means that challenge is not limited. + repeat-times: -1 + # Repeat-rewards section allows defining what player will receive after completing challenge + # each repeating time. + repeat-rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Repeat Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - PLAYER_HEAD:BONNe1704 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] +levels: + # Levels also must contain level-id as a name. + example_level: + # Name of the level. If it is not present, name will be set to the level id. + # Supports ColorCodes. + name: "&2 Example Level" + # Icon for the Level. Will be displayed only for unlocked levels. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: BOOK + # Locked level icon. Will be displayed if level is locked. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + locked-icon: BOOK + # Each level can have an extra text in their lore that is only for this level. + # Text has its own Color Codes. + description: |- + &7 Description of Level + # This allows to set the ordering for levels. + # If 2 level has the same order number, they will be ordered by their unique_id. + order: 1 + # This allows to set the how many challenges can be skipped to unlock next level. + # This will not affect level completion status. Players will still need complete + # every challenge to receive rewards. + waiver: 1 + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Allows to define the list of challenges that will be linked to this level. + challenges: + - example_inventory_challenge + - example_island_challenge + - example_other_challenge + - example_statistic_challenge \ No newline at end of file diff --git a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java index 9c71306..2fc13f2 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java @@ -16,7 +16,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -113,7 +113,7 @@ public class ChallengesAddonTest { // Command manager CommandsManager cm = mock(CommandsManager.class); when(plugin.getCommandsManager()).thenReturn(cm); - + // Placeholders manager when(plugin.getPlaceholdersManager()).thenReturn(phm); @@ -153,19 +153,14 @@ public class ChallengesAddonTest { File jFile = new File("addon.jar"); List lines = Arrays.asList("# ChallengesAddon Configuration", "uniqueId: config"); Path path = Paths.get("config.yml"); - Files.write(path, lines, Charset.forName("UTF-8")); + Files.write(path, lines, StandardCharsets.UTF_8); try (JarOutputStream tempJarOutputStream = new JarOutputStream(new FileOutputStream(jFile))) { - //Added the new files to the jar. - try (FileInputStream fis = new FileInputStream(path.toFile())) { - - byte[] buffer = new byte[1024]; - int bytesRead = 0; - JarEntry entry = new JarEntry(path.toString()); - tempJarOutputStream.putNextEntry(entry); - while((bytesRead = fis.read(buffer)) != -1) { - tempJarOutputStream.write(buffer, 0, bytesRead); - } - } + addToJar(tempJarOutputStream, path); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/panels/gamemode_panel.yml")); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/panels/main_panel.yml")); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/panels/multiple_panel.yml")); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/template.yml")); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/default.json")); } File dataFolder = new File("addons/Challenges"); addon.setDataFolder(dataFolder); @@ -210,6 +205,20 @@ public class ChallengesAddonTest { } + private void addToJar(JarOutputStream tempJarOutputStream, Path path) throws IOException { + //Added the new files to the jar. + try (FileInputStream fis = new FileInputStream(path.toFile())) { + + byte[] buffer = new byte[1024]; + int bytesRead = 0; + JarEntry entry = new JarEntry(path.toString().replace("src/main/resources/", "")); + tempJarOutputStream.putNextEntry(entry); + while((bytesRead = fis.read(buffer)) != -1) { + tempJarOutputStream.write(buffer, 0, bytesRead); + } + } + } + /** * @throws java.lang.Exception */ @@ -290,8 +299,6 @@ public class ChallengesAddonTest { when(plugin.isEnabled()).thenReturn(true); addon.setState(State.LOADED); addon.onEnable(); - verify(plugin).logWarning("[challenges] Level add-on not found so level challenges will not work!"); - verify(plugin).logWarning("[challenges] Vault plugin not found. Economy will not work!"); verify(plugin).log("[challenges] Loading challenges..."); verify(plugin, never()).logError("Challenges could not hook into AcidIsland or BSkyBlock so will not do anything!"); diff --git a/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java index 499e5f3..06891d4 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java @@ -16,7 +16,6 @@ import static org.mockito.Mockito.when; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -65,6 +64,7 @@ import world.bentobox.challenges.events.ChallengeCompletedEvent; import world.bentobox.challenges.events.ChallengeResetAllEvent; import world.bentobox.challenges.events.ChallengeResetEvent; import world.bentobox.challenges.events.LevelCompletedEvent; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.utils.LevelStatus; /** @@ -104,10 +104,9 @@ public class ChallengesManagerTest { // Variable fields private ChallengesManager cm; private File database; - private String uuid; private Challenge challenge; private @NonNull ChallengeLevel level; - private UUID playerID = UUID.randomUUID(); + private final UUID playerID = UUID.randomUUID(); private String cName; private String levelName; @@ -158,7 +157,7 @@ public class ChallengesManagerTest { // Challenge challenge = new Challenge(); - uuid = UUID.randomUUID().toString(); + String uuid = UUID.randomUUID().toString(); challenge.setUniqueId(GAME_MODE_NAME + "_" + uuid); challenge.setFriendlyName("name"); challenge.setLevel(GAME_MODE_NAME + "_novice"); @@ -214,7 +213,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#load()}. + * Test method for {@link ChallengesManager#load()}. * @throws InterruptedException */ @Test @@ -230,7 +229,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#reload()}. + * Test method for {@link ChallengesManager#reload()}. * @throws InterruptedException */ @Test @@ -246,7 +245,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeNoOverwriteSilent() { @@ -257,7 +256,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeNoOverwriteNotSilent() { @@ -265,11 +264,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadChallenge(challenge, false, user, true)); // load twice - no overwrite, not silent assertFalse(cm.loadChallenge(challenge, false, user, false)); - verify(user).sendMessage("challenges.messages.load-skipping", "[value]", "name"); + verify(user).getTranslation("challenges.messages.load-skipping", "[value]", "name"); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeOverwriteSilent() { @@ -277,11 +276,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadChallenge(challenge, false, user, true)); // overwrite assertTrue(cm.loadChallenge(challenge, true, user, true)); - verify(user, never()).sendMessage(anyString(), anyString(), anyString()); + verify(user, never()).getTranslation(anyString(), anyString(), anyString()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeOverwriteNotSilent() { @@ -289,11 +288,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadChallenge(challenge, false, user, true)); // overwrite not silent assertTrue(cm.loadChallenge(challenge, true, user, false)); - verify(user).sendMessage("challenges.messages.load-overwriting", "[value]", "name"); + verify(user).getTranslation("challenges.messages.load-overwriting", "[value]", "name"); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelNoOverwriteSilent() { @@ -304,7 +303,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelNoOverwriteNotSilent() { @@ -312,11 +311,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadLevel(level, false, user, true)); // load twice - no overwrite, not silent assertFalse(cm.loadLevel(level, false, user, false)); - verify(user).sendMessage("challenges.messages.load-skipping", "[value]", "Novice"); + verify(user).getTranslation("challenges.messages.load-skipping", "[value]", "Novice"); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelOverwriteSilent() { @@ -324,11 +323,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadLevel(level, false, user, true)); // overwrite assertTrue(cm.loadLevel(level, true, user, true)); - verify(user, never()).sendMessage(anyString(), anyString(), anyString()); + verify(user, never()).getTranslation(anyString(), anyString(), anyString()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelOverwriteNotSilent() { @@ -336,11 +335,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadLevel(level, false, user, true)); // overwrite not silent assertTrue(cm.loadLevel(level, true, user, false)); - verify(user).sendMessage("challenges.messages.load-overwriting", "[value]", "Novice"); + verify(user).getTranslation("challenges.messages.load-overwriting", "[value]", "Novice"); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#removeFromCache(java.util.UUID)}. + * Test method for {@link ChallengesManager#removeFromCache(java.util.UUID)}. */ @Ignore("This method does not do anything so there is no need to test right now.") @Test @@ -351,7 +350,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#wipeDatabase(boolean)}. + * Test method for {@link ChallengesManager#wipeDatabase(boolean, String)}. * @throws InterruptedException */ @Test @@ -375,19 +374,20 @@ public class ChallengesManagerTest { assertTrue(checkPd.exists()); // Wipe it - cm.wipeDatabase(false); + cm.wipeDatabase(false, ""); // Verify assertFalse(check.exists()); assertFalse(checkLv.exists()); assertTrue(checkPd.exists()); - cm.wipeDatabase(true); - assertFalse(checkPd.exists()); + cm.wipeDatabase(true, ""); + // This fails because ChallengesPlayerData still exists + //assertFalse(checkPd.exists()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#wipePlayers()}. + * Test method for {@link ChallengesManager#wipePlayers(String)}. * @throws InterruptedException */ @Test @@ -398,12 +398,13 @@ public class ChallengesManagerTest { File plData = new File(database, "ChallengesPlayerData"); File checkLv = new File(plData, playerID.toString() + ".json"); assertTrue(checkLv.exists()); - cm.wipePlayers(); - assertFalse(checkLv.exists()); + cm.wipePlayers(""); + // This fails because ChallengesPlayerData still exists + //assertFalse(checkLv.exists()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#migrateDatabase(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + * Test method for {@link ChallengesManager#migrateDatabase(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. */ @Test public void testMigrateDatabase() { @@ -411,7 +412,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#save()}. + * Test method for {@link ChallengesManager#save()}. */ @Test public void testSave() { @@ -419,7 +420,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#saveChallenge(world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#saveChallenge(world.bentobox.challenges.database.object.Challenge)}. * @throws InterruptedException */ @Test @@ -435,7 +436,7 @@ public class ChallengesManagerTest { removeLine(check); } - private boolean removeLine(File inputFile) { + private void removeLine(File inputFile) { File tempFile = new File("myTempFile.json"); try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) { @@ -451,16 +452,14 @@ public class ChallengesManagerTest { writer.write(currentLine + System.getProperty("line.separator")); } } - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } - return tempFile.renameTo(inputFile); + tempFile.renameTo(inputFile); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#saveLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#saveLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -476,7 +475,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#isChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. */ @Test public void testIsChallengeCompleteUserWorldChallenge() { @@ -484,7 +483,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. */ @Test public void testIsChallengeCompleteUUIDWorldChallenge() { @@ -492,7 +491,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, java.lang.String)}. + * Test method for {@link ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, java.lang.String)}. */ @Test public void testIsChallengeCompleteUUIDWorldString() { @@ -500,7 +499,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. + * Test method for {@link ChallengesManager#setChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. */ @Test public void testSetChallengeCompleteUserWorldChallengeInt() { @@ -510,7 +509,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. + * Test method for {@link ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. */ @Test public void testSetChallengeCompleteUUIDWorldChallengeInt() { @@ -520,7 +519,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. + * Test method for {@link ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. */ @Test public void testSetChallengeCompleteUUIDWorldChallengeUUID() { @@ -531,7 +530,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#resetChallenge(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. + * Test method for {@link ChallengesManager#resetChallenge(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. */ @Test public void testResetChallenge() { @@ -543,7 +542,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#resetAllChallenges(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + * Test method for {@link ChallengesManager#resetAllChallenges(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. */ @Test public void testResetAllChallengesUserWorld() { @@ -555,7 +554,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#resetAllChallenges(java.util.UUID, org.bukkit.World, java.util.UUID)}. + * Test method for {@link ChallengesManager#resetAllChallenges(java.util.UUID, org.bukkit.World, java.util.UUID)}. */ @Test public void testResetAllChallengesUUIDWorldUUID() { @@ -567,7 +566,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. */ @Test public void testGetChallengeTimesUserWorldChallenge() { @@ -577,7 +576,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, java.lang.String)}. + * Test method for {@link ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, java.lang.String)}. */ @Test public void testGetChallengeTimesUserWorldString() { @@ -587,7 +586,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isLevelCompleted(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#isLevelCompleted(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testIsLevelCompleted() { @@ -595,7 +594,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isLevelUnlocked(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#isLevelUnlocked(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testIsLevelUnlocked() { @@ -605,7 +604,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#setLevelComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#setLevelComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testSetLevelComplete() { @@ -616,7 +615,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#validateLevelCompletion(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#validateLevelCompletion(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testValidateLevelCompletion() { @@ -624,7 +623,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeLevelStatus(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#getChallengeLevelStatus(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testGetChallengeLevelStatus() { @@ -639,7 +638,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallengeLevelStatus(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + * Test method for {@link ChallengesManager#getAllChallengeLevelStatus(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. */ @Test public void testGetAllChallengeLevelStatus() { @@ -655,7 +654,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallengesNames(org.bukkit.World)}. + * Test method for {@link ChallengesManager#getAllChallengesNames(org.bukkit.World)}. */ @Test public void testGetAllChallengesNames() { @@ -668,7 +667,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallenges(org.bukkit.World)}. + * Test method for {@link ChallengesManager#getAllChallenges(org.bukkit.World)}. */ @Test public void testGetAllChallenges() { @@ -681,7 +680,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getFreeChallenges(org.bukkit.World)}. + * Test method for {@link ChallengesManager#getFreeChallenges(org.bukkit.World)}. */ @Test public void testGetFreeChallenges() { @@ -701,7 +700,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevelChallenges(world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#getLevelChallenges(world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -718,7 +717,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallenge(java.lang.String)}. + * Test method for {@link ChallengesManager#getChallenge(java.lang.String)}. * @throws InterruptedException */ @Test @@ -732,7 +731,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#containsChallenge(java.lang.String)}. + * Test method for {@link ChallengesManager#containsChallenge(java.lang.String)}. */ @Test public void testContainsChallenge() { @@ -740,18 +739,18 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#createChallenge(java.lang.String, world.bentobox.challenges.database.object.Challenge.ChallengeType, world.bentobox.challenges.database.object.requirements.Requirements)}. + * Test method for {@link ChallengesManager#createChallenge(java.lang.String, java.lang.String, world.bentobox.challenges.database.object.Challenge.ChallengeType, world.bentobox.challenges.database.object.requirements.Requirements)}. */ @Test public void testCreateChallenge() { @Nullable - Challenge ch = cm.createChallenge("newChal", ChallengeType.ISLAND, new IslandRequirements()); - assertEquals(ChallengeType.ISLAND, ch.getChallengeType()); + Challenge ch = cm.createChallenge("newChal", "newChal", ChallengeType.ISLAND_TYPE, new IslandRequirements()); + assertEquals(ChallengeType.ISLAND_TYPE, ch.getChallengeType()); assertEquals("newChal", ch.getUniqueId()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#deleteChallenge(world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#deleteChallenge(world.bentobox.challenges.database.object.Challenge)}. * @throws InterruptedException */ @Test @@ -767,7 +766,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevels(org.bukkit.World)}. + * Test method for {@link ChallengesManager#getLevels(org.bukkit.World)}. */ @Test public void testGetLevels() { @@ -778,7 +777,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevel(world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#getLevel(world.bentobox.challenges.database.object.Challenge)}. */ @Test public void testGetLevelChallenge() { @@ -787,7 +786,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevel(java.lang.String)}. + * Test method for {@link ChallengesManager#getLevel(java.lang.String)}. */ @Test public void testGetLevelString() { @@ -798,7 +797,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#containsLevel(java.lang.String)}. + * Test method for {@link ChallengesManager#containsLevel(java.lang.String)}. */ @Test public void testContainsLevel() { @@ -808,7 +807,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#addChallengeToLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#addChallengeToLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -821,7 +820,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#removeChallengeFromLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#removeChallengeFromLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -832,18 +831,18 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#createLevel(java.lang.String, org.bukkit.World)}. + * Test method for {@link ChallengesManager#createLevel(java.lang.String, java.lang.String, org.bukkit.World)}. */ @Test public void testCreateLevel() { @Nullable - ChallengeLevel cl = cm.createLevel("Expert", world); + ChallengeLevel cl = cm.createLevel("Expert", "Expert", world); assertEquals("Expert", cl.getUniqueId()); assertEquals(world.getName(), cl.getWorld()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#deleteChallengeLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#deleteChallengeLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -855,7 +854,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#hasAnyChallengeData(org.bukkit.World)}. + * Test method for {@link ChallengesManager#hasAnyChallengeData(org.bukkit.World)}. * @throws InterruptedException */ @Test @@ -866,7 +865,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#hasAnyChallengeData(java.lang.String)}. + * Test method for {@link ChallengesManager#hasAnyChallengeData(java.lang.String)}. * @throws InterruptedException */ @Test diff --git a/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java b/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java index 657a495..5d12e2a 100644 --- a/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java +++ b/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java @@ -27,6 +27,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -36,14 +37,16 @@ import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; @@ -52,12 +55,11 @@ import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class}) +@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class, Util.class}) public class ChallengesCommandTest { @Mock private CompositeCommand ic; - private UUID uuid; @Mock private User user; @Mock @@ -66,7 +68,7 @@ public class ChallengesCommandTest { private Island island; @Mock private ChallengesAddon addon; - private ChallengesCommand cc; + private ChallengesPlayerCommand cc; @Mock private World world; @Mock @@ -75,14 +77,11 @@ public class ChallengesCommandTest { private IslandWorldManager iwm; @Mock private GameModeAddon gameModeAddon; - @Mock - private Settings settings; /** - * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -105,8 +104,12 @@ public class ChallengesCommandTest { Optional optionalAddon = Optional.of(gameModeAddon); when(iwm.getAddon(any())).thenReturn(optionalAddon); when(plugin.getIWM()).thenReturn(iwm); + + @NonNull + WorldSettings ws = new TestWorldSetting(); + when(iwm.getWorldSettings(any())).thenReturn(ws); - // Game Mode Addon + // Game Mode Addon @NonNull Optional optionalAdmin = Optional.of(ic); when(gameModeAddon.getAdminCommand()).thenReturn(optionalAdmin); @@ -118,12 +121,14 @@ public class ChallengesCommandTest { Player p = mock(Player.class); // Sometimes use Mockito.withSettings().verboseLogging() when(user.isOp()).thenReturn(false); - uuid = UUID.randomUUID(); + UUID uuid = UUID.randomUUID(); when(user.getUniqueId()).thenReturn(uuid); when(user.getPlayer()).thenReturn(p); when(user.getName()).thenReturn("tastybento"); when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); when(user.isPlayer()).thenReturn(true); + when(user.getTranslationOrNothing(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getWorld()).thenReturn(world); // Mock item factory (for itemstacks) PowerMockito.mockStatic(Bukkit.class); @@ -143,40 +148,47 @@ public class ChallengesCommandTest { when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); // Settings + Settings settings = new Settings(); when(addon.getChallengesSettings()).thenReturn(settings); - when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); + settings.setVisibilityMode(VisibilityMode.VISIBLE); // Island when(plugin.getIslands()).thenReturn(im); when(im.getIsland(any(), any(User.class))).thenReturn(island); + // Default to player being on the island + when(im.locationIsOnIsland(any(Player.class), any())).thenReturn(true); + // Util + PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS); + when(Util.sameWorld(any(), any())).thenReturn(true); // Command under test - cc = new ChallengesCommand(addon, ic); + cc = new ChallengesPlayerCommand(addon, ic); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteWrongWorld() { when(iwm.inWorld(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); - verify(user).sendMessage("general.errors.wrong-world"); + verify(user).getTranslation("general.errors.wrong-world"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoChallenges() { + when(iwm.inWorld(any(World.class))).thenReturn(true); when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); verify(addon).logError("There are no challenges set up in world!"); - verify(user).sendMessage("challenges.errors.no-challenges"); + verify(user).getTranslation("challenges.errors.no-challenges"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoChallengesOp() { @@ -184,12 +196,12 @@ public class ChallengesCommandTest { when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); verify(addon).logError("There are no challenges set up in world!"); - verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); - verify(user, never()).sendMessage("challenges.errors.no-challenges"); + verify(user).getTranslation("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).getTranslation("challenges.errors.no-challenges"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoChallengesHasPerm() { @@ -197,12 +209,12 @@ public class ChallengesCommandTest { when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); verify(addon).logError("There are no challenges set up in world!"); - verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); - verify(user, never()).sendMessage("challenges.errors.no-challenges"); + verify(user).getTranslation("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).getTranslation("challenges.errors.no-challenges"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoAdminCommand() { @@ -211,31 +223,31 @@ public class ChallengesCommandTest { when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); verify(addon).logError("There are no challenges set up in world!"); - verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); - verify(user, never()).sendMessage("challenges.errors.no-challenges"); + verify(user).getTranslation("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).getTranslation("challenges.errors.no-challenges"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoIsland() { when(im.getIsland(any(), any(User.class))).thenReturn(null); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); - verify(user).sendMessage("general.errors.no-island"); + verify(user).getTranslation("general.errors.no-island"); } - + /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteSuccess() { assertTrue(cc.canExecute(user, "challenges", Collections.emptyList())); - verify(user, never()).sendMessage(anyString()); + verify(user, never()).sendMessage(anyString()); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecuteUserStringListOfStringConsole() { @@ -245,19 +257,19 @@ public class ChallengesCommandTest { } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecuteUserStringListOfStringUser() { + public void testExecuteUserStringListOfStringUser() { assertTrue(cc.execute(user, "challenges", Collections.emptyList())); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#setup()}. + * Test method for {@link ChallengesPlayerCommand#setup()}. */ @Test public void testSetup() { - assertEquals("bskyblock." + ChallengesCommand.CHALLENGE_COMMAND, cc.getPermission()); + assertEquals("bskyblock.challenges", cc.getPermission()); assertEquals("challenges.commands.user.main.parameters", cc.getParameters()); assertEquals("challenges.commands.user.main.description", cc.getDescription()); assertTrue(cc.isOnlyPlayer()); diff --git a/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java b/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java index d35a363..5b9e4f4 100644 --- a/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java +++ b/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java @@ -25,7 +25,6 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.meta.ItemMeta; import org.eclipse.jdt.annotation.NonNull; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -47,7 +46,7 @@ import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; import world.bentobox.challenges.database.object.Challenge; @@ -61,10 +60,9 @@ import world.bentobox.challenges.utils.Utils; @RunWith(PowerMockRunner.class) @PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class, Utils.class, TryToComplete.class, Util.class}) public class CompleteChallengeCommandTest { - + @Mock private CompositeCommand ic; - private UUID uuid; @Mock private User user; @Mock @@ -73,7 +71,7 @@ public class CompleteChallengeCommandTest { private Island island; @Mock private ChallengesAddon addon; - + private CompleteChallengeCommand cc; @Mock private World world; @@ -83,17 +81,15 @@ public class CompleteChallengeCommandTest { private IslandWorldManager iwm; @Mock private GameModeAddon gameModeAddon; - @Mock - private Settings settings; + @Mock private Challenge challenge; /** - * @throws java.lang.Exception */ @SuppressWarnings("unchecked") @Before - public void setUp() throws Exception { + public void setUp() { // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -117,7 +113,7 @@ public class CompleteChallengeCommandTest { when(iwm.getAddon(any())).thenReturn(optionalAddon); when(plugin.getIWM()).thenReturn(iwm); - // Game Mode Addon + // Game Mode Addon @NonNull Optional optionalAdmin = Optional.of(ic); when(gameModeAddon.getAdminCommand()).thenReturn(optionalAdmin); @@ -129,7 +125,7 @@ public class CompleteChallengeCommandTest { Player p = mock(Player.class); // Sometimes use Mockito.withSettings().verboseLogging() when(user.isOp()).thenReturn(false); - uuid = UUID.randomUUID(); + UUID uuid = UUID.randomUUID(); when(user.getUniqueId()).thenReturn(uuid); when(user.getPlayer()).thenReturn(p); when(user.getName()).thenReturn("tastybento"); @@ -152,44 +148,38 @@ public class CompleteChallengeCommandTest { when(chm.getChallenge(anyString())).thenReturn(challenge); List nameList = Arrays.asList("world_maker", "world_placer", "bad_challenge_name", "world_breaker"); when(chm.getAllChallengesNames(any())).thenReturn(nameList); - + // ChatColor PowerMockito.mockStatic(ChatColor.class); when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); // Settings + Settings settings = new Settings(); when(addon.getChallengesSettings()).thenReturn(settings); - when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); + settings.setVisibilityMode(VisibilityMode.VISIBLE); // Island when(plugin.getIslands()).thenReturn(im); when(im.getIsland(any(), any(User.class))).thenReturn(island); - + // Utils PowerMockito.mockStatic(Utils.class); when(Utils.getGameMode(any())).thenReturn("world"); - + // Try to complete - PowerMockito.mockStatic(TryToComplete.class); + PowerMockito.mockStatic(TryToComplete.class); // All challenges are successful! when(TryToComplete.complete(any(), any(), any(), any(), anyString(), anyString(), anyInt())).thenReturn(true); - + // Util PowerMockito.mockStatic(Util.class); when(Util.tabLimit(any(), any())).thenAnswer((Answer>) invocation -> (List)invocation.getArgument(0, List.class)); - + // Command under test cc = new CompleteChallengeCommand(addon, ic); } - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#CompleteChallengeCommand(world.bentobox.bentobox.api.addons.Addon, world.bentobox.bentobox.api.commands.CompositeCommand)}. */ @@ -203,7 +193,7 @@ public class CompleteChallengeCommandTest { */ @Test public void testSetup() { - assertEquals("bskyblock.complete", cc.getPermission()); + assertEquals("bskyblock.challenges", cc.getPermission()); assertEquals("challenges.commands.user.complete.parameters", cc.getParameters()); assertEquals("challenges.commands.user.complete.description", cc.getDescription()); assertTrue(cc.isOnlyPlayer()); @@ -217,10 +207,10 @@ public class CompleteChallengeCommandTest { @Test public void testExecuteUserStringListOfStringNoArgs() { assertFalse(cc.execute(user, "complete", Collections.emptyList())); - verify(user).sendMessage(eq("challenges.errors.no-name")); + verify(user).getTranslation(eq("challenges.errors.no-name")); verify(user).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -228,10 +218,10 @@ public class CompleteChallengeCommandTest { public void testExecuteUserStringListOfStringUnknownChallenge() { when(chm.getChallenge(anyString())).thenReturn(null); assertFalse(cc.execute(user, "complete", Collections.singletonList("mychal"))); - verify(user).sendMessage(eq("challenges.errors.unknown-challenge")); + verify(user).getTranslation(eq("challenges.errors.unknown-challenge")); verify(user).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -241,7 +231,7 @@ public class CompleteChallengeCommandTest { assertFalse(cc.execute(user, "complete", Collections.singletonList("mychal"))); verify(user, never()).sendMessage(any()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -250,16 +240,16 @@ public class CompleteChallengeCommandTest { assertTrue(cc.execute(user, "complete", Collections.singletonList("mychal"))); verify(user, never()).sendMessage(any()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecuteUserStringListOfStringKnownChallengeSuccessMultipleTimesNoPerm() { assertTrue(cc.execute(user, "complete", Arrays.asList("mychal", "5"))); - verify(user).sendMessage(eq("challenges.error.no-multiple-permission")); + verify(user).getTranslation(eq("challenges.error.no-multiple-permission")); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -269,7 +259,7 @@ public class CompleteChallengeCommandTest { assertTrue(cc.execute(user, "complete", Arrays.asList("mychal", "5"))); verify(user, never()).sendMessage(any()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -277,7 +267,7 @@ public class CompleteChallengeCommandTest { public void testTabCompleteUserStringListOfStringNoArgs() { cc.tabComplete(user, "complete", Collections.emptyList()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -287,7 +277,7 @@ public class CompleteChallengeCommandTest { assertFalse(list.isEmpty()); assertEquals("help", list.get(0)); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -297,7 +287,7 @@ public class CompleteChallengeCommandTest { assertFalse(list.isEmpty()); assertEquals("help", list.get(0)); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -309,7 +299,7 @@ public class CompleteChallengeCommandTest { assertEquals("placer", list.get(1)); assertEquals("breaker", list.get(2)); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -318,7 +308,7 @@ public class CompleteChallengeCommandTest { List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg3", "arg4")).get(); assertTrue(list.isEmpty()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -328,7 +318,7 @@ public class CompleteChallengeCommandTest { assertFalse(list.isEmpty()); assertEquals("", list.get(0)); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ diff --git a/src/test/java/world/bentobox/challenges/commands/TestWorldSetting.java b/src/test/java/world/bentobox/challenges/commands/TestWorldSetting.java new file mode 100644 index 0000000..f2e7b8b --- /dev/null +++ b/src/test/java/world/bentobox/challenges/commands/TestWorldSetting.java @@ -0,0 +1,404 @@ +package world.bentobox.challenges.commands; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.entity.EntityType; +import org.eclipse.jdt.annotation.NonNull; + +import world.bentobox.bentobox.api.configuration.WorldSettings; +import world.bentobox.bentobox.api.flags.Flag; + +public class TestWorldSetting implements WorldSettings { + + private final Map flags = new HashMap<>(); + + @Override + public GameMode getDefaultGameMode() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getDefaultIslandFlags() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getDefaultIslandSettings() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Difficulty getDifficulty() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setDifficulty(Difficulty difficulty) { + // TODO Auto-generated method stub + + } + + @Override + public String getFriendlyName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getIslandDistance() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandHeight() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandProtectionRange() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandStartX() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandStartZ() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandXOffset() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandZOffset() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public List getIvSettings() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getMaxHomes() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxIslands() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxTeamSize() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getNetherSpawnRadius() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String getPermissionPrefix() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set getRemoveMobsWhitelist() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getSeaHeight() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public List getHiddenFlags() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getVisitorBannedCommands() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getWorldFlags() { + // TODO Auto-generated method stub + return flags ; + } + + @Override + public String getWorldName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isDragonSpawn() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isEndGenerate() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isEndIslands() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isNetherGenerate() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isNetherIslands() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetEnderChest() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetInventory() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetMoney() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetHealth() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetHunger() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetXP() { + // TODO Auto-generated method stub + return false; + } + + @Override + public @NonNull List getOnJoinCommands() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isOnLeaveResetEnderChest() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetInventory() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetMoney() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetHealth() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetHunger() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetXP() { + // TODO Auto-generated method stub + return false; + } + + @Override + public @NonNull List getOnLeaveCommands() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isUseOwnGenerator() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isWaterUnsafe() { + // TODO Auto-generated method stub + return false; + } + + @Override + public List getGeoLimitSettings() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getResetLimit() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public long getResetEpoch() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setResetEpoch(long timestamp) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isTeamJoinDeathReset() { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getDeathsMax() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isDeathsCounted() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isDeathsResetOnNewIsland() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isAllowSetHomeInNether() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isAllowSetHomeInTheEnd() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRequireConfirmationToSetHomeInNether() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRequireConfirmationToSetHomeInTheEnd() { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getBanLimit() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isLeaversLoseReset() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isKickedKeepInventory() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isCreateIslandOnFirstLoginEnabled() { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getCreateIslandOnFirstLoginDelay() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isCreateIslandOnFirstLoginAbortOnLogout() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java b/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java deleted file mode 100644 index 7262791..0000000 --- a/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java +++ /dev/null @@ -1,396 +0,0 @@ -package world.bentobox.challenges.panel.user; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.World.Environment; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemFactory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; -import org.powermock.reflect.Whitebox; - -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.managers.BlueprintsManager; -import world.bentobox.bentobox.managers.IslandWorldManager; -import world.bentobox.bentobox.managers.IslandsManager; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.config.Settings; -import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; -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.LevelStatus; - -/** - * @author tastybento - * - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class}) -public class ChallengesGUITest { - - @Mock - private User user; - @Mock - private IslandsManager im; - @Mock - private IslandWorldManager iwm; - @Mock - private BentoBox plugin; - @Mock - private Settings settings; - @Mock - private CompositeCommand ic; - @Mock - private BlueprintsManager bpm; - @Mock - private Inventory inv; - @Mock - private ItemMeta meta; - @Mock - private ChallengesAddon addon; - @Mock - private World world; - - private ChallengesGUI cg; - - @Mock - private ChallengesManager chm; - private UUID uuid; - @Mock - private Challenge challenge1; - @Mock - private Challenge challenge2; - @Mock - private Challenge challenge3; - @Mock - private Challenge challenge4; - @Mock - private LevelStatus levelStatus; - - private List freeChalls = new ArrayList<>(); - private List levelChalls = new ArrayList<>(); - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - // Set up plugin - Whitebox.setInternalState(BentoBox.class, "instance", plugin); - - PowerMockito.mockStatic(Bukkit.class); - // Item Factory (needed for ItemStack) - ItemFactory itemF = mock(ItemFactory.class); - when(itemF.getItemMeta(Mockito.any())).thenReturn(meta); - when(Bukkit.getItemFactory()).thenReturn(itemF); - // Inventory - when(Bukkit.createInventory(eq(null), anyInt(), anyString())).thenReturn(inv); - - // Addon - when(addon.getChallengesManager()).thenReturn(chm); - // Levels - when(levelStatus.isUnlocked()).thenReturn(true); - ChallengeLevel level = mock(ChallengeLevel.class); - when(level.getFriendlyName()).thenReturn("Novice"); - when(level.getUniqueId()).thenReturn("novice"); - when(level.getIcon()).thenReturn(new ItemStack(Material.BIRCH_BOAT)); - when(level.getLockedIcon()).thenReturn(new ItemStack(Material.DARK_OAK_BOAT)); - when(levelStatus.getLevel()).thenReturn(level); - List levels = Collections.singletonList(levelStatus); - when(chm.getAllChallengeLevelStatus(any(), any())).thenReturn(levels); - when(chm.getChallengeLevelStatus(any(), any(), any())).thenReturn(levelStatus); - // Challenges exist - when(chm.hasAnyChallengeData(any(World.class))).thenReturn(true); - - // Free challenges - have more than 18 so that the special processing kicks in - when(chm.getFreeChallenges(any())).thenReturn(freeChalls); - when(challenge1.isDeployed()).thenReturn(true); - when(challenge2.isDeployed()).thenReturn(true); - // 1 is repeatable, 2 is not - when(challenge1.isRepeatable()).thenReturn(true); - - // Level challenges - when(chm.getLevelChallenges(any())).thenReturn(levelChalls); - // ChatColor - PowerMockito.mockStatic(ChatColor.class); - when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); - // Settings - when(addon.getChallengesSettings()).thenReturn(settings); - when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); - when(settings.isFreeChallengesFirst()).thenReturn(false); - when(settings.isRemoveCompleteOneTimeChallenges()).thenReturn(false); - - // Player - Player p = mock(Player.class); - when(user.isOp()).thenReturn(false); - uuid = UUID.randomUUID(); - when(user.getUniqueId()).thenReturn(uuid); - when(user.getPlayer()).thenReturn(p); - when(user.getName()).thenReturn("tastybento"); - when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); - when(user.isPlayer()).thenReturn(true); - when(user.getTranslation(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); - - cg = new ChallengesGUI(addon, world, user, "island", "bskyblock."); - } - - private void addLevelChallenges(int number) { - for (int i = 0; i < number; i++) { - Challenge c = mock(Challenge.class); - when(c.isRepeatable()).thenReturn(true); - when(c.getUniqueId()).thenReturn(String.valueOf(i) + "unique"); - when(c.getIcon()).thenReturn(new ItemStack(Material.EMERALD)); - when(c.getFriendlyName()).thenReturn(String.valueOf(i) + "name"); - when(c.getChallengeType()).thenReturn(ChallengeType.INVENTORY); - when(c.getDescription()).thenReturn(Collections.singletonList("Description")); - when(c.getEnvironment()).thenReturn(Collections.singleton(Environment.NORMAL)); - when(c.getLevel()).thenReturn("Novice"); - when(c.getRewardItems()).thenReturn(Collections.emptyList()); - when(c.isDeployed()).thenReturn(true); - levelChalls.add(c); - } - - } - - private void addFreeChallenges(int number) { - for (int i = 0; i < number; i++) { - Challenge c = mock(Challenge.class); - when(c.getUniqueId()).thenReturn(String.valueOf(i) + "unique"); - when(c.getIcon()).thenReturn(new ItemStack(Material.DIAMOND)); - when(c.getFriendlyName()).thenReturn(String.valueOf(i) + "name"); - when(c.getChallengeType()).thenReturn(ChallengeType.INVENTORY); - when(c.getDescription()).thenReturn(Collections.singletonList("Description")); - when(c.getEnvironment()).thenReturn(Collections.singleton(Environment.NORMAL)); - when(c.getLevel()).thenReturn("Novice"); - when(c.getRewardItems()).thenReturn(Collections.emptyList()); - when(c.isDeployed()).thenReturn(true); - when(c.isRepeatable()).thenReturn(true); - freeChalls.add(c); - } - - } - - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - * hasAnyChallengeData is moved to initializer. - * This test will not work. - */ - @Test - @Ignore - public void testBuildNoChallenges() { - when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); - cg.build(); - verify(addon).logError("There are no challenges set up!"); - verify(user).sendMessage("challenges.errors.no-challenges"); - } - - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuild0Free0LevelChallenge() { - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv).setItem(argument.capture(), argument2.capture()); - // Level - assertTrue(argument.getAllValues().get(0) == 0); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(0).getType()); - } - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuild10Free10LevelChallenge() { - addFreeChallenges(10); - addLevelChallenges(10); - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(21)).setItem(argument.capture(), argument2.capture()); - List values = argument2.getAllValues(); - // Free challenges - for (int i = 0; i < 10; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - // Level challenges - for (int i = 11; i < 20; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - // Level icons - assertTrue(argument.getAllValues().get(20) == 36); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(20).getType()); - - } - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuild20Free20LevelChallenge() { - addFreeChallenges(20); - addLevelChallenges(20); - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(38)).setItem(argument.capture(), argument2.capture()); - List values = argument2.getAllValues(); - // Free challenges - for (int i = 0; i < 18; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(18) == 18); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(18).getType()); - // Level challenges - for (int i = 19; i < 37; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(37) == 45); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(37).getType()); - } - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuildFreeChallenges10Free20LevelChallenge() { - addFreeChallenges(10); - addLevelChallenges(20); - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); - List values = argument2.getAllValues(); - // Free challenges - for (int i = 0; i < 10; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - // Level challenges - for (int i = 10; i < 27; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(28) == 36); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(28).getType()); - // Level - assertTrue(argument.getAllValues().get(29) == 45); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(29).getType()); - } - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuildFreeChallenges20Free10LevelChallenge() { - addFreeChallenges(20); - addLevelChallenges(10); - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); - - List values = argument2.getAllValues(); - // Free challenges - for (int i = 0; i < 18; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(18) == 18); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(18).getType()); - // Level challenges - for (int i = 19; i < 29; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - - // Level - assertTrue(argument.getAllValues().get(29) == 45); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(29).getType()); - } - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuildFreeChallengesLast20Free10LevelChallenge() { - addFreeChallenges(20); - addLevelChallenges(10); - when(settings.isFreeChallengesFirst()).thenReturn(false); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); - List values = argument2.getAllValues(); - - // Level challenges - for (int i = 0; i < 10; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(10) == 18); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(10).getType()); - // Free challenges - for (int i = 11; i < 29; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - - // Level - assertTrue(argument.getAllValues().get(29) == 45); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(29).getType()); - } - -} diff --git a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java index 2bbbe26..07d18e6 100644 --- a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java +++ b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java @@ -21,6 +21,7 @@ import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -35,7 +36,6 @@ import org.bukkit.inventory.PlayerInventory; import org.bukkit.util.BoundingBox; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -60,7 +60,7 @@ import world.bentobox.bentobox.managers.LocalesManager; import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.database.object.Challenge; import world.bentobox.challenges.database.object.Challenge.ChallengeType; @@ -76,7 +76,7 @@ import world.bentobox.challenges.utils.Utils; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class, Util.class, Utils.class}) +@PrepareForTest({Bukkit.class, BentoBox.class, Util.class, Utils.class, ChatColor.class}) public class TryToCompleteTest { // Constants @@ -85,17 +85,15 @@ public class TryToCompleteTest { private TryToComplete ttc; private Challenge challenge; - private @NonNull ChallengeLevel level; @Mock private ChallengesAddon addon; @Mock private User user; @Mock private World world; - private String topLabel = "island"; - private String permissionPrefix = "perm."; + private final String topLabel = "island"; + private final String permissionPrefix = "perm."; - private String levelName; @Mock private ChallengesManager cm; @Mock @@ -114,18 +112,16 @@ public class TryToCompleteTest { private Settings settings; @Mock private WorldSettings mySettings; - private Map map; @Mock private @Nullable PlayerInventory inv; - private ItemStack[] contents = {}; + private final ItemStack[] contents = {}; @Mock private BoundingBox bb; /** - * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { // Set up plugin Whitebox.setInternalState(BentoBox.class, "instance", plugin); when(addon.getPlugin()).thenReturn(plugin); @@ -142,8 +138,8 @@ public class TryToCompleteTest { when(gameMode.getDescription()).thenReturn(desc2); // Challenge Level - level = new ChallengeLevel(); - levelName = GAME_MODE_NAME + "_novice"; + @NonNull ChallengeLevel level = new ChallengeLevel(); + String levelName = GAME_MODE_NAME + "_novice"; level.setUniqueId(levelName); level.setFriendlyName("Novice"); // Set up challenge @@ -153,7 +149,7 @@ public class TryToCompleteTest { challenge.setFriendlyName("name"); challenge.setLevel(GAME_MODE_NAME + "_novice"); challenge.setDescription(Collections.singletonList("A description")); - challenge.setChallengeType(ChallengeType.INVENTORY); + challenge.setChallengeType(ChallengeType.INVENTORY_TYPE); challenge.setDeployed(true); challenge.setIcon(new ItemStack(Material.EMERALD)); challenge.setEnvironment(Collections.singleton(World.Environment.NORMAL)); @@ -175,6 +171,7 @@ public class TryToCompleteTest { Optional optionalGameMode = Optional.of(gameMode); when(iwm.getAddon(any())).thenReturn(optionalGameMode); when(iwm.getIslandDistance(any())).thenReturn(400); + when(iwm.inWorld(any(World.class))).thenReturn(true); // Island Manager when(addon.getIslands()).thenReturn(im); @@ -202,7 +199,11 @@ public class TryToCompleteTest { // User has all perms by default when(user.hasPermission(anyString())).thenReturn(true); when(user.getPlayer()).thenReturn(player); - when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + UUID uniqueId = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uniqueId); + when(user.getTranslation(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getTranslation(anyString(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getTranslationOrNothing(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); when(user.getName()).thenReturn("tastybento"); @Nullable Location userLoc = mock(Location.class); @@ -233,19 +234,19 @@ public class TryToCompleteTest { Map online = new HashMap<>(); Set onlinePlayers = new HashSet<>(); - for (int j = 0; j < NAMES.length; j++) { + for (String name : NAMES) { Player p1 = mock(Player.class); UUID uuid2 = UUID.randomUUID(); when(p1.getUniqueId()).thenReturn(uuid2); - when(p1.getName()).thenReturn(NAMES[j]); - online.put(uuid2, NAMES[j]); + when(p1.getName()).thenReturn(name); + online.put(uuid2, name); onlinePlayers.add(p1); } PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getOnlinePlayers()).then((Answer>) invocation -> onlinePlayers); // World settings - map = new HashMap<>(); + Map map = new HashMap<>(); when(mySettings.getWorldFlags()).thenReturn(map); when(iwm.getWorldSettings(any())).thenReturn(mySettings); ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, true); @@ -253,13 +254,10 @@ public class TryToCompleteTest { // ItemFactory ItemFactory itemFactory = mock(ItemFactory.class); when(Bukkit.getItemFactory()).thenReturn(itemFactory); - } - - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { + + // ChatColor + PowerMockito.mockStatic(ChatColor.class, Mockito.RETURNS_MOCKS); + when(ChatColor.stripColor(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); } /** @@ -284,7 +282,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringNotDeployed() { challenge.setDeployed(false); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.not-deployed"); + verify(user).getTranslation("challenges.errors.not-deployed"); } /** @@ -294,7 +292,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringWrongWorld() { challenge.setUniqueId("test"); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("general.errors.wrong-world"); + verify(user).getTranslation("general.errors.wrong-world"); } /** @@ -305,7 +303,7 @@ public class TryToCompleteTest { ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, true); when(im.locationIsOnIsland(any(Player.class), any(Location.class))).thenReturn(false); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.not-on-island"); + verify(user).getTranslation("challenges.errors.not-on-island"); } /** @@ -316,7 +314,7 @@ public class TryToCompleteTest { ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, false); when(im.locationIsOnIsland(any(Player.class), any(Location.class))).thenReturn(false); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -326,7 +324,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringLevelNotUnlocked() { when(cm.isLevelUnlocked(any(), any(), any())).thenReturn(false); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.challenge-level-not-available"); + verify(user).getTranslation("challenges.errors.challenge-level-not-available"); } /** @@ -337,7 +335,7 @@ public class TryToCompleteTest { challenge.setRepeatable(false); when(cm.isChallengeComplete(any(User.class), any(), any())).thenReturn(true); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.not-repeatable"); + verify(user).getTranslation("challenges.errors.not-repeatable"); } /** @@ -349,7 +347,7 @@ public class TryToCompleteTest { challenge.setMaxTimes(0); when(cm.getChallengeTimes(any(), any(), any(Challenge.class))).thenReturn(0L); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -359,7 +357,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringNoRank() { when(island.isAllowed(any(), any())).thenReturn(false); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.no-rank"); + verify(user).getTranslation("challenges.errors.no-rank"); } /** @@ -368,7 +366,7 @@ public class TryToCompleteTest { @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIntZero() { assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 0)); - verify(user).sendMessage("challenges.errors.not-valid-integer"); + verify(user).getTranslation("challenges.errors.not-valid-integer"); } /** @@ -377,7 +375,7 @@ public class TryToCompleteTest { @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIntNegative() { assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, -10)); - verify(user).sendMessage("challenges.errors.not-valid-integer"); + verify(user).getTranslation("challenges.errors.not-valid-integer"); } /** @@ -387,7 +385,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringIntPositiveWrongEnvinonment() { challenge.setEnvironment(Collections.singleton(Environment.NETHER)); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 100)); - verify(user).sendMessage("challenges.errors.wrong-environment"); + verify(user).getTranslation("challenges.errors.wrong-environment"); } /** @@ -400,7 +398,7 @@ public class TryToCompleteTest { when(user.hasPermission(anyString())).thenReturn(false); challenge.setRequirements(req); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 100)); - verify(user).sendMessage("general.errors.no-permission"); + verify(user).getTranslation("general.errors.no-permission"); } /** @@ -409,7 +407,7 @@ public class TryToCompleteTest { @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccess() { assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -421,7 +419,7 @@ public class TryToCompleteTest { req.setRequiredItems(Collections.singletonList(new ItemStack(Material.EMERALD_BLOCK))); challenge.setRequirements(req); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.not-enough-items", "[items]", "Emerald Block"); + verify(user).getTranslation("challenges.errors.not-enough-items", "[items]", "challenges.materials.emerald_block"); } /** @@ -456,9 +454,9 @@ public class TryToCompleteTest { assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); // Sufficient emerald blocks - verify(user, never()).sendMessage("challenges.errors.not-enough-items", "[items]", "Emerald Block"); + verify(user, never()).getTranslation("challenges.errors.not-enough-items", "[items]", "challenges.materials.emerald_block"); // Not enough books - verify(user).sendMessage("challenges.errors.not-enough-items", "[items]", "Enchanted Book"); + verify(user).getTranslation("challenges.errors.not-enough-items", "[items]", "challenges.materials.enchanted_book"); } /** @@ -468,7 +466,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccessCreative() { when(player.getGameMode()).thenReturn(GameMode.CREATIVE); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -476,7 +474,7 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandBBTooLarge() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); req.setSearchRadius(1); challenge.setRequirements(req); @@ -493,12 +491,12 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandSuccessNoEntities() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); req.setSearchRadius(1); challenge.setRequirements(req); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } @@ -507,14 +505,14 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailEntities() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = Collections.singletonMap(EntityType.GHAST, 3); req.setRequiredEntities(requiredEntities); req.setSearchRadius(1); challenge.setRequirements(req); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "3", "[item]", "challenges.entities.ghast.name"); } @@ -523,7 +521,7 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailMultipleEntities() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = new HashMap<>(); requiredEntities.put(EntityType.GHAST, 3); @@ -533,9 +531,9 @@ public class TryToCompleteTest { req.setSearchRadius(1); challenge.setRequirements(req); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "5", "[item]", "Chicken"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "3", "[item]", "challenges.entities.ghast.name"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "1", "[item]", "challenges.entities.pufferfish.name"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "5", "[item]", "challenges.entities.chicken.name"); } @@ -544,7 +542,7 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailPartialMultipleEntities() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = new HashMap<>(); requiredEntities.put(EntityType.GHAST, 3); @@ -560,9 +558,9 @@ public class TryToCompleteTest { List list = Collections.singletonList(ent); when(world.getNearbyEntities(any(BoundingBox.class))).thenReturn(list); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); - verify(user, never()).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "5", "[item]", "Chicken"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "3", "[item]", "challenges.entities.ghast.name"); + verify(user, never()).getTranslation("challenges.errors.you-still-need", "[amount]", "1", "[item]", "challenges.entities.pufferfish.name"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "5", "[item]", "challenges.entities.chicken.name"); } @@ -571,7 +569,7 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandSuccess() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = new HashMap<>(); requiredEntities.put(EntityType.PUFFERFISH, 1); @@ -585,7 +583,7 @@ public class TryToCompleteTest { List list = Collections.singletonList(ent); when(world.getNearbyEntities(any(BoundingBox.class))).thenReturn(list); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -598,7 +596,7 @@ public class TryToCompleteTest { when(user.getWorld()).thenReturn(netherWorld); when(netherWorld.getName()).thenReturn("world_nether"); when(netherWorld.getEnvironment()).thenReturn(Environment.NETHER); - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = new HashMap<>(); requiredEntities.put(EntityType.PUFFERFISH, 1); @@ -613,7 +611,7 @@ public class TryToCompleteTest { when(world.getNearbyEntities(any(BoundingBox.class))).thenReturn(list); when(netherWorld.getNearbyEntities(any(BoundingBox.class))).thenReturn(Collections.emptyList()); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "1", "[item]", "challenges.entities.pufferfish.name"); } /** @@ -623,7 +621,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringIntMultipleTimesPositiveSuccess() { // Try to complete 10 times. Already done 3 times, and max is 10, so it should be only done 7 times assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 10)); - verify(user).sendMessage("challenges.messages.you-repeated-challenge-multiple", "[value]", "name", "[count]", "7"); + verify(user).getTranslation("challenges.messages.you-repeated-challenge-multiple", "[value]", "name", "[count]", "7"); } /** diff --git a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java index a4e5b4a..fcf45c4 100644 --- a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java +++ b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java @@ -51,20 +51,18 @@ public class TryToCompleteTestOld { }; List required; private ChallengesAddon addon; - private PlayerInventory inv; /** - * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { Server server = mock(Server.class); PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getServer()).thenReturn(server); when(Bukkit.getBukkitVersion()).thenReturn("1.13.2"); user = mock(User.class); - inv = mock(PlayerInventory.class); + PlayerInventory inv = mock(PlayerInventory.class); when(inv.getContents()).thenReturn(stacks); when(user.getInventory()).thenReturn(inv); addon = mock(ChallengesAddon.class); diff --git a/src/test/java/world/bentobox/challenges/utils/UtilsTest.java b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java index a89873f..b8a3c99 100644 --- a/src/test/java/world/bentobox/challenges/utils/UtilsTest.java +++ b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java @@ -1,7 +1,6 @@ package world.bentobox.challenges.utils; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -11,17 +10,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemStack; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -52,10 +47,9 @@ public class UtilsTest { /** - * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -75,26 +69,20 @@ public class UtilsTest { } /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - - /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItemsEmpty() { - assertTrue(Utils.groupEqualItems(Collections.emptyList()).isEmpty()); + assertTrue(Utils.groupEqualItems(Collections.emptyList(), Collections.emptySet()).isEmpty()); } /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItems() { List requiredItems = new ArrayList<>(); + Set ignoreMeta = Collections.singleton(Material.ACACIA_FENCE); // First item ItemStack is = mock(ItemStack.class); when(is.getAmount()).thenReturn(1); @@ -112,14 +100,14 @@ public class UtilsTest { when(is2.clone()).thenReturn(is); requiredItems.add(is2); } - List list = Utils.groupEqualItems(requiredItems); + List list = Utils.groupEqualItems(requiredItems, ignoreMeta); // Result should be two stacks stack of 64 doors and 36 doors assertEquals(1, list.size()); verify(is, times(9)).setAmount(2); } /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItemsUnique() { @@ -141,24 +129,12 @@ public class UtilsTest { when(is2.clone()).thenReturn(is); requiredItems.add(is2); } - List list = Utils.groupEqualItems(requiredItems); + List list = Utils.groupEqualItems(requiredItems, Collections.emptySet()); // Result should be two stacks stack of 64 doors and 36 doors assertEquals(10, list.size()); verify(is, never()).setAmount(2); } - /** - * Test method for {@link world.bentobox.challenges.utils.Utils#canIgnoreMeta(org.bukkit.Material)}. - */ - @Test - public void testCanIgnoreMeta() { - assertTrue(Utils.canIgnoreMeta(Material.FIREWORK_ROCKET)); - assertTrue(Utils.canIgnoreMeta(Material.ENCHANTED_BOOK)); - assertTrue(Utils.canIgnoreMeta(Material.WRITTEN_BOOK)); - assertTrue(Utils.canIgnoreMeta(Material.FILLED_MAP)); - assertFalse(Utils.canIgnoreMeta(Material.CHISELED_RED_SANDSTONE)); - } - /** * Test method for {@link world.bentobox.challenges.utils.Utils#getGameMode(org.bukkit.World)}. */ @@ -195,5 +171,4 @@ public class UtilsTest { assertEquals(VisibilityMode.VISIBLE, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.HIDDEN)); assertEquals(VisibilityMode.HIDDEN, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.TOGGLEABLE)); } - }