From 8383c93cf95429d5a4cd2002c6b4ebac0f4f7bd1 Mon Sep 17 00:00:00 2001 From: BONNe Date: Wed, 22 Apr 2020 01:19:46 +0300 Subject: [PATCH] Bug fix release (#228) * Fix issue when users could not select non-block items as icons for challenges and levels. (#190) * English update (#193) * Organized imports * Minor code cleanup * Updated English locale file. * Translate zh-CN.yml via GitLocalize (#188) * Make default translation looking a bit nicer (#192) * Make default translation looking a bit nicer * Updating a few friendly names and rewording some phrases * Add generic .gitignore * Fix novice level Update `chiseledmaker` name in `novice` level. * Adapt literal style (#197) Improve translations and process as a YAML string. * Test coverage (#199) * Test coverage for Challenges Command * Added CompleteChallengeCommand test class * Added Utils test class * Added ChallengesGUI test class * Fix code smells from sonarcloud analysis * Added .gitignore * Added Travis CI config file * WIP ChallengesManager Test class * Added ChallengesManager test class * Removed debug * Removed code smells. * Added ChallengesAddon test class. * Added onDisbale test * Added new TryToComplete test class - WIP Covers inventory challenges. * Added Island Challenge entity tests to TryToComplete test class * Fix a bug with challenge deletion. If challenge has been left in a level, then system did not remove challenge from it and was kept as ghost challenge, preventing from completing level. * Fixes tests * Updated travis.yml * All strings to spanish (#200) * Translate es.yml via GitLocalize * Translate es.yml via GitLocalize * Translate es.yml via GitLocalize * Fix LevelListRequestHandler. This handler did not return list of strings but list of challenge levels, that is incorrect. Not it should work correctly. * Create ro.yml * Create id.yml * Remove blanks files now that GitLocalize is fixed. * Initial Russian translation (#207) * Translate ru.yml via GitLocalize Co-authored-by: @mt-gitlocalize @IPeredero @LoveBiscuit * Changed build character from # to b * Add German translation (#210) * Translate de.yml via GitLocalize * Translate de.yml via GitLocalize * Translate de.yml via GitLocalize Co-authored-by: xXjojojXx <36734820+xXjojojXx@users.noreply.github.com> Co-authored-by: FunnysBanana <51290016+FunnysBanana@users.noreply.github.com> Co-authored-by: mt-gitlocalize * Czech translation. Credit @Polda18 * Added a uniqueId sanitization when creating challenges/levels This will help fixing issues with spaces, hyphens and accents in non-English languages. * Fixes bug with checking entities in nether and end (#219) https://github.com/BentoBoxWorld/Challenges/issues/218 Adds test case to check for compliance. * Add 7 new placeholders for Challenges Addon. - `[gamemode]_challenge_total_completion_count` returns number of sum of challenge completions for user. - `[gamemode]_challenge_completed_count` returns number of completed challenges (at least once) for user. - `[gamemode]_challenge_uncompleted_count` returns number of uncompleted challenges for user. - `[gamemode]_challenge_completed_level_count` returns number of completed levels for user. - `[gamemode]_challenge_uncompleted_level_count` returns number of uncompleted levels for user. - `[gamemode]_challenge_unlocked_level_count` returns number of unlocked levels for user. - `[gamemode]_challenge_locked_level_count` returns number of locked levels for user. Fixes #224 * Add 2 new placeholders: - `[gamemode]_challenge_latest_level_name` returns latest unlocked challenge level name - `[gamemode]_challenge_latest_level_id` returns latest unlocked challenge level id Fixes #226 * Fix broken tests due to placeholder additions. https://github.com/BentoBoxWorld/Challenges/commit/b5ecffb725d78c154f469d7e2868574ca7ecff30 https://github.com/BentoBoxWorld/Challenges/commit/2958ca8b6c98f9c8d389aa93c4a2846f578fff43 * Added default perms for aoneblock * Downgrade to 0.8.1 version * Add option to quit from conversation by writing "cancel" in chat. Move sanitizeInput to a GuiUtil class. * Change latest version to 0.8.1 --- .gitignore | 134 ++ .travis.yml | 23 + README.md | 2 +- pom.xml | 43 +- .../bentobox/challenges/ChallengesAddon.java | 85 +- .../challenges/ChallengesImportManager.java | 724 +++++----- .../challenges/ChallengesManager.java | 506 ++++--- .../commands/ChallengesCommand.java | 17 +- .../commands/ChallengesUserCommand.java | 1 - .../commands/CompleteChallengeCommand.java | 216 +-- .../challenges/commands/admin/Challenges.java | 2 +- .../commands/admin/CompleteCommand.java | 6 +- .../commands/admin/ReloadChallenges.java | 4 +- .../commands/admin/ResetCommand.java | 6 +- .../commands/admin/ShowChallenges.java | 3 +- .../bentobox/challenges/config/Settings.java | 3 +- .../challenges/database/object/Challenge.java | 9 +- .../database/object/ChallengeLevel.java | 8 +- .../database/object/ChallengesPlayerData.java | 549 ++++---- .../object/adapters/RequirementsAdapter.java | 3 +- .../requirements/InventoryRequirements.java | 6 +- .../requirements/IslandRequirements.java | 8 +- .../requirements/OtherRequirements.java | 7 +- .../object/requirements/Requirements.java | 3 +- .../handlers/ChallengeDataRequestHandler.java | 1 - .../handlers/ChallengeListRequestHandler.java | 3 +- .../CompletedChallengesRequestHandler.java | 7 +- .../handlers/LevelListRequestHandler.java | 6 +- .../challenges/listeners/ResetListener.java | 3 +- .../challenges/listeners/SaveListener.java | 1 - .../bentobox/challenges/panel/CommonGUI.java | 10 +- .../challenges/panel/GameModesGUI.java | 6 +- .../challenges/panel/admin/AdminGUI.java | 91 +- .../panel/admin/EditChallengeGUI.java | 15 +- .../challenges/panel/admin/EditLevelGUI.java | 16 +- .../challenges/panel/admin/EditLoreGUI.java | 1216 +++++++++-------- .../panel/admin/EditSettingsGUI.java | 21 +- .../panel/admin/ListChallengesGUI.java | 3 +- .../challenges/panel/admin/ListLevelsGUI.java | 3 +- .../panel/admin/ListLibraryGUI.java | 5 +- .../challenges/panel/admin/ListUsersGUI.java | 9 +- .../panel/admin/ManageBlocksGUI.java | 8 +- .../panel/admin/ManageEntitiesGUI.java | 8 +- .../challenges/panel/user/ChallengesGUI.java | 5 +- .../challenges/panel/user/MultipleGUI.java | 4 +- .../panel/util/ChallengeTypeGUI.java | 5 +- .../panel/util/ConfirmationGUI.java | 4 +- .../challenges/panel/util/ItemSwitchGUI.java | 9 +- .../challenges/panel/util/NumberGUI.java | 1038 +++++++------- .../panel/util/SelectBlocksGUI.java | 40 +- .../panel/util/SelectChallengeGUI.java | 10 +- .../panel/util/SelectEntityGUI.java | 11 +- .../panel/util/SelectEnvironmentGUI.java | 5 +- .../challenges/panel/util/StringListGUI.java | 41 +- .../challenges/tasks/TryToComplete.java | 409 +++--- .../bentobox/challenges/utils/GuiUtils.java | 18 +- .../bentobox/challenges/utils/HeadLib.java | 15 +- .../bentobox/challenges/utils/Utils.java | 7 +- .../bentobox/challenges/web/WebManager.java | 12 +- .../challenges/web/object/LibraryEntry.java | 3 +- src/main/resources/addon.yml | 10 + src/main/resources/default.json | 29 +- src/main/resources/locales/cs.yml | 498 +++++++ src/main/resources/locales/de.yml | 628 +++++++++ src/main/resources/locales/en-US.yml | 496 ++++--- src/main/resources/locales/es.yml | 965 ++++++++----- src/main/resources/locales/ru.yml | 613 +++++++++ src/main/resources/locales/zh-CN.yml | 870 ++++++------ .../challenges/ChallengesAddonTest.java | 456 +++++-- .../challenges/ChallengesManagerTest.java | 868 ++++++++++++ .../commands/ChallengesCommandTest.java | 268 ++++ .../CompleteChallengeCommandTest.java | 342 +++++ .../panel/user/ChallengesGUITest.java | 392 ++++++ .../challenges/tasks/TryToCompleteTest.java | 758 +++++++--- .../tasks/TryToCompleteTestOld.java | 242 ++++ .../bentobox/challenges/utils/UtilsTest.java | 199 +++ 76 files changed, 9420 insertions(+), 3650 deletions(-) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 src/main/resources/locales/cs.yml create mode 100644 src/main/resources/locales/de.yml create mode 100644 src/main/resources/locales/ru.yml create mode 100644 src/test/java/world/bentobox/challenges/ChallengesManagerTest.java create mode 100644 src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java create mode 100644 src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java create mode 100644 src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java create mode 100644 src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java create mode 100644 src/test/java/world/bentobox/challenges/utils/UtilsTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d09ebd --- /dev/null +++ b/.gitignore @@ -0,0 +1,134 @@ +### Others ### +*.cmd +*.sh +*.prefs + +### Maven ### +/mvn +/target/lib +/target/maven-archiver +/target/classes +/target/maven-status +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +out/ + + +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/shelf + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries +.gradle + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + + +### Eclipse ### +*.pydevproject +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans +/target/ +checkstyle.xml +classes/ +/.DS_Store diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c2ad6fc --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: java +sudo: false +addons: + sonarcloud: + organization: "bentobox-world" + +jdk: + - openjdk8 + - openjdk11 + +matrix: + allow_failures: + - jdk: openjdk11 + +script: + #- sonar-scanner + - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar -Dsonar.projectKey=BentoBoxWorld_Challenges + #- echo "${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}" + +cache: + directories: + - '$HOME/.m2/repository' + - '$HOME/.sonar/cache' diff --git a/README.md b/README.md index ae8a54e..f95d526 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Add-on for BentoBox to provide challenges for any BentoBox GameMode. ## Where to find Currently Challenges Addon is in **Beta stage**, so it may or may not contain bugs... a lot of bugs. Also it means, that some features are not working or implemented. -Latest official **Beta Release is 0.8.0**, and you can download it from [Release tab](https://github.com/BentoBoxWorld/Challenges/releases) +Latest official **Beta Release is 0.8.1**, and you can download it from [Release tab](https://github.com/BentoBoxWorld/Challenges/releases) But it will work with BentoBox 1.6.x and BentoBox 1.7.x. Latest development builds will be based on **Minecraft 1.14.4** and **BentoBox 1.8.0**. diff --git a/pom.xml b/pom.xml index 320a870..42fcfdc 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ UTF-8 UTF-8 1.8 - 1.7.4 + 2.0.2 1.14.4-R0.1-SNAPSHOT 1.7.0 @@ -42,7 +42,7 @@ ${build.version}-SNAPSHOT - 0.8.0 + 0.8.1 -LOCAL @@ -56,7 +56,7 @@ - -#${env.BUILD_NUMBER} + -b${env.BUILD_NUMBER} @@ -129,24 +129,25 @@ ${spigot.version} provided - - org.mockito - mockito-all - 1.10.19 - test - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito - ${powermock.version} - test - + + + org.mockito + mockito-core + 3.0.0 + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito2 + ${powermock.version} + test + world.bentobox bentobox diff --git a/src/main/java/world/bentobox/challenges/ChallengesAddon.java b/src/main/java/world/bentobox/challenges/ChallengesAddon.java index d632dfb..e2fd286 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesAddon.java +++ b/src/main/java/world/bentobox/challenges/ChallengesAddon.java @@ -1,11 +1,14 @@ package world.bentobox.challenges; -import org.bukkit.Material; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; + import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.configuration.Config; @@ -18,6 +21,7 @@ import world.bentobox.challenges.commands.ChallengesUserCommand; import world.bentobox.challenges.commands.admin.Challenges; import world.bentobox.challenges.commands.admin.ChallengesAdminCommand; import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.database.object.ChallengeLevel; import world.bentobox.challenges.handlers.ChallengeDataRequestHandler; import world.bentobox.challenges.handlers.ChallengeListRequestHandler; import world.bentobox.challenges.handlers.CompletedChallengesRequestHandler; @@ -87,7 +91,7 @@ public class ChallengesAddon extends Addon { * This flag allows to complete challenges in any part of the world. It will not limit * player to their island. Useful for skygrid without protection flags. */ - public static Flag CHALLENGES_WORLD_PROTECTION = + public static final Flag CHALLENGES_WORLD_PROTECTION = new Flag.Builder("CHALLENGES_WORLD_PROTECTION", Material.GRASS_BLOCK).type(Flag.Type.WORLD_SETTING).defaultSetting(true).build(); /** @@ -95,7 +99,7 @@ public class ChallengesAddon extends Addon { * that only Island owner can complete challenge. * By default it is set to Visitor. */ - public static Flag CHALLENGES_ISLAND_PROTECTION = + public static final Flag CHALLENGES_ISLAND_PROTECTION = new Flag.Builder("CHALLENGES_ISLAND_PROTECTION", Material.COMMAND_BLOCK).defaultRank(RanksManager.VISITOR_RANK).build(); @@ -154,7 +158,8 @@ public class ChallengesAddon extends Addon { List hookedGameModes = new ArrayList<>(); this.getPlugin().getAddonsManager().getGameModeAddons().forEach(gameModeAddon -> { - if (!this.settings.getDisabledGameModes().contains(gameModeAddon.getDescription().getName())) + if (!this.settings.getDisabledGameModes().contains( + gameModeAddon.getDescription().getName())) { if (gameModeAddon.getPlayerCommand().isPresent()) { @@ -172,6 +177,8 @@ public class ChallengesAddon extends Addon { CHALLENGES_WORLD_PROTECTION.addGameModeAddon(gameModeAddon); CHALLENGES_ISLAND_PROTECTION.addGameModeAddon(gameModeAddon); + + this.registerPlaceholders(gameModeAddon); } }); @@ -237,7 +244,7 @@ public class ChallengesAddon extends Addon { if (this.settings.getAutoSaveTimer() > 0) { - this.getPlugin().getServer().getScheduler().runTaskTimerAsynchronously( + Bukkit.getScheduler().runTaskTimerAsynchronously( this.getPlugin(), bukkitTask -> ChallengesAddon.this.challengesManager.save(), this.settings.getAutoSaveTimer() * 60 * 20, @@ -263,7 +270,7 @@ public class ChallengesAddon extends Addon { { this.loadSettings(); this.challengesManager.reload(); - this.getLogger().info("Challenges addon reloaded."); + this.log("Challenges addon reloaded."); } } @@ -308,6 +315,72 @@ public class ChallengesAddon extends Addon { } + /** + * This method registers placeholders into GameMode addon. + * @param gameModeAddon GameMode addon where placeholders must be hooked in. + */ + private void registerPlaceholders(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 index 709d999..e0feb65 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesImportManager.java +++ b/src/main/java/world/bentobox/challenges/ChallengesImportManager.java @@ -1,8 +1,5 @@ package world.bentobox.challenges; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.Expose; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; @@ -21,12 +18,16 @@ 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.challenges.database.object.ChallengeLevel; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; import world.bentobox.challenges.utils.Utils; @@ -42,14 +43,14 @@ public class ChallengesImportManager * @param challengesAddon */ public ChallengesImportManager(ChallengesAddon challengesAddon) - { + { this.addon = challengesAddon; } -// --------------------------------------------------------------------- -// Section: Default Challenge Loader -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Default Challenge Loader + // --------------------------------------------------------------------- /** @@ -78,45 +79,46 @@ public class ChallengesImportManager } // default configuration should be removed. - // user made configuration should not!. + // user made configuration should not!. boolean removeAtEnd = - !Files.exists(Paths.get(this.addon.getDataFolder().getPath() + "/default.json")); + !Files.exists(Paths.get(this.addon.getDataFolder().getPath() + "/default.json")); // Safe json configuration to Challenges folder. - this.addon.saveResource("default.json", false); + 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(); + // 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); + }); - // 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); - }); + 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) { @@ -127,132 +129,132 @@ public class ChallengesImportManager this.addon.getChallengesManager().save(); if (removeAtEnd) - { - // Remove default.yml file from resources to avoid interacting with it. - new File(this.addon.getDataFolder(), "default.json").delete(); - } + { + // 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(); + /** + * 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"); - } + // 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; - } + 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); + 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); - }); + // 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) - { - e.printStackTrace(); - return false; - } + 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().save(); + this.addon.getChallengesManager().save(); - return true; - } + return true; + } -// --------------------------------------------------------------------- -// Section: Default generation -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // 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 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 + * @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 (overwrite) + { + if (user.isPlayer()) + { + user.sendMessage("challenges.messages.defaults-file-overwrite"); + } + else + { + this.addon.logWarning("challenges.messages.defaults-file-overwrite"); + } - defaultFile.delete(); - } - else - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.defaults-file-exist"); - } - else - { - this.addon.logWarning("challenges.errors.defaults-file-exist"); - } + defaultFile.delete(); + } + else + { + if (user.isPlayer()) + { + user.sendMessage("challenges.errors.defaults-file-exist"); + } + else + { + this.addon.logWarning("challenges.errors.defaults-file-exist"); + } - return false; - } - } + return false; + } + } try { @@ -261,102 +263,102 @@ public class ChallengesImportManager 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, "")); + 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()); + 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())); + 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()); + return clone; + }). + collect(Collectors.toList()); - DefaultDataHolder defaultChallenges = new DefaultDataHolder(); - defaultChallenges.setChallengeList(challengeList); - defaultChallenges.setLevelList(levelList); - defaultChallenges.setVersion(this.addon.getDescription().getVersion()); + DefaultDataHolder defaultChallenges = new DefaultDataHolder(); + defaultChallenges.setChallengeList(challengeList); + defaultChallenges.setLevelList(levelList); + defaultChallenges.setVersion(this.addon.getDescription().getVersion()); - BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(defaultFile), StandardCharsets.UTF_8)); - writer.write(Objects.requireNonNull( - new DefaultJSONHandler(this.addon).toJsonString(defaultChallenges))); - writer.close(); + 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"); - } + if (user.isPlayer()) + { + user.sendMessage("challenges.errors.defaults-file-error"); + } - this.addon.logError("Could not save json file: " + e.getMessage()); - return false; - } + 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"); - } - } + { + if (user.isPlayer()) + { + user.sendMessage("challenges.messages.defaults-file-completed", "[world]", world.getName()); + } + else + { + this.addon.logWarning("challenges.messages.defaults-file-completed"); + } + } - return true; - } + return true; + } -// --------------------------------------------------------------------- -// Section: Private classes for default challenges -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // 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 + private static final class DefaultJSONHandler { - /** - * This constructor inits JSON builder that will be used to parse challenges. - * @param addon Challenges Adddon - */ - DefaultJSONHandler(ChallengesAddon addon) + /** + * 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(); + 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.addon = addon; + this.gson = builder.setPrettyPrinting().create(); } @@ -365,7 +367,7 @@ public class ChallengesImportManager * @param instance Instance that must be parsed to json string. * @return String that contains JSON information from instance object. */ - String toJsonString(DefaultDataHolder instance) + String toJsonString(DefaultDataHolder instance) { // Null check if (instance == null) @@ -374,193 +376,193 @@ public class ChallengesImportManager return null; } - return this.gson.toJson(instance); + 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"); + /** + * 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); + 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 + 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 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; - } + 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); - } + /** + * 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 - // --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - /** - * Holds JSON builder object. - */ - private Gson gson; + /** + * Holds JSON builder object. + */ + private Gson gson; - /** - * Holds ChallengesAddon object. - */ - private ChallengesAddon addon; + /** + * 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 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 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 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 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 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 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; - } + /** + * 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"; - } + /** + * @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. - } + /** + * @param uniqueId - unique ID the uniqueId to set + */ + @Override + public void setUniqueId(String uniqueId) + { + // method not used. + } - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - /** - * Holds a list with default challenges. - */ - @Expose - private List challengeList; + /** + * Holds a list with default challenges. + */ + @Expose + private List challengeList; - /** - * Holds a list with default levels. - */ - @Expose - private List challengeLevelList; + /** + * 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; - } + /** + * Holds a variable that stores in which addon version file was made. + */ + @Expose + private String version; + } -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - private ChallengesAddon addon; + private 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/ChallengesManager.java index 4deebb5..26e7653 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesManager.java +++ b/src/main/java/world/bentobox/challenges/ChallengesManager.java @@ -1,8 +1,6 @@ package world.bentobox.challenges; -import org.eclipse.jdt.annotation.NonNull; - import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -20,6 +18,8 @@ import java.util.stream.Collectors; import org.bukkit.Bukkit; import org.bukkit.World; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.logs.LogEntry; import world.bentobox.bentobox.api.user.User; @@ -44,7 +44,7 @@ import world.bentobox.challenges.utils.Utils; /** - * This class manges challenges. It allows access to all data that is stored to database. + * This class manages challenges. It allows access to all data that is stored to database. * It also provides information about challenge level status for each user. */ public class ChallengesManager @@ -108,6 +108,11 @@ public class ChallengesManager * String for free Challenge Level. */ public static final String FREE = ""; + public static final String VALUE = "[value]"; + public static final String USER_ID = "user-id"; + public static final String CHALLENGE_ID = "challenge-id"; + public static final String ADMIN_ID = "admin-id"; + public static final String RESET = "RESET"; // --------------------------------------------------------------------- @@ -143,7 +148,7 @@ public class ChallengesManager { // Sort by challenges level order numbers return Integer.compare(this.getLevel(o1.getLevel()).getOrder(), - this.getLevel(o2.getLevel()).getOrder()); + this.getLevel(o2.getLevel()).getOrder()); } } }; @@ -190,6 +195,7 @@ public class ChallengesManager */ public void load() { + this.addon.log("Loading challenges..."); this.challengeCacheData.clear(); this.levelCacheData.clear(); @@ -200,17 +206,15 @@ public class ChallengesManager } this.playerCacheData.clear(); + loadAndValidate(); + } - this.addon.getLogger().info("Loading challenges..."); + private void loadAndValidate() { this.challengeDatabase.loadObjects().forEach(this::loadChallenge); this.levelDatabase.loadObjects().forEach(this::loadLevel); - // this validate challenge levels this.validateChallenges(); - - // It is not necessary to load all players in memory. -// this.playersDatabase.loadObjects().forEach(this::loadPlayerData); } @@ -219,24 +223,17 @@ public class ChallengesManager */ public void reload() { + this.addon.log("Reloading challenges..."); if (!this.playerCacheData.isEmpty()) { // 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.addon.getLogger().info("Reloading challenges..."); - - this.challengeDatabase = new Database<>(addon, Challenge.class); - this.levelDatabase = new Database<>(addon, ChallengeLevel.class); - this.playersDatabase = new Database<>(addon, ChallengesPlayerData.class); - - this.challengeDatabase.loadObjects().forEach(this::loadChallenge); - this.levelDatabase.loadObjects().forEach(this::loadLevel); - - this.validateChallenges(); - // It is not necessary to load all players in memory. -// this.playersDatabase.loadObjects().forEach(this::loadPlayerData); + loadAndValidate(); } @@ -262,17 +259,10 @@ public class ChallengesManager * @return - true if imported */ public boolean loadChallenge(@NonNull Challenge challenge, - boolean overwrite, - User user, - boolean silent) + boolean overwrite, + User user, + boolean silent) { - if (challenge == null) - { - this.addon.logError( - "Tried to load NULL element from Database. One challenge is broken and will not work."); - return false; - } - if (this.challengeCacheData.containsKey(challenge.getUniqueId())) { if (!overwrite) @@ -280,7 +270,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-skipping", - "[value]", challenge.getFriendlyName()); + VALUE, challenge.getFriendlyName()); } return false; @@ -290,7 +280,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-overwriting", - "[value]", challenge.getFriendlyName()); + VALUE, challenge.getFriendlyName()); } } } @@ -299,7 +289,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-add", - "[value]", challenge.getFriendlyName()); + VALUE, challenge.getFriendlyName()); } } @@ -330,28 +320,21 @@ public class ChallengesManager * @return boolean that indicate about load status. */ public boolean loadLevel(@NonNull ChallengeLevel level, - boolean overwrite, - User user, - boolean silent) + boolean overwrite, + User user, + boolean silent) { - if (level == null) - { - this.addon.logError( - "Tried to load NULL element from Database. One level is broken and will not work."); - return false; - } - if (!this.isValidLevel(level)) { if (user != null) { user.sendMessage("challenges.errors.load-error", - "[value]", level.getFriendlyName()); + VALUE, level.getFriendlyName()); } else { this.addon.logError( - "Challenge Level '" + level.getUniqueId() + "' is not valid and skipped"); + "Challenge Level '" + level.getUniqueId() + "' is not valid and skipped"); } return false; @@ -364,7 +347,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-skipping", - "[value]", level.getFriendlyName()); + VALUE, level.getFriendlyName()); } return false; @@ -374,7 +357,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-overwriting", - "[value]", level.getFriendlyName()); + VALUE, level.getFriendlyName()); } } } @@ -383,7 +366,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-add", - "[value]", level.getFriendlyName()); + VALUE, level.getFriendlyName()); } } @@ -396,6 +379,8 @@ 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) { @@ -417,14 +402,11 @@ public class ChallengesManager */ public void removeFromCache(UUID playerID) { - if (!this.settings.isStoreAsIslandData()) + if (!this.settings.isStoreAsIslandData() && this.playerCacheData.containsKey(playerID.toString())) { - if (this.playerCacheData.containsKey(playerID.toString())) - { - // save before remove - this.savePlayerData(playerID.toString()); - this.playerCacheData.remove(playerID.toString()); - } + // save before remove + this.savePlayerData(playerID.toString()); + this.playerCacheData.remove(playerID.toString()); } // TODO: It would be necessary to remove also data, if they stores islands. @@ -450,7 +432,7 @@ public class ChallengesManager // If challenge's level is not found, then set it as free challenge. challenge.setLevel(FREE); this.addon.logWarning("Challenge's " + challenge.getUniqueId() + " level was not found in the database. " + - "To avoid any errors with missing level, challenge was added to the FREE level!"); + "To avoid any errors with missing level, challenge was added to the FREE level!"); } }); } @@ -681,13 +663,13 @@ public class ChallengesManager this.levelCacheData.remove(level.getUniqueId()); level.setUniqueId( - addonName + level.getUniqueId().substring(world.getName().length())); + addonName + level.getUniqueId().substring(world.getName().length())); Set challengesID = new HashSet<>(level.getChallenges()); level.getChallenges().clear(); challengesID.forEach(challenge -> - level.getChallenges().add(addonName + challenge.substring(world.getName().length()))); + level.getChallenges().add(addonName + challenge.substring(world.getName().length()))); this.levelDatabase.saveObject(level); this.levelCacheData.put(level.getUniqueId(), level); @@ -703,6 +685,7 @@ 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); @@ -741,36 +724,36 @@ public class ChallengesManager { switch (challenge.getChallengeType()) { - case INVENTORY: - InventoryRequirements inventoryRequirements = new InventoryRequirements(); - inventoryRequirements.setRequiredItems(challenge.getRequiredItems()); - inventoryRequirements.setTakeItems(challenge.isTakeItems()); + 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()); + 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()); + 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; + otherRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); + challenge.setRequirements(otherRequirements); + break; } // This save should not involve any upgrades in other parts. @@ -1083,6 +1066,7 @@ public class ChallengesManager * @param gameMode - World Name where levels should be searched. * @return Level status - how many challenges still to do on which level */ + @NonNull private List getAllChallengeLevelStatus(String storageDataID, String gameMode) { this.addPlayerData(storageDataID); @@ -1110,15 +1094,14 @@ public class ChallengesManager doneChallengeCount = (int) level.getChallenges().stream().filter(playerData::isChallengeDone).count(); result.add(new LevelStatus( - level, - previousLevel, - challengesToDo, - level.getChallenges().size() == doneChallengeCount, - challengesToDo <= 0)); + level, + previousLevel, + challengesToDo, + level.getChallenges().size() == doneChallengeCount, + challengesToDo <= 0)); previousLevel = level; } - return result; } @@ -1130,6 +1113,7 @@ public class ChallengesManager * @param level Level which status must be calculated. * @return LevelStatus of given level. */ + @Nullable private LevelStatus getChallengeLevelStatus(@NonNull String storageDataID, World world, @NonNull ChallengeLevel level) { this.addPlayerData(storageDataID); @@ -1149,43 +1133,20 @@ public class ChallengesManager int challengesToDo = previousLevel == null ? 0 : (previousLevel.getChallenges().size() - level.getWaiverAmount()) - - (int) previousLevel.getChallenges().stream().filter(playerData::isChallengeDone).count(); + (int) previousLevel.getChallenges().stream().filter(playerData::isChallengeDone).count(); // As level already contains unique ids of challenges, just iterate through them. int doneChallengeCount = (int) level.getChallenges().stream().filter(playerData::isChallengeDone).count(); return new LevelStatus( - level, - previousLevel, - challengesToDo, - level.getChallenges().size() == doneChallengeCount, - challengesToDo <= 0); + level, + previousLevel, + challengesToDo, + level.getChallenges().size() == doneChallengeCount, + challengesToDo <= 0); } } - - /** - * Check is playerData can see given level. - * TODO: not an optimal way. Faster would be to check previous level challenges. - * @param storageDataID - playerData ID - * @param level - level - * @return true if level is unlocked - */ - private boolean isLevelUnlocked(@NonNull String storageDataID, - World world, - ChallengeLevel level) - { - this.addPlayerData(storageDataID); - - return this.islandWorldManager.getAddon(world).filter(gameMode -> - this.getAllChallengeLevelStatus(storageDataID, gameMode.getDescription().getName()). - stream(). - filter(LevelStatus::isUnlocked). - anyMatch(lv -> lv.getLevel().equals(level))). - isPresent(); - } - - /** * This method returns if given user has been already completed given level. * @param levelID Level that must be checked. @@ -1317,17 +1278,17 @@ public class ChallengesManager String storageID = this.getDataUniqueID(userID, Util.getWorld(world)); this.setChallengeComplete(storageID, challenge.getUniqueId(), completionCount); this.addLogEntry(storageID, new LogEntry.Builder("COMPLETE"). - data("user-id", userID.toString()). - data("challenge-id", challenge.getUniqueId()). - data("completion-count", Integer.toString(completionCount)). - build()); + data(USER_ID, userID.toString()). + data(CHALLENGE_ID, challenge.getUniqueId()). + data("completion-count", Integer.toString(completionCount)). + build()); // Fire event that user completes challenge - Bukkit.getServer().getPluginManager().callEvent( - new ChallengeCompletedEvent(challenge.getUniqueId(), - userID, - false, - completionCount)); + Bukkit.getPluginManager().callEvent( + new ChallengeCompletedEvent(challenge.getUniqueId(), + userID, + false, + completionCount)); } @@ -1344,17 +1305,17 @@ public class ChallengesManager this.setChallengeComplete(storageID, challenge.getUniqueId()); this.addLogEntry(storageID, new LogEntry.Builder("COMPLETE"). - data("user-id", userID.toString()). - data("challenge-id", challenge.getUniqueId()). - data("admin-id", adminID == null ? "OP" : adminID.toString()). - build()); + data(USER_ID, userID.toString()). + data(CHALLENGE_ID, challenge.getUniqueId()). + data(ADMIN_ID, adminID == null ? "OP" : adminID.toString()). + build()); // Fire event that admin completes user challenge - Bukkit.getServer().getPluginManager().callEvent( - new ChallengeCompletedEvent(challenge.getUniqueId(), - userID, - true, - 1)); + Bukkit.getPluginManager().callEvent( + new ChallengeCompletedEvent(challenge.getUniqueId(), + userID, + true, + 1)); } @@ -1369,18 +1330,18 @@ public class ChallengesManager String storageID = this.getDataUniqueID(userID, Util.getWorld(world)); this.resetChallenge(storageID, challenge.getUniqueId()); - this.addLogEntry(storageID, new LogEntry.Builder("RESET"). - data("user-id", userID.toString()). - data("challenge-id", challenge.getUniqueId()). - data("admin-id", adminID == null ? "RESET" : adminID.toString()). - build()); + this.addLogEntry(storageID, new LogEntry.Builder(RESET). + data(USER_ID, userID.toString()). + data(CHALLENGE_ID, challenge.getUniqueId()). + data(ADMIN_ID, adminID == null ? RESET : adminID.toString()). + build()); // Fire event that admin resets user challenge - Bukkit.getServer().getPluginManager().callEvent( - new ChallengeResetEvent(challenge.getUniqueId(), - userID, - true, - "RESET")); + Bukkit.getPluginManager().callEvent( + new ChallengeResetEvent(challenge.getUniqueId(), + userID, + true, + RESET)); } @@ -1408,16 +1369,16 @@ public class ChallengesManager this.islandWorldManager.getAddon(world).ifPresent(gameMode -> { this.resetAllChallenges(storageID, gameMode.getDescription().getName()); this.addLogEntry(storageID, new LogEntry.Builder("RESET_ALL"). - data("user-id", userID.toString()). - data("admin-id", adminID == null ? "ISLAND_RESET" : adminID.toString()). - build()); + data(USER_ID, userID.toString()). + data(ADMIN_ID, adminID == null ? "ISLAND_RESET" : adminID.toString()). + build()); // Fire event that admin resets user challenge - Bukkit.getServer().getPluginManager().callEvent( - new ChallengeResetAllEvent(gameMode.getDescription().getName(), - userID, - adminID != null, - adminID == null ? "ISLAND_RESET" : "RESET_ALL")); + Bukkit.getPluginManager().callEvent( + new ChallengeResetAllEvent(gameMode.getDescription().getName(), + userID, + adminID != null, + adminID == null ? "ISLAND_RESET" : "RESET_ALL")); }); } @@ -1469,11 +1430,19 @@ public class ChallengesManager * @param world World where level must be checked. * @param level Level that must be checked. * @param user User who need to be checked. - * @return true, if level is already completed. + * @return true, if level is unlocked. */ public boolean isLevelUnlocked(User user, World world, ChallengeLevel level) { - return this.isLevelUnlocked(this.getDataUniqueID(user, Util.getWorld(world)), world, level); + String storageDataID = this.getDataUniqueID(user, Util.getWorld(world)); + this.addPlayerData(storageDataID); + + return this.islandWorldManager.getAddon(world).filter(gameMode -> this.getAllChallengeLevelStatus(storageDataID, gameMode.getDescription().getName()). + stream(). + filter(LevelStatus::isUnlocked). + anyMatch(lv -> lv.getLevel().equals(level))). + isPresent(); + } @@ -1489,14 +1458,14 @@ public class ChallengesManager this.setLevelComplete(storageID, level.getUniqueId()); this.addLogEntry(storageID, new LogEntry.Builder("COMPLETE_LEVEL"). - data("user-id", user.getUniqueId().toString()). - data("level", level.getUniqueId()).build()); + data(USER_ID, user.getUniqueId().toString()). + data("level", level.getUniqueId()).build()); // Fire event that user completes level - Bukkit.getServer().getPluginManager().callEvent( - new LevelCompletedEvent(level.getUniqueId(), - user.getUniqueId(), - false)); + Bukkit.getPluginManager().callEvent( + new LevelCompletedEvent(level.getUniqueId(), + user.getUniqueId(), + false)); } @@ -1515,14 +1484,15 @@ public class ChallengesManager /** * This method returns LevelStatus object for given challenge level. + * @param uniqueId UUID of user who need to be validated. * @param world World where level must be validated. * @param level Level that must be validated. - * @param user User who need to be validated. * @return LevelStatus of given level. */ - public LevelStatus getChallengeLevelStatus(UUID user, World world, ChallengeLevel level) + @Nullable + public LevelStatus getChallengeLevelStatus(UUID uniqueId, World world, ChallengeLevel level) { - return this.getChallengeLevelStatus(this.getDataUniqueID(user, Util.getWorld(world)), world, level); + return this.getChallengeLevelStatus(this.getDataUniqueID(uniqueId, Util.getWorld(world)), world, level); } @@ -1533,13 +1503,35 @@ public class ChallengesManager * @param world - World where levels should be searched. * @return Level status - how many challenges still to do on which level */ + @NonNull public List getAllChallengeLevelStatus(User user, World world) { return this.islandWorldManager.getAddon(world).map(gameMode -> this.getAllChallengeLevelStatus( this.getDataUniqueID(user, Util.getWorld(world)), gameMode.getDescription().getName())). - orElse(Collections.emptyList()); + orElse(Collections.emptyList()); + } + + + /** + * This method returns latest ChallengeLevel object that is unlocked. + * @param user user who latest unlocked level must be returned. + * @param world World where level operates. + * @return ChallengeLevel for latest unlocked level. + */ + @Nullable + public ChallengeLevel getLatestUnlockedLevel(User user, World world) + { + LevelStatus lastStatus = null; + + for (Iterator statusIterator = this.getAllChallengeLevelStatus(user, world).iterator(); + statusIterator.hasNext() && (lastStatus == null || !lastStatus.isUnlocked());) + { + lastStatus = statusIterator.next(); + } + + return lastStatus != null ? lastStatus.getLevel() : null; } @@ -1592,12 +1584,12 @@ public class ChallengesManager { // Free Challenges hides under FREE level. return this.islandWorldManager.getAddon(world).map(gameMode -> - this.challengeCacheData.values().stream(). - filter(challenge -> challenge.getLevel().equals(FREE) && - challenge.matchGameMode(gameMode.getDescription().getName())). - sorted(Comparator.comparing(Challenge::getOrder)). - collect(Collectors.toList())). - orElse(Collections.emptyList()); + this.challengeCacheData.values().stream(). + filter(challenge -> challenge.getLevel().equals(FREE) && + challenge.matchGameMode(gameMode.getDescription().getName())). + sorted(Comparator.comparing(Challenge::getOrder)). + collect(Collectors.toList())). + orElse(Collections.emptyList()); } @@ -1609,10 +1601,10 @@ public class ChallengesManager public List getLevelChallenges(ChallengeLevel level) { return level.getChallenges().stream(). - map(this::getChallenge). - filter(Objects::nonNull). - sorted(Comparator.comparing(Challenge::getOrder)). - collect(Collectors.toList()); + map(this::getChallenge). + filter(Objects::nonNull). + sorted(Comparator.comparing(Challenge::getOrder)). + collect(Collectors.toList()); } @@ -1622,6 +1614,7 @@ public class ChallengesManager * @param name - unique name of challenge * @return - challenge or null if it does not exist */ + @Nullable public Challenge getChallenge(String name) { if (this.challengeCacheData.containsKey(name)) @@ -1659,30 +1652,7 @@ public class ChallengesManager */ public boolean containsChallenge(String name) { - if (this.challengeCacheData.containsKey(name)) - { - return true; - } - else - { - // check database. - if (this.challengeDatabase.objectExists(name)) - { - Challenge challenge = this.challengeDatabase.loadObject(name); - - if (challenge != null) - { - this.challengeCacheData.put(name, challenge); - return true; - } - else - { - this.addon.logError("Tried to load NULL challenge object!"); - } - } - } - - return false; + return getChallenge(name) != null; } @@ -1692,6 +1662,7 @@ public class ChallengesManager * @param requirements - requirements object, as it is not changeable anymore. * @return Challenge that is currently created. */ + @Nullable public Challenge createChallenge(String uniqueID, Challenge.ChallengeType type, Requirements requirements) { if (!this.containsChallenge(uniqueID)) @@ -1722,15 +1693,65 @@ public class ChallengesManager { if (this.challengeCacheData.containsKey(challenge.getUniqueId())) { + // First remove challenge from its owner level. + + if (!challenge.getLevel().equals(FREE)) + { + ChallengeLevel level = this.getLevel(challenge.getLevel()); + + if (level != null) + { + this.removeChallengeFromLevel(challenge, level); + } + } + + // Afterwards remove challenge from the database. + this.challengeCacheData.remove(challenge.getUniqueId()); this.challengeDatabase.deleteObject(challenge); - - this.addon.getPlugin().getPlaceholdersManager(). - unregisterPlaceholder("challenges_challenge_repetition_count_" + challenge.getUniqueId()); } } + /** + * This method returns number of challenges in given world. + * @param world World where challenge count must be returned. + * @return Number of challenges in given world. + */ + public int getChallengeCount(World world) + { + return this.getAllChallenges(world).size(); + } + + + /** + * This method returns number of completed challenges in given world. + * @param user User which completed challenge count must be returned. + * @param world World where challenge count must be returned. + * @return Number of completed challenges by given user in given world. + */ + public long getCompletedChallengeCount(User user, World world) + { + return this.getAllChallenges(world).stream(). + filter(challenge -> this.getChallengeTimes(user, world, challenge) > 0). + count(); + } + + + /** + * This method returns total number of all completion times for all challenges in given world. + * @param user User which total completion count must be returned. + * @param world World where challenge count must be returned. + * @return Sum of completion count for all challenges by given user in given world. + */ + public long getTotalChallengeCompletionCount(User user, World world) + { + return this.getAllChallenges(world).stream(). + mapToLong(challenge -> this.getChallengeTimes(user, world, challenge)). + sum(); + } + + // --------------------------------------------------------------------- // Section: Level related methods // --------------------------------------------------------------------- @@ -1764,12 +1785,30 @@ public class ChallengesManager } + /** + * This method returns list of challenge levels in given gameMode. + * @param world for which levels must be searched. + * @return List with challengeLevel uniqueIds in given world. + */ + public List getLevelNames(@NonNull World world) + { + return this.islandWorldManager.getAddon(world).map(gameMode -> + this.levelCacheData.values().stream(). + sorted(ChallengeLevel::compareTo). + filter(level -> level.matchGameMode(gameMode.getDescription().getName())). + map(ChallengeLevel::getUniqueId). + collect(Collectors.toList())). + orElse(Collections.emptyList()); + } + + /** * Get challenge level by its challenge. * * @param challenge - challenge which level must be returned. * @return - challenge level or null if it does not exist */ + @Nullable public ChallengeLevel getLevel(Challenge challenge) { if (!challenge.getLevel().equals(FREE)) @@ -1787,6 +1826,7 @@ public class ChallengesManager * @param name - unique name of challenge level * @return - challenge level or null if it does not exist */ + @Nullable public ChallengeLevel getLevel(String name) { if (this.levelCacheData.containsKey(name)) @@ -1854,7 +1894,7 @@ public class ChallengesManager /** * This method adds given challenge to given challenge level. * @param newChallenge Challenge who must change owner. - * @param newLevel Level who must add new challenge + * @param newLevel Level to add to - must exist already */ public void addChallengeToLevel(Challenge newChallenge, ChallengeLevel newLevel) { @@ -1870,7 +1910,7 @@ public class ChallengesManager { ChallengeLevel oldLevel = this.getLevel(newChallenge.getLevel()); - if (!oldLevel.equals(newLevel)) + if (oldLevel == null || !oldLevel.equals(newLevel)) { this.removeChallengeFromLevel(newChallenge, newLevel); newLevel.getChallenges().add(newChallenge.getUniqueId()); @@ -1905,6 +1945,7 @@ public class ChallengesManager * @param uniqueID - new ID for challenge level. * @return ChallengeLevel that is currently created. */ + @Nullable public ChallengeLevel createLevel(String uniqueID, World world) { if (!this.containsLevel(uniqueID)) @@ -1952,7 +1993,7 @@ public class ChallengesManager this.levelDatabase.deleteObject(challengeLevel); this.addon.getPlugin().getPlaceholdersManager(). - unregisterPlaceholder("challenges_completed_challenge_count_per_level_" + challengeLevel.getUniqueId()); + unregisterPlaceholder("challenges_completed_challenge_count_per_level_" + challengeLevel.getUniqueId()); } } @@ -1965,7 +2006,7 @@ public class ChallengesManager public boolean hasAnyChallengeData(@NonNull World world) { return this.islandWorldManager.getAddon(world).filter(gameMode -> - this.hasAnyChallengeData(gameMode.getDescription().getName())).isPresent(); + this.hasAnyChallengeData(gameMode.getDescription().getName())).isPresent(); } @@ -1977,8 +2018,47 @@ public class ChallengesManager public boolean hasAnyChallengeData(@NonNull String gameMode) { return this.challengeDatabase.loadObjects().stream().anyMatch( - challenge -> challenge.matchGameMode(gameMode)) || - this.levelDatabase.loadObjects().stream().anyMatch( - level -> level.matchGameMode(gameMode)); + challenge -> challenge.matchGameMode(gameMode)) || + this.levelDatabase.loadObjects().stream().anyMatch( + level -> level.matchGameMode(gameMode)); + } + + + /** + * This method returns number of levels in given world. + * @param world World where level count must be returned. + * @return Number of levels in given world. + */ + public int getLevelCount(World world) + { + return this.getLevels(world).size(); + } + + + /** + * This method returns number of completed levels in given world. + * @param user User which completed level count must be returned. + * @param world World where level count must be returned. + * @return Number of completed levels by given user in given world. + */ + public long getCompletedLevelCount(User user, World world) + { + return this.getAllChallengeLevelStatus(user, world).stream(). + filter(LevelStatus::isComplete). + count(); + } + + + /** + * This method returns number of unlocked levels in given world. + * @param user User which unlocked level count must be returned. + * @param world World where level count must be returned. + * @return Number of unlocked levels by given user in given world. + */ + public long getUnlockedLevelCount(User user, World world) + { + return this.getAllChallengeLevelStatus(user, world).stream(). + filter(LevelStatus::isUnlocked). + count(); } } \ 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 index e0e2502..7012c9b 100644 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java @@ -1,12 +1,11 @@ package world.bentobox.challenges.commands; import java.util.List; -import java.util.Optional; import world.bentobox.bentobox.api.addons.GameModeAddon; -import world.bentobox.challenges.ChallengesAddon; 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; @@ -27,10 +26,7 @@ public class ChallengesCommand extends CompositeCommand @Override public boolean canExecute(User user, String label, List args) { - Optional optionalAddon = this.getAddon().getPlugin().getIWM().getAddon(this.getWorld()); - - if (!optionalAddon.isPresent()) - { + if (!getIWM().inWorld(getWorld())) { // Not a GameMode world. user.sendMessage("general.errors.wrong-world"); return false; @@ -39,13 +35,14 @@ public class ChallengesCommand extends CompositeCommand if (!((ChallengesAddon) this.getAddon()).getChallengesManager().hasAnyChallengeData(this.getWorld())) { // Do not open gui if there is no challenges. - - this.getAddon().getLogger().severe("There are no challenges set up in " + this.getWorld() + "!"); + 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 = optionalAddon.get().getAdminCommand().orElseGet(this::getParent).getTopLabel(); + 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 @@ -56,7 +53,7 @@ public class ChallengesCommand extends CompositeCommand return false; } - if (this.getPlugin().getIslands().getIsland(this.getWorld(), user.getUniqueId()) == null) + 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"); diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java index f30bd2c..4c90d22 100644 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java @@ -6,7 +6,6 @@ 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.config.SettingsUtils.GuiMode; import world.bentobox.challenges.panel.GameModesGUI; diff --git a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java index 306348a..840759f 100644 --- a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java @@ -1,7 +1,9 @@ package world.bentobox.challenges.commands; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import world.bentobox.bentobox.api.addons.Addon; @@ -19,128 +21,128 @@ import world.bentobox.challenges.utils.Utils; */ public class CompleteChallengeCommand extends CompositeCommand { - /** - * Default constructor for Composite Command. - * @param addon Challenges addon. - * @param cmd Parent Command. - */ - public CompleteChallengeCommand(Addon addon, CompositeCommand cmd) - { - super(addon, cmd, "complete"); - this.addon = (ChallengesAddon) addon; - } + /** + * Default constructor for Composite Command. + * @param addon Challenges addon. + * @param cmd Parent Command. + */ + public CompleteChallengeCommand(Addon addon, CompositeCommand cmd) + { + super(addon, cmd, "complete"); + this.addon = (ChallengesAddon) addon; + } - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setOnlyPlayer(true); - this.setPermission("complete"); - this.setParametersHelp("challenges.commands.user.complete.parameters"); - this.setDescription("challenges.commands.user.complete.description"); - } + /** + * {@inheritDoc} + */ + @Override + public void setup() + { + this.setOnlyPlayer(true); + this.setPermission("complete"); + this.setParametersHelp("challenges.commands.user.complete.parameters"); + this.setDescription("challenges.commands.user.complete.description"); + } - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - if (args.isEmpty()) - { - user.sendMessage("challenges.errors.no-name"); - this.showHelp(this, user); - return false; - } - else if (!args.get(0).isEmpty()) - { - // Add world name back at the start - String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(0); - Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName); + /** + * {@inheritDoc} + */ + @Override + public boolean execute(User user, String label, List args) + { + if (args.isEmpty()) + { + user.sendMessage("challenges.errors.no-name"); + this.showHelp(this, user); + return false; + } + else + { + // Add world name back at the start + String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(0); + Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName); - if (challenge != null) - { - int count = args.size() == 2 ? Integer.valueOf(args.get(1)) : 1; + if (challenge != null) + { + int count = args.size() == 2 ? Integer.valueOf(args.get(1)) : 1; - boolean canMultipleTimes = - user.hasPermission(this.getPermission() + ".multiple"); + boolean canMultipleTimes = + user.hasPermission(this.getPermission() + ".multiple"); - if (!canMultipleTimes && count > 1) - { - user.sendMessage("challenges.error.no-multiple-permission"); - count = 1; - } + if (!canMultipleTimes && count > 1) + { + user.sendMessage("challenges.error.no-multiple-permission"); + count = 1; + } - return TryToComplete.complete(this.addon, - user, - challenge, - this.getWorld(), - this.getTopLabel(), - this.getPermissionPrefix(), - count); - } - else - { - user.sendMessage("challenges.errors.unknown-challenge"); - this.showHelp(this, user); - return false; - } - } - - this.showHelp(this, user); - return false; - } + return TryToComplete.complete(this.addon, + user, + challenge, + this.getWorld(), + this.getTopLabel(), + this.getPermissionPrefix(), + count); + } + else + { + user.sendMessage("challenges.errors.unknown-challenge"); + this.showHelp(this, user); + return false; + } + } + } - /** - * {@inheritDoc} - */ - @Override - public Optional> tabComplete(User user, String alias, List args) - { - String lastString = args.get(args.size() - 1); + /** + * {@inheritDoc} + */ + @Override + public Optional> tabComplete(User user, String alias, List args) + { + if (args.isEmpty()) return Optional.empty(); - final List returnList = new ArrayList<>(); - final int size = args.size(); + String lastString = args.get(args.size() - 1); - switch (size) - { - case 3: - // 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())); + final List returnList = new ArrayList<>(); + final int size = args.size(); - break; - case 4: - // Suggest a number of completions. - if (lastString.isEmpty() || lastString.matches("[0-9]*")) - { - returnList.add(""); - } + switch (size) + { + 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())); + break; + case 4: + // Suggest a number of completions. + if (lastString.isEmpty() || lastString.matches("[0-9]*")) + { + returnList.add(""); + } - break; - default: - { - returnList.add("help"); - break; - } - } + break; + default: + { + returnList.add("help"); + break; + } + } - return Optional.of(Util.tabLimit(returnList, lastString)); - } + return Optional.of(Util.tabLimit(returnList, lastString)); + } -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - /** - * Variable that holds challenge addon. Single casting. - */ - private ChallengesAddon addon; + /** + * 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 index 1377c9a..1567503 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java @@ -2,9 +2,9 @@ package world.bentobox.challenges.commands.admin; import java.util.List; -import world.bentobox.challenges.ChallengesAddon; 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; 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 7e73eb9..d0d3f67 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java @@ -1,7 +1,11 @@ package world.bentobox.challenges.commands.admin; -import java.util.*; +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; 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 05be3fb..6022895 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java @@ -2,11 +2,11 @@ package world.bentobox.challenges.commands.admin; import java.util.List; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; 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; /** 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 d8b5089..649b728 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java @@ -1,7 +1,11 @@ package world.bentobox.challenges.commands.admin; -import java.util.*; +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; 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 6226fc5..a735dc3 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java @@ -1,12 +1,11 @@ package world.bentobox.challenges.commands.admin; import java.util.List; -import java.util.logging.Level; -import world.bentobox.challenges.ChallengesAddon; 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; public class ShowChallenges extends CompositeCommand { diff --git a/src/main/java/world/bentobox/challenges/config/Settings.java b/src/main/java/world/bentobox/challenges/config/Settings.java index 0688599..49df416 100644 --- a/src/main/java/world/bentobox/challenges/config/Settings.java +++ b/src/main/java/world/bentobox/challenges/config/Settings.java @@ -13,10 +13,9 @@ 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.GuiMode; 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; 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 b532f52..cff291f 100644 --- a/src/main/java/world/bentobox/challenges/database/object/Challenge.java +++ b/src/main/java/world/bentobox/challenges/database/object/Challenge.java @@ -13,6 +13,7 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; import com.google.gson.annotations.Expose; import com.google.gson.annotations.JsonAdapter; @@ -717,7 +718,7 @@ public class Challenge implements DataObject * This method sets the level value. * @param level the level new value. */ - public void setLevel(String level) + public void setLevel(@NonNull String level) { this.level = level; } @@ -1050,7 +1051,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()); } @@ -1129,7 +1130,7 @@ public class Challenge implements DataObject clone.setRequirements(this.requirements.clone()); clone.setRewardText(this.rewardText); clone.setRewardItems(this.rewardItems.stream().map(ItemStack::clone). - collect(Collectors.toCollection(() -> new ArrayList<>(this.rewardItems.size())))); + collect(Collectors.toCollection(() -> new ArrayList<>(this.rewardItems.size())))); clone.setRewardExperience(this.rewardExperience); clone.setRewardMoney(this.rewardMoney); clone.setRewardCommands(new ArrayList<>(this.rewardCommands)); @@ -1138,7 +1139,7 @@ public class Challenge implements DataObject clone.setMaxTimes(this.maxTimes); clone.setRepeatExperienceReward(this.repeatExperienceReward); clone.setRepeatItemReward(this.repeatItemReward.stream().map(ItemStack::clone). - collect(Collectors.toCollection(() -> new ArrayList<>(this.repeatItemReward.size())))); + collect(Collectors.toCollection(() -> new ArrayList<>(this.repeatItemReward.size())))); clone.setRepeatMoneyReward(this.repeatMoneyReward); clone.setRepeatRewardCommands(new ArrayList<>(this.repeatRewardCommands)); } 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 ae22355..7fb26bf 100644 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java @@ -1,15 +1,17 @@ package world.bentobox.challenges.database.object; -import com.google.gson.annotations.Expose; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import com.google.gson.annotations.Expose; + import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.challenges.ChallengesManager; 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 820c17a..db306a9 100644 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java @@ -1,9 +1,16 @@ package world.bentobox.challenges.database.object; -import com.google.gson.annotations.Expose; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + import org.eclipse.jdt.annotation.NonNull; -import java.util.*; + +import com.google.gson.annotations.Expose; import world.bentobox.bentobox.api.logs.LogEntry; import world.bentobox.bentobox.database.objects.DataObject; @@ -18,334 +25,334 @@ import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter; */ public class ChallengesPlayerData implements DataObject { - /** - * Constructor ChallengesPlayerData creates a new ChallengesPlayerData instance. - */ - public ChallengesPlayerData() - { - } + /** + * Constructor ChallengesPlayerData creates a new ChallengesPlayerData instance. + */ + public ChallengesPlayerData() + { + } - /** - * Creates a player data entry - * - * @param uniqueId - the player's UUID in string format - */ - public ChallengesPlayerData(String uniqueId) - { - this.uniqueId = uniqueId; - } + /** + * Creates a player data entry + * + * @param uniqueId - the player's UUID in string format + */ + public ChallengesPlayerData(String uniqueId) + { + this.uniqueId = uniqueId; + } -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - /** - * This variable stores each player UUID as string. - */ - @Expose - private String uniqueId = ""; + /** + * This variable stores each player UUID as string. + */ + @Expose + private String uniqueId = ""; - /** - * Challenge map, where key = unique challenge name and Value = number of times - * completed - */ - @Expose - private Map challengeStatus = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + /** + * Challenge map, where key = unique challenge name and Value = number of times + * completed + */ + @Expose + private Map challengeStatus = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - /** - * Map of challenges completion time where key is challenges unique id and value is - * timestamp when challenge was completed last time. - */ - @Expose - private Map challengesTimestamp = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + /** + * Map of challenges completion time where key is challenges unique id and value is + * timestamp when challenge was completed last time. + */ + @Expose + private Map challengesTimestamp = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - /** - * Set of Strings that contains all challenge levels that are completed. - */ - @Expose - private Set levelsDone = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + /** + * Set of Strings that contains all challenge levels that are completed. + */ + @Expose + private Set levelsDone = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - /** - * Stores history about challenge completion. - */ - @Adapter(LogEntryListAdapter.class) - @Expose - private List history = new LinkedList<>(); + /** + * Stores history about challenge completion. + */ + @Adapter(LogEntryListAdapter.class) + @Expose + private List history = new LinkedList<>(); -// --------------------------------------------------------------------- -// Section: Getters -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Getters + // --------------------------------------------------------------------- - /** - * @return uniqueID - * @see DataObject#getUniqueId() - */ - @Override - public String getUniqueId() - { - return uniqueId; - } + /** + * @return uniqueID + * @see DataObject#getUniqueId() + */ + @Override + public String getUniqueId() + { + return uniqueId; + } - /** - * This method returns the challengeStatus value. - * @return the value of challengeStatus. - */ - public Map getChallengeStatus() - { - return challengeStatus; - } + /** + * This method returns the challengeStatus value. + * @return the value of challengeStatus. + */ + public Map getChallengeStatus() + { + return challengeStatus; + } - /** - * This method returns the challengesTimestamp value. - * @return the value of challengesTimestamp. - */ - public Map getChallengesTimestamp() - { - return challengesTimestamp; - } + /** + * This method returns the challengesTimestamp value. + * @return the value of challengesTimestamp. + */ + public Map getChallengesTimestamp() + { + return challengesTimestamp; + } - /** - * This method returns the levelsDone value. - * @return the value of levelsDone. - */ - public Set getLevelsDone() - { - return levelsDone; - } + /** + * This method returns the levelsDone value. + * @return the value of levelsDone. + */ + public Set getLevelsDone() + { + return levelsDone; + } - /** - * This method returns the history object. - * @return the history object. - */ - public List getHistory() - { - return history; - } + /** + * This method returns the history object. + * @return the history object. + */ + public List getHistory() + { + return history; + } -// --------------------------------------------------------------------- -// Section: Setters -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Setters + // --------------------------------------------------------------------- - /** - * @param uniqueId - unique ID the uniqueId to set - * @see DataObject#setUniqueId(String) - */ - @Override - public void setUniqueId(String uniqueId) - { - this.uniqueId = uniqueId; - } + /** + * @param uniqueId - unique ID the uniqueId to set + * @see DataObject#setUniqueId(String) + */ + @Override + public void setUniqueId(String uniqueId) + { + this.uniqueId = uniqueId; + } - /** - * This method sets the challengeStatus value. - * @param challengeStatus the challengeStatus new value. - * - */ - public void setChallengeStatus(Map challengeStatus) - { - this.challengeStatus = challengeStatus; - } + /** + * This method sets the challengeStatus value. + * @param challengeStatus the challengeStatus new value. + * + */ + public void setChallengeStatus(Map challengeStatus) + { + this.challengeStatus = challengeStatus; + } - /** - * This method sets the challengesTimestamp value. - * @param challengesTimestamp the challengesTimestamp new value. - * - */ - public void setChallengesTimestamp(Map challengesTimestamp) - { - this.challengesTimestamp = challengesTimestamp; - } + /** + * This method sets the challengesTimestamp value. + * @param challengesTimestamp the challengesTimestamp new value. + * + */ + public void setChallengesTimestamp(Map challengesTimestamp) + { + this.challengesTimestamp = challengesTimestamp; + } - /** - * This method sets the levelsDone value. - * @param levelsDone the levelsDone new value. - * - */ - public void setLevelsDone(Set levelsDone) - { - this.levelsDone = levelsDone; - } + /** + * This method sets the levelsDone value. + * @param levelsDone the levelsDone new value. + * + */ + public void setLevelsDone(Set levelsDone) + { + this.levelsDone = levelsDone; + } - /** - * This method sets the history object value. - * @param history the history object new value. - */ - public void setHistory(List history) - { - this.history = history; - } + /** + * This method sets the history object value. + * @param history the history object new value. + */ + public void setHistory(List history) + { + this.history = history; + } -// --------------------------------------------------------------------- -// Section: Other Methods -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Other Methods + // --------------------------------------------------------------------- - /** - * Resets all challenges and levels in GameMode for this player - * - * @param gameMode GameMode which challenges must be reset. - */ - public void reset(@NonNull String gameMode) - { - challengeStatus.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); - challengesTimestamp.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); - levelsDone.removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); - } + /** + * Resets all challenges and levels in GameMode for this player + * + * @param gameMode GameMode which challenges must be reset. + */ + public void reset(@NonNull String gameMode) + { + challengeStatus.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); + challengesTimestamp.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); + levelsDone.removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); + } - /** - * Mark a challenge as having been completed. Will increment the number of times and - * timestamp - * - * @param challengeName - unique challenge name - */ - public void setChallengeDone(@NonNull String challengeName) - { - this.addChallengeDone(challengeName, 1); - } + /** + * Mark a challenge as having been completed. Will increment the number of times and + * timestamp + * + * @param challengeName - unique challenge name + */ + public void setChallengeDone(@NonNull String challengeName) + { + this.addChallengeDone(challengeName, 1); + } - /** - * Mark a challenge as having been completed. Will increment the number of times and - * timestamp - * - * @param challengeName - unique challenge name - * @param times - how many new times should be added - */ - public void addChallengeDone(@NonNull String challengeName, int times) - { - int newTimes = challengeStatus.getOrDefault(challengeName, 0) + times; - challengeStatus.put(challengeName, newTimes); - challengesTimestamp.put(challengeName, System.currentTimeMillis()); - } + /** + * Mark a challenge as having been completed. Will increment the number of times and + * timestamp + * + * @param challengeName - unique challenge name + * @param times - how many new times should be added + */ + public void addChallengeDone(@NonNull String challengeName, int times) + { + int newTimes = challengeStatus.getOrDefault(challengeName, 0) + times; + challengeStatus.put(challengeName, newTimes); + challengesTimestamp.put(challengeName, System.currentTimeMillis()); + } - /** - * Set the number of times a challenge has been done - * - * @param challengeName - unique challenge name - * @param times - the number of times to set - */ - public void setChallengeTimes(@NonNull String challengeName, @NonNull int times) - { - challengeStatus.put(challengeName, times); - challengesTimestamp.put(challengeName, System.currentTimeMillis()); - } + /** + * Set the number of times a challenge has been done + * + * @param challengeName - unique challenge name + * @param times - the number of times to set + */ + public void setChallengeTimes(@NonNull String challengeName, @NonNull int times) + { + challengeStatus.put(challengeName, times); + challengesTimestamp.put(challengeName, System.currentTimeMillis()); + } - /** - * Check if a challenge has been done - * - * @param challengeName - unique challenge name - * @return true if done at least once - */ - public boolean isChallengeDone(@NonNull String challengeName) - { - return this.getTimes(challengeName) > 0; - } + /** + * Check if a challenge has been done + * + * @param challengeName - unique challenge name + * @return true if done at least once + */ + public boolean isChallengeDone(@NonNull String challengeName) + { + return this.getTimes(challengeName) > 0; + } - /** - * Check how many times a challenge has been done - * - * @param challengeName - unique challenge name - * @return - number of times - */ - public int getTimes(@NonNull String challengeName) - { - return challengeStatus.getOrDefault(challengeName, 0); - } + /** + * Check how many times a challenge has been done + * + * @param challengeName - unique challenge name + * @return - number of times + */ + public int getTimes(@NonNull String challengeName) + { + return challengeStatus.getOrDefault(challengeName, 0); + } - /** - * This method adds given level id to completed level set. - * @param uniqueId from ChallengeLevel object. - */ - public void addCompletedLevel(@NonNull String uniqueId) - { - this.levelsDone.add(uniqueId); - } + /** + * This method adds given level id to completed level set. + * @param uniqueId from ChallengeLevel object. + */ + public void addCompletedLevel(@NonNull String uniqueId) + { + this.levelsDone.add(uniqueId); + } - /** - * This method returns if given level is done. - * @param uniqueId of ChallengeLevel object. - * @return true if level is completed, otherwise false - */ - public boolean isLevelDone(@NonNull String uniqueId) - { - return !this.levelsDone.isEmpty() && this.levelsDone.contains(uniqueId); - } + /** + * This method returns if given level is done. + * @param uniqueId of ChallengeLevel object. + * @return true if level is completed, otherwise false + */ + public boolean isLevelDone(@NonNull String uniqueId) + { + return !this.levelsDone.isEmpty() && this.levelsDone.contains(uniqueId); + } - /** - * This method adds given LogEntry to history. - * - * @param entry of type LogEntry - */ - public void addHistoryRecord(@NonNull LogEntry entry) - { - this.history.add(entry); - } + /** + * This method adds given LogEntry to history. + * + * @param entry of type LogEntry + */ + public void addHistoryRecord(@NonNull LogEntry entry) + { + this.history.add(entry); + } - /** - * @see Object#hashCode() - * @return object hashCode value. - */ - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); - return result; - } + /** + * @see Object#hashCode() + * @return object hashCode value. + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } - /** - * @see java.lang.Object#equals(java.lang.Object) - * @param obj Other object. - * @return boolean that indicate if objects are equals. - */ - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } + /** + * @see java.lang.Object#equals(java.lang.Object) + * @param obj Other object. + * @return boolean that indicate if objects are equals. + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } - if (!(obj instanceof ChallengesPlayerData)) - { - return false; - } + if (!(obj instanceof ChallengesPlayerData)) + { + return false; + } - ChallengesPlayerData other = (ChallengesPlayerData) obj; + ChallengesPlayerData other = (ChallengesPlayerData) obj; - if (uniqueId == null) - { - return other.uniqueId == null; - } - else - { - return uniqueId.equalsIgnoreCase(other.uniqueId); - } - } + if (uniqueId == null) + { + return other.uniqueId == null; + } + else + { + return uniqueId.equalsIgnoreCase(other.uniqueId); + } + } } \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/RequirementsAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/RequirementsAdapter.java index 63ddb5a..4d4e90b 100644 --- a/src/main/java/world/bentobox/challenges/database/object/adapters/RequirementsAdapter.java +++ b/src/main/java/world/bentobox/challenges/database/object/adapters/RequirementsAdapter.java @@ -7,6 +7,8 @@ package world.bentobox.challenges.database.object.adapters; +import java.lang.reflect.Type; + import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -15,7 +17,6 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; -import java.lang.reflect.Type; import world.bentobox.challenges.database.object.requirements.Requirements; 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 0a2f24e..7023a2a 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,13 +7,15 @@ package world.bentobox.challenges.database.object.requirements; -import com.google.gson.annotations.Expose; -import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; +import org.bukkit.inventory.ItemStack; + +import com.google.gson.annotations.Expose; + /** * This class contains all necessary requirements to complete inventory type challenge. 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 abfd0d9..dac9ab4 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 @@ -7,14 +7,16 @@ package world.bentobox.challenges.database.object.requirements; -import com.google.gson.annotations.Expose; -import org.bukkit.Material; -import org.bukkit.entity.EntityType; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; + +import com.google.gson.annotations.Expose; + /** * This class contains all necessary requirements to complete island type challenge. 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 92aadf7..fb1bf44 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 @@ -7,12 +7,9 @@ package world.bentobox.challenges.database.object.requirements; -import com.google.gson.annotations.Expose; -import org.eclipse.jdt.annotation.NonNull; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; -import java.util.Set; + +import com.google.gson.annotations.Expose; /** 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 9b3538e..09a54f5 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 @@ -7,10 +7,11 @@ package world.bentobox.challenges.database.object.requirements; -import com.google.gson.annotations.Expose; import java.util.HashSet; import java.util.Set; +import com.google.gson.annotations.Expose; + /** * This abstract class allows to define requirements for each challenge. diff --git a/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java index 303b4de..c099961 100644 --- a/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java @@ -5,7 +5,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; - import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.database.object.Challenge; diff --git a/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java index c270bb3..5c067cf 100644 --- a/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java @@ -1,10 +1,11 @@ package world.bentobox.challenges.handlers; -import org.bukkit.Bukkit; import java.util.Collections; import java.util.Map; +import org.bukkit.Bukkit; + import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; diff --git a/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java index 3452a34..3ea05bb 100644 --- a/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java @@ -1,10 +1,13 @@ package world.bentobox.challenges.handlers; +import java.util.Collections; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + import org.bukkit.Bukkit; import org.bukkit.World; -import java.util.*; -import java.util.stream.Collectors; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; diff --git a/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java index 8d8f809..f449297 100644 --- a/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java @@ -1,10 +1,11 @@ package world.bentobox.challenges.handlers; -import org.bukkit.Bukkit; import java.util.Collections; import java.util.Map; +import org.bukkit.Bukkit; + import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; @@ -51,7 +52,8 @@ public class LevelListRequestHandler extends AddonRequestHandler return Collections.emptyList(); } - return this.addon.getChallengesManager().getLevels(Bukkit.getWorld((String) metaData.get("world-name"))); + return this.addon.getChallengesManager().getLevelNames( + Bukkit.getWorld((String) metaData.get("world-name"))); } diff --git a/src/main/java/world/bentobox/challenges/listeners/ResetListener.java b/src/main/java/world/bentobox/challenges/listeners/ResetListener.java index 5f5f043..294ea8d 100644 --- a/src/main/java/world/bentobox/challenges/listeners/ResetListener.java +++ b/src/main/java/world/bentobox/challenges/listeners/ResetListener.java @@ -7,10 +7,9 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; +import world.bentobox.challenges.ChallengesAddon; /** * Resets challenges when the island is reset diff --git a/src/main/java/world/bentobox/challenges/listeners/SaveListener.java b/src/main/java/world/bentobox/challenges/listeners/SaveListener.java index ccb043a..f20e588 100644 --- a/src/main/java/world/bentobox/challenges/listeners/SaveListener.java +++ b/src/main/java/world/bentobox/challenges/listeners/SaveListener.java @@ -6,7 +6,6 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.world.WorldSaveEvent; import world.bentobox.challenges.ChallengesAddon; diff --git a/src/main/java/world/bentobox/challenges/panel/CommonGUI.java b/src/main/java/world/bentobox/challenges/panel/CommonGUI.java index e33e8aa..39897d6 100644 --- a/src/main/java/world/bentobox/challenges/panel/CommonGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/CommonGUI.java @@ -9,7 +9,11 @@ import java.util.function.Consumer; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.conversations.*; +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; @@ -1078,6 +1082,10 @@ public abstract class CommonGUI } }). withLocalEcho(false). + // On cancel conversation will be closed. + withEscapeSequence("cancel"). + // Use null value in consumer to detect if user has abandoned conversation. + addConversationAbandonedListener(abandonedEvent -> consumer.accept(null)). withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). buildConversation(user.getPlayer()); diff --git a/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java b/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java index bbc26c8..d60e7a4 100644 --- a/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java @@ -1,12 +1,12 @@ package world.bentobox.challenges.panel; -import org.bukkit.Material; -import org.bukkit.World; - 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; diff --git a/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java index 0355542..a0efdd7 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java @@ -1,19 +1,16 @@ package world.bentobox.challenges.panel.admin; -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.ValidatingPrompt; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; - +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; @@ -251,24 +248,31 @@ public class AdminGUI extends CommonGUI icon = new ItemStack(Material.BOOK); clickHandler = (panel, user, clickType, slot) -> { - this.getNewUniqueID(challenge -> { - String newName = Utils.getGameMode(this.world) + "_" + challenge; + 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(newName, type, requirements), - this.topLabel, - this.permissionPrefix, - this).build(); - }); + 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 newName = Utils.getGameMode(this.world) + "_" + input; - return !this.addon.getChallengesManager().containsChallenge(newName); + String uniqueId = Utils.getGameMode(this.world) + "_" + input; + return !this.addon.getChallengesManager().containsChallenge(uniqueId); }, this.user.getTranslation("challenges.gui.questions.admin.unique-id") ); @@ -286,16 +290,25 @@ public class AdminGUI extends CommonGUI icon = new ItemStack(Material.BOOK); clickHandler = (panel, user, clickType, slot) -> { - this.getNewUniqueID(level -> { - String newName = Utils.getGameMode(this.world) + "_" + level; + 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(); + 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; @@ -659,7 +672,7 @@ public class AdminGUI extends CommonGUI @Override protected boolean isInputValid(ConversationContext context, String input) { - return stringValidation.apply(input); + return stringValidation.apply(GuiUtils.sanitizeInput(input)); } @@ -679,7 +692,7 @@ public class AdminGUI extends CommonGUI protected String getFailedValidationText(ConversationContext context, String invalidInput) { - return user.getTranslation("challenges.errors.unique-id", "[id]", invalidInput); + return user.getTranslation("challenges.errors.unique-id", "[id]", GuiUtils.sanitizeInput(invalidInput)); } @@ -699,11 +712,15 @@ public class AdminGUI extends CommonGUI protected Prompt acceptValidatedInput(ConversationContext context, String input) { // Add answer to consumer. - consumer.accept(input); + 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 -> consumer.accept(null)). withLocalEcho(false). withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). buildConversation(user.getPlayer()); diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java index 1884278..f064182 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java @@ -1,10 +1,6 @@ package world.bentobox.challenges.panel.admin; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -12,6 +8,11 @@ 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; @@ -499,7 +500,11 @@ public class EditChallengeGUI extends CommonGUI clickHandler = (panel, user, clickType, slot) -> { this.getFriendlyName(reply -> { - this.challenge.setFriendlyName(reply); + if (reply != null) + { + this.challenge.setFriendlyName(reply); + } + this.build(); }, this.user.getTranslation("challenges.gui.questions.admin.challenge-name"), diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java index db7a81f..bd6b841 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java @@ -22,7 +22,11 @@ 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.*; +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; @@ -334,9 +338,13 @@ public class EditLevelGUI extends CommonGUI icon = new ItemStack(Material.DROPPER); clickHandler = (panel, user, clickType, slot) -> { - this.getFriendlyName(reply -> { - this.challengeLevel.setFriendlyName(reply); - this.build(); + this.getFriendlyName(reply -> + { + if (reply != null) + { + this.challengeLevel.setFriendlyName(reply); + } + this.build(); }, this.user.getTranslation("challenges.gui.questions.admin.level-name"), this.challengeLevel.getFriendlyName() diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java index 1f53843..860de1a 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java @@ -1,14 +1,18 @@ 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 java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.stream.Collectors; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelListener; @@ -26,605 +30,609 @@ import world.bentobox.challenges.utils.GuiUtils; */ 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. - */ - 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); - - 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); - - 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); - } - } + 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 + // --------------------------------------------------------------------- - -// --------------------------------------------------------------------- -// 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."; + + 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 index b1a6b3d..40abf68 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java @@ -128,7 +128,6 @@ public class EditSettingsGUI extends CommonGUI } - @SuppressWarnings("deprecation") private PanelItem getSettingsButton(Button button) { ItemStack icon; @@ -434,7 +433,7 @@ public class EditSettingsGUI extends CommonGUI if (status) { materials.forEach(material -> - this.settings.setLockedLevelIcon(new ItemStack(material))); + this.settings.setLockedLevelIcon(new ItemStack(material))); } this.build(); @@ -501,14 +500,14 @@ public class EditSettingsGUI extends CommonGUI 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")); + 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")); + 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")); + this.user.getTranslation("challenges.gui.descriptions.visibility.toggleable")); values.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]",this.settings.getVisibilityMode().name())); + "[value]",this.settings.getVisibilityMode().name())); description = values; @@ -529,19 +528,19 @@ public class EditSettingsGUI extends CommonGUI if (clickType.isRightClick()) { this.settings.setVisibilityMode( - Utils.getPreviousValue(VisibilityMode.values(), - this.settings.getVisibilityMode())); + Utils.getPreviousValue(VisibilityMode.values(), + this.settings.getVisibilityMode())); } else { this.settings.setVisibilityMode( - Utils.getNextValue(VisibilityMode.values(), - this.settings.getVisibilityMode())); + Utils.getNextValue(VisibilityMode.values(), + this.settings.getVisibilityMode())); } // Rebuild just this icon panel.getInventory().setItem(slot, - this.getSettingsButton(button).getItem()); + this.getSettingsButton(button).getItem()); return true; }; diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java index 3894efe..ccdb1cf 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java @@ -1,10 +1,11 @@ package world.bentobox.challenges.panel.admin; +import java.util.List; + import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.World; -import java.util.List; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java index d51ffdf..4a9dad5 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java @@ -1,10 +1,11 @@ package world.bentobox.challenges.panel.admin; +import java.util.List; + import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.World; -import java.util.List; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java index 4ee9d11..179cbb1 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java @@ -1,11 +1,12 @@ 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 java.util.ArrayList; -import java.util.List; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java index db38cbf..e22eead 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java @@ -1,16 +1,17 @@ package world.bentobox.challenges.panel.admin; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.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; diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java index c24e6d9..eab6371 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java @@ -1,10 +1,16 @@ 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 java.util.*; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java index 135621d..ed29747 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java @@ -1,11 +1,17 @@ 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 java.util.*; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java index f29f873..38005ad 100644 --- a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java @@ -1,11 +1,12 @@ 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 java.util.List; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; @@ -77,7 +78,7 @@ public class ChallengesGUI extends CommonGUI // Do not open gui if there is no challenges. if (!this.challengesManager.hasAnyChallengeData(this.world)) { - this.addon.getLogger().severe("There are no challenges set up!"); + this.addon.logError("There are no challenges set up!"); this.user.sendMessage("challenges.errors.no-challenges"); return; } diff --git a/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java b/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java index 509f29c..26a9690 100644 --- a/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java @@ -1,10 +1,10 @@ package world.bentobox.challenges.panel.user; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; 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; diff --git a/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java index 7956082..68bf018 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java @@ -7,12 +7,13 @@ 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.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; diff --git a/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java index 20bfb13..5a979cc 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java @@ -1,10 +1,10 @@ package world.bentobox.challenges.panel.util; -import org.bukkit.Material; - 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; diff --git a/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java index 4969f1e..e7caf56 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java @@ -1,14 +1,15 @@ package world.bentobox.challenges.panel.util; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + import org.bukkit.Material; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.inventory.ItemStack; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.BiConsumer; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelListener; diff --git a/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java b/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java index 5ae5d9b..4f00cbc 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java @@ -1,13 +1,18 @@ package world.bentobox.challenges.panel.util; -import org.bukkit.Material; -import org.bukkit.conversations.*; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; 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; @@ -21,505 +26,528 @@ import world.bentobox.challenges.utils.GuiUtils; */ 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 -> { - 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; - } - } - - 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. - */ - 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). - 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; + 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 -> 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/SelectBlocksGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java index d7c6855..d3ffdcb 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java @@ -1,17 +1,20 @@ 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 java.util.*; -import java.util.function.BiConsumer; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.utils.GuiUtils; @@ -28,7 +31,36 @@ public class SelectBlocksGUI public SelectBlocksGUI(User user, boolean singleSelect, BiConsumer> consumer) { - this(user, singleSelect, new HashSet<>(), 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); } diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java index 1b37a97..16cc3b7 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java @@ -1,17 +1,21 @@ 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 java.util.*; -import java.util.function.BiConsumer; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.database.object.Challenge; import world.bentobox.challenges.utils.GuiUtils; diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java index b4019f6..4104f8a 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java @@ -1,18 +1,23 @@ 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 java.util.*; -import java.util.function.BiConsumer; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.utils.GuiUtils; diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java index 313f103..928998a 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java @@ -1,12 +1,13 @@ package world.bentobox.challenges.panel.util; -import org.bukkit.Material; -import org.bukkit.World; import java.util.Collections; import java.util.Set; import java.util.function.BiConsumer; +import 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; diff --git a/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java b/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java index 39daf73..0e7ea8f 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java @@ -1,11 +1,6 @@ package world.bentobox.challenges.panel.util; -import org.bukkit.Material; -import org.bukkit.conversations.*; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -13,6 +8,16 @@ 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; @@ -151,7 +156,15 @@ public class StringListGUI icon = new ItemStack(Material.WHITE_STAINED_GLASS_PANE); clickHandler = (panel, user, clickType, slot) -> { - this.getStringInput(value -> this.value.add(value), + 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; @@ -210,7 +223,15 @@ public class StringListGUI clickHandler((panel, user1, clickType, i) -> { this.getStringInput( - value -> this.value.set(stringIndex, value), + value -> { + if (value != null) + { + this.value.set(stringIndex, value); + } + + // Reopen GUI + this.build(); + }, this.user.getTranslation("challenges.gui.descriptions.admin.edit-text-line"), element); @@ -277,12 +298,14 @@ public class StringListGUI { // Add answer to consumer. consumer.accept(answer); - // Reopen GUI - StringListGUI.this.build(); // 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 -> consumer.accept(null)). withLocalEcho(false). withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). buildConversation(user.getPlayer()); diff --git a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java index 7db1474..1b29ace 100644 --- a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java +++ b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java @@ -1,11 +1,23 @@ -/** - * - */ package world.bentobox.challenges.tasks; -import org.bukkit.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Entity; @@ -13,8 +25,6 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.util.BoundingBox; -import java.util.*; -import java.util.stream.Collectors; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; @@ -38,9 +48,9 @@ import world.bentobox.challenges.utils.Utils; */ public class TryToComplete { -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- /** * Challenges addon variable. @@ -82,9 +92,9 @@ public class TryToComplete */ private final ChallengeResult EMPTY_RESULT = new ChallengeResult(); -// --------------------------------------------------------------------- -// Section: Builder -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Builder + // --------------------------------------------------------------------- @Deprecated public TryToComplete label(String label) @@ -141,9 +151,9 @@ public class TryToComplete } -// --------------------------------------------------------------------- -// Section: Constructor -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- /** @@ -155,18 +165,18 @@ public class TryToComplete * @param permissionPrefix - Permission prefix for GameMode addon. */ public TryToComplete(ChallengesAddon addon, - User user, - Challenge challenge, - World world, - String topLabel, - String permissionPrefix) + User user, + Challenge challenge, + World world, + String topLabel, + String permissionPrefix) { this.addon = addon; this.world = world; this.permissionPrefix = permissionPrefix; this.user = user; this.manager = addon.getChallengesManager(); - // To avoid any modifications that may accure to challenges in current completion + // To avoid any modifications that may occur to challenges in current completion // just clone it. this.challenge = challenge.clone(); this.topLabel = topLabel; @@ -184,11 +194,11 @@ public class TryToComplete * @return true, if challenge is completed, otherwise false. */ public static boolean complete(ChallengesAddon addon, - User user, - Challenge challenge, - World world, - String topLabel, - String permissionPrefix) + User user, + Challenge challenge, + World world, + String topLabel, + String permissionPrefix) { return TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 1); } @@ -206,28 +216,28 @@ public class TryToComplete * @return true, if challenge is completed, otherwise false. */ public static boolean complete(ChallengesAddon addon, - User user, - Challenge challenge, - World world, - String topLabel, - String permissionPrefix, - int maxTimes) + User user, + Challenge challenge, + World world, + String topLabel, + String permissionPrefix, + int maxTimes) { return new TryToComplete(addon, user, challenge, world, topLabel, permissionPrefix). - build(maxTimes).meetsRequirements; + build(maxTimes).meetsRequirements; } -// --------------------------------------------------------------------- -// Section: Methods -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- /** * This method checks if challenge can be done, and complete it, if it is possible. * @return ChallengeResult object, that contains completion status. */ - public ChallengeResult build(int maxTimes) + ChallengeResult build(int maxTimes) { // Check if can complete challenge ChallengeResult result = this.checkIfCanCompleteChallenge(maxTimes); @@ -251,7 +261,7 @@ public class TryToComplete returnItem.setAmount(amount); this.user.getInventory().addItem(returnItem).forEach((k, v) -> - this.user.getWorld().dropItem(this.user.getLocation(), v)); + this.user.getWorld().dropItem(this.user.getLocation(), v)); }); } @@ -269,7 +279,7 @@ public class TryToComplete // Clone is necessary because otherwise it will chane reward itemstack // amount. this.user.getInventory().addItem(reward.clone()).forEach((k, v) -> - this.user.getWorld().dropItem(this.user.getLocation(), v)); + this.user.getWorld().dropItem(this.user.getLocation(), v)); } // Money Reward @@ -292,14 +302,14 @@ public class TryToComplete if (this.addon.getChallengesSettings().isBroadcastMessages()) { - for (Player player : this.addon.getServer().getOnlinePlayers()) + 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()); + "[name]", this.user.getName(), + "[value]", this.challenge.getFriendlyName()); } } } @@ -308,11 +318,11 @@ public class TryToComplete 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); } } @@ -329,7 +339,7 @@ public class TryToComplete for (int i = 0; i < rewardFactor; i++) { this.user.getInventory().addItem(reward.clone()).forEach((k, v) -> - this.user.getWorld().dropItem(this.user.getLocation(), v)); + this.user.getWorld().dropItem(this.user.getLocation(), v)); } } @@ -337,12 +347,12 @@ public class TryToComplete if (this.addon.isEconomyProvided()) { this.addon.getEconomyProvider().deposit(this.user, - this.challenge.getRepeatMoneyReward() * rewardFactor); + (double)this.challenge.getRepeatMoneyReward() * rewardFactor); } // Experience Repeat Reward this.user.getPlayer().giveExp( - this.challenge.getRepeatExperienceReward() * rewardFactor); + this.challenge.getRepeatExperienceReward() * rewardFactor); // Run commands for (int i = 0; i < rewardFactor; i++) @@ -353,8 +363,8 @@ 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())); + "[value]", this.challenge.getFriendlyName(), + "[count]", Integer.toString(result.getFactor())); } else { @@ -367,7 +377,7 @@ public class TryToComplete // Check level completion for non-free challenges if (!result.wasCompleted() && - !this.challenge.getLevel().equals(ChallengesManager.FREE)) + !this.challenge.getLevel().equals(ChallengesManager.FREE)) { ChallengeLevel level = this.manager.getLevel(this.challenge); @@ -381,7 +391,7 @@ public class TryToComplete // Clone is necessary because otherwise it will chane reward itemstack // amount. this.user.getInventory().addItem(reward.clone()).forEach((k, v) -> - this.user.getWorld().dropItem(this.user.getLocation(), v)); + this.user.getWorld().dropItem(this.user.getLocation(), v)); } // Money Reward @@ -406,7 +416,7 @@ public class TryToComplete if (!player.getUniqueId().equals(this.user.getUniqueId())) { User.getInstance(player).sendMessage("challenges.messages.name-has-completed-level", - "[name]", this.user.getName(), "[value]", level.getFriendlyName()); + "[name]", this.user.getName(), "[value]", level.getFriendlyName()); } } } @@ -417,11 +427,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,15 +452,15 @@ public class TryToComplete IslandRequirements requirements = this.challenge.getRequirements(); if (result.meetsRequirements && - requirements.isRemoveEntities() && - !requirements.getRequiredEntities().isEmpty()) + requirements.isRemoveEntities() && + !requirements.getRequiredEntities().isEmpty()) { this.removeEntities(result.entities, result.getFactor()); } if (result.meetsRequirements && - requirements.isRemoveBlocks() && - !requirements.getRequiredBlocks().isEmpty()) + requirements.isRemoveBlocks() && + !requirements.getRequiredBlocks().isEmpty()) { this.removeBlocks(result.blocks, result.getFactor()); } @@ -461,11 +471,11 @@ public class TryToComplete if (this.getInventoryRequirements().isTakeItems()) { int sumEverything = result.requiredItems.stream(). - mapToInt(itemStack -> itemStack.getAmount() * result.getFactor()). - sum(); + mapToInt(itemStack -> itemStack.getAmount() * result.getFactor()). + sum(); Map removedItems = - this.removeItems(result.requiredItems, result.getFactor()); + this.removeItems(result.requiredItems, result.getFactor()); int removedAmount = removedItems.values().stream().mapToInt(num -> num).sum(); @@ -489,11 +499,11 @@ public class TryToComplete } if (requirements.isTakeExperience() && - this.user.getPlayer().getGameMode() != GameMode.CREATIVE) + this.user.getPlayer().getGameMode() != GameMode.CREATIVE) { // Cannot take anything from creative game mode. this.user.getPlayer().setTotalExperience( - this.user.getPlayer().getTotalExperience() - requirements.getRequiredExperience()); + this.user.getPlayer().getTotalExperience() - requirements.getRequiredExperience()); } } } @@ -509,7 +519,6 @@ public class TryToComplete ChallengeResult result; ChallengeType type = this.challenge.getChallengeType(); - // Check the world if (!this.challenge.isDeployed()) { @@ -522,36 +531,36 @@ public class TryToComplete 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"); result = EMPTY_RESULT; } // Player is not on island else if (ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.world) && - !this.addon.getIslands().locationIsOnIsland(this.user.getPlayer(), this.user.getLocation())) + !this.addon.getIslands().locationIsOnIsland(this.user.getPlayer(), this.user.getLocation())) { this.user.sendMessage("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"); 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"); 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"); result = EMPTY_RESULT; @@ -564,7 +573,7 @@ public class TryToComplete } // 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"); result = EMPTY_RESULT; @@ -597,7 +606,6 @@ public class TryToComplete { result.setCompleted(this.manager.isChallengeComplete(this.user, this.world, this.challenge)); } - // Everything fails till this point. return result; } @@ -610,7 +618,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)); } @@ -678,7 +686,7 @@ public class TryToComplete try { if (!this.addon.getServer().dispatchCommand(this.addon.getServer().getConsoleSender(), - cmd.replace("[player]", this.user.getName()))) + cmd.replace("[player]", this.user.getName()))) { this.showError(cmd); } @@ -702,9 +710,9 @@ public class TryToComplete } -// --------------------------------------------------------------------- -// Section: Inventory Challenge -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Inventory Challenge + // --------------------------------------------------------------------- /** @@ -730,7 +738,7 @@ public class TryToComplete if (Utils.canIgnoreMeta(required.getType())) { numInInventory = - Arrays.stream(this.user.getInventory().getContents()). + Arrays.stream(this.user.getInventory().getContents()). filter(Objects::nonNull). filter(i -> i.getType().equals(required.getType())). mapToInt(ItemStack::getAmount). @@ -739,7 +747,7 @@ public class TryToComplete else { numInInventory = - Arrays.stream(this.user.getInventory().getContents()). + Arrays.stream(this.user.getInventory().getContents()). filter(Objects::nonNull). filter(i -> i.isSimilar(required)). mapToInt(ItemStack::getAmount). @@ -749,8 +757,8 @@ public class TryToComplete if (numInInventory < required.getAmount()) { this.user.sendMessage("challenges.errors.not-enough-items", - "[items]", - Util.prettifyText(required.getType().toString())); + "[items]", + Util.prettifyText(required.getType().toString())); return EMPTY_RESULT; } @@ -764,9 +772,9 @@ public class TryToComplete // Return the result return new ChallengeResult(). - setMeetsRequirements(). - setCompleteFactor(maxTimes). - setRequiredItems(requiredItems); + setMeetsRequirements(). + setCompleteFactor(maxTimes). + setRequiredItems(requiredItems); } @@ -782,26 +790,24 @@ public class TryToComplete for (ItemStack required : requiredItemList) { int amountToBeRemoved = required.getAmount() * factor; - List itemsInInventory; if (Utils.canIgnoreMeta(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) @@ -828,7 +834,7 @@ public class TryToComplete if (amountToBeRemoved > 0) { this.addon.logError("Could not remove " + amountToBeRemoved + " of " + required.getType() + - " from player's inventory!"); + " from player's inventory!"); } } @@ -836,9 +842,9 @@ public class TryToComplete } -// --------------------------------------------------------------------- -// Section: Island Challenge -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Island Challenge + // --------------------------------------------------------------------- /** @@ -870,6 +876,11 @@ public class TryToComplete Island island = this.addon.getIslands().getIsland(this.world, this.user); + if (island == null) { + // Just in case. Should never hit because there is a check if the player is on this island further up + return EMPTY_RESULT; + } + if (boundingBox.getMinX() < island.getMinX()) { boundingBox.expand(BlockFace.EAST, Math.abs(island.getMinX() - boundingBox.getMinX())); @@ -894,15 +905,15 @@ 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() + - " | Search Distance: " + requirements.getSearchRadius() + - " | Location: " + this.user.getLocation().toString() + - " | Center: " + island.getCenter().toString() + - " | Range: " + range); + " | BoundingBox: " + boundingBox.toString() + + " | Search Distance: " + requirements.getSearchRadius() + + " | Location: " + this.user.getLocation().toString() + + " | Center: " + island.getCenter().toString() + + " | Range: " + range); return EMPTY_RESULT; } @@ -939,15 +950,15 @@ 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())) - { - return Double.compare(o1.getLocation().distance(this.user.getLocation()), - o2.getLocation().distance(this.user.getLocation())); - } - else - { - return o1.getType().compareTo(o2.getType()); - } + if (o1.getType().equals(o2.getType())) + { + return Double.compare(o1.getLocation().distance(this.user.getLocation()), + o2.getLocation().distance(this.user.getLocation())); + } + else + { + return o1.getType().compareTo(o2.getType()); + } }); for (int x = (int) boundingBox.getMinX(); x <= boundingBox.getMaxX(); x++) @@ -989,7 +1000,7 @@ public class TryToComplete for (Map.Entry entry : blocksFound.entrySet()) { factor = Math.min(factor, - entry.getValue() / requiredMap.get(entry.getKey())); + entry.getValue() / requiredMap.get(entry.getKey())); } } @@ -1000,12 +1011,12 @@ public class TryToComplete } this.user.sendMessage("challenges.errors.not-close-enough", - "[number]", - String.valueOf(this.getIslandRequirements().getSearchRadius())); + "[number]", + String.valueOf(this.getIslandRequirements().getSearchRadius())); blocks.forEach((k, v) -> user.sendMessage("challenges.errors.you-still-need", - "[amount]", String.valueOf(v), - "[item]", Util.prettifyText(k.toString()))); + "[amount]", String.valueOf(v), + "[item]", Util.prettifyText(k.toString()))); // kick garbage collector @@ -1025,8 +1036,8 @@ public class TryToComplete * @return ChallengeResult */ private ChallengeResult searchForEntities(Map requiredMap, - int factor, - BoundingBox boundingBox) + int factor, + BoundingBox boundingBox) { if (requiredMap.isEmpty()) { @@ -1042,7 +1053,7 @@ public class TryToComplete if (o1.getType().equals(o2.getType())) { return Double.compare(o1.getLocation().distance(this.user.getLocation()), - o2.getLocation().distance(this.user.getLocation())); + o2.getLocation().distance(this.user.getLocation())); } else { @@ -1050,11 +1061,11 @@ public class TryToComplete } }); - this.world.getNearbyEntities(boundingBox).forEach(entity -> { + user.getWorld().getNearbyEntities(boundingBox).forEach(entity -> { // Check if entity is inside challenge bounding box if (requiredMap.containsKey(entity.getType())) { - entityQueue.add(entity); + entityQueue.add(entity); entitiesFound.putIfAbsent(entity.getType(), 1); entitiesFound.computeIfPresent(entity.getType(), (reqEntity, amount) -> amount + 1); @@ -1074,7 +1085,7 @@ public class TryToComplete for (Map.Entry entry : entitiesFound.entrySet()) { factor = Math.min(factor, - entry.getValue() / requiredMap.get(entry.getKey())); + entry.getValue() / requiredMap.get(entry.getKey())); } } @@ -1085,8 +1096,8 @@ public class TryToComplete } minimalRequirements.forEach((reqEnt, amount) -> this.user.sendMessage("challenges.errors.you-still-need", - "[amount]", String.valueOf(amount), - "[item]", Util.prettifyText(reqEnt.toString()))); + "[amount]", String.valueOf(amount), + "[item]", Util.prettifyText(reqEnt.toString()))); // Kick garbage collector entitiesFound.clear(); @@ -1129,26 +1140,26 @@ public class TryToComplete private void removeEntities(Queue entityQueue, int factor) { Map entities = this.getIslandRequirements().getRequiredEntities().isEmpty() ? - new EnumMap<>(EntityType.class) : new EnumMap<>(this.getIslandRequirements().getRequiredEntities()); + new EnumMap<>(EntityType.class) : new EnumMap<>(this.getIslandRequirements().getRequiredEntities()); - // Increase required entities by factor. - entities.entrySet().forEach(entry -> entry.setValue(entry.getValue() * factor)); + // Increase required entities by factor. + entities.entrySet().forEach(entry -> entry.setValue(entry.getValue() * factor)); - // Go through entity queue and remove entities that are requried. - entityQueue.forEach(entity -> { - if (entities.containsKey(entity.getType())) - { - entities.computeIfPresent(entity.getType(), (reqEntity, amount) -> amount - 1); - entities.entrySet().removeIf(e -> e.getValue() == 0); - entity.remove(); - } - }); + // Go through entity queue and remove entities that are requried. + entityQueue.forEach(entity -> { + if (entities.containsKey(entity.getType())) + { + entities.computeIfPresent(entity.getType(), (reqEntity, amount) -> amount - 1); + entities.entrySet().removeIf(e -> e.getValue() == 0); + entity.remove(); + } + }); } -// --------------------------------------------------------------------- -// Section: Other challenge -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Other challenge + // --------------------------------------------------------------------- /** @@ -1160,60 +1171,60 @@ public class TryToComplete { OtherRequirements requirements = this.getOtherRequirements(); - if (!this.addon.isLevelProvided() && - requirements.getRequiredIslandLevel() != 0) - { - this.user.sendMessage("challenges.errors.missing-addon"); - } - else if (!this.addon.isEconomyProvided() && - requirements.getRequiredMoney() != 0) - { - this.user.sendMessage("challenges.errors.missing-addon"); - } - else if (this.addon.isEconomyProvided() && requirements.getRequiredMoney() < 0) - { - this.user.sendMessage("challenges.errors.incorrect"); - } - else if (this.addon.isEconomyProvided() && - !this.addon.getEconomyProvider().has(this.user, requirements.getRequiredMoney())) + if (!this.addon.isLevelProvided() && + requirements.getRequiredIslandLevel() != 0) + { + this.user.sendMessage("challenges.errors.missing-addon"); + } + else if (!this.addon.isEconomyProvided() && + requirements.getRequiredMoney() != 0) + { + this.user.sendMessage("challenges.errors.missing-addon"); + } + else if (this.addon.isEconomyProvided() && requirements.getRequiredMoney() < 0) + { + this.user.sendMessage("challenges.errors.incorrect"); + } + else if (this.addon.isEconomyProvided() && + !this.addon.getEconomyProvider().has(this.user, requirements.getRequiredMoney())) { this.user.sendMessage("challenges.errors.not-enough-money", - "[value]", - Double.toString(requirements.getRequiredMoney())); + "[value]", + Double.toString(requirements.getRequiredMoney())); + } + else if (requirements.getRequiredExperience() < 0) + { + this.user.sendMessage("challenges.errors.incorrect"); } - else if (requirements.getRequiredExperience() < 0) - { - this.user.sendMessage("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())); + "[value]", + Integer.toString(requirements.getRequiredExperience())); } - else if (this.addon.isLevelProvided() && - this.addon.getLevelAddon().getIslandLevel(this.world, this.user.getUniqueId()) < requirements.getRequiredIslandLevel()) + 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())); + TextVariables.NUMBER, + String.valueOf(requirements.getRequiredIslandLevel())); } else { - // calculate factor + // calculate factor - if (this.addon.isEconomyProvided() && requirements.isTakeMoney()) - { - factor = Math.min(factor, (int) (this.addon.getEconomyProvider().getBalance(this.user) / requirements.getRequiredMoney())); - } + if (this.addon.isEconomyProvided() && requirements.isTakeMoney()) + { + factor = Math.min(factor, (int) (this.addon.getEconomyProvider().getBalance(this.user) / requirements.getRequiredMoney())); + } - if (requirements.getRequiredExperience() > 0 && requirements.isTakeExperience()) - { - factor = Math.min(factor, this.user.getPlayer().getTotalExperience() / requirements.getRequiredExperience()); - } + if (requirements.getRequiredExperience() > 0 && requirements.isTakeExperience()) + { + factor = Math.min(factor, this.user.getPlayer().getTotalExperience() / requirements.getRequiredExperience()); + } return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor); } @@ -1222,9 +1233,9 @@ public class TryToComplete } -// --------------------------------------------------------------------- -// Section: Title parsings -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Title parsings + // --------------------------------------------------------------------- /** @@ -1268,9 +1279,9 @@ public class TryToComplete } -// --------------------------------------------------------------------- -// Section: Simple getter methods -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Simple getter methods + // --------------------------------------------------------------------- /** @@ -1303,9 +1314,9 @@ public class TryToComplete } -// --------------------------------------------------------------------- -// Section: Private classes -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Result classes + // --------------------------------------------------------------------- /** @@ -1313,7 +1324,7 @@ public class TryToComplete * * @author tastybento */ - private class ChallengeResult + class ChallengeResult { /** * This method sets that challenge meets all requirements at least once. @@ -1467,5 +1478,15 @@ public class TryToComplete * challenge requirements. */ private Queue entities; + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ChallengeResult [completed=" + completed + ", meetsRequirements=" + meetsRequirements + ", factor=" + + factor + ", requiredItems=" + requiredItems + ", removedItems=" + removedItems + ", blocks=" + + blocks + ", entities=" + entities + "]"; + } } } diff --git a/src/main/java/world/bentobox/challenges/utils/GuiUtils.java b/src/main/java/world/bentobox/challenges/utils/GuiUtils.java index 62076be..40b069c 100644 --- a/src/main/java/world/bentobox/challenges/utils/GuiUtils.java +++ b/src/main/java/world/bentobox/challenges/utils/GuiUtils.java @@ -1,15 +1,13 @@ 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; @@ -423,4 +421,16 @@ public class GuiUtils 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 index 3dcae81..1bbae6a 100644 --- a/src/main/java/world/bentobox/challenges/utils/HeadLib.java +++ b/src/main/java/world/bentobox/challenges/utils/HeadLib.java @@ -15,14 +15,21 @@ package world.bentobox.challenges.utils; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; +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 java.lang.reflect.Field; -import java.util.*; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; import world.bentobox.bentobox.BentoBox; diff --git a/src/main/java/world/bentobox/challenges/utils/Utils.java b/src/main/java/world/bentobox/challenges/utils/Utils.java index d5ad9a2..b67593a 100644 --- a/src/main/java/world/bentobox/challenges/utils/Utils.java +++ b/src/main/java/world/bentobox/challenges/utils/Utils.java @@ -1,11 +1,12 @@ package world.bentobox.challenges.utils; +import java.util.ArrayList; +import java.util.List; + import org.bukkit.Material; import org.bukkit.World; import org.bukkit.inventory.ItemStack; -import java.util.ArrayList; -import java.util.List; import world.bentobox.bentobox.BentoBox; @@ -25,7 +26,7 @@ public class Utils { List returnItems = new ArrayList<>(requiredItems.size()); - // Group all equal items in singe stack, as otherwise it will be too complicated to check if all + // Group all equal items in single stack, as otherwise it will be too complicated to check if all // items are in players inventory. for (ItemStack item : requiredItems) { diff --git a/src/main/java/world/bentobox/challenges/web/WebManager.java b/src/main/java/world/bentobox/challenges/web/WebManager.java index 48b088e..702d7ef 100644 --- a/src/main/java/world/bentobox/challenges/web/WebManager.java +++ b/src/main/java/world/bentobox/challenges/web/WebManager.java @@ -1,11 +1,16 @@ package world.bentobox.challenges.web; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Comparator; +import java.util.List; + +import org.bukkit.World; + import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import org.bukkit.World; -import java.nio.charset.StandardCharsets; -import java.util.*; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.user.User; @@ -121,7 +126,6 @@ public class WebManager * @param user User who inits request. * @param world Target world where challenges should be loaded. * @param entry Entry that contains information about requested object. - * @return {@code true} if request was successful, {@code false} otherwise. */ public void requestEntryGitHubData(User user, World world, LibraryEntry entry) { 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 15f384c..69e6528 100644 --- a/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java +++ b/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java @@ -1,11 +1,12 @@ package world.bentobox.challenges.web.object; -import com.google.gson.JsonObject; 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. diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml index 53633f5..cdbe733 100755 --- a/src/main/resources/addon.yml +++ b/src/main/resources/addon.yml @@ -57,3 +57,13 @@ permissions: skygrid.admin.challenges: description: Access challenge admin commands default: op + + aoneblock.challenges: + description: Let the player use the '/aoneblock challenges' command + default: true + aoneblock.challenges.multiple: + description: Let the player complete challenge multiple times + default: true + aoneblock.admin.challenges: + description: Access challenge admin commands + default: op diff --git a/src/main/resources/default.json b/src/main/resources/default.json index d9e15b7..0163aa4 100644 --- a/src/main/resources/default.json +++ b/src/main/resources/default.json @@ -143,7 +143,7 @@ "requiredPermissions": [] } }, - "rewardText": "Change your logs for some clay", + "rewardText": "Exchange your logs for some clay", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CLAY\n amount: 4\n" ], @@ -151,7 +151,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Change your logs for some clay balls", + "repeatRewardText": "Exchange your logs for some clay balls", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -586,7 +586,7 @@ "friendlyName": "Pumpkin collector", "deployed": true, "description": [ - "Grow and collect some pumpkin" + "Grow and collect some pumpkins" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PUMPKIN\n", "order": 3, @@ -622,7 +622,7 @@ "repeatRewardCommands": [] }, { - "uniqueId": "chisedmaker", + "uniqueId": "chiseledmaker", "friendlyName": "Chiseled stone bricks crafter", "deployed": true, "description": [ @@ -663,7 +663,7 @@ }, { "uniqueId": "crackedmaker", - "friendlyName": "Cracked stone bricks", + "friendlyName": "Cracked stone bricks crafter", "deployed": true, "description": [ "Smelt some bricks to get cracked stone bricks." @@ -895,7 +895,7 @@ "requiredPermissions": [] } }, - "rewardText": "Iron for tools crafting and some cats.", + "rewardText": "Iron to craft some tools and a couple of 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" @@ -961,7 +961,7 @@ "friendlyName": "Visit nether", "deployed": true, "description": [ - "Nether is a horrible place, but it has some useful stuff." + "The nether is a horrible place, but it has some useful materials." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: NETHERRACK\n", "order": 3, @@ -1064,7 +1064,6 @@ "requiredPermissions": [] } }, - "rewardText": "Some prismarine shards and sea pickles", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PRISMARINE_SHARD\n amount: 16\n", @@ -1174,7 +1173,7 @@ "friendlyName": "Iron Farm", "deployed": true, "description": [ - "Use your villagers to produce free iron golems." + "Use your villagers to spawn free iron golems." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_BLOCK\n", "order": 1, @@ -1302,7 +1301,7 @@ "friendlyName": "Villager Breeder", "deployed": true, "description": [ - "Start to create a slave army." + "Start to create a villager army." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EMERALD_BLOCK\n", "order": 1, @@ -1605,7 +1604,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your iron stuff to some diamond", + "rewardText": "Exchange your iron stuff to some diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 4\n" ], @@ -1627,7 +1626,7 @@ "friendlyName": "Trader", "deployed": true, "description": [ - "Use your slaves to get some gems" + "Use your villagers to get some gems" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EMERALD\n", "order": 4, @@ -1645,7 +1644,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your emeralds to some diamond", + "rewardText": "Exchange your emeralds to some diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 4\n" ], @@ -2564,7 +2563,7 @@ "mushroomfarm", "melonfarm", "melonmaker", - "chisedmaker", + "chiseledmaker", "crackedmaker", "treefarm", "cobblegenerator", @@ -2707,4 +2706,4 @@ } ], "version": "0.8.0" -} \ No newline at end of file +} diff --git a/src/main/resources/locales/cs.yml b/src/main/resources/locales/cs.yml new file mode 100644 index 0000000..b7767b3 --- /dev/null +++ b/src/main/resources/locales/cs.yml @@ -0,0 +1,498 @@ +########################################################################################### +# 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 # +# # +# Translation by: CZghost # +########################################################################################### + +meta: + authors: + - BONNe + +challenges: + commands: + admin: + main: + parameters: '' + description: 'Hlavní administrátorský příkaz. Otevře GUI.' + import: + description: 'Importovat výzvy ze souboru challenges.yml|Parametr overwrite znamená, že výzvy nebo úrovně se stejnou ID budou přepsána.' + parameters: '[overwrite]' + reload: + description: 'Znovu načíst výzvy z databáze|Parametr hard znamená, že doplňek zresetuje připojení k databázi.' + parameters: '[hard]' + show: + description: 'Tato metoda vypíše do chatu všechny výzvy, které ve světě existují.' + parameters: '' + defaults: + description: 'Tato metoda ukáže podpříkazy, které umožní import/export výchozích výzev.' + parameters: '[command]' + defaults-import: + description: 'Tato metoda umožňuje import výchozích výzev.' + parameters: '' + defaults-generate: + description: 'Tato metoda umožňuje export existujících výzev do souboru default.json.' + parameters: '[overwrite] - umožní přepsat existující soubor.' + complete: + description: 'Tento příkaz umožňuje dokončit výzvu hráči bez GUI.' + parameters: ' ' + reset: + description: 'Tento příkaz umožňuje resetovat výzvu hráči bez GUI. Pokud je "challenge_id" nastaveno na "all", potom příkaz zresetuje hráči všechny výzvy.' + parameters: ' ' + migrate: + description: 'Tato metoda umožňuje migrovat data výzev, která referují na svět nynějšího herního módu, do nového formátu úložiště 0.8.0.' + parameters: '' + user: + main: + description: 'Tato metoda otevárá GUI Výzev.' + parameters: '' + complete: + description: 'Tato metoda umožňuje dokončit výzvu bez GUI.' + parameters: ' [count]' + gui: + title: + admin: + gui-title: '&aAdministrace Výzev' + edit-challenge-title: '&aEditovat výzvu' + edit-level-title: '&aEditovat úroveň' + settings-title: '&aEditovat nastavení' + choose-challenge-title: '&aZvolit výzvu' + choose-level-title: '&aZvolit úroveň' + choose-user-title: '&aZvolit hráče' + manage-blocks: '&aSpravovat bloky' + manage-entities: '&aSpravovat entity' + confirm-title: '&aPotvrzení' + manage-items: '&aSpravovat předměty' + manage-numbers: '&aČíselník' + select-block: '&aZvolit blok' + select-challenge: '&aZvolit výzvu' + select-entity: '&aZvolit entitu' + toggle-environment: '&aPřepnout prostředí' + edit-text-fields: '&aEditovat textová pole' + + library-title: '&aStáhnutelné knihovny' + + lore-add: '&aPřidat řádek příběhu' + lore-remove: '&aOdstranit řádek příběhu' + lore-edit: '&aEditovat příběh' + + type-select: "&aZvolit typ výzvy" + challenges: '&6Výzvy' + game-modes: '&6Zvolit herní mód' + + multiple-complete: '&6Kolikrát?' + buttons: + admin: + complete: 'Dokončit výzvy hráče' + reset: 'Resetovat výzvy hráče' + create-challenge: 'Přidat novou výzvu' + create-level: 'Přidat novou úroveň' + edit-challenge: 'Editovat výzvu' + edit-level: 'Editovat úroveň' + delete-challenge: 'Odstranit výzvu' + delete-level: 'Odstranit úroveň' + import: 'Importovat výzvy starého pluginu ASkyBlock' + settings: 'Editovat nastavení' + properties: 'Vlastnosti' + requirements: 'Požadavky' + rewards: 'Odměny' + challenges: 'Výzvy' + deployment: 'Zveřejnění' + icon: 'Ikona' + locked-icon: 'Ikona zamčení' + description: 'Popis' + order: 'Pořadí' + environment: 'Prostředí' + remove-on-complete: 'Odstranit po dokončení' + name: 'Viditelný název' + required-entities: 'Požadované entity' + remove-entities: 'Zabít entity' + required-blocks: 'Požadované bloky' + remove-blocks: 'Odstranit bloky' + search-radius: 'Radius hledání' + required-permissions: 'Požadovaná oprávnění' + required-items: 'Požadované předměty' + remove-items: 'Odstranit předměty' + required-experience: 'Požadované zkušenosti' + remove-experience: 'Odstranit zkušenosti' + required-level: 'Požadovaná úroveň ostrova' + required-money: 'Požadované peníze' + remove-money: 'Odstranit peníze' + reward-text: 'Zpráva odměny' + reward-items: 'Odměnit předměty' + reward-experience: 'Odměnit zkušenostmi' + reward-money: 'Odměnit penězmi' + reward-commands: 'Příkazy odměny' + repeatable: 'Opakovatelná' + repeat-count: 'Max. počet opakování' + repeat-reward-text: 'Zpráva opakované odměny' + repeat-reward-items: 'Odměnit předměty po opakování' + repeat-reward-experience: 'Odměnit zkušenostmi po opakování' + repeat-reward-money: 'Odměnit penězmi po opakování' + repeat-reward-commands: 'Odměnit příkazy po opakování' + waiver-amount: 'Nedokončené výzvy' + add-challenge: 'Přidat výzvu' + remove-challenge: 'Odstranit výzvu' + reset-on-new: 'Resetovat na novém ostrově' + broadcast: 'Oznámit dokončení' + remove-completed: 'Odstranit po dokončení' + glow: 'Svítit při dokončení' + free-at-top: 'Výzvy zdarma první' + line-length: 'Délka řádku příběhu' + visibility-mode: 'Mód viditelnosti výzvy' + toggle-user-list: 'Seznam hráčů' + remove-selected: 'Odstranit vybrané' + add: 'Přidat' + show-eggs: 'Přepnout mód zobrazení' + accept: 'Přijmout' + decline: 'Odmítnout' + save: 'Uložit' + cancel: 'Zrušit' + input: 'Vstup' + value: 'Hodnota' + set: '=' + increase: '+' + reduce: '-' + multiply: '*' + clear: 'Vyčistit' + remove-empty: 'Odstranit prázdné' + number: '[number]' + level-lore: 'Popis úrovně' + challenge-lore: 'Popis výzvy' + gui-view-mode: 'Ukázat všechny herní módy' + gui-mode: 'Samostatné GUI výzev' + history-store: 'Historie výzev' + history-lifespan: 'Dosah historie' + island-store: 'Ukládat dle ostrovů' + default-locked-icon: 'Ikona zamčené úrovně' + input-mode: 'Přepnout režim vstupu' + title-enable: 'Titul dokončení' + title-showtime: 'Titul ukázky' + default-import: 'Import výchozích výzev' + default-export: 'Export existujících výzev' + complete-wipe: 'Vymazat databáze doplňku' + challenge-wipe: 'Vymazat databáze výzev' + players-wipe: 'Vymazat databáze hráčů' + + library: 'Webová knihovna' + download: 'Stáhnout knihovny' + + type: + island: '&6Typ ostrova' + inventory: '&6Typ inventáře' + other: '&6Jiný typ' + next: 'Další' + previous: 'Předchozí' + return: 'Zpět' + + value: "Dokončeno" + increase: "Zvýšit" + reduce: "Snížit" + descriptions: + admin: + save: 'Uložit a vrátit se do předchozího GUI.' + cancel: 'Vrátit se do předchozího GUI. Změny nebudou uloženy.' + input: 'Otevřít vstup tetového pole.' + set: 'Operace nastavení. Klikání na čísla změní hodnotu na zvolené číslo.' + increase: 'Operace zvýšení. Klikání na čísla zvýší hodnotu o zvolené číslo.' + reduce: 'Operace snížení. Klikání na čísla sníží hodnotu o zvolené číslo.' + multiply: 'Operace násobení. Klikání na čísla znásobí hodnotu zvoleným číslem.' + import: 'Umožňuje import výzev ze starého pluginu ASkyblock.|Klinkutí pravým myšítkem zapíná/vypíná mód přepisování.|Vlož soubor Challenges.yml do složky ./BentoBox/addons/Challenges.' + complete: 'Umožňuje dokončit výzvy jakémukoliv hráči.|Hráči nedostanou odměnu za dokončení.' + reset: 'Umožňuje resetovat dokončené výzvy hráče.|Kliknutí pravým myšítkem zapíná/vypíná funkci resetu všeho.' + create-challenge: 'Umožňuje přidat novou výzvu.|Jako výchozí se objeví v seznamu výzev zdarma.' + create-level: 'Umožňuje přidat novou úroveň.' + edit-challenge: 'Umožňuje editovat nastavení jakékoliv výzvy.' + edit-level: 'Umožňuje editovat nastavení jakékoliv úrovně.' + delete-challenge: 'Umožňuje odstranit jakoukoliv výzvu.' + delete-level: 'Umožňuje odstranit jakoukoliv úroveň.' + settings: 'Umožňuje změnit nastavení doplňku.' + properties: 'Umožňuje změnit obecné nastavení' + requirements: 'Umožňuje spravovat požadavky' + rewards: 'Umožňuje spravovat odměny' + challenges: 'Umožňuje spravovat úroveň výzev (přidat / odebrat).' + deployment: 'Umožňuje hráčům dokončit (zobrazit) výzvu.' + icon-challenge: 'Ikona, která bude zobrazena v GUI panelech pro tuto výzvu.' + icon-level: 'Ikona, která bude zobrazena v GUI panelech pro tuto úroveň.' + locked-icon: 'Ikona, která bude zobrazena v GUI panelech, je-li úroveň zamčena.' + description: 'Umožňuje editovat popis.' + order: 'Umožňuje změnit číslo pořadí.' + environment: 'Umožňuje změnit prostředí, kde výzvy fungují.' + remove-on-complete: 'Umožňuje odebrat výzvu z GUI hráče po jejím dokončení.' + name-challenge: 'Umožňuje změnit viditelné jméno výzvy.' + name-level: 'Umožňuje změnit viditelné jméno úrovně.' + required-entities: 'Umožňuje přidat/editovat/odebrat požadované entity.|Entity:|' + remove-entities: 'Umožňuje odebrat (zabít) entity při dokončení výzvy.' + required-blocks: 'Umožňuje přidat/editovat/odebrat požadované bloky.|Bloky:|' + remove-blocks: 'Umožňuje odebrat (nahradit vzduchem) bloky při dokončení výzvy.' + search-radius: 'Radius okolo umístění hráče, kde se budou požadované entity a bloky hledat.' + required-permissions: 'Požadovaná oprávnění hráče pro možnost dokončit výzvu.|Oprávnění:' + required-items: 'Požadované předměty v inventáři hráče.|Předměty:' + remove-items: 'Umožňuje odebrat předměty z inventáře hráče po dokončení výzvy.' + required-experience: 'Umožňuje definovat požadované zkušenosti hráče pro dokončení výzvy.' + remove-experience: 'Umožňuje odebrat požadované zkušenosti.' + required-level: 'Umožňuje definovat požadovanou úroveň ostrova pro tuto výzvu.|&cVyžaduje doplňek Level.' + required-money: 'Umožňuje definovat požadované peníze v účtu hráče.|&cVyžaduje pluginy Vault a Economy.' + remove-money: 'Umožňuje odebrat pořadované peníze z účtu hráče.|&cVyžaduje pluginy Vault a Economy.' + reward-text: 'Umožňuje změnit zprávu, která se odešle hráči po dokončení výzvy.' + reward-items: 'Umožňuje změnit odměnu za první dokončení výzvy.|Předměty:' + reward-experience: 'Umožňuje změnit zkušenostní odměnu za první dokončení výzvy.' + reward-money: 'Umožňuje změnit peněžní odměnu za první dokončení výzvy.|&cVyžaduje pluginy Vault a Economy.' + reward-commands: 'Umožňuje definovat příkazy odměny, které se vykonají po prvním dokončení výzvy.|***Přidáním "[SELF]" na začátek znamená, že příkaz bude proveden hráčem, např. "/kill"|***Řetězec "[player]" bude nahrazen jménem hráče, např. "/kill [player]" bude nahrazen příkazem "/kill BONNe1704"|Příkazy:' + repeatable: 'Umožňuje definovat, zda je výzva opakovatelná nebo ne.' + repeat-count: 'Umožňuje definovat maximální počet opakování. Pokud je hodnota nastavena na 0 nebo nižší, pak nebude počet opakování limitován.' + repeat-reward-text: 'Umožňuje změnit zprávu, která se odešle hráči po opakovaném dokončení výzvy.' + repeat-reward-items: 'Umožňuje změnit odměnu za opakované dokončení výzvy.|Předměty:' + repeat-reward-experience: 'Umožňuje změnit zkušenostní odměnu za opakované dokončení výzvy.' + repeat-reward-money: 'Umožňuje změnit peněžní odměnu za opakované dokončení výzvy.|&cVyžaduje pluginy Vault a Economy.' + repeat-reward-commands: 'Umožňuje definovat příkazy odměny, které se vykonají po opakovaném dokončení výzvy.|***Přidáním "[SELF]" na začátek znamená, že příkaz bude proveden hráčem, např. "/kill"|***Řetězec "[player]" bude nahrazen jménem hráče, např. "/kill [player]" bude nahrazen příkazem "/kill BONNe1704"|Příkazy:' + waiver-amount: 'Umožňuje nastavit, kolik výzev může zůstat nedokončených k odemčení další úrovně.' + reward-text-level: 'Umožňuje změnit zprávu, která se odešle hráči po dokončení všech výzev v úrovni.' + add-challenge: 'Umožňuje přidat existující výzvu do aktuální úrovně.' + remove-challenge: 'Umožňuje odstranit jakoukoliv výzvu z aktuální úrovně.' + reset-on-new: 'Zapíná/vypíná možnost, která resetuje všechny výzvy hráče, pokud hráč restartuje ostrov, opustí ostrov nebo byl vykopnut.' + broadcast: 'Zapíná/vypíná oznámení online hráčům o prvním dokončení výzvy.' + remove-completed: 'Zapíná/vypíná skrývání výzev, které jsou dokončené a nelze je opakovat.' + glow: 'Zapíná/vypíná efekt záření pro dokončené výzvy.' + free-at-top: 'Umožňuje změnit umístění výzvy zdarma. Skutečně to znamená, že výzvy budou první, jinak poslední.' + line-length: 'Umožňuje modifikovat maximální délku řádku příběhu. To neovlivní uložené objekty.' + toggle-user-list: 'Přepnout na jiný seznam hráčů.' + mode-online: 'Hráči, kteří jsou aktuálně online.' + mode-in-world: 'Hráči, kteří jsou ve světě tohoto herního módu.' + mode-with-island: 'Hráči, kteří mají v tomto herním módu ostrov.' + selected: 'Zvoleno' + remove-selected: 'Odstranit zvolené položky.|Můžeš zvolit položky kliknutím pravým myšítkem.' + show-eggs: 'Přepnout zobrazení mobů mezi spawnovacím vajíčkem nebo hlavou moba.' + level-lore: 'Umožňuje modifikovat, které položky popisu úrovně by měly být viditelné.' + challenge-lore: 'Umožňuje modifikovat, které položky popisu výzvy by měly být viditelné.' + gui-view-mode: 'Umožňuje nastavit, zda GUI /challenges má zobrazit herní módy nebo výzvy ve světě hráče.' + history-store: 'Umožňuje zapnout/vypnout ukládání historie výzev.' + history-lifespan: 'Umožňuje modifikovat, kolik dní budou data historie uložena.|0 znamená navždy.' + island-store: 'Umožňuje zapnout/vypnout ukládání dat výzev dle ostrova. To znamená, že výzvy budou stejné pro celý tým, pokud bude funkce zapnuta.|Toto NEZKONVERTUJE data kliknutím. POSTUP BUDE ZTRACEN.' + default-locked-icon: 'Umožňuje změnit výchozí ikonu zamčené úrovně.|Tato volba může být přepsána každou úrovní.' + gui-mode: 'Umožňuje zapnout/vypnout samostatné GUI výzev.|&2Vyžaduje restart serveru.' + + visibility-mode: 'Umožňuje přepnout, zda by se nezveřejněné výzvy měly zobrazovat nebo ne.' + + click-to-edit: '&4Klikni sem k editaci vstupu.' + edit-text-line: '&6 Edituj textovou zprávu!' + add-text-line: '&6 Přidej novou textovou zprávu!' + input-mode: 'Přepni mezi módem vstupu v chatu a kovadlině.' + title-enable: 'Umožňuje zapnout/vypnout zprávu titulku, který se ukáže, když hráč dokončí výzvu.' + title-showtime: 'Umožňuje modifikovat, jak dlouho se bude zpráva titulku pro hráče zobrazovat.' + default-import: 'Umožňuje import výchozích výzev.' + default-export: 'Umožňuje export existujících výzev do souboru defaults.json.' + complete-wipe: 'Umožňuje kompletně vymazat všechny databáze výzev doplňku. Zahrnuje data hráčů!' + + challenge-wipe: 'Umožňuje kompletně vymazat databáze výzev a jejich úrovní!' + players-wipe: 'Umožňuje kompletně vymazat databázi hráčů!' + + library: 'Otevře GUI, které ukáže všechny dostupné veřejné knihovny výzev.' + + library-author: 'od &e[author]' + library-version: '&9Vytvořeno ve verzi Challenges [version]' + library-lang: '&aJazyk: [lang]' + library-gamemode: '&aPrimárně pro [gamemode]' + + download: 'Umožňuje manuálně aktualizovat dostupné knihovny výzev. |Klikni pravým myšítkem k zapnutí čištění cache.' + download-disabled: 'Stahovač dat GitHub je v pluginu BentoBox zakázán. Bez toho nemůžeš použít knihovny!' + + lore: + level: "Řetězec úrovně. | Reprezentuje překlad 'challenges.gui.challenge-description.level'." + status: "Řetězec statusu. | Reprezentuje překlad 'challenges.gui.challenge-description.completed'." + count: "Řetězec počtu dončení. | Reprezentuje překlad pro 'challenges.gui.challenge-description.completed-times', 'challenges.gui.challenge-description.completed-times-of' and 'challenges.gui.challenge-description.maxed-reached'." + description: "Řetězec popisu. | Devinován v objektu výzev - challenge.description." + warnings: "Řetězec varování. | Reprezentuje překlad pro: | '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: "Řetězec prostředí. | Definován v objektu výzev - challenge.environment." + requirements: "Řetězec požadavků. | Reprezentuje překlad pro: | 'challenges.gui.challenge-description.required-level' | 'challenges.gui.challenge-description.required-money' | 'challenges.gui.challenge-description.required-experience' | and challenge.requiredItems, challenge.requiredBlocks or challenge.requiredEntities." + reward_text: "Řetězec odměn. | Definován v challenge.rewardText a challenge.repeatRewardText" + reward_other: "Řetězec ostatních odměn. | Reprezentuje překlad pro: | 'challenges.gui.challenge-description.experience-reward' | 'challenges.gui.challenge-description.money-reward' | 'challenges.gui.challenge-description.not-repeatable'." + reward_items: "Předměty odměny. | Seznam předmětů, které budou odměněny, je definován v challenge.rewardItems and challenge.repeatRewardItems." + reward_commands: "Příkazy odměny. | Seznam příkazů, které budou odměněny, je definován v challenge.rewardCommands and challenge.repeatRewardCommands." + + level_status: "Řetězec statusu. | Reprezentuje překlad 'challenges.gui.level-description.completed'." + challenge_count: "Řetězec počtu dokončených výzev. | Reprezentuje překlad pro 'challenges.gui.level-description.completed-challenges-of'" + unlock_message: "Řetězec zprávy odemčení. | Definován v objektu výzev Level - challengeLevel.unlockMessage." + waiver_amount: "Řetězec počtu povolených nedokončených výzev k odemčení další úrovně. | Reprezentuje překlad pro 'challenges.gui.level-description.waver-amount'" + level_reward_text: "Řetězec odměn. | Definován v challengeLevel.rewardText" + level_reward_other: "Řetězec ostatních odměn. | Reprezentuje překlad pro: | 'challenges.gui.level-description.experience-reward' | 'challenges.gui.level-description.money-reward'." + level_reward_items: "Předměty odměny. | Seznam předmětů, které budou odměněny, je definován v challengeLevel.rewardItems." + level_reward_commands: "Příkazy odměny. | Seznam příkazů, které budou odměněny, je definován v challengeLevel.rewardCommands." + + current-value: '|&6Nynější hodnota: [value].' + enabled: 'Povoleno' + disabled: 'Zakázáno' + type: + island: '&aUmožňuje požadovat bloky nebo moby okolo hráče' + inventory: '&aUmožňuje požadovat předměty v inventáři hráče' + other: '&aUmožňuje požadovat položky z ostatních pluginů/doplňků' + the-end: '- End' + nether: '- Nether' + normal: '- Svět' + entity: '- [entity] : [count]' + block: '- [block] : [count]' + permission: '- [permission]' + item: '- [count] x [item]' + item-meta: ' ([meta])' + item-enchant: ' - [enchant] [level]' + command: '- [command]' + level-unlocked: 'Klikni k zobrazení výzev [level]!' + level-locked: 'Dokonči dalších [count] výzev [level] k odemčení další úrovně!' + + increase-by: "&aNavýšit počet dokončení o [value]" + reduce-by: "&cSnížit počet dokončení o [value]" + + visibility: + visible: "Všechny výzvy jsou pro všechny viditelné" + hidden: "Jen zveřejněné výzvy jsou viditelné." + toggleable: "Umožňuje přepnout, zda by měly být nezveřejněné výzvy viditelné" + + challenge-description: + level: '&FÚroveň: [level]' + completed: '&BDokončeno' + completed-times-of: 'Dokončeno [donetimes] z [maxtimes]' + maxed-reached: 'Dokončeno [donetimes] z [maxtimes]' + completed-times: 'Dokončeno [donetimes]' + warning-items-take: '&cVšechny požadované předměty budou po dokončení výzvy odebrány!' + objects-close-by: '&cVšechny požadované bloky a entity musí být blízko tebe na tvém ostrově!' + warning-entities-kill: '&cVšechny požadované entity budou zabity po dokončení této výzvy!' + warning-blocks-remove: '&cVšechny požadované blocks budou odstraněny po dokončení této výzvy!' + not-repeatable: '&cTato výzva není opakovatelná!' + experience-reward: '&6Zkuš. odměna: [value]' + money-reward: '&6Peněžní odměna: $[value]' + required-experience: '&6Potřebné zkuš.: [value]' + required-money: '&6Potřebné peníze: $[value]' + required-island-level: '&6Potřebná úroveň ostrova: [value]' + environment: 'Požadovaná prostředí:' + reward-items: '&6Odměněné předměty:' + reward-commands: '&6Odměněné příkazy:' + required-items: 'Potřebné předměty:' + required-entities: 'Potřebné entity:' + required-blocks: 'Potřebné bloky:' + level-description: + completed: '&BDokončeno' + completed-challenges-of: '&3Dokončil jsi [number] z [max] výzev v této úrovni.' + waver-amount: '&6Lze přeskočit [value] výzev k odemčení další úrovně.' + experience-reward: '&6Zkuš. odměna: [value]' + money-reward: '&6Peněžní odměna: $[value]' + reward-items: '&6Odměněné předměty:' + reward-commands: '&6Odměněné příkazy:' + item-description: + item: '- [count] x [item]' + item-meta: ' ([meta])' + item-enchant: ' - [enchant] [level]' + item-name: ' [name]' + item-lore: ' Příběh přemětu:' + book-meta: ' [title] od [author]' + recipe-count: ' [count] receptů' + armor-color: ' [color]' + potion-type-extended-upgraded: ' Rozšířen a aktualizován [name]' + potion-type-upgraded: ' Aktualizován [name]' + potion-type-extended: ' Rozšířen [name]' + potion-type: ' [name]' + custom-effects: ' Vlastní efekty:' + potion-effect: ' [effect] x [amplifier] na [duration]t' + skull-owner: ' [owner]' + egg-meta: ' [mob]' + fish-meta: ' [body-color] s [pattern-color] [pattern]' + + questions: + prefix: "&2[SERVER]: " + + admin: + number: "Napiš číslo do chatu a stiskni Enter k přijetí a opět stiskni Enter." + unique-id: "Napiš unikátní objektové jméno a stiskni Enter." + challenge-name: "Napiš do chatu zobrazované jméno pro aktuální výzvu." + level-name: "Napiš do chatu zobrazované jméno pro aktuální úroveň." + + titles: +# Title and subtitle my contain variable in [] that will be replaced with proper message from challenge object. +# [friendlyName] will be replaced with challenge friendly name. +# [level] will be replaced with level friendly name. +# [rewardText] will be replaced with challenge reward text. + challenge-title: 'Úspěšně dokončeno' + challenge-subtitle: '[friendlyName]' +# Title and subtitle my contain variable in [] that will be replaced with proper message from level object. +# [friendlyName] will be replaced with level friendly name. +# [rewardText] will be replaced with level reward text. + level-title: 'Úspěšně dokončeno' + level-subtitle: '[friendlyName]' + messages: + admin: + hit-things: 'Bouchni do věcí pro jejich přidání do seznamu požadovaných věcí. Klikni pravým myšítkem, jakmile budeš hotov.' + you-added: 'Přidal jsi jeden [thing] do výzvy' + challenge-created: '[challenge]&r vytvořeno!' + complete-wipe: '&cDoufám, že máš zálohy, neboť jsi právě vyprázdnil všechny databáze doplňku Challenges!' + + challenge-wipe: '&cDoufám, že máš zálohy, neboť jsi právě vyprázdnil výzvy a jejich úrovně z databází!' + players-wipe: '&cDoufám, že máš zálohy, neboť jsi právě vyprázdnil dokončené výzvy hráčů z databází!' + + completed: '&2Dokončil jsi výzvu [name] hráči [player]!' + already-completed: '&2Tato výzva již byla dokončena!' + reset: '&2Resetoval jsi výzvu [name] hráči [player]!' + reset-all: '&2Všechny výzvy hráče [player] jsou resetovány!' + not-completed: '&2Tato výzva ještě není dokončena!' + + migrate-start: '&2Začít migrovat data doplňku Challenges.' + migrate-end: '&2Data doplňku Challenges jsou aktualizována na nový formát.' + migrate-not: '&2Všechna data jsou platná.' + + start-downloading: '&5Začínám stahovat a importovat knihovnu výzev.' + you-completed-challenge: '&2Dokončil jsi výzvu [value]&r&2!' + you-repeated-challenge: '&2Zopakoval jsi výzvu [value]&r&2!' + you-repeated-challenge-multiple: '&2Zopakoval jsi výzvu [value] &r&2[count]x!' + you-completed-level: '&2Dokončil jsi úroveň [value]&r&2!' + name-has-completed-challenge: '&5[name] dokončil výzvu [value]&r&5!' + name-has-completed-level: '&5[name] dokončil úroveň [value]&r&5!' + import-levels: 'Začínám importovat úrovně' + import-challenges: 'Začínám importovat výzvy' + no-levels: 'Varování: Žádné úrovně nejsu definovány v souboru challenges.yml' + import-number: 'Importováno [number] výzev' + load-skipping: '"[value]" již existuje - přeskakuji' + load-overwriting: 'Přepisuji "[value]"' + load-add: 'Přidávám nový objekt: [value]' + defaults-file-overwrite: 'Soubor defaults.json existuje. Bude přepsán.' + defaults-file-completed: 'Soubor defaults.json je naplněn výzvami ze světa [world]!' + errors: + no-name: '&cChybějící název výzvy' + unknown-challenge: '&cNeznámá výzva' + unique-id: '&cUnikátní ID "[id]" není platné.' + wrong-icon: '&cUvedený materiál "[value]" je neplatný a nelze jej použít jako ikonu.' + not-valid-integer: '&cUvedené celé číslo "[value]" je neplatné!|Hodnota by měla být mezi [min] a [max].' + not-a-integer: '&cUvedená hodnota "[value]" není celé číslo!' + not-deployed: '&cVýzva není zveřejněna!' + not-on-island: '&cK tomuto musíš být na svém ostrově!' + challenge-level-not-available: '&cNeodemkl jsi úroveň k dokončení této výzvy.' + not-repeatable: '&cTato výzva není opakovatelná!' + wrong-environment: '&cJsi ve špatném prostředí!' + not-enough-items: '&cNemáš dostatek [items] k dokončení této výzvy!' + not-close-enough: '&cMusíš stát alespoň [number] bloků od požadovaných položek.' + you-still-need: '&cStále potřebuješ [amount] x [item]' + missing-addon: '&cNelze dokončit výzvu. Vyžadovaný doplňek nebo plugin chybí.' + incorrect: '&cNelze dokončit výzvu. Požadavky nejsou správně.' + not-enough-money: '&cJe důležité mít [value] na svém účtu k dokončení této výzvy.' + not-enough-experience: '&cJe důležité mít [value] zkuš. k dokončení této výzvy.' + island-level: '&cÚroveň tvého ostrova musí být [number] k dokončení této výzvy!' + import-no-file: '&cNelze najít soubor challenges.yml k importu!' + no-load: '&cChyba: Nelze načíst soubor challenges.yml. [message]' + load-error: '&cChyba: Nelze načíst [value].' + no-rank: "&cNa toto nemáš hodnost." + cannot-remove-items: '&cNěkteré předměty nelze odebrat z tvého inventáře!' + exist-challenges-or-levels: '&cVe tvém světě již existují výzvy. Nelze pokračovat!' + defaults-file-exist: '&cSoubor defaults.json již existuje. Použij mód přepsání k jeho nahrazení!' + defaults-file-error: '&cVyskytla se chyba během vytváření souboru defaults.json! Podívej se do konzole!' + no-challenges: '&cVýzvy nejsou v aktuálním světě implementovány!' + no-challenges-admin: '&cVýzvy nejsou v aktuálním světě implementovány! Měl bys použít &5/[command] &ck jejich přidání!' + missing-level: '&cÚroveň výzev [level] není v databázi definována. To může způsobit nějaké chyby!' + missing-arguments: '&cPříkaz postrádá argumenty.' + no-multiple-permission: "&cNemáš oprávnění k dokončení výzev vícekrát najednou." +protection: + flags: + CHALLENGES_ISLAND_PROTECTION: + description: "&5&oPřepnout, kdo může\n&5&odokončit výzvy" + name: "Protekce výzev" + CHALLENGES_WORLD_PROTECTION: + description: "&5&oToto umožní zapnout/vypnout\n&5&opožadavky hráčů\n&5&obýt na jejich ostrově\n&5&ok dokončení výzvy." + name: "Limitace výzev na ostrov" + hint: "Žádné výzvy mimo ostrov" +version: 11 diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml new file mode 100644 index 0000000..50df0f7 --- /dev/null +++ b/src/main/resources/locales/de.yml @@ -0,0 +1,628 @@ +--- +challenges: + commands: + admin: + main: + description: Hauptadministrationsbefehl. Öffnet GUI. + defaults-generate: + parameters: "[overwrite] - Erlaubt es eine bereits existierende Datei zu überschreiben" + description: Bestehende Herausforderungen in default.json Datei exportieren. + 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: + description: Zeigt Unterbefehle zum Importieren/Exportieren der Standardherausforderungen. + parameters: "[command]" + defaults-import: + description: Importiert die Standardherausforderungen. + complete: + 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: " " + import: + description: |- + Herausforderungen aus der challenges.yml importieren + Parameter überschreiben bedeutet, dass Herausforderungen oder Level mit der gleichen ID überschrieben werden. + parameters: "[overwrite]" + reload: + description: |- + Herausforderungen aus der Datenbank neu laden + Parameter hard bedeutet, dass Addon die Verbindung zur Datenbank zurücksetzen wird. + parameters: "[hard]" + 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?" + 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-level: Level entfernen + requirements: Anforderungen + rewards: Belohnungen + deployment: Aufstellung + icon: Symbol + locked-icon: Gesperrtes Icon + order: Order + required-experience: Benötigte Erfahrung + remove-experience: Erfahrung entfernen + required-level: Benötigtes Insellevel + reward-items: Item Belohnung + reward-experience: Erfahrungsbelohnung + reward-money: Geld Belohnung + reward-commands: Belohnungs-Befehle + 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 + line-length: Länge der Striche + cancel: Abbrechen + input: Eingabe + set: "=" + increase: "+" + reduce: "-" + multiply: "*" + clear: Löschen + number: "[number]" + history-lifespan: Übersicht Lebensdauer + 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 + delete-challenge: Herausforderung entfernen + properties: Eigenschaften + challenges: Herausforderungen + description: Beschreibung + environment: Umgebung + remove-on-complete: Nach Fertigstellung entfernen + required-money: Benötigtes Geld + remove-money: Geld entfernen + reward-text: Belohnungsnachricht + repeatable: Wiederholbar + free-at-top: Freie Herausforderungen zuerst + add: Hinzufügen + accept: Akzeptieren + decline: Ablehnen + save: speichern + value: Wert + remove-empty: Leer entfernen + input-mode: Eingabemodus wechseln + title-enable: Fertigstellungstitel + next: Nächste Seite + previous: Vorherige Seite + return: Zurück + value: Abgeschlossen + increase: Erhöhen + reduce: Reduzieren + 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. + 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 + selected: Ausgewählt + 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]." + enabled: Aktiv + disabled: Deaktiviert + 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]" + reward-items: "&6Item Belohnungen:" + reward-commands: "& 6Belohnungsbefehle:" + required-items: 'Erforderliche Items:' + level: "&fLevel: [level]" + completed: "&bAbgeschlossen" + warning-items-take: "&cAlle erforderlichen Items werden aus deinem Inventar + genommen, wenn du diese Herausforderung abschließt!" + environment: 'Erforderliche Umgebungen:' + required-entities: 'Erforderliche Einheiten:' + required-blocks: 'Erforderliche Blöcke:' + 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: Schreibe den eindeutigen Namen des Objekts und drücke 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. + titles: + challenge-subtitle: "[friendlyName]" + level-subtitle: "[friendlyName]" + challenge-title: Erfolgreich abgeschlossen + level-title: Erfolgreich abgeschlossen + 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!" + 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 + [count] mal wiederholt!" + 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-deployed: "&cHerausforderung wird nicht eingesetzt!" + not-on-island: "&cDu musst auf deiner Insel sein, um das zu tun!" + not-repeatable: "&cDiese Herausforderung ist nicht wiederholbar!" + 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." + 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!" + 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. +protection: + flags: + CHALLENGES_ISLAND_PROTECTION: + description: "&5&Umschalten, wer &5&Herausforderungen erledigen kann" + name: Herausforderungen Schutz + CHALLENGES_WORLD_PROTECTION: + 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." + name: Herausforderungen Inselbegrenzung + hint: Keine Herausforderungen außerhalb der Insel +version: 11 +meta: + authors: + - xXjojoXx diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 36a4a68..ba5f5e2 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -14,38 +14,42 @@ challenges: 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.' + 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 connection to database.' + description: |- + Reload challenges from the database + Parameter hard means that addon will reset the connection to the database. parameters: '[hard]' show: - description: 'This method prints in chat all challenges that exist in world.' + description: 'Prints all challenges in the chat which exist in this world.' parameters: '' defaults: - description: 'This method shows subcommands that allows to import/export default challenges.' + description: 'Shows subcommands to import/export the default challenges.' parameters: '[command]' defaults-import: - description: 'This method allows to import default challenges.' + description: 'Import the default challenges.' parameters: '' defaults-generate: - description: 'This method allows to export existing challenges into default.json file.' + description: 'Export existing challenges to default.json file.' parameters: '[overwrite] - allows to overwrite existing file.' complete: - description: 'This command allows to complete challenge for player without GUI.' + description: 'Complete a challenge for a player.' parameters: ' ' reset: - description: 'This command allows to reset challenge for player without GUI. If "challenge_id" is set to "all", then it will reset all challenges.' + description: 'Reset a challenge for a player. If "challenge_id" is set to "all", then it will reset all challenges.' parameters: ' ' migrate: - description: 'This method allows to migrate challenges data that refers to current game mode world to new 0.8.0 storage format.' + description: 'Migrate current game world challenges data to 0.8.0 storage format.' parameters: '' user: main: - description: 'This method opens Challenges GUI.' + description: 'Open Challenges GUI.' parameters: '' complete: - description: 'This method allows to complete challenge without GUI.' + description: 'Complete challenge.' parameters: ' [count]' gui: title: @@ -89,8 +93,8 @@ challenges: edit-level: 'Edit level' delete-challenge: 'Remove challenge' delete-level: 'Remove level' - import: 'Import ASkyblock Challenges' - settings: 'Edit Settings' + import: 'Import ASkyblock challenges' + settings: 'Edit settings' properties: 'Properties' requirements: 'Requirements' rewards: 'Rewards' @@ -102,15 +106,15 @@ challenges: 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' + 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' @@ -128,20 +132,20 @@ challenges: 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' + 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' + visibility-mode: 'Challenge visibility mode' + toggle-user-list: 'User list' + remove-selected: 'Remove selected' add: 'Add' - show-eggs: 'Switch View mode' + show-eggs: 'Switch view mode' accept: 'Accept' decline: 'Decline' save: 'Save' @@ -155,22 +159,22 @@ challenges: 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' + 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' + 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' + 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' @@ -188,142 +192,246 @@ challenges: reduce: "Reduce" descriptions: admin: - save: 'Save and return to previous GUI.' - cancel: 'Return to previous GUI. Changes will not be saved.' + 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 numbers will change value to selected number.' - increase: 'Increase operation. Clicking on numbers will increase value by selected number.' - reduce: 'Reduce operation. Clicking on numbers will reduce value by selected number.' - multiply: 'Multiply operation. Clicking on numbers will multiply value by selected number.' - import: 'Allows to import ASkyblock Challenges.|On right click it enables/disables overwrite mode.|Place Challenges.yml inside ./BentoBox/addons/Challenges folder.' - complete: 'Allows to complete challenges for any user.|User will not get reward for completion.' - reset: 'Allows to reset completed user challenges.|Right click enables/disables Reset all functionality.' - create-challenge: 'Allows to add new Challenge.|By default it will be in free challenges list.' - create-level: 'Allows to add new Level.' - edit-challenge: 'Allows to edit any Challenge settings.' - edit-level: 'Allows to edit any Level settings.' - delete-challenge: 'Allows remove any Challenge.' - delete-level: 'Allows remove any Level.' - settings: 'Allows to change addon settings.' - properties: 'Allows to change general properties' - requirements: 'Allows to manage requirements' - rewards: 'Allows to manage rewards' - challenges: 'Allows to manage level challenges (add / remove).' + 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 level is locked.' - description: 'Allows to edit description.' - order: 'Allows to change order number.' - environment: 'Allows to change environment where challenge operates.' - remove-on-complete: 'Allows to remove challenge from player GUI after it is completed.' - name-challenge: 'Allows to change challenge display name.' - name-level: 'Allows to change level display name.' - required-entities: 'Allows to add/edit/remove required entities.|Entities:|' - remove-entities: 'Allows to remove (kill) entities on challenge completion.' - required-blocks: 'Allows to add/edit/remove required blocks.|Blocks:|' - remove-blocks: 'Allows to remove (replace with air) blocks on challenge completion.' - search-radius: 'Radius around player location where required entities and blocks will be searched.' - required-permissions: 'Required permissions for player to be able to complete challenge.|Permission:' - required-items: 'Required items in player"s inventory.|Items:' - remove-items: 'Allows to remove items from player inventory after challenge completion.' - required-experience: 'Allows to define required experience for user to complete challenge.' - remove-experience: 'Allows to remove remove required experience.' - required-level: 'Allows to define required island level for this challenge.|&cRequires Level addon.' - required-money: 'Allows to define required money in player"s account.|&cRequires Vault and Economy plugin.' - remove-money: 'Allows to remove required money from player"s account.|&cRequires Vault and Economy plugin.' - reward-text: 'Allows to change message that will be sent to player after challenges completion.' - reward-items: 'Allows to change first time completion reward items.|Items:' - reward-experience: 'Allows to change first time completion reward Experience.' - reward-money: 'Allows to change first time completion reward Money.|&cRequires Vault and Economy plugin.' - reward-commands: 'Allows to define reward commands that will be called after first time completion.|***Adding "[SELF]" at the start means that command will be run by player, f.e. "/kill"|***String "[player]" will be replaced with player name, f.e. "/kill [player]" will be transformed to "/kill BONNe1704"|Commands:' - repeatable: 'Allows to define if challenge is repeatable or not.' - repeat-count: 'Allows to define maximal repeat count. If value is set 0 or smaller, then there are no limitations.' - repeat-reward-text: 'Allows to change message that will be sent to player after challenge repeated completion.' - repeat-reward-items: 'Allows to change repeated completion reward items.|Items:' - repeat-reward-experience: 'Allows to change repeated completion reward Experience.' - repeat-reward-money: 'Allows to change repeated completion reward Money.|&cRequires Vault and Economy plugin.' - repeat-reward-commands: 'Allows to define reward commands that will be called after challenge repeated completion.|***Adding "[SELF]" at the start means that command will be run by player, f.e. "/kill"|***String "[player]" will be replaced with player name, f.e. "/kill [player]" will be transformed to "/kill BONNe1704"|Commands:' - waiver-amount: 'Allows to set how many challenges can be left undone to unlock next level.' - reward-text-level: 'Allows to change message that will be sent to player after completing all challenges in level.' - add-challenge: 'Allows to add existing challenge to current level.' - remove-challenge: 'Allows remove any challenge from current level.' - reset-on-new: 'Enables/Disables option, that resets all player challenges if player restarts island, leave island or was kicked out.' - broadcast: 'Enables/Disables broadcast to online players about first time challenge completion.' + 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. + &cRequires Level addon.' + required-money: |- + Define the required money in player"s account. + &cRequires Vault and an Economy plugin.' + remove-money: |- + Remove required money from player"s account. + &cRequires 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. + &cRequires 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. + &cRequires 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 glowing effect for completed challenges.' - free-at-top: 'Allows to change free challenges location. True mean that challenges will be first, otherwise they will be last.' - line-length: 'Allows to modify maximal line length in lore box. Will not affect stored objects.' + 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 currently are online.' - mode-in-world: 'Players which is in GameMode world.' - mode-with-island: 'Players which has island in GameMode.' + 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.|You can select elements with right mouse button.' + 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: 'Allows to modify which elements of level description should be visible.' - challenge-lore: 'Allows to modify which elements of challenge description should be visible.' - gui-view-mode: 'Allows to set if /challenges GUI should show GameModes or challenges in players world.' - history-store: 'Allows to enable/disable challenges history storage.' - history-lifespan: 'Allows to modify how many days history data will be saved.|0 means forever.' - island-store: 'Allows to enable/disable challenges data storing per island. This means that challenges will be the same on whole team, if this is enabled.|Will NOT convert data on click. PROGRESS WILL BE LOST.' - default-locked-icon: 'Allows to change default locked level icon.|This option can be overwritten by each level.' - gui-mode: 'Allows to enable/disable single challenges GUI.|&2Requires server restart.' - - visibility-mode: 'Allows to switch if undeployed challenges should be displayed or not.' + 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. + &cWill 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. + &2Requires a server restart.' + visibility-mode: 'Show/hide undeployed challenges.' click-to-edit: '&4Click here to edit input.' - edit-text-line: '&6 Edit text message!' - add-text-line: '&6 Add new text message!' + edit-text-line: '&6Edit text message!' + add-text-line: '&6Add new text message!' input-mode: 'Switch between chat and anvil input modes.' - title-enable: 'Allows to enable/disable title message that will be showed when player complete challenge.' - title-showtime: 'Allows to modify how long title message will be visible for player.' - default-import: 'Allows to import default challenges.' - default-export: 'Allows to export existing challenges into defaults.json file.' - complete-wipe: 'Allows to completely clear all challenges addon databases. Includes player data!' + 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: 'Allows to completely clear challenges and their level databases!' - players-wipe: 'Allows to completely clear player database!' + challenge-wipe: 'Completely clear challenges and their level databases!' + players-wipe: 'Completely clear player database!' - library: 'Opens GUI that shows all available public Challenges Libraries.' + library: 'Open GUI that shows all available public Challenges Libraries.' library-author: 'by &e[author]' - library-version: '&9Made on Challenges [version]' + library-version: '&9Made in Challenges [version]' library-lang: '&aLanguage: [lang]' library-gamemode: '&aPrimary for [gamemode]' - download: 'Allows manually update available challenges libraries. |Right click to enable cache clearing.' + 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' | and 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: '|&6Current value: [value].' + 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: |- + &6Current value: [value]. enabled: 'Active' disabled: 'Disabled' type: - island: '&aallows to require blocks or mobs around player' - inventory: '&aallows to require items in players inventory' - other: '&aallows to require things from other plugins/addons' + island: '&arequire blocks or mobs around player' + inventory: '&arequire items in the player"s inventory' + other: '&arequire things from other plugins/addons' the-end: '- The End' nether: '- Nether' normal: '- Overworld' @@ -341,17 +449,17 @@ challenges: reduce-by: "&cReduce completion count by [value]" visibility: - visible: "All challenges are visible for everyone" + visible: "All challenges are visible to everyone" hidden: "Only Deployed challenges are visible." - toggleable: "Allows to toggle if undeployed challenges should be displayed" + toggleable: "Toggle if undeployed challenges should be displayed" challenge-description: - level: '&FLevel: [level]' - completed: '&BCompleted' + level: '&fLevel: [level]' + completed: '&bCompleted' completed-times-of: 'Completed [donetimes] out of [maxtimes]' maxed-reached: 'Completed [donetimes] out of [maxtimes]' completed-times: 'Completed [donetimes]' - warning-items-take: '&cAll required items are taken when you complete this challenge!' + warning-items-take: '&cAll required items are taken from your inventory when you complete this challenge!' objects-close-by: '&cAll required blocks and entities must be close to you on your island!' warning-entities-kill: '&cAll required entities will be killed when you complete this challenge!' warning-blocks-remove: '&cAll required blocks will be removed when you complete this challenge!' @@ -368,9 +476,9 @@ challenges: required-entities: 'Required Entities:' required-blocks: 'Required Blocks:' level-description: - completed: '&BCompleted' - completed-challenges-of: '&3You have completed [number] of [max] challenges in this level.' - waver-amount: '&6Can skip [value] challenges to unlock next level.' + completed: '&bCompleted' + completed-challenges-of: '&3You have completed [number] out of [max] challenges in this level.' + waver-amount: '&6[value] challenges can be skipped to unlock next level.' experience-reward: '&6Exp reward: [value]' money-reward: '&6Money reward: $[value]' reward-items: '&6Reward Items:' @@ -398,32 +506,32 @@ challenges: prefix: "&2[SERVER]: " admin: - number: "Write a number in chat and press enter to accept it and press enter." - unique-id: "Write object unique name and press enter." - challenge-name: "Write in chat display name for current challenge." - level-name: "Write in chat display name for current level." + 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." titles: -# Title and subtitle my contain variable in [] that will be replaced with proper message from challenge object. +# 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 challenge reward text. +# [rewardText] will be replaced with the challenge reward text. challenge-title: 'Successfully completed' challenge-subtitle: '[friendlyName]' -# Title and subtitle my contain variable in [] that will be replaced with proper message from level object. +# 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 level reward text. +# [rewardText] will be replaced with the level reward text. level-title: 'Successfully completed' level-subtitle: '[friendlyName]' messages: admin: - hit-things: 'Hit things to add them to the list of things required. Right click when done.' + 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: '&cHope you have backups, as you just empty all Challenges Addon databases!' + complete-wipe: '&cHopefully you have backups, because you just erased all the Challenges Addon databases!' - challenge-wipe: '&cHope you have backups, as you just empty Challenges and their levels from databases!' - players-wipe: '&cHope you have backups, as you just empty player completed challenges from databases!' + challenge-wipe: '&cHopefully you have backups, because you just erased all the Challenges and their levels!' + players-wipe: '&cHopefully you have backups, because you just erase all the player completed challenges!' completed: '&2You completed challenge [name] for [player]!' already-completed: '&2This challenge was already completed!' @@ -432,7 +540,7 @@ challenges: not-completed: '&2This challenge is not completed yet!' migrate-start: '&2Start migrating challenges addon data.' - migrate-end: '&2Challenges addon data is updated to new format.' + migrate-end: '&2Challenges addon data updated to new format.' migrate-not: '&2All data is valid.' start-downloading: '&5Starting to download and import Challenges Library.' @@ -456,41 +564,43 @@ challenges: unknown-challenge: '&cUnknown challenge' unique-id: '&cUniqueID "[id]" is not valid.' wrong-icon: '&cGiven material "[value]" is not valid and cannot be used as icon.' - not-valid-integer: '&cGiven integer "[value]" is not valid!|Value should be between [min] and [max].' - not-a-integer: '&cGiven value "[value]" is not integer!' + not-valid-integer: |- + &cGiven integer "[value]" is not valid! + Value should be between [min] and [max]. + not-a-integer: '&cGiven value "[value]" is not an integer!' not-deployed: '&cChallenge is not deployed!' not-on-island: '&cYou must be on your island to do that!' - challenge-level-not-available: '&cYou have not unlocked level to complete this challenge.' + challenge-level-not-available: '&cYou have not unlocked the required level to complete this challenge.' not-repeatable: '&cThis challenge is not repeatable!' - wrong-environment: '&cYou are in wrong environment!' + wrong-environment: '&cYou are in the wrong environment!' not-enough-items: '&cYou do not have enough [items] to complete this challenge!' not-close-enough: '&cYou must be standing within [number] blocks of all required items.' you-still-need: '&cYou still need [amount] x [item]' - missing-addon: '&cCannot complete challenge. Required addon or plugin is missing.' - incorrect: '&cCannot complete challenge. Requirements are incorrect.' + missing-addon: '&cCannot complete challenge: Required addon or plugin is missing.' + incorrect: '&cCannot complete challenge: Requirements are incorrect.' not-enough-money: '&cIt is necessary to have [value] on your account to complete the challenge.' - not-enough-experience: '&cIt is necessary to have [value] EXP to complete challenge.' - island-level: '&cYour island must be level [number] to complete this challenge!' + not-enough-experience: '&cIt is necessary to have [value] EXP to complete this challenge.' + island-level: '&cYour island must be level [number] or greater to complete this challenge!' import-no-file: '&cCould not find challenges.yml file to import!' no-load: '&cError: Could not load challenges.yml. [message]' load-error: '&cError: Cannot load [value].' - no-rank: "&cYou do not have rank to do that." - cannot-remove-items: '&cSome items cannot be removed from inventory!' - exist-challenges-or-levels: '&cIn your world already exist challenges. Cannot proceed!' + no-rank: "&cYou do not have rank that is high enough to do that." + cannot-remove-items: '&cSome items cannot be removed from your inventory!' + exist-challenges-or-levels: '&cChallenges already exist in your world. Cannot proceed!' defaults-file-exist: '&cdefaults.json already exists. Use overwrite mode to replace it!' defaults-file-error: '&cThere was an error while creating defaults.json file! Check console!' - no-challenges: '&cChallenges are not implemented in current world!' - no-challenges-admin: '&cChallenges are not implemented in current world! You should use &5/[command] &cto adding them!' - missing-level: '&cChallenge Level [level] is not defined in database. It may case some errors!' + no-challenges: '&cChallenges are not implemented in this world yet!' + no-challenges-admin: '&cChallenges are not implemented in this world yet! Use &5/[command] &cto add them!' + missing-level: '&cChallenge Level [level] is not defined in the database. It may cause errors!' missing-arguments: '&cCommand is missing arguments.' - no-multiple-permission: "&cYou do not have permission to complete challenge multiple times at once." + no-multiple-permission: "&cYou do not have permission to complete this challenge multiple times at once." protection: flags: CHALLENGES_ISLAND_PROTECTION: description: "&5&oToggle who can\n&5&ocomplete challenges" name: "Challenges protection" CHALLENGES_WORLD_PROTECTION: - description: "&5&oThis allows to enable/disable\n&5&orequirement for players to\n&5&obe on their island to\n&5&ocomplete a challenge." + description: "&5&oEnable/disable\n&5&orequirement for players to\n&5&obe on their island to\n&5&ocomplete a challenge." name: "Challenges Island limitation" hint: "No challenges outside island" version: 11 diff --git a/src/main/resources/locales/es.yml b/src/main/resources/locales/es.yml index f5716fc..4a45e9c 100755 --- a/src/main/resources/locales/es.yml +++ b/src/main/resources/locales/es.yml @@ -1,345 +1,624 @@ -########################################################################################################### -# Este es un archivo YML. Tenga cuidado al editar. Revisa tus ediciones en un verificador de YAML como # -# el de http://yaml-online-parser.appspot.com # -########################################################################################################### - -meta: - authors: - - SrAcosta - +--- challenges: - commands: - admin: - main: - parameters: '' - description: 'Comando de administrador principal. Abrir GUI.' - complete: - description: 'Marcar desafío completado' - parameters: ' ' - create: - description: 'Abre el GUI que permite crear desafío..' - parameters: '' - surrounding: - description: 'Crea un desafío envolvente.' - parameters: '' - import: - description: 'Importar retos desde challenges.yml' - parameters: '' - reload: - description: 'Recargar retos de la base de datos' - parameters: '' - reset: - description: 'Restablecer el desafío a 0 veces / incompleto' - parameters: ' ' - show: - description: 'Este método imprime en el chat todos los desafíos que existen en el mundo.' - parameters: '' - user: - description: 'Este método abre el GUI de los desafíos.' - parameters: '' - gui: - title: - admin: - gui-title: '&aDesafíos de Admin' - edit-challenge-title: '&aEditar desafio' - edit-level-title: '&aEditar nivel' - settings-title: '&aEditar configuracion' - choose-challenge-title: '&aElegir desafío' - choose-level-title: '&aElegir nivel' - choose-user-title: '&aElegir jugador' - manage-blocks: '&aAdministrar bloques' - manage-entities: '&aAdministrar Entidades' - confirm-title: '&aConfirmación' - manage-items: '&aAdministrar items' - manage-numbers: '&aTeclado numérico' - select-block: '&aSeleccionar bloque' - select-challenge: '&aSeleccione Desafío' - select-entity: '&aSelecione entidad' - toggle-environment: '&aModificar ambiente' - edit-text-fields: '&aEditar campos de texto' - challenges: '&aDesafios' - buttons: - admin: - complete: 'Completar los desafios del usuario' - reset: 'Reiniciar los desafios del usuario' - create-challenge: 'Agregar nuevo desafio' - create-level: 'Agregar nuevo nivel' - edit-challenge: 'Editar desafio' - edit-level: 'Editar nivel' - delete-challenge: 'Remover desafio' - delete-level: 'Remover nivel' - import: 'Importar desafios de ASkyblock' - backward: 'Importar desafios 0.3.0' - backward-player: 'Arreglar 0.3.0 PlayerData' - settings: 'Editar configuracion' - properties: 'Propiedades' - requirements: 'Requerimientos' - rewards: 'Recompensas' - challenges: 'Desafios' - type: 'Tipo de desafio' - deployment: 'Despliegue' - icon: 'Icono' - locked-icon: 'Icono de bloqueo' - description: 'Descripcion' - order: 'Orrden' - environment: 'Ambientet' - remove-on-complete: 'Eliminar después de la finalización' - name: 'Nombre amigable' - required-entities: 'Requiere entidades' - remove-entities: 'Remover entidades' - required-blocks: 'Requiere bloques' - remove-blocks: 'Remover bloques' - search-radius: 'Buscar radios' - required-permissions: 'Requiere Permisos' - required-items: 'Requiere Items' - remove-items: 'Remover items' - required-experience: 'Requiere experiencia' - remove-experience: 'Remover experiencia' - required-level: 'Requiere nivel de isla' - required-money: 'Requiere dinero' - remove-money: 'Remover dinero' - reward-text: 'Mensaje de recompensa' - reward-items: 'Items de recompensa' - reward-experience: 'Experiencia de recompensa' - reward-money: 'Dinero de recompensa' - reward-commands: 'Comandos de recompensa' - repeatable: 'Repetible' - repeat-count: 'Tiempos máximos' - repeat-reward-text: 'Repetir recompensa de mensaje' - repeat-reward-items: 'Repetir recompensa de items' - repeat-reward-experience: 'Repetir la experiencia de recompensa' - repeat-reward-money: 'Repetir recompensa dinero' - repeat-reward-commands: 'Repetir los comandos de recompensa' - waiver-amount: 'Cantidad de exención' - add-challenge: 'Añadir desafío' - remove-challenge: 'Eliminar desafío' - reset-on-new: 'Restablecer en la isla nueva' - broadcast: 'Emitir completacion' - remove-completed: 'Eliminar después de completar' - glow: 'Resplandor cuando se completa' - free-at-top: 'Los desafíos libres primero' - line-length: 'Longitud de la línea del lore' - toggle-user-list: 'Lista de usuarios' - remove-selected: 'Eliminar selección' - add: 'Agregar' - show-eggs: 'Cambiar modo de vista' - accept: 'Aceptar' - decline: 'Denegar' - save: 'Guardar' - cancel: 'Cancelar' - input: 'Input' - value: 'Valor' - set: '=' - increase: '+' - reduce: '-' - multiply: '*' - clear: 'Limpiar' - remove-empty: 'Remover vacio' - number: '[number]' - level-lore: 'Descripcion de nivel' - challenge-lore: 'Descripcion de desafio' - gui-view-mode: 'Mostrar todos los modos de juego' - gui-mode: 'Gui simple de desafios' - history-store: 'Historial de desafios' - history-lifespan: 'Historia LifeSpan' - island-store: 'Tienda por isla' - default-locked-icon: 'Icono de nivel bloqueado' - next: 'Siguiente' - previous: 'Anterior' - return: 'Regreso' - descriptions: - admin: - save: 'Guardar y volver a la anterior GUI.' - cancel: 'Regresar a la anterior GUI. Los cambios no serán guardados.' - input: 'Abrir entrada de campo de texto.' - set: 'Configuración de la operación. Al hacer clic en los números cambiará el valor al número seleccionado.' - increase: 'Aumente la operación. Al hacer clic en los números aumentará el valor por el número seleccionado.' - reduce: 'Reducir la operación. Al hacer clic en los números se reducirá el valor por el número seleccionado.' - multiply: 'Multiplicar la operación. Al hacer clic en los números se multiplicará el valor por el número seleccionado.' - import: 'Permite importar Desafíos de ASkyblock. | Al hacer click con el botón derecho, habilita / deshabilita el modo de sobrescritura.' - complete: 'Permite completar desafíos para cualquier usuario. | El usuario no obtendrá una recompensa por completar.' - reset: 'Permite restablecer los desafíos completados del usuario. | El click derecho habilita / inhabilita la función Restablecer todas.' - create-challenge: 'Permite agregar un nuevo desafío. | Por defecto estará en la lista de desafíos gratis.' - create-level: 'Permite agregar nuevo nivel.' - edit-challenge: 'Permite editar cualquier configuración de desafío.' - edit-level: 'Permite editar cualquier configuración de nivel.' - delete-challenge: 'Permite eliminar cualquier Desafío.' - delete-level: 'Permite eliminar cualquier nivel.' - backward: 'Permite importar desafíos desde 0.3.0 y por debajo de la versión adicional.' - backward-player: 'Permite corregir PlayerData dañado desde la versión 0.3.0|&2ÚSELO SOLO SI ES NECESARIO|&2PUEDE NO TRABAJAR EN TODAS LAS SITUACIONES' - settings: 'Permite cambiar la configuración del addon.' - properties: 'Permite cambiar propiedades generales' - requirements: 'Permite gestionar requerimientos' - rewards: 'Permite gestionar recompensas' - challenges: 'Permite gestionar retos de nivel (add / remove).' - deployment: 'Permite a los usuarios completar (view) desafio.' - icon-challenge: 'Icono que se mostrará en los paneles GUI para este desafío.' - icon-level: 'Icono que se mostrará en los paneles GUI para este nivel.' - locked-icon: 'Icono que se mostrará en los paneles de la GUI si el nivel está bloqueado.' - description: 'Permite editar la descripción.' - order: 'Permite cambiar el número de pedido.' - environment: 'Permite cambiar el ambiente donde opera el desafío.' - type: 'Permite cambiar el tipo de desafío. Cada tipo tiene sus propios requisitos.' - remove-on-complete: 'Permite eliminar el desafío de la GUI del jugador una vez completado.' - name-challenge: 'ermite cambiar el nombre de visualización del desafío.' - name-level: 'Permite cambiar el nombre de visualización de nivel.' - required-entities: 'Permite a añadir / editar / eliminar Entidades requeridas.|Entities:|' - remove-entities: 'Permite eliminar (kill) Entidades en la finalización del desafío.' - required-blocks: 'Permite agregar / editar / eliminar bloques requeridos.|Blocks:|' - remove-blocks: 'Permite eliminar (reemplazar con aire) bloques al completar el desafío.' - search-radius: 'Radio alrededor de la ubicación del jugador donde se buscarán las entidades y bloques requeridos.' - required-permissions: 'Permisos requeridos para que el jugador pueda completar el desafío.|Permission:' - required-items: 'Elementos requeridos en el inventario del jugador.|Items:' - remove-items: 'Permite eliminar objetos del inventario del jugador después de completar el desafío.' - required-experience: 'Permite definir la experiencia requerida para que el usuario complete el desafío.' - remove-experience: 'Permite eliminar la experiencia requerida.' - required-level: 'Permite definir el nivel de isla requerido para este desafío.|&cRequires Level addon.' - required-money: 'Permite definir el dinero requerido en la cuenta del jugador.|&cRequires Vault and Economy plugin.' - remove-money: 'Permite eliminar el dinero requerido de la cuenta del jugador.|&cRequires Vault and Economy plugin.' - reward-text: 'Permite cambiar el mensaje que se enviará al jugador después de completar los desafíos.' - reward-items: 'Permite cambiar los elementos de recompensa por primera vez.|Items:' - reward-experience: 'Permite cambiar la recompensa de experiencia por primera vez.' - reward-money: 'Permite cambiar la recompensa de dinero por primera vez.|&cRequires Vault and Economy plugin.' - reward-commands: 'Permite definir los comandos de recompensa que se llamarán después de que se completen por primera vez. | *** Agregar "[SELF]" en el inicio significa que el jugador ejecutará el comando, es decir, "/ kill" | *** String "[player]" se reemplazará con el nombre del jugador, por ejemplo. "/ kill [jugador]" se transformará en "/ kill BONNe1704"|Commands:' - repeatable: 'Permite definir si el desafío es repetible o no.' - repeat-count: 'Permite definir el recuento máximo de repeticiones. Si el valor se establece en 0 o menos, entonces no hay limitaciones.' - repeat-reward-text: 'Permite cambiar el mensaje que se enviará al jugador después de completar el desafío repetidamente.' - repeat-reward-items: 'Permite cambiar los elementos de recompensa de finalización repetida.|Items:' - repeat-reward-experience: 'Permite cambiar la recompensa de experiencia de finalización repetida.' - repeat-reward-money: 'Permite cambiar el dinero de recompensa de finalización repetida.|&cRequires Vault and Economy plugin.' - repeat-reward-commands: 'Permite definir los comandos de recompensa que se llamarán después de completar el desafío repetidamente. | *** Agregar "[SELF]" en el inicio significa que el jugador ejecutará el comando, es decir, "/ kill" | *** String "[player]" se reemplazará con el nombre del jugador, por ejemplo. "/ kill [jugador]" se transformará en "/ kill BONNe1704"|Commands:' - waiver-amount: 'Permite establecer cuántos desafíos pueden dejarse para deshacer para desbloquear el siguiente nivel.' - reward-text-level: 'Permite cambiar el mensaje que se enviará al jugador después de completar todos los desafíos en el nivel.' - add-challenge: 'Permite agregar desafío existente al nivel actual.' - remove-challenge: 'Permite eliminar cualquier desafío del nivel actual.' - reset-on-new: 'Habilita / deshabilita la opción, que restablece todos los desafíos del jugador si el jugador reinicia la isla, deja la isla o fue expulsado.' - broadcast: 'Habilita / deshabilita la transmisión a los jugadores en línea cuando se completa el primer desafío.' - remove-completed: 'Habilita / deshabilita los desafíos de ocultación que se completan y no se pueden repetir.' - glow: 'Habilita / deshabilita el efecto brillante para los desafíos completados.' - free-at-top: 'Permite cambiar la ubicación de los desafíos libres. Cierto significa que los desafíos serán los primeros, de lo contrario serán los últimos.' - line-length: 'Permite modificar la longitud máxima de la línea en el cuadro de conocimientos. No afectará a los objetos almacenados.' - toggle-user-list: 'Cambiar a la lista de jugadores diferentes.' - mode-online: 'Jugadores que actualmente están en línea.' - mode-in-world: 'Jugadores que está en el mundo de GameMode.' - mode-with-island: 'Jugadores que tiene isla en GameMode.' - selected: 'Seleccionado' - remove-selected: 'Eliminar elementos seleccionados. | Puede seleccionar elementos con el botón derecho del ratón.' - show-eggs: 'Cambia la vista de entidad entre el modo Huevo o el modo Cabeza.' - level-lore: 'Permite modificar qué elementos de la descripción de nivel deben ser visibles.' - challenge-lore: 'Permite modificar qué elementos de la descripción de desafío deberían ser visibles.' - gui-view-mode: 'Permite establecer si / desafíos GUI debe mostrar GameModes o desafíos en el mundo de los jugadores.' - history-store: 'Permite habilitar / deshabilitar el almacenamiento del historial de desafíos.' - history-lifespan: 'Permite modificar cuántos días se guardarán los datos históricos. | 0 significa para siempre.' - island-store: 'Permite activar / desactivar cadenas de datos de desafíos por isla. Esto significa que los desafíos serán los mismos en todo el equipo, si esto está habilitado. NO convertirá los datos al hacer click. EL PROGRESO SE PERDERÁ.' - default-locked-icon: 'Permite cambiar el ícono de nivel bloqueado predeterminado. | Esta opción puede ser sobrescrita por cada nivel.' - gui-mode: 'Permite habilitar / deshabilitar la GUI de desafíos individuales. |&2Requiere reinicio del servidor. -' - current-value: '|&6Valor actual: [value].' - enabled: 'Activo' - disabled: 'Desactivado' - type: - island: '- Tipo de isla: | (Permite requerir bloques o mobs alrededor del jugador).' - inventory: '- Tipo de inventario: | (permite requerir objetos en el inventario de jugadores)' - other: '- Otro tipo: | (permite requerir cosas de otros complementos / complementos)' - the-end: '- El End' - nether: '- Nether' - normal: '- Sobre mundo' - entity: '- [entity] : [count]' - block: '- [block] : [count]' - permission: '- [permission]' - item: '- [count] x [item]' - item-meta: ' ([meta])' - item-enchant: ' - [enchant] [level]' - command: '- [command]' - level-unlocked: 'Click para ver [level] desafios!' - level-locked: 'Completa [count] mas [level] desafios para desbloquear el siguiente nivel!' - challenge-description: - level: '&FNivel: [level]' - completed: '&BCompletadp' - completed-times-of: 'Completado [donetimes] de [maxtimes]' - maxed-reached: 'Completado [donetimes] de [maxtimes]' - completed-times: 'Completado [donetimes]' - warning-items-take: '&cTodos los elementos necesarios se toman cuando completa este desafío!' - objects-close-by: '&cTodos los bloques y entidades requeridos deben estar cerca de usted en su isla!' - warning-entities-kill: '&cTodas las entidades requeridas serán eliminadas cuando completes este desafío!' - warning-blocks-remove: '&cTodos los bloques requeridos serán eliminados cuando completes este desafío!' - not-repeatable: '&cEste reto no es repetible!' - experience-reward: '&6Recompensa de XP: [value]' - money-reward: '&6Recompensa de dinero: $[value]' - required-experience: '&6Requiere XP: [value]' - required-money: '&6Requiere Dinero: $[value]' - required-island-level: '&6Nivel de isla requerido: [value]' - environment: 'Entornos Requeridos:' - reward-items: '&6Artículos de recompensa:' - reward-commands: '&6Comandos de recompensa:' - required-items: 'Objetos requeridos:' - required-entities: 'Entidades Requeridas:' - required-blocks: 'Bloques requeridos:' - level-description: - completed: '&BCompletado' - completed-challenges-of: '&3Haz completado [number] de [max] desafios en este nivel.' - waver-amount: '&6Puede saltar [value] desafíos para desbloquear el siguiente nivel.' - experience-reward: '&6Recompensa de XP: [value]' - money-reward: '&6Recompensa de dinero: $[value]' - reward-items: '&6Artículos de recompensa:' - reward-commands: '&6Comandos de recompensa:' - - questions: - prefix: "&2[SERVER]: " - - admin: - number: "Write a number in chat and press enter to accept it and press enter." - unique-id: "Write object unique name and press enter." - challenge-name: "Write in chat display name for current challenge." - level-name: "Write in chat display name for current level." - messages: - admin: - hit-things: 'Golpea cosas para agregarlas a la lista de cosas requeridas. Haga click derecho cuando haya terminado.' - you-added: 'Has añadido uno [thing] al desafio' - challenge-created: '[challenge] creado!' - you-completed-challenge: '&2Completaste el [value] desafio!' - you-repeated-challenge: '&2Repetiste el [value] desafio!' - you-completed-level: '&2Completaste el [value] nivel!' - name-has-completed-challenge: '&5[name] ha completado el [value] desafio!' - name-has-completed-level: '&5[name] ha completado el [value] nivel!' - import-levels: 'Empezar a importar niveles' - import-challenges: 'Empezar a importar desafios' - no-levels: 'Advertencia: No hay niveles definidos en challenges.yml' - import-number: 'Importado [number] desafios' - load-skipping: '"[value]" ya existe - saltando' - load-overwriting: 'Sobrescribiendo "[value]"' - load-add: 'Añadiendo nuevo objeto: [value]' - errors: - no-name: '&cFalta el nombre del desafío' - unknown-challenge: '&cDesafío desconocido' - unique-id: '&cIdentificación única "[id]" no es valida.' - wrong-icon: '&cMaterial dado "[value]" no es válido y no puede ser usado como icono.' - not-valid-integer: '&cEntero dado "[value]" no es válido! | El valor debe estar entre [min] y [max].' - not-a-integer: '&cValor dado "[value]" no es entero!' - not-deployed: '&cEl desafío no está desplegado!' - not-on-island: '&cDebes estar en tu isla para hacer eso!' - challenge-level-not-available: '&cNo has desbloqueado el nivel para completar este desafío.e.' - not-repeatable: '&cEste reto no es repetible.!' - wrong-environment: '&cEstás en el entorno incorrecto!' - not-enough-items: '&cNo tienes [ítems] suficientes para completar este desafío! !' - not-close-enough: '&cDebes estar dentro de los bloques [number] de todos los elementos requeridos.' - you-still-need: '&cUsted todavía necesita [amount] x [item]' - missing-addon: '&cNo puedo completar el desafío. Falta el addon o plugin requerido' - incorrect: '&cNo se pudo completar el desafío. Los requisitos son incorrectos.' - not-enough-money: '&cEs necesario tener [value] en su cuenta para completar el desafío.' - not-enough-experience: '&cEs necesario tener [value] EXP para completar este desafio.' - island-level: '&cTu isla debe ser de nivel [number] para completar este desafío!' - import-no-file: '&cNo se puede encontrar el archivo challenge.yml para importar' - no-load: '&cError: No se pudieron cargar challenge.yml. [message]' - load-error: '&cError: No se pudo cargar [value].' - no-rank: "&cNo tienes rango para hacer eso." + commands: + admin: + create: + description: Abre el GUI que permite crear desafío.. + parameters: "" + main: + description: Comando de administrador principal. Abrir GUI. + surrounding: + description: Crea un desafío envolvente. + parameters: "" + import: + parameters: "[overwrite]" + description: |- + Importar desafíos desde challenge.yml + La sobrescritura de parámetros significa que se sobrescribirán los desafíos o niveles con la misma ID. + defaults-generate: + parameters: "[overwrite] - permite sobrescribir el archivo existente." + description: Exportar los desafíos existentes al archivo default.json. + complete: + parameters: " " + description: Completar un desafío para un jugador. + reset: + parameters: " " + description: Restablecer un desafío para un jugador. Si "challenge_id" se + establece en "all", restableceran todos los desafíos. + defaults-import: + description: Importar los desafíos predeterminados. + reload: + description: |- + Recargar desafíos desde la base de datos + El parámetro hard significa que el addon restablecerá la conexión a la base de datos. + parameters: "[hard]" + defaults: + parameters: "[command]" + description: Muestra subcomandos para importar / exportar los desafíos predeterminados. + migrate: + description: Migrar los datos actuales de los desafíos del mundo del juego + al formato de almacenamiento 0.8.0. + show: + description: Imprime todos los desafíos en el chat que existen en este mundo. + user: + description: Este método abre el GUI de los desafíos. + complete: + parameters: " [count]" + description: Completa el desafío. + main: + description: Abrir GUI de Desafíos. + errors: + load-error: "&cError: No se pudo cargar [value]." + no-name: "&cFalta el nombre del desafío" + unknown-challenge: "&cDesafío desconocido" + unique-id: '&cLa UniqueID "[id]" no es valida.' + wrong-icon: '&cEl material dado "[value]" no es válido y no puede ser usado como + icono.' + not-deployed: "&c¡El desafío no está desplegado!" + not-on-island: "&c¡Debes estar en tu isla para hacer eso!" + not-repeatable: "&c¡Este reto no es repetible!" + not-enough-items: "&c¡No tienes [ítems] suficientes para completar este desafío!" + not-close-enough: "&cYou must be standing within [number] blocks of all required + items." + you-still-need: "&cTodavía necesitas [amount] x [item]" + not-enough-money: "&cEs necesario tener [value] en tu cuenta para completar el + desafío." + import-no-file: "&c¡No se puede encontrar el archivo challenge.yml para importar!" + no-load: "&cError: No se pudo cargar challenge.yml. [message]" + defaults-file-exist: "&cdefaults.json ya existe. ¡Utiliza el modo de sobrescritura + para reemplazarlo!" + defaults-file-error: "&c¡Se produjo un error al crear el archivo defaults.json! + ¡Compruebe la consola!" + missing-arguments: "&cFaltan argumentos de comando." + wrong-environment: "&c¡Estás en el entorno equivocado!" + missing-addon: "&cNo se puede completar el desafío: falta un addon o plugin requerido." + exist-challenges-or-levels: "&cLos desafíos ya existen en tu mundo. ¡No se puede + proceder!" + no-challenges: "&c¡Los desafíos aún no se han implementado en este mundo!" + no-challenges-admin: "&c¡Los desafíos aún no se han implementado en este mundo! + ¡Usa &5/[command] &cpara agregarlos!" + missing-level: "&cEl nivel de desafío [level] no está definido en la base de datos. + ¡Puede causar errores!" + no-multiple-permission: "&cNo tienes permiso para completar este desafío varias + veces a la vez." + not-a-integer: '&c¡El valor dado "[value]" no es entero!' + challenge-level-not-available: "&cNo has desbloqueado el nivel requerido para + completar este desafío." + incorrect: "&cNo se puede completar el desafío: los requisitos son incorrectos." + not-enough-experience: "&cEs necesario tener [value] EXP para completar este desafío." + island-level: "&c¡Tu isla debe ser de nivel [number] o mayor para completar este + desafío!" + no-rank: "&cNo tienes un rango lo suficientemente alto como para hacer eso." + cannot-remove-items: "&c¡Algunos items no se pueden eliminar de tu inventario!" + not-valid-integer: |- + &cEl número entero "[value]" no es válido. + El valor debe estar entre [min] y [max]. + gui: + buttons: + admin: + accept: Aceptar + add: Agregar + backward: Importar desafios 0.3.0 + backward-player: Arreglar 0.3.0 PlayerData + cancel: Cancelar + clear: Limpiar + create-level: Agregar nuevo nivel + decline: Denegar + deployment: Despliegue + edit-level: Editar nivel + history-lifespan: Historia LifeSpan + icon: Icono + increase: "+" + input: Input + line-length: Longitud de la línea del lore + multiply: "*" + number: "[number]" + properties: Propiedades + reduce: "-" + remove-empty: Remover vacio + remove-experience: Remover experiencia + remove-money: Remover dinero + repeatable: Repetible + repeat-count: Tiempos máximos + requirements: Requerimientos + reward-commands: Comandos de recompensa + reward-experience: Experiencia de recompensa + reward-items: Items de recompensa + reward-money: Dinero de recompensa + rewards: Recompensas + reward-text: Mensaje de recompensa + save: Guardar + set: "=" + value: Valor + complete: Completar los desafíos del usuario + reset: Reiniciar los desafíos del usuario + create-challenge: Agregar nuevo desafío + edit-challenge: Editar desafío + delete-challenge: Eliminar desafio + delete-level: Eliminar nivel + challenges: Desafíos + locked-icon: Icono bloqueado + description: Descripción + order: Orden + environment: Ambiente + remove-on-complete: Remover al finalizar + required-experience: Experiencia requerida + required-level: Nivel de isla requerido + required-money: Dinero requerido + repeat-reward-text: Repetir mensaje de recompensa + repeat-reward-items: Repetir items de recompensa + repeat-reward-experience: Repetir experiencia de recompensa + repeat-reward-money: Repetir dinero de recompensa + repeat-reward-commands: Repetir comandos de recompensa + remove-completed: Remover al completar + glow: Brillar (glow) al completar + free-at-top: Desafíos libres primero + input-mode: Cambiar modo de entrada (input) + type: + island: "&6Tipo de isla" + inventory: "&6Tipo de inventario" + other: "&6Otro tipo" + required-items: Items requeridos + remove-items: Eliminar items + gui-mode: GUI de Desafíos individuales + complete-wipe: Limpiar bases de datos de addon + challenge-wipe: Limpiar base de datos de desafíos + players-wipe: Limpiar base de datos del usuario + title-enable: Título de finalización + library: Biblioteca web + download: Descargar bibliotecas + import: Importar desafíos ASkyblock + settings: Editar configuración + name: Nombre amigable + required-entities: Entidades requeridas + remove-entities: Matar entidades + required-blocks: Bloques requeridos + remove-blocks: Eliminar bloques + search-radius: Radio de búsqueda + required-permissions: Permisos requeridos + waiver-amount: Cantidad de exención + add-challenge: Añadir desafío + remove-challenge: Eliminar desafío + reset-on-new: Restablecer en nueva isla + broadcast: Difusión completada + visibility-mode: Modo de visibilidad de desafío + toggle-user-list: Lista de usuarios + remove-selected: Eliminar selección + show-eggs: Cambiar modo de vista + level-lore: Descripción del nivel + challenge-lore: Descripción del desafío + gui-view-mode: Mostrar todos los modos de juego + history-store: Desafíos de la historia + island-store: Almacenar por isla + default-locked-icon: Icono de nivel bloqueado + title-showtime: Tiempo de presentación del título + default-import: Importar desafíos predeterminados + default-export: Exportar desafíos existentes + next: Siguiente + previous: Anterior + return: Volver + value: Completar + increase: Incrementar + reduce: Reducir + challenge-description: + completed-times: Completado [donetimes] + completed-times-of: Completado [donetimes] de [maxtimes] + maxed-reached: Completado [donetimes] de [maxtimes] + required-blocks: 'Bloques requeridos:' + required-island-level: "&6Nivel de isla requerido: [value]" + reward-commands: "&6Comandos de recompensa:" + objects-close-by: "&c¡Todos los bloques y entidades requeridos deben estar cerca + de ti en tu isla!" + warning-entities-kill: "&c¡Todas las entidades requeridas serán eliminadas cuando + completes este desafío!" + warning-blocks-remove: "&c¡Todos los bloques requeridos serán eliminados cuando + completes este desafío!" + not-repeatable: "&c¡Este desafío no es repetible!" + experience-reward: "&6Recompensa de EXP: [value]" + money-reward: "&6Recompensa de dinero: [value]$" + required-experience: "&6EXP requerida: [value]" + required-money: "&6Dinero requerido: [value]$" + environment: 'Entornos requeridos:' + reward-items: "&6Items de recompensa:" + required-items: 'Items requeridos:' + required-entities: 'Entidades requeridas:' + level: "&fNivel: [level]" + completed: "&bCompletado" + warning-items-take: "&c¡Todos los items necesarios son tomados de tu inventario + cuando se completa este desafío!" + descriptions: + admin: + backward: Permite importar desafíos desde 0.3.0 y por debajo de la versión + adicional. + backward-player: Permite corregir PlayerData dañado desde la versión 0.3.0|&2ÚSELO + SOLO SI ES NECESARIO|&2PUEDE NO TRABAJAR EN TODAS LAS SITUACIONES + deployment: Permite a los usuarios completar (view) desafio. + icon-challenge: Icono que se mostrará en los paneles GUI para este desafío. + icon-level: Icono que se mostrará en los paneles GUI para este nivel. + input: Abrir entrada de campo de texto. + selected: Seleccionado + remove-completed: Habilitar / deshabilitar la ocultación de desafíos que se + completan y no se pueden repetir. + toggle-user-list: Cambiar a una lista de jugadores diferente. + show-eggs: Cambiar la vista de entidad entre el modo Huevo o el modo Cabeza. + click-to-edit: "&4Haga clic aquí para editar la entrada (input)." + input-mode: Cambiar entre los modos de entrada (input) de chat y yunque. + library-author: por &e[author] + library-lang: "&aIdioma: [lang]" + library-gamemode: "&aPrimariamente para [gamemode]" + edit-challenge: Editar configuración del desafío. + description: Editar descripción. + name-challenge: Cambiar el nombre del desafío. + name-level: Cambiar el nombre del nivel. + remove-entities: Eliminar (kill) entidades al completar el desafío. + remove-blocks: Eliminar (reemplazar con aire) bloques al completar el desafío. + reward-text: Cambiar el mensaje que se enviará al jugador después de completar + los desafíos. + repeatable: Definir si el desafío es repetible o no. + free-at-top: Cambiar la ubicación de los desafíos gratuitos. Verdadero (true) + significa que los desafíos serán los primeros, de lo contrario serán los + últimos. + line-length: Modificar la longitud máxima de la línea en el "Lore box". No + afectará a los objetos almacenados. + level-lore: Modificar qué elementos de descripción de nivel deben estar visibles. + challenge-lore: Modificar qué elementos de descripción de desafío deben estar + visibles. + gui-view-mode: Establecer si "/challenges GUI" debe mostrar Modos de Juego + o desafíos en el mundo del jugador. + default-export: Exportar los desafíos existentes al archivo defaults.json. + complete-wipe: Limpiar completamente todas las bases de datos del addon de + desafíos. ¡Incluye datos del jugador! + challenge-wipe: "¡Los desafíos y sus bases de datos de nivel han sido completamente + limpiados!" + players-wipe: La base de datos del jugador se ha limpiado completamente! + library: Abrir GUI que muestre todas las bibliotecas públicas de desafíos + disponibles. + library-version: "&9Hecho en Challenges [versión]" + cancel: Volver a la GUI anterior. Los cambios no se guardarán. + challenges: Gestionar nivel de desafíos (agregar / eliminar). + remove-on-complete: Eliminar un desafío de la GUI del jugador después de completarlo. + remove-items: Retirar los items del inventario del jugador después de completar + el desafío. + required-experience: Definir la experiencia requerida para que un usuario + complete el desafío. + remove-experience: Eliminar experiencia requerida. + reward-experience: Cambiar la recompensa de experiencia por completar por + primera vez + repeat-reward-text: Cambiar el mensaje que se enviará al jugador después de + completar un desafío repetido. + repeat-reward-experience: Cambiar la recompensa de experiencia por completar + repetido. + add-challenge: Agregar un desafío existente al nivel actual. + remove-challenge: Eliminar un desafío del nivel actual. + reset-on-new: Habilitar / deshabilitar el reinicio de todos los desafíos del + jugador si se reinicia, se va o es expulsado de una isla. + broadcast: Habilitar / deshabilitar la transmisión sobre la primera vez que + se completa el desafío para todos los jugadores en línea. + glow: Habilitar / deshabilitar el efecto brillante para los desafíos completados. + edit-text-line: "&6¡Editar mensaje de texto!" + add-text-line: "&6¡Agregar un nuevo mensaje de texto!" + title-enable: Habilitar / deshabilitar el mensaje de título que se mostrará + a los jugadores cuando completen un desafío. + title-showtime: Modificar cuánto tiempo serán visibles los mensajes de título + para el jugador. + import: |- + Importar desafíos ASkyblock. + Al hacer clic derecho, habilitar/deshabilita el modo de sobrescritura. + Coloque challenges.yml dentro de la carpeta ./BentoBox/addons/Challenges. + complete: |- + Completar desafíos para cualquier usuario. + El usuario no recibirá ninguna recompensa por completarlo. + required-items: |- + items necesarios en el inventario del jugador. + Items: + required-level: |- + Definir el nivel de isla requerido para este desafío. + &cRequiere el addon Level. + required-money: |- + Definir el dinero requerido en la cuenta del jugador. + &cRequiere Vault y el plugin de Economy. + remove-money: |- + Retirar el dinero requerido de la cuenta del jugador. + &cRequiere Vault y el plugin de Economy. + reward-items: |- + Cambiar los items de recompensa de finalización por primera vez. + Items: + reward-money: |- + Cambiar el dinero de recompensa de finalización por primera vez. + &cRequiere el complemento Vault and Economy. + reward-commands: |- + Definir los comandos de recompensa que se invocarán una vez que se completen por primera vez. + *** Agregar "[SELF]" al comienzo significa que el comando será ejecutado por el jugador, por ejemplo, "/ kill" + *** La cadena "[player]" se reemplazará con el nombre del jugador, por ejemplo, "/ kill [player]" se transformará en "/ kill BONNe1704" + Comandos: + repeat-reward-items: |- + Cambiar los items de recompensa de finalización repetida. + Items: + repeat-reward-money: |- + Cambiar la recompensa de dinero por finalización repetida. + &cRequiere Vault y el plugin Economy. + repeat-reward-commands: |- + Definir los comandos de recompensa que se ejecutarán después de completar el desafío repetidamente. + *** Agregar "[SELF]" al comienzo significa que el comando será ejecutado por el jugador, por ejemplo, "/ kill" + *** La cadena "[player]" se reemplazará con el nombre del jugador, por ejemplo, "/ kill [player]" se transformará en "/ kill BONNe1704" + Comandos: + history-lifespan: |- + Modificar cuántos días deben almacenarse los datos del historial. + 0 significa para siempre. + island-store: |- + Habilitar/deshabilitar el almacenamiento de datos de desafíos por isla. Esto significa que los desafíos serán los mismos para todo el equipo si está habilitado. + &cNO convertirá datos al hacer clic. EL PROGRESO SE PERDERÁ. + default-locked-icon: |- + Cambiar el icono de nivel bloqueado predeterminado. + Esta opción puede ser sobrescrita por cada nivel. + gui-mode: |- + Habilitar / deshabilitar GUI de desafíos únicos. + &2Requiere reiniciar el servidor. + download: |- + Actualizar manualmente las bibliotecas de desafíos disponibles. + Haz clic derecho para habilitar la limpieza de caché. + lore: + level: |- + Cadena de nivel. + Representa la traducción challenges.gui.challenge-description.level + status: |- + Cadena de estado. + Representa la traducción challenges.gui.challenge-description.completed + count: |- + Cadena de contador de finalizado/completado. + Representa la traducción de challenges.gui.challenge-description.completed-times challenges.gui.challenge-description.completed-times-of and challenges.gui.challenge-description.maxed-reached + description: |- + Cadena de descripción. + Definido en el objeto de desafíos - challenge.description. + warnings: |- + Cadena de advertencia. + Representa la traducción para: + 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: |- + Cadena de entorno. + Definido en el objeto de desafíos: challenge.environment. + requirements: |- + Cadena de requisitos. + Representa la traducción para: + challenges.gui.challenge-description.required-level challenges.gui.challenge-description.required-money challenges.gui.challenge-description.required-experience challenge.requiredItems' challenge.requiredBlocks' o challenge.requiredEntities. + reward_text: |- + Cadena de recompensa. + Definido en challenge.rewardText y challenge.repeatRewardText + reward_other: |- + Cadena de otra recompensa. + Representa la traducción para: + challenges.gui.challenge-description.experience-reward challenges.gui.challenge-description.money-reward challenges.gui.challenge-description.not-repeatable + reward_items: |- + Items de recompensa. + Lista de elementos que se recompensarán definidos en challenge.rewardItems y challenge.repeatRewardItems. + reward_commands: |- + Comandos de recompensa. + Lista de comandos que serán recompensados definidos en challenge.rewardCommands y challenge.repeatRewardCommands. + level_status: |- + Cadena de estado. + Representa la traducción challenges.gui.level-description.completed + challenge_count: |- + Cadena de contador de desafíos completados. + Representa la traducción de challenges.gui.level-description.completed-challenges-of + unlock_message: |- + Cadena de mensaje desbloqueado. + Definido en Nivel de objeto de desafíos - challengeLevel.unlockMessage + waiver_amount: |- + Cadena de contador de desafíos enviables para desbloquear el siguiente nivel. + Representa la traducción de challenges.gui.level-description.waver-amount + level_reward_text: |- + Cadena de recompensa. + Definido en challengeLevel.rewardText + level_reward_other: |- + Cadena de otra recompensa. + Representa la traducción para: + challenges.gui.level-description.experience-reward challenges.gui.level-description.money-reward + level_reward_items: |- + Items de recompensa. + Lista de elementos que se recompensarán definidos en challengeLevel.rewardItems + level_reward_commands: |- + Comandos de recompensa. + Lista de comandos que serán recompensados definidos en challengeLevel.rewardCommands + download-disabled: El descargador de datos de GitHub está deshabilitado en + BentoBox. ¡Sin él, no puedes usar Bibliotecas! + create-level: Añadir nuevo nivel. + edit-level: Editar configuración de nivel. + delete-challenge: Eliminar un desafío. + delete-level: Eliminar un nivel. + settings: Cambiar ajustes. + properties: Cambiar propiedades generales + requirements: Administrar requisitos + rewards: Administrar recompensas + order: Cambiar número de orden. + environment: Cambiar el entorno del desafío. + search-radius: Radio alrededor de la ubicación del jugador donde se buscarán + las entidades y los bloques necesarios. + history-store: Habilitar / deshabilitar el almacenamiento del historial de + desafíos. + default-import: Importar desafíos predeterminados. + save: Guardar y volver a la GUI anterior. + set: Establecer operación. Al hacer clic en los números cambiará el valor + al número seleccionado. + increase: Aumentar la operación. Al hacer clic en los números, aumentará el + valor en el número seleccionado. + reduce: Reduce la operación. Al hacer clic en los números se reducirá el valor + en el número seleccionado. + multiply: Multiplicar la operación. Al hacer clic en los números, se multiplicará + el valor por el número seleccionado. + locked-icon: Icono que se mostrará en los paneles de la GUI si el nivel está + bloqueado. + repeat-count: Definir recuento de repetición máxima. Si el valor se establece + en 0, no hay limitaciones. + waiver-amount: Establece la cantidad de desafíos que un jugador puede dejar + fuera para desbloquear el siguiente nivel. + reward-text-level: Cambia el mensaje que se enviará al jugador después de + completar todos los desafíos en un nivel. + mode-online: Jugadores que están actualmente en línea. + mode-in-world: Jugadores en un mundo GameMode. + mode-with-island: Jugadores que tienen una isla en un mundo GameMode. + visibility-mode: Mostrar / ocultar desafíos no implementados. + reset: |- + Restablecer desafíos de usuario completados. + El clic derecho habilita / deshabilita Restablecer toda la funcionalidad. + create-challenge: |- + Agrega un nuevo desafío. + Estará en la lista de desafíos gratuitos de forma predeterminada. + required-entities: |- + Agregar / editar / eliminar entidades requeridas. + Entidades: + required-blocks: |- + Agregar / editar / eliminar bloques necesarios. + Bloques: + required-permissions: |- + Permisos requeridos a para que el jugador pueda completar este desafío. + Permiso: + remove-selected: |- + Eliminar elementos seleccionados. + Seleccione elementos con el botón derecho del mouse. + block: "- [block] : [count]" + command: "- [command]" + disabled: Desactivado + entity: "- [entity] : [count]" + item: "- [count] x [item]" + item-enchant: " - [enchant] [level]" + item-meta: " ([meta])" + nether: "- Nether" + permission: "- [permission]" + the-end: "- El End" + enabled: Activado + normal: "- Overworld" + level-unlocked: Click para ver los desafíos de nivel [level]! + level-locked: Completa [count] desafíos mas de nivel [level] para desbloquear + el siguiente nivel! + increase-by: '&aIncrementar contador de "Finalizado" en [valor]' + reduce-by: '&cReducir contador de "Finalizado" en [valor]' + type: + island: "&arequire bloques o mobs alrededor del jugador" + other: "&arequiere cosas de otros plugins/addons" + inventory: "&aRequiere items en el inventario del jugador" + current-value: "&6Valor actual: [value]." + visibility: + hidden: Solo los desafíos desplegados son visibles. + visible: Todos los desafíos son visibles para todos + toggleable: Alternar si se deben mostrar desafíos no implementados + level-description: + reward-commands: "&6Comandos de recompensa:" + experience-reward: "&6Recompensa de EXP: [value]" + money-reward: "&6Recompensa de dinero: [value]$" + reward-items: "&6Items de recompensa:" + waver-amount: "&6Se pueden omitir [value] desafíos para desbloquear el siguiente + nivel." + completed: "&bCompletado" + completed-challenges-of: "&3Has completado [number] de [max] desafíos en este + nivel." + questions: + prefix: "&2[SERVER]: " + admin: + number: Escribie un número en el chat y presiona Entrar. + challenge-name: Escribe en el chat el nombre para el desafío actual. + level-name: Escribe en el chat el nombre para el nivel actual. + unique-id: Escriba el nombre único del objeto y presione Entrar. + title: + admin: + choose-challenge-title: "&aElegir desafío" + choose-level-title: "&aElegir nivel" + choose-user-title: "&aElegir jugador" + confirm-title: "&aConfirmación" + edit-level-title: "&aEditar nivel" + edit-text-fields: "&aEditar campos de texto" + manage-blocks: "&aAdministrar bloques" + manage-items: "&aAdministrar items" + manage-numbers: "&aTeclado numérico" + select-block: "&aSeleccionar bloque" + settings-title: "&aEditar configuracion" + toggle-environment: "&aModificar ambiente" + gui-title: "&aAdministrador de Desafíos" + edit-challenge-title: "&aEditar desafío" + manage-entities: "&aAdministrar entidades" + select-challenge: "&aSeleccionar desafío" + select-entity: "&aSelecionar entidad" + library-title: "&aBibliotecas descargables" + lore-add: "&aAgregar elemento Lore" + lore-remove: "&aEliminar Elemento Lore" + lore-edit: "&aEditar Lore" + type-select: "&aElegir el tipo de desafío" + challenges: "&6Desafíos" + game-modes: "&6Elegir modo de juego" + multiple-complete: "&6¿Cuántas veces?" + item-description: + item: "- [count] x [item]" + item-enchant: "- [enchant] [level]" + item-name: "[name]" + item-lore: 'Descripción del item:' + potion-type-upgraded: "[name] actualizado" + potion-type: "[name]" + skull-owner: "[owner]" + egg-meta: "[mob]" + book-meta: "[title] por [author]" + recipe-count: "[count] recetas" + potion-type-extended-upgraded: Extendido y actualizado [name] + potion-type-extended: "[name] extendido" + potion-effect: "[effect] x [amplifier] por [duration]t" + fish-meta: "[body-color] con [patter-color] [pattern]" + item-meta: "([meta])" + armor-color: "[color]" + custom-effects: 'Efectos personalizados:' + messages: + import-challenges: Empezar a importar desafios + import-levels: Empezar a importar niveles + load-add: 'Añadiendo nuevo objeto: [value]' + load-overwriting: Sobrescribiendo "[value]" + load-skipping: '"[value]" ya existe - saltando' + no-levels: 'Advertencia: No hay niveles definidos en challenges.yml' + admin: + you-added: Has añadido un/a [thing] al desafio + challenge-created: "[challenge]&r creado!" + completed: "&2¡Has completado el desafío [name] para [player]!" + already-completed: "&2¡Este desafío ya se completó!" + reset: "&2¡Has reiniciado el desafío [name] para [player]!" + reset-all: "&2¡Todos los desafíos de [player] se han reiniciado!" + not-completed: "&2¡Este desafío aún no se ha completado!" + migrate-start: "&2Iniciar la migración de los datos de addon de desafíos." + migrate-not: "&2Todos los datos son válidos." + start-downloading: "&5Comenzar a descargar e importar la Biblioteca de desafíos." + migrate-end: "&2Datos de addon de desafíos actualizados a nuevo formato." + hit-things: Haz clic en las cosas para agregarlas a la lista de cosas requeridas. + Haz clic derecho cuando hayas terminado. + complete-wipe: "¡Espero que tengas copias de seguridad, porque acabas de borrar + todas las bases de datos del addon de Desafíos!" + players-wipe: "¡Espero que tengas copias de seguridad, porque acabas de borrar + todos los desafíos completados por el jugador!" + challenge-wipe: "¡Espero que tengas copias de seguridad, porque acabas de borrar + todos los desafíos y sus niveles!" + you-completed-challenge: "&2¡Has completado el desafío [value]!" + you-repeated-challenge: "&2¡Has repetido el desafío [value]!" + you-repeated-challenge-multiple: "&2¡Has repetido el &r&2desafío [value] [count] + veces!" + you-completed-level: "&2¡Has completado el nivel [value]!" + name-has-completed-challenge: "&5¡[name] ha completado el desafío [value]!" + name-has-completed-level: "&5¡[name] ha completado el nivel [value]!" + import-number: Se han importado [number] desafíos + defaults-file-overwrite: defaults.json ya existe. Se sobrescribirá. + defaults-file-completed: "¡El archivo defaults.json está lleno de desafíos de + [world]!" + titles: + challenge-subtitle: "[friendlyName]" + level-subtitle: "[friendlyName]" + challenge-title: Completado con éxito + level-title: Completado con éxito +meta: + authors: + - BONNe protection: - flags: - CHALLENGES_ISLAND_PROTECTION: - description: "&5&oModifica quien puede\n&5&ocompletar desafios" - name: "Proteccion de desafios" - CHALLENGES_WORLD_PROTECTION: - description: "&5&oTEsto permite habilitar/deshabilitar\n&5&olos requisito para que los jugadores al\n&5&oestar en su isla puedan\n&5&ocompletar un reto." - name: "Limitaciones de la isla" - hint: "No hay retos fuera de la isla" -version: 9 \ No newline at end of file + flags: + CHALLENGES_ISLAND_PROTECTION: + name: Proteccion de desafios + description: |- + &5&oModifica quien puede + &5&ocompletar desafíos + CHALLENGES_WORLD_PROTECTION: + name: Limitaciones de la isla + hint: No hay desafíos fuera de la isla + description: "&5&oHabilitar/deshabilitar &5&orequerimiento para que los jugadores + &5&o esten en su isla para &5&ocompletar un desafío." +version: 11 diff --git a/src/main/resources/locales/ru.yml b/src/main/resources/locales/ru.yml new file mode 100644 index 0000000..a79e7e0 --- /dev/null +++ b/src/main/resources/locales/ru.yml @@ -0,0 +1,613 @@ +--- +challenges: + commands: + admin: + main: + description: Основная команда администратора. Открывает GUI. + import: + parameters: "[overwrite]" + description: |- + Импортировать челленджи из challenge.yml + Перезапись параметра означает, что челленджи или уровни с одинаковым ID будут перезаписаны. + reload: + parameters: "[hard]" + description: |- + Перезагрузить челленджей из базы данных + Параметр hard означает, что аддон сбросит соединение с базой данных. + defaults: + parameters: "[command]" + description: Показывает подкоманды для импорта/экспорта челленджей по умолчанию. + defaults-generate: + parameters: "[overwrite] - позволяет перезаписать существующий файл." + description: Экспортируйте существующие челленджи в файл default.json. + complete: + parameters: "<игрок> <челлендж_id>" + description: Завершите челлендж для игрока. + reset: + parameters: "<игрок> <челлендж_id>" + description: Сбросить челлендж для игрока. Если «challenge_id» установлен + на «all», он сбросит все челленджи. + migrate: + description: Миграция текущего игрового мира и данных о заданиях в формат + хранения 0.8.0. + show: + description: Отображает все челленджи в чате, которые существуют в этом мире. + defaults-import: + description: Импорт челленджей по умолчанию. + user: + complete: + parameters: "<челлендж_id> [count]" + description: Завершить челлендж. + main: + description: Открытый GUI челленджей. + gui: + title: + admin: + gui-title: "&aУправление челленджами и заданиями" + edit-challenge-title: "&aРедактировать задание" + edit-level-title: "&aРедактировать уровень" + settings-title: "&aИзменить настройки" + choose-challenge-title: "&aВыберите челлендж" + choose-level-title: "&aВыберите уровень" + choose-user-title: "&aВыберите игрока" + manage-blocks: "&aУправление блоками" + manage-entities: "&aУправление сущностями" + confirm-title: "&aПодтверждение" + manage-items: "&aУправление предметами" + manage-numbers: "&aЦифровая панель" + select-block: "&aВыберите блок" + select-challenge: "&aВыберите челлендж" + select-entity: "&aВыберите сущность" + toggle-environment: "&aПереключить окружение" + edit-text-fields: "&aРедактировать текстовые поля" + library-title: "&aЗагружаемые библиотеки" + lore-add: "&aДобавить элемент описания" + lore-remove: "&aУдалить элемент описания" + lore-edit: "&aРедактировать описание" + type-select: "&aВыберите тип челленджа" + challenges: "&6Челленджи" + game-modes: "&6Выберите игровой режим" + multiple-complete: "&6Сколько раз?" + buttons: + admin: + complete: Завершить челлендж игрока + reset: Сбросить челлендж игрока + create-challenge: Добавить новый челлендж + create-level: Добавить новый уровень + edit-challenge: Редактировать челлендж + edit-level: Редактировать уровень + delete-challenge: Удалить челлендж + delete-level: Удалить уровень + properties: Свойства + requirements: Требования + rewards: Награды + challenges: Челленджи + deployment: Отображение + icon: Иконка + locked-icon: Заблокированная иконка + description: Описание + order: Порядок + environment: Окружение + remove-on-complete: Удалить после завершения + required-experience: Требуемый опыт + remove-experience: Удалить опыт + required-level: Требуемый уровень острова + required-money: Требуемое кол-во денег + remove-money: Удалить деньги + reward-text: Сообщение награды + reward-items: Предметы в награду + reward-experience: Награда в виде опыта + reward-money: Денежная награда + reward-commands: Награда в виде команды + repeatable: Повторяемость + repeat-count: Макс. Раз + repeat-reward-text: Повторное сообщение награды + repeat-reward-items: Повторный предмет в награде + repeat-reward-experience: Повторный опыт в награде + repeat-reward-money: Повторная денежная награда + repeat-reward-commands: Повторная команда в награде + remove-completed: Удалить после завершения + glow: Свечение, когда закончено + free-at-top: Сначала свободные челленджи + line-length: Длина линии описания + add: Добавить + accept: Принять + decline: Отклонить + save: Сохранить + cancel: Отменить + input: Ввод + value: Значение + clear: Очистить + remove-empty: Удалить пустое + history-lifespan: История LifeSpan + input-mode: Переключить режим ввода + title-enable: Заглавие при завершении + library: Веб-библиотека + download: Скачать библиотеки + type: + island: "&6Тип острова" + inventory: "&6Тип инвентаря" + other: "&6Другой тип" + import: Импортировать челленджи из ASkyblock + settings: Изменить настройки + name: Дружественное имя + required-entities: Требуемые сущности + remove-entities: Убить сущностей + required-blocks: Требуемые блоки + remove-blocks: Удалить блоки + search-radius: Радиус поиска + required-permissions: Требуемые разрешения + required-items: Требуемые предметы + remove-items: Удалить предметы + waiver-amount: Сумма отказа + add-challenge: Добавить челлендж + remove-challenge: Удалить челлендж + reset-on-new: Сбросить на новом острове + broadcast: Оповещение о завершении + visibility-mode: Режим видимости челленджа + toggle-user-list: Список игроков + remove-selected: Удалить выбранные + show-eggs: Переключить режим просмотра + level-lore: Описание уровня + challenge-lore: Описание челленджа + gui-view-mode: Отображать игровые режимы + gui-mode: Единый GUI челленджей + history-store: История челленджей + island-store: Хранить на острове + default-locked-icon: Иконка заблокированного уровня + title-showtime: Время показа заглавия + default-import: Импорт челленджей по умолчанию + default-export: Экспорт челленджей по умолчанию + complete-wipe: Очистит базу данных аддона + challenge-wipe: Очистит базу данных челленджей + players-wipe: Очистит базу данных игроков + set: "=" + increase: "+" + reduce: "-" + multiply: "*" + number: "[number]" + next: Следующая + previous: Предыдущая + return: Назад + value: Завершить + increase: Увеличить + reduce: Уменьшить + descriptions: + admin: + input: Открыть текстовое поле ввода. + deployment: Позволяет игрокам завершить (просмотреть) челлендж. + icon-challenge: Иконка, который будет отображаться в GUI для этой задачи. + icon-level: Иконка, который будет отображаться в GUI для этого уровня. + remove-completed: Включает/Отключает скрытие челленджей, которые выполнены + и не могут быть повторены. + toggle-user-list: Переключиться на другой список игроков. + selected: Выбрано + show-eggs: Переключить вид сущности между режимом Egg или Head. + click-to-edit: "&4Нажмите здесь, чтобы редактировать ввод." + input-mode: Переключайтесь между режимами ввода чата и наковальни. + library-lang: "&aЯзык: [lang]" + library-gamemode: "&aОсновной для [gamemode]" + download-disabled: Загрузчик данных GitHub отключен в BentoBox. Без этого + вы не можете использовать библиотеки! + create-level: Добавить новый уровень. + edit-challenge: Изменить настройки челленджа. + edit-level: Изменить настройки уровня. + delete-challenge: Удалить челлендж. + delete-level: Удалить уровень. + settings: Изменить настройки. + properties: Изменить общие свойства + requirements: Управление требованиями + rewards: Управление наградами + description: Редактировать описание. + order: Изменить номер порядка. + environment: Измените окружение челленджа. + name-challenge: Изменить отображаемое название челленджа. + name-level: Изменить отображаемое название уровня. + remove-entities: Удалит (убьет) сущности по завершению челленджа. + remove-blocks: Удалит (заменит на воздух) блоки по завершению челленджа. + search-radius: Радиус вокруг местоположения игрока, где будут находиться нужные + сущности и блоки. + reward-text: Изменить сообщение, которое будет отправлено игроку после завершения + челленджа. + repeatable: Определите, можно ли будет перепройти челлендж или нет. + free-at-top: Поменяйте местами челленджи. Истина означает, что вызовы будут + первыми, иначе - они будут последними. + line-length: Измените максимальную длину строки в поле описания. Не повлияет + на хранимые объекты. + level-lore: Изменить, какие элементы описания уровня должны быть видны. + challenge-lore: Изменить, какие элементы описания челленджа должны быть видимыми. + gui-view-mode: Укажите, должен ли /challenge GUI отображать игровые режимы + или челленджи в мире игрока. + history-store: Включить/Отключить хранение истории челленджей. + default-import: Импорт челленджей по умолчанию. + default-export: Экспор существующих челленджей в файл defaults.json. + complete-wipe: Полностью очистить все проблемы базы данных аддона. Включает + в себя данные игрока! + challenge-wipe: Полная чистка базы данных челленджей и их уровней! + players-wipe: Полностью очищенная база данных игроков! + library: Откройте GUI, который показывает все доступные публичные библиотеки. + library-version: "&9Сделано в Челленджах [version]" + save: Сохраните и вернитесь к предыдущему GUI. + cancel: Вернитесь к предыдущему GUI. Изменения не будут сохранены. + set: Установить сумму. Нажатие на цифру установит значение на выбранный число. + increase: Увеличить сумму. Нажатие на цифру увеличит значение на выбранный + число. + reduce: Уменьшить сумму. Нажатие на цифру уменьшит значение на выбранный число. + multiply: Умножьте сумму. Нажатие на цифру умножит значение на выбранное число. + challenges: Управляйте уровнями челленджей (добавляйте/удаляйте). + locked-icon: Иконка, который будет отображаться на панелях GUI, если уровень + заблокирован. + remove-on-complete: Удалить челлендж из GUI игрока после его завершения. + remove-items: Удалить предметы из инвентаря игрока после завершения челленджа. + required-experience: Определите необходимый опыт для игрока, чтобы выполнить + челлендж. + remove-experience: Удалить необходимый опыт. + reward-experience: Изменить опыт-награду за первое завершение челленджа. + repeat-count: Определите максимальное количество повторений. Если значение + установлено на 0, ограничений нет. + repeat-reward-text: Изменить сообщение, которое будет отправлено игроку после + повторного завершения челленджа. + repeat-reward-experience: Изменить опыт-награду за повторное завершение челленджа. + waiver-amount: Установите количество челленджей, которые игрок сможет пропустить, + чтобы разблокировать следующий уровень. + reward-text-level: Измените сообщение, которое будет отправлено игроку после + завершения всех челленджей на уровне. + add-challenge: Добавить существующий челлендж на текущий уровень. + remove-challenge: Удалить челлендж с текущего уровня. + reset-on-new: Включает/Отключает сброс всех челленджей для игрока, если он + перезайдет, выйдет или будет выгнан с острова. + broadcast: Включает/Отключает оповещение на первое прохождения челленджа для + всех онлайн игроков. + glow: Включает/Отключает эффект свечения для выполненных челленджей. + mode-online: Игроки, которые сейчас онлайн. + mode-in-world: Игроки в GameMode мире. + mode-with-island: Игроки, у которых есть остров в GameMode мире. + visibility-mode: Показать/Cкрыть неразрешенные челленджи. + edit-text-line: "&6Редактировать текстовое сообщение!" + add-text-line: "&6Добавить новое текстовое сообщение!" + title-enable: Включите/Отключите заглавное сообщение, которое будет показано + игрокам, когда они выполнят челлендж. + title-showtime: Измените, как долго заглавные сообщения будут видны игроку. + import: |- + Импортировать вызовы из ASkyblock. + При щелчке правой кнопкой он включает/отключает режим перезаписи. + Поместите challenge.yml в папку ./BentoBox/addons/Challenges. + complete: |- + Выполняйте челленджи для любого пользователя. + Пользователь не получит вознаграждение за завершение. + reset: |- + Сброс завершенных пользовательских челленджей. + Правый клик включает/отключает сброс всех функций. + create-challenge: |- + Добавить новый челлендж. + По умолчанию будет в списке свободных челленджей. + required-entities: |- + Добавить/редактировать/удалить требуемые сущности. + Сущности: + required-blocks: |- + Добавить/редактировать/удалить требуемые блоки. + Блоки: + required-permissions: |- + Требуемые разрешения для игрока, чтобы иметь возможность выполнить этот челлендж. + Разрешение: + required-items: 'Необходимые предметы в инвентаре игрока. Предметы:' + required-level: |- + Укажите необходимый уровень острова для этого челленджа. + &cТребуется аддон Level.' + required-money: |- + Укажите необходимое кол-во денег на счету игрока. + &cТребуется Vault и любой плагин на экономику.' + remove-money: |- + Снять необходимое кол-во денег со счета игрока. + &cТребуется Vault и любой плагин на экономику.' + reward-items: 'Изменить награду в виде предмета за первое завершение челленджа. + Предметы:' + reward-money: |- + Изменить денежное вознаграждение за первое выполнение челленджа. + &cТребуется Vault и любой плагин на экономику. + reward-commands: |- + Установите вознаграждения в виде команд, которые будут выполняться после первого завершения. + ***Добавление "[SELF]" в начале, означает, что команда будет запущена игроком, например, "/kill" + ***Строка "[player]" будет заменено на имя игрока, например "/kill [player]" будет выполняться как "/kill BONNe1704" + Команды: + repeat-reward-items: |- + Изменить предметы в награде за повторное выполнение челленджа. + Предметы: + repeat-reward-money: |- + Изменить денежное вознаграждение за повторное выполнение челленджа. + &cТребуется Vault и любой плагин на экономику. + repeat-reward-commands: |- + Установите команды в виде награды, которые будут выполняться после повторного выполнения челленджа. + ***Добавление "[SELF]" в начале, означает, что команда будет запущена игроком, например, "/kill" + ***Строка "[player]" будет заменено на имя игрока, например "/kill [player]" будет выполняться как "/kill BONNe1704" + Команды: + remove-selected: |- + Удалить выбранные элементы. + Выделите элементы правой кнопкой мыши. + history-lifespan: |- + Изменить, сколько дней, данные истории должны храниться. + Значение 0 - означает навсегда. + island-store: |- + Включить/Отключить хранение данных челленджей на каждом острове. Это означает, что челленджи будут одинаковыми для всей команды, если это включено. + &cНЕ будет конвертировать данные по клику. Прогресс будет потерян.' + default-locked-icon: |- + Изменить иконку заблокированного уровня по умолчанию. + Эта опция может быть перезаписана каждым уровнем. + gui-mode: |- + Включить/Отключить одиночный вызов GUI. + &2Потребуется перезапустить сервер.' + download: |- + Обновить вручную доступные библиотеки челленджей. + Щелкните правой кнопкой мыши, чтобы включить очистку кэша.' + 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 + and 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' + or 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 and challenge.repeatRewardItems. + reward_commands: |- + Награда в виде команды. + Список команд которые будут выполнены в виде награды, определены в challenge.rewardCommands and 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 + library-author: автор &e[автор] + enabled: Включено + disabled: Отключено + the-end: "- Край" + nether: "- Ад" + normal: "- Верхний мир" + entity: "- [entity] : [count]" + permission: "- [permission]" + level-unlocked: Нажмите, чтобы увидеть [level] челленджей! + level-locked: Заверши больше [count] заданий, уровня [level], чтобы открыть + этот уровень! + increase-by: "&aУвеличьте кол-во завершений на [value]" + reduce-by: "&aУменьшите кол-во завершений на [value]" + visibility: + hidden: Видны только отображаемые челленджи. + visible: Все челленджи видны всем + toggleable: Переключить, если неиспользуемые челленджи должны отображаться + type: + island: "&aтребовать блоков или мобов вокруг игрока" + other: "&aтребовать предметы от других плагинов/аддонов" + inventory: "&aтребовать предметы в инвентаре игрока" + current-value: "&6Текущее значение: [value]." + block: "- [block] : [count]" + item: "- [count] x [item]" + item-meta: "([meta])" + item-enchant: "- [enchant] [level]" + command: "- [command]" + challenge-description: + completed-times-of: Завершено [donetimes] из [maxtimes] + maxed-reached: Завершено [donetimes] из [maxtimes] + completed-times: Завершено [donetimes] + objects-close-by: "&cВсе необходимые блоки и сущности должны быть рядом с вами, + на вашем острове!" + warning-entities-kill: "&cВсе необходимые сущности будут убиты, когда вы завершите + этот челлендж!" + warning-blocks-remove: "&cВсе необходимые блоки будут удалены, когда вы выполните + этот челлендж!" + not-repeatable: "&cЭтот челлендж нельзя повторить!" + experience-reward: "&6Опыта в награде: [value]" + money-reward: "&6Денежная награда: $[value]" + required-experience: "&6Требуется опыта: [value]" + required-money: "&6Требуется денег: $[value]" + required-island-level: "&6Требуемый уровень острова: [value]" + environment: 'Обязательные условия:' + reward-items: "&6Пердметы в награде:" + reward-commands: "&6Команды в награде:" + required-items: 'Требуемые предметы:' + required-entities: 'Требуемые сущности:' + required-blocks: 'Требуемые блоки:' + level: "&fУровень: [level]" + completed: "&bЗавершено" + warning-items-take: "&cВсе необходимые предметы будут изъяты из вашего инвентаря, + когда вы завершите этот челлендж!" + level-description: + experience-reward: "&6Опыта в награде: [value]" + money-reward: "&6Денежная награда: $[value]" + reward-items: "&6Предметы в награде:" + reward-commands: "&6Команды в награде:" + waver-amount: "&6[value] челленджи могут быть пропущены, чтобы разблокировать + следующий уровень." + completed: "&bЗавершено" + completed-challenges-of: "&3Вы завершили [number] из [max] челленджей на этом + уровне." + item-description: + item: "- [count] x [item]" + potion-type-extended-upgraded: Расширен и обновлен [name] + potion-type-upgraded: Улучшен [name] + potion-type-extended: Расширен [name] + custom-effects: 'Индивидуальные эффекты:' + potion-effect: "[effect] x [amplifier] для [duration]t" + fish-meta: "[body-color] с [pattern-color] [pattern]" + item-meta: "([meta])" + item-enchant: "- [enchant] [level]" + item-name: "[name]" + armor-color: "[color]" + potion-type: "[name]" + skull-owner: "[owner]" + egg-meta: "[mob]" + item-lore: 'уздечка:' + book-meta: "[title] [author]" + recipe-count: "[count] рецептов" + questions: + prefix: "&2[СЕРВЕР]:" + admin: + unique-id: Введите уникальное имя объекта и нажмите клавишу ENTER. + number: Напишите номер в чате и нажмите ENTER. + challenge-name: Напишите отображаемое имя в чате для текущего челленджа. + level-name: Напишите отображаемое имя в чате для текущего уровня. + titles: + challenge-title: Успешно завершено + challenge-subtitle: "[friendlyName]" + level-title: Успешно завершено + level-subtitle: "[friendlyName]" + messages: + admin: + you-added: Вы добавили один [thing] в челлендж + challenge-created: "[challenge]&r создан!" + completed: "&2Вы завершили челлендж [name] для игрока [player]!" + already-completed: "&2Этот челлендж уже был выполнен!" + reset: "&2Вы сбрасываете челлендж [name] для игрока [player]!" + reset-all: "&2Все [player] челленджи были сброшены!" + not-completed: "&2Этот челлендж еще не завершен!" + migrate-start: "&2Начать миграцию данных аддона для челленджей." + migrate-not: "&2Все данные действительны." + start-downloading: "&5Начинаем скачивать и импортировать Challenges Library." + migrate-end: "&2Данные аддона челленджей обновлены до нового формата." + hit-things: Нажмите на предметы, чтобы добавить их в список необходимых предметов. + Щелкните правой кнопкой мыши, когда закончите. + complete-wipe: "&cНадеюсь, у вас есть резервные копии, потому что вы просто + стерли все данные из базы аддона Challenges!" + challenge-wipe: "&cНадеюсь, у вас есть резервные копии, потому что вы просто + стерли все челленджи и их уровни!" + players-wipe: "&cНадеюсь, у вас есть резервные копии, потому что вы просто стерли + все выполненные игроками челленджи!" + you-completed-challenge: "&2Вы завершили [value] &r&2челлендж!" + you-repeated-challenge: "&2Ты перепрошел [value] &r&2челлендж!" + you-repeated-challenge-multiple: "&2Вы перепрошли [value] &r&2челлендж [count] + раз!" + you-completed-level: "&2Вы завершили [value] &r&2уровень!" + name-has-completed-challenge: "&5[name] завершил [value] &r&5челлендж!" + name-has-completed-level: "&5[name] завершил [value] &r&5уровень!" + import-levels: Начало импорта уровней + import-challenges: Начало импорта челленджей + no-levels: 'Предупреждение: Уровни не определены в challenge.yml' + import-number: Импортировано [number] челленджей + load-skipping: '"[value]" уже существует - пропуск' + load-overwriting: Перезапись "[value]" + load-add: 'Добавление нового объекта: [value]' + defaults-file-overwrite: defaults.json существует. Это будет перезаписано. + defaults-file-completed: defaults.json файл заполнен челленджами из [world]! + errors: + no-name: "&cОтсутствует имя челленджа" + unknown-challenge: "&cНеизвестный челлендж" + unique-id: '&cУникальный идентификатор "[id]" недействителен.' + wrong-icon: '&cДанный материал "[value]" недействителен и не может использоваться + в качестве иконки.' + not-deployed: "&cЧеллендж не отображается!" + not-on-island: "&cВы должны быть на своем острове, чтобы сделать это!" + not-repeatable: "&cЭтот челлендж нельзя пройти повторно!" + not-enough-items: "&cУ тебя недостаточно [items] чтобы завершить этот челлендж!" + not-close-enough: "&cВы должны стоять в пределах [number] блоков от всех необходимых + предметов." + you-still-need: "&cВам еще нужно [amount] x [item]" + not-enough-money: "&cНеобходимо иметь [value] на вашем аккаунте, чтобы завершить + челлендж." + import-no-file: "&cНе могу найти challenges.yml файл для импорта!" + no-load: "&cОшибка: Не смог загрузить challenges.yml. [message]" + load-error: "&cОшибка: Не удается загрузить [value]." + defaults-file-exist: "&cdefaults.json уже существует. Используйте режим перезаписи, + чтобы заменить его!" + defaults-file-error: "&cПроизошла ошибка при создании файла defaults.json! Проверьте + консоль!" + missing-arguments: "&cВ команде отсутствуют аргументы." + wrong-environment: "&cВы находитесь в неправильном окружении!" + missing-addon: "&cНевозможно выполнить задание: отсутствует обязательный аддон + или плагин." + exist-challenges-or-levels: "&cЧелленджи уже существуют в вашем мире. Не могу + продолжить!" + no-challenges: "&cЧелленджи еще не реализованы в этом мире!" + no-challenges-admin: "&cЧелленджи еще не реализованы в этом мире! Используйте + \ &5/[command] &cчтобы добавить их!" + missing-level: "&cУровень челленджа [level] не определен в базе данных. Это может + привести к ошибкам!" + no-multiple-permission: "&cУ вас нет разрешения на выполнение этого задания несколько + раз одновременно." + not-a-integer: '&cДанное значение "[value]" не является целым числом!' + challenge-level-not-available: "&cВы не разблокировали необходимый уровень для + выполнения этого челленджа." + incorrect: "&cНевозможно выполнить челлендж: требования неверны." + not-enough-experience: "&cНеобходимо иметь [value] опыта, чтобы завершить этот + челлендж." + island-level: "&cВаш остров должен быть [number] уровня или больше, чтобы завершить + этот челлендж!" + no-rank: "&cУ вас нет достаточно высокого ранга, чтобы сделать это." + cannot-remove-items: "&cНекоторые предметы не могут быть удалены из вашего инвентаря!" + not-valid-integer: |- + &cЗаданное целое число "[value]" не является действительным! + Значение должно быть между [min] и [max]. +protection: + flags: + CHALLENGES_ISLAND_PROTECTION: + description: |- + &5&oПереключить, кто может + &5&oзавершить челлендж + name: Защита челленджа + CHALLENGES_WORLD_PROTECTION: + name: Ограничения острова челленджей + hint: Нет челленджей за пределами острова + description: |- + &5&oВключить/Выключить + &5&oтребование к игрокам + &5&oбыть на их острове, чтобы + &5&oвыполнить челлендж. +version: 11 +meta: + authors: + - BONNe diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index 84efc71..690e0e9 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -1,405 +1,471 @@ -########################################################################################### -# 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 # -########################################################################################### - -### Credits ### -meta: - authors: [] - +--- challenges: - commands: - admin: - main: - parameters: '' - description: '打开管理员菜单' - import: - description: '从challenges.yml文件中导入挑战以及挑战等级' - parameters: '[overwrite]' - reload: - description: '重载挑战组件' - parameters: '[hard]' - show: - description: '在聊天中列出当前世界的所有挑战' - parameters: '' - defaults: - description: '显示导入/导出插件自带挑战的子指令' - parameters: '[command]' - defaults-import: - description: '导入插件自带挑战' - parameters: '' - defaults-generate: - description: '将现有的挑战导出到default.json文件中。' - parameters: '[overwrite] - allows to overwrite existing file.' - complete: - description: '通过指令让某个玩家完成指定挑战' - parameters: ' ' - user: - main: - description: '打开空岛挑战菜单' - parameters: '' - complete: - description: '通过指令完成挑战' - parameters: ' [count]' - gui: - title: - admin: - gui-title: '&a岛屿挑战管理' - edit-challenge-title: '&a编辑挑战' - edit-level-title: '&a编辑挑战等级' - settings-title: '&a编辑设定' - choose-challenge-title: '&a选择挑战' - choose-level-title: '&a选择挑战等级' - choose-user-title: '&a选择玩家' - manage-blocks: '&a管理方块' - manage-entities: '&a管理实体' - confirm-title: '&a确认' - manage-items: '&a管理物品' - manage-numbers: '&a数字垫' - select-block: '&a选择方块' - select-challenge: '&a选择挑战' - select-entity: '&a选择实体' - toggle-environment: '&a切换环境' - edit-text-fields: '&a编辑文本字段' - challenges: '&a岛屿挑战' - game-modes: '&a选择游戏模式' - buttons: - admin: - complete: '完成某个玩家的挑战' - reset: '重置某个玩家的挑战' - create-challenge: '创建新的挑战' - create-level: '创建新的挑战等级' - edit-challenge: '编辑挑战' - edit-level: '编辑挑战等级' - delete-challenge: '删除挑战' - delete-level: '删除挑战等级' - import: '导入ASkyBlock插件的挑战内容' - settings: '编辑设定' - properties: '属性' - requirements: '要求' - rewards: '奖励' - challenges: '挑战' - type: '挑战类型' - deployment: '是否开启该挑战' - icon: '图标' - locked-icon: '图标锁' - description: '介绍' - order: '顺序' - environment: '环境' - remove-on-complete: '完成后删除该挑战' - name: '挑战名称' - required-entities: '检测的实体' - remove-entities: '完成任务后是否杀死实体' - required-blocks: '检测的方块' - remove-blocks: '完成任务后是否删除方块' - search-radius: '搜索半径' - required-permissions: '需要的权限' - required-items: '需要的物品' - remove-items: '完成任务后是否删除物品' - required-experience: '需要的经验值' - remove-experience: '完成任务后是否删除经验值' - required-level: '需要的岛屿等级' - required-money: '需要的金钱' - remove-money: '完成任务后是否删除金钱' - reward-text: '奖励信息' - reward-items: '物品奖励' - reward-experience: '经验奖励' - reward-money: '金钱奖励' - reward-commands: '指令奖励' - repeatable: '是否可重复挑战' - repeat-count: '最大时间' - repeat-reward-text: '重复奖励信息' - repeat-reward-items: '重复物品奖励' - repeat-reward-experience: '重复经验奖励' - repeat-reward-money: '重复金钱奖励' - repeat-reward-commands: '重复指令奖励' - waiver-amount: '豁免金额' - add-challenge: '添加挑战' - remove-challenge: '删除挑战' - reset-on-new: '在新的岛屿上重置' - broadcast: '成功任务后发出广播' - remove-completed: '完成任务后删除图标' - glow: '完成任务后发光' - free-at-top: '免费挑战优先排列' - line-length: 'Lore长度' - toggle-user-list: '用户列表' - remove-selected: '删除选定' - add: '添加' - show-eggs: '切换视图模式' - accept: '接受' - decline: '拒绝' - save: '保存' - cancel: '取消' - input: '输入' - value: '值' - set: '=' - increase: '+' - reduce: '-' - multiply: '*' - clear: '清空' - remove-empty: '删除空栏' - number: '[number]' - level-lore: '等级介绍' - challenge-lore: '挑战介绍' - gui-view-mode: '显示所有GameModes' - gui-mode: '挑战GUI' - history-store: '挑战历史' - history-lifespan: '历史LifeSpan' - island-store: '每个岛屿的商店' - default-locked-icon: '等级图标锁' - input-mode: '切换输入模式' - title-enable: '显示标题' - title-showtime: '标题显示时间' - default-import: '导入默认挑战' - default-export: '导出现有挑战' - complete-wipe: '抹除插件数据库!' - next: '下一页' - previous: '上一页' - return: '返回上一级' - descriptions: - admin: - save: '保存并返回上一级菜单' - cancel: '取消任何操作并返回上一级菜单' - input: '打开文本字段输入' - set: '设置操作,击数字会将值更改为所选数字。' - increase: '增加操作。 单击数字将增加所选数字的值。' - reduce: '减少操作。 单击数字将减少所选数字的值。' - multiply: '乘法运算。 单击数字会将值乘以所选数字。' - import: '导入ASkyblock挑战。|右键单击它启用/禁用覆盖模式。|将Challenges.yml放在./BentoBox/addons/Challenges文件夹中。' - complete: '为某个玩家完成某个挑战|玩家无法获得完成奖励。' - reset: '重置已完成的玩家挑战。|右键单击启用/禁用重置所有功能。' - create-challenge: '添加新挑战。|默认情况下,它将在免费挑战列表中。' - create-level: '添加新的挑战等级。' - edit-challenge: '编辑某个挑战设定' - edit-level: '编辑某个挑战等级设定' - delete-challenge: '删除某个挑战' - delete-level: '删除某个挑战等级' - settings: '修改组件设定' - properties: '修改常规属性' - requirements: '修改挑战需求' - rewards: '修改挑战奖励' - challenges: '管理挑战级别 (添加/删除).' - deployment: '查看某玩家已完成的挑战' - icon-challenge: '将在此挑战的GUI面板中显示的图标。' - icon-level: '将在此级别的GUI面板中显示的图标。' - locked-icon: '如果级别被锁定,将在GUI面板中显示的图标。' - description: '编辑介绍' - order: '更改挑战顺序' - environment: '改变挑战运作的环境。' - type: '更改挑战类型(不同类型的挑战侦测方式不同)' - remove-on-complete: '在玩家完成挑战后是否删除该挑战图标于任务面板中' - name-challenge: '修改挑战名称' - name-level: '修改挑战等级名称' - required-entities: '修改挑战需要的实体.|实体:|' - remove-entities: '是否在挑战结束后删除挑战需要的实体' - required-blocks: '修改挑战需要的方块.|方块:|' - remove-blocks: '是否在挑战结束后删除任务需求方块(替换为空气)' - search-radius: '玩家完成挑战时检测实体/方块的范围(半径)' - required-permissions: '玩家需要具有以下权限才能完成挑战|权限:' - required-items: '玩家背包中需要有以下物品才能完成挑战|物品:' - remove-items: '是否在完成挑战后删除玩家背包中的挑战需求物品' - required-experience: '玩家需要有该项目所设置的经验值才可以完成挑战' - remove-experience: '是否在玩家完成挑战后删除任务需求的经验值' - required-level: '玩家需要岛屿等级达到该项目所设置的等级才能完成挑战.|&c需要 Level 组件.' - required-money: '玩家需要有该项目所设置的金钱才能完成挑战.|&c需要经济前置.' - remove-money: '是否在玩家完成挑战后删除任务需求的金钱.|&c需要经济前置.' - reward-text: '设置完成任务后的奖励信息' - reward-items: '设置完成任务后的物品奖励.|物品:' - reward-experience: '设置完成任务后的经验奖励.' - reward-money: '设置完成任务后的金钱奖励.|&c需要经济前置.' - reward-commands: '设置完成任务后的指令奖励.|指令:' - repeatable: '挑战是否可以重复' - repeat-count: '重复挑战的次数,如果设置为0则为无限制' - repeat-reward-text: '重复挑战完成后的信息' - repeat-reward-items: '重复挑战完成后的物品奖励.|物品:' - repeat-reward-experience: '重复挑战完成后的经验奖励.' - repeat-reward-money: '重复挑战完成后的金钱奖励.|&c需要经济前置.' - repeat-reward-commands: '重复挑战完成后的指令奖励.||指令:' - waiver-amount: '完成该数量的挑战玩家方能解锁下一挑战级别的挑战' - reward-text-level: '完成某挑战级别所有挑战后发送给玩家的信息' - add-challenge: '将现有挑战添加到当前挑战级别' - remove-challenge: '从当前级别删除挑战' - reset-on-new: '允许/禁止,当玩家重置/踢出岛屿后都会重置挑战' - broadcast: '允许/禁止,当玩家完成第一次挑战后向全服玩家广播' - remove-completed: '允许/禁止,在玩家挑战列表中隐藏已完成的挑战' - glow: '允许/禁用,在已完成的挑战中加上附魔效果' - free-at-top: '改变免费挑战的位置,如果为true免费挑战会放在前排,如果为false免费挑战将放在后排' - line-length: '修改每条lore的最大长度' - toggle-user-list: '切换到不同的玩家列表' - mode-online: '目前在线的玩家' - mode-in-world: '属于游戏模式中的世界的玩家.' - mode-with-island: '属于游戏模式中的岛屿的玩家.' - selected: '已选中' - remove-selected: '删除所选目标|您可以使用鼠标右键选择目标' - show-eggs: '在Egg模式或Head模式之间切换实体视图' - level-lore: '修改挑战级别介绍的哪些目标是可见的' - challenge-lore: '修改挑战介绍的哪些目标是可见的' - gui-view-mode: '如果玩家输入/challenges,菜单应该显示GameModes还是挑战' - history-store: '允许启用/禁用质询历史存储' - history-lifespan: '允许修改将保存历史数据的天数| 0表示永久' - island-store: '启用/禁用挑战每个岛的数据存储。如果启用此选项,整个岛屿团队的挑战进度将是相同的。|不会在点击时转换数据。进展将会失败。' - default-locked-icon: '更改默认锁定级别图标。|此级别可以覆盖此选项。' - gui-mode: '启用/禁用单一挑战GUI。|&2要求服务器重启。' - click-to-edit: '&4点击此处编辑输入.' - edit-text-line: '&6编辑文本!' - add-text-line: '&6 添加新的文本!' - input-mode: '在聊天和铁砧输入模式之间切换。' - title-enable: '启用/禁用玩家完成挑战时显示的标题消息。' - title-showtime: '修改标题消息对玩家可见的时间。' - default-import: '导入插件自带挑战' - default-export: '将现有的挑战导出至 defaults.json 文件中.' - complete-wipe: '清空插件数据库中的所有挑战,包括玩家的挑战数据!' - current-value: '|&6当前值: [value].' - enabled: '有效' - disabled: '禁用' - type: - island: '- 岛屿类型:| (主要通过检测玩家身边的方块和生物)' - inventory: '- 库存类型:| (主要通过检测玩家的背包物品)' - other: '- 其他类型:| (主要通过检测服务器的其他插件/组件)' - the-end: '- 末地' - nether: '- 地狱' - normal: '- 主世界' - entity: '- [entity] : [count]' - block: '- [block] : [count]' - permission: '- [permission]' - item: '- [count] x [item]' - item-meta: ' ([meta])' - item-enchant: ' - [enchant] [level]' - command: '- [command]' - level-unlocked: '点击查看 [level] 级别的挑战!' - level-locked: '请完成 [count] 个 [level] 级别的挑战来解锁这个挑战级别!' - challenge-description: - level: '&F挑战级别: [level]' - completed: '&B已完成' - completed-times-of: '完成次数: [donetimes] 上限: [maxtimes]' - maxed-reached: '完成次数: [donetimes] 上限: [maxtimes]' - completed-times: '已完成 [donetimes]' - warning-items-take: '&c完成此挑战后,该挑战的需要物品将会被清空' - objects-close-by: '&c任务需求的方块/生物不能离你太远!(超出侦测范围)' - warning-entities-kill: '&c完成此挑战后,该挑战需要的生物将会被清空' - warning-blocks-remove: '&c完成此挑战后,该挑战需要的方块将会被清空' - not-repeatable: '&c该挑战不可重复!' - experience-reward: '&6经验奖励: [value]' - money-reward: '&6金钱奖励: $[value]' - required-experience: '&6所需经验: [value]' - required-money: '&6所需金钱: $[value]' - required-island-level: '&6所需岛屿等级: [value]' - environment: '所需实体:' - reward-items: '&6物品奖励:' - reward-commands: '&6指令奖励:' - required-items: '所需物品:' - required-entities: '所需实体:' - required-blocks: '所需方块:' - level-description: - completed: '&B已完成' - completed-challenges-of: '&你已经完成 [number] 个该级别的挑战,达到了 [max]个挑战.' - waver-amount: '&6可以跳过[value] 个挑战来解锁下一个挑战级别.' - experience-reward: '&6经验奖励: [value]' - money-reward: '&6金钱奖励: $[value]' - reward-items: '&6物品奖励:' - reward-commands: '&6指令奖励:' - item-description: - item: '- [count] x [item]' - item-meta: ' ([meta])' - item-enchant: ' - [enchant] [level]' - item-name: ' [name]' - item-lore: ' 物品Lore:' - book-meta: ' [title] by [author]' - recipe-count: ' [count] recipes' - armor-color: ' [color]' - potion-type-extended-upgraded: ' [name]' - potion-type-upgraded: ' Upgraded [name]' - potion-type-extended: ' Extended [name]' - potion-type: ' [name]' - 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 chat and press enter to accept it and press enter." - unique-id: "Write object unique name and press enter." - challenge-name: "Write in chat display name for current challenge." - level-name: "Write in chat display name for current level." - - titles: -# Title and subtitle my contain variable in [] that will be replaced with proper message from challenge object. -# [friendlyName] will be replaced with challenge friendly name. -# [level] will be replaced with level friendly name. -# [rewardText] will be replaced with challenge reward text. - challenge-title: '完成挑战' - challenge-subtitle: '[friendlyName]' -# Title and subtitle my contain variable in [] that will be replaced with proper message from level object. -# [friendlyName] will be replaced with level friendly name. -# [rewardText] will be replaced with level reward text. - level-title: '成功挑战级别' - level-subtitle: '[friendlyName]' - messages: - admin: - hit-things: '点击物品将它们添加到所需的事物列表中。 完成后右键单击。' - you-added: '你在挑战中添加了一个[thing]' - challenge-created: '[challenge]&r created!' - complete-wipe: '&c希望你有备份,因为你已经创建了所有Challenges Addon数据库!' - completed: '&2已为[player]完成挑战[name]!' - already-completed: '&2这个挑战已经完成' - you-completed-challenge: '&2你已经完成了 [value] &r&2挑战!' - you-repeated-challenge: '&2你已经重复完成了 [value] &r&2挑战!' - you-repeated-challenge-multiple: '&2你重复完成了 [value] &r&2挑战 [count] 次!' - you-completed-level: '&2你完成了 [value] &r&2级别!' - name-has-completed-challenge: '&5[name] 已完成 [value] &r&5挑战!' - name-has-completed-level: '&5[name] 已完成 [value] &r&5挑战级别!' - import-levels: '开始导入挑战级别' - import-challenges: '开始导入挑战' - no-levels: '警告: challenges.yml文件中没有定义挑战级别' - import-number: '导入 [number] 个挑战' - load-skipping: '"[value]" 已存在 - 跳过' - load-overwriting: '覆盖 "[value]"' - load-add: '添加新的对象: [value]' - defaults-file-overwrite: 'defaults.json存在。 它将被覆盖。' - defaults-file-completed: 'defaults.json文件填充了来自[world]的挑战!' - errors: - no-name: '&c缺少挑战名称' - unknown-challenge: '&c未知挑战' - unique-id: '&cUniqueID "[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你没有足够的[items]来完成这个挑战!' - not-close-enough: '&c你必须站在拥有[number]个任务需求方块的旁边.' - you-still-need: '&c你还需要 [amount] x [item]' - missing-addon: '&c无法完成挑战。 缺少必需的插件或组件。' - incorrect: '&c无法完成挑战。 要求不正确。' - not-enough-money: '&c您的帐户必须有[value]金钱才能完成挑战。.' - not-enough-experience: '&c必须有[value]EXP才能完成挑战' - island-level: '&c你的岛屿等级需要达到[number]才能完成挑战' - import-no-file: '&c找不到challenge.yml文件导入!' - no-load: '&c错误:无法加载challenge.yml。 [message]' - load-error: '&c错误:无法加载 [value].' - no-rank: "&c你没有等级可以做到这一点." - cannot-remove-items: '&c有些物品无法从你的背包中删除!' - exist-challenges-or-levels: '&c在你的世界里已经存在挑战。 无法继续!' - defaults-file-exist: '&cdefaults.json已经存在。 使用覆盖模式替换它!' - defaults-file-error: '&c创建defaults.json文件时出错! 检查控制台!' - no-challenges: '&c当前世界没有实施挑战!' - no-challenges-admin: '&c当前世界没有实施挑战!你应该使用 &5/[label] challenges &c添加他们!' - missing-level: '&c挑战级别[level]未在数据库中定义. 可能会出现错误!' - missing-arguments: '&c命令缺少参数.' + 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: - description: "&5&o切换谁可以\n&5&o挖槽挑战" - name: "挑战保护" - CHALLENGES_WORLD_PROTECTION: - description: "&5&o为玩家启用/禁用\n&5&o要求他们在他们的岛屿上\n&5&o才能完成挑战." - name: "挑战岛屿限制" - hint: "请在自己的岛屿完成挑战!" + flags: + CHALLENGES_ISLAND_PROTECTION: + name: 挑战保护 + description: |- + &5&o切换谁可以 + &5&o完成挑战 + CHALLENGES_WORLD_PROTECTION: + description: |- + &5&o为玩家启用/禁用 + &5&o要求他们在他们的岛屿上 + &5&o才能完成挑战. + hint: 请在自己的岛屿完成挑战! + name: 挑战岛屿限制 version: 11 +meta: + authors: + - BONNe diff --git a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java index b0d8992..937324f 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java @@ -1,117 +1,421 @@ -/** - * - */ package world.bentobox.challenges; -import static org.mockito.Matchers.any; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; -import java.util.HashMap; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.List; -import java.util.Map; +import java.util.Optional; import java.util.UUID; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; import java.util.logging.Logger; import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.enchantments.Enchantment; +import org.bukkit.UnsafeValues; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFactory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.PluginManager; -import org.bukkit.potion.PotionData; -import org.bukkit.potion.PotionType; -import org.junit.BeforeClass; +import org.bukkit.scheduler.BukkitScheduler; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.After; +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; import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.Challenge.ChallengeType; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.addons.Addon.State; +import world.bentobox.bentobox.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.configuration.Config; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.AddonsManager; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.FlagsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; /** * @author tastybento * */ +@SuppressWarnings("deprecation") @RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, User.class, Config.class }) public class ChallengesAddonTest { + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private Island island; + + private ChallengesAddon addon; + @Mock + private BentoBox plugin; + @Mock + private FlagsManager fm; + @Mock + private Settings settings; + @Mock + private GameModeAddon gameMode; + @Mock + private AddonsManager am; + @Mock + private BukkitScheduler scheduler; + @Mock + private PlaceholdersManager phm; + /** * @throws java.lang.Exception */ - @BeforeClass - public static void setUpBeforeClass() throws Exception { - Server server = mock(Server.class); - World world = mock(World.class); - world = mock(World.class); - Mockito.when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); - Mockito.when(server.getWorld("world")).thenReturn(world); - Mockito.when(server.getVersion()).thenReturn("BSB_Mocking"); + @Before + public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger()); + //when(plugin.isEnabled()).thenReturn(true); + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); - PluginManager pluginManager = mock(PluginManager.class); - when(server.getPluginManager()).thenReturn(pluginManager); - - ItemFactory itemFactory = mock(ItemFactory.class); - when(server.getItemFactory()).thenReturn(itemFactory); - - Bukkit.setServer(server); - - PotionMeta potionMeta = mock(PotionMeta.class); - when(itemFactory.getItemMeta(any())).thenReturn(potionMeta); - - OfflinePlayer offlinePlayer = mock(OfflinePlayer.class); - when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(offlinePlayer); - when(offlinePlayer.getName()).thenReturn("tastybento"); + // Placeholders manager + when(plugin.getPlaceholdersManager()).thenReturn(phm); - when(Bukkit.getItemFactory()).thenReturn(itemFactory); + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + when(user.isOp()).thenReturn(false); + UUID uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + island = mock(Island.class); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + + // Locales + // Return the reference (USE THIS IN THE FUTURE) + when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + + // Server + PowerMockito.mockStatic(Bukkit.class); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class)); + + // Addon + addon = new ChallengesAddon(); + 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")); + 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); + } + } + } + File dataFolder = new File("addons/Challenges"); + addon.setDataFolder(dataFolder); + addon.setFile(jFile); + AddonDescription desc = new AddonDescription.Builder("bentobox", "challenges", "1.3").description("test").authors("BONNe").build(); + addon.setDescription(desc); + // Addons manager + when(plugin.getAddonsManager()).thenReturn(am); + // One game mode + when(am.getGameModeAddons()).thenReturn(Collections.singletonList(gameMode)); + AddonDescription desc2 = new AddonDescription.Builder("bentobox", "BSkyBlock", "1.3").description("test").authors("tasty").build(); + when(gameMode.getDescription()).thenReturn(desc2); + + // Player command + CompositeCommand cmd = mock(CompositeCommand.class); + @NonNull + Optional opCmd = Optional.of(cmd); + when(gameMode.getPlayerCommand()).thenReturn(opCmd); + // Admin command + when(gameMode.getAdminCommand()).thenReturn(opCmd); + + // Flags manager + when(plugin.getFlagsManager()).thenReturn(fm); + when(fm.getFlags()).thenReturn(Collections.emptyList()); + + // The database type has to be created one line before the thenReturn() to work! + when(plugin.getSettings()).thenReturn(settings); + DatabaseType value = DatabaseType.JSON; + when(settings.getDatabaseType()).thenReturn(value); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(scheduler); + ItemMeta meta = mock(ItemMeta.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(itemFactory.getItemMeta(any())).thenReturn(meta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + UnsafeValues unsafe = mock(UnsafeValues.class); + when(unsafe.getDataVersion()).thenReturn(777); + when(Bukkit.getUnsafe()).thenReturn(unsafe); + } + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + new File("addon.jar").delete(); + new File("config.yml").delete(); + deleteAll(new File("addons")); + deleteAll(new File("database")); + + } + + private void deleteAll(File file) throws IOException { + if (file.exists()) { + Files.walk(file.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onLoad()}. + */ @Test - public void test() { - - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - Challenge challenges = new Challenge(); - challenges.setChallengeType(ChallengeType.ISLAND); - Map map = new HashMap<>(); - map.put(Material.DIRT, 5); - map.put(Material.ACACIA_FENCE_GATE, 3); - challenges.setRequiredBlocks(map); - challenges.setIcon(new ItemStack(Material.ACACIA_FENCE_GATE)); - List requiredItems = new ArrayList<>(); - ItemStack result = new ItemStack(Material.POTION, 55); - ItemStack result2 = new ItemStack(Material.SPLASH_POTION, 22); - ItemStack result3 = new ItemStack(Material.LINGERING_POTION, 11); + public void testOnLoad() { + addon.onLoad(); + // Check that config.yml file has been saved + File check = new File("addons/Challenges","config.yml"); + assertTrue(check.exists()); + } - PotionMeta potionMeta = (PotionMeta) result.getItemMeta(); - PotionData potionData = new PotionData(PotionType.FIRE_RESISTANCE, true, false); - potionMeta.setBasePotionData(potionData); - result.setItemMeta(potionMeta); - - PotionMeta potionMeta2 = (PotionMeta) result2.getItemMeta(); - PotionData potionData2 = new PotionData(PotionType.SPEED, true, false); - potionMeta2.setBasePotionData(potionData2); - potionMeta2.addEnchant(Enchantment.BINDING_CURSE, 1, true); - result2.setItemMeta(potionMeta2); - - requiredItems.add(result); - requiredItems.add(result2); - requiredItems.add(result3); - challenges.setRequiredItems(requiredItems); - String json = gson.toJson(challenges); + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableDisabledPlugin() { + when(plugin.isEnabled()).thenReturn(false); + addon.onEnable(); + verify(plugin).logError("[challenges] BentoBox is not available or disabled!"); + assertEquals(Addon.State.DISABLED, addon.getState()); + } - Logger.getAnonymousLogger().info(json); - + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableDisabledAddon() { + when(plugin.isEnabled()).thenReturn(true); + addon.setState(State.DISABLED); + addon.onEnable(); + verify(plugin).logError("[challenges] Challenges Addon is not available or disabled!"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableIncompatibleDatabase() { + // The database type has to be created one line before the thenReturn() to work! + DatabaseType value = DatabaseType.YAML; + when(settings.getDatabaseType()).thenReturn(value); + when(plugin.isEnabled()).thenReturn(true); + addon.setState(State.LOADED); + addon.onEnable(); + verify(plugin).logError("[challenges] BentoBox database is not compatible with Challenges Addon."); + verify(plugin).logError("[challenges] Please use JSON based database type."); + assertEquals(State.INCOMPATIBLE, addon.getState()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableHooked() { + addon.onLoad(); + 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] Economy plugin not found so money options 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!"); + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableNotHooked() { + addon.onLoad(); + when(am.getGameModeAddons()).thenReturn(Collections.emptyList()); + when(plugin.isEnabled()).thenReturn(true); + addon.setState(State.LOADED); + addon.onEnable(); + verify(plugin).log("[challenges] Loading challenges..."); + verify(plugin).logError("[challenges] Challenges could not hook into AcidIsland or BSkyBlock so will not do anything!"); + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onReload()}. + */ + @Test + public void testOnReloadNotHooked() { + addon.onReload(); + verify(plugin, never()).log(anyString()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onDisable()}. + */ + @Test + public void testOnDisable() { + this.testOnEnableHooked(); + addon.onDisable(); + + // Verify database saved exists + File chDir = new File("database", "Challenge"); + assertTrue(chDir.exists()); + File lvDir = new File("database", "ChallengeLevel"); + assertTrue(lvDir.exists()); + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getChallengesManager()}. + */ + @Test + public void testGetChallengesManager() { + assertNull(addon.getChallengesManager()); + this.testOnEnableHooked(); + assertNotNull(addon.getChallengesManager()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getPermissionPrefix()}. + */ + @Test + public void testGetPermissionPrefix() { + assertEquals("addon.", addon.getPermissionPrefix()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getImportManager()}. + */ + @Test + public void testGetImportManager() { + assertNull(addon.getImportManager()); + this.testOnEnableHooked(); + assertNotNull(addon.getImportManager()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getWebManager()}. + */ + @Test + public void testGetWebManager() { + assertNull(addon.getWebManager()); + this.testOnEnableHooked(); + assertNotNull(addon.getWebManager()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getChallengesSettings()}. + */ + @Test + public void testGetChallengesSettings() { + assertNull(addon.getChallengesSettings()); + addon.onLoad(); + assertNotNull(addon.getChallengesSettings()); + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#isEconomyProvided()}. + */ + @Test + public void testIsEconomyProvided() { + assertFalse(addon.isEconomyProvided()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getEconomyProvider()}. + */ + @Test + public void testGetEconomyProvider() { + assertNull(addon.getEconomyProvider()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#isLevelProvided()}. + */ + @Test + public void testIsLevelProvided() { + assertFalse(addon.isLevelProvided()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getLevelAddon()}. + */ + @Test + public void testGetLevelAddon() { + assertNull(addon.getLevelAddon()); } } diff --git a/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java new file mode 100644 index 0000000..bbe3b8e --- /dev/null +++ b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java @@ -0,0 +1,868 @@ +package world.bentobox.challenges; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +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; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.World; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.PluginManager; +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; +import org.mockito.Mock; +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.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.config.Settings; +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.events.ChallengeCompletedEvent; +import world.bentobox.challenges.events.ChallengeResetAllEvent; +import world.bentobox.challenges.events.ChallengeResetEvent; +import world.bentobox.challenges.events.LevelCompletedEvent; +import world.bentobox.challenges.utils.LevelStatus; + +/** + * @author tastybento + * + */ +@SuppressWarnings("deprecation") +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, Util.class}) +public class ChallengesManagerTest { + + // Constants + private static final String GAME_MODE_NAME = "BSkyBlock"; + + // Mocks + @Mock + private ChallengesAddon addon; + @Mock + private Settings settings; + @Mock + private IslandWorldManager iwm; + @Mock + private Server server; + @Mock + private PluginManager pim; + @Mock + private ItemFactory itemFactory; + @Mock + private User user; + @Mock + private World world; + @Mock + private GameModeAddon gameModeAddon; + @Mock + private PlaceholdersManager plhm; + + // Variable fields + private ChallengesManager cm; + private File database; + private String uuid; + private Challenge challenge; + private @NonNull ChallengeLevel level; + private UUID playerID = UUID.randomUUID(); + private String cName; + private String levelName; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(addon.getPlugin()).thenReturn(plugin); + + // IWM + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.inWorld(any(World.class))).thenReturn(true); + + // Placeholders + when(plugin.getPlaceholdersManager()).thenReturn(plhm); + + // Settings for Database + world.bentobox.bentobox.Settings s = mock(world.bentobox.bentobox.Settings.class); + when(plugin.getSettings()).thenReturn(s); + when(s.getDatabaseType()).thenReturn(DatabaseType.JSON); + + // Addon Settings + when(addon.getChallengesSettings()).thenReturn(settings); + when(settings.isStoreHistory()).thenReturn(true); + when(settings.getLifeSpan()).thenReturn(10); + + // Database + database = new File("database"); + tearDown(); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + when(Bukkit.getPluginManager()).thenReturn(pim); + when(Bukkit.getWorld(anyString())).thenReturn(world); + + ItemMeta meta = mock(ItemMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(meta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + UnsafeValues unsafe = mock(UnsafeValues.class); + when(unsafe.getDataVersion()).thenReturn(777); + when(Bukkit.getUnsafe()).thenReturn(unsafe); + + // Challenge + challenge = new Challenge(); + uuid = UUID.randomUUID().toString(); + challenge.setUniqueId(GAME_MODE_NAME + "_" + uuid); + challenge.setFriendlyName("name"); + challenge.setLevel(GAME_MODE_NAME + "_novice"); + challenge.setDescription(Collections.singletonList("A description")); + + // Challenge Level + level = new ChallengeLevel(); + levelName = GAME_MODE_NAME + "_novice"; + level.setUniqueId(levelName); + level.setFriendlyName("Novice"); + + // User + when(user.getUniqueId()).thenReturn(playerID); + + // Util + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(any())).thenReturn(world); + + // Addon + AddonDescription desc = new AddonDescription.Builder("main", GAME_MODE_NAME, "1.0").build(); + when(gameModeAddon.getDescription()).thenReturn(desc); + Optional opAddon = Optional.of(gameModeAddon); + when(iwm.getAddon(any())).thenReturn(opAddon); + + // Challenge name + cName = GAME_MODE_NAME + "_" + uuid; + + // Class under test + cm = new ChallengesManager(addon); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + // Clean up JSON database + // Clean up file system + if (database.exists()) { + Files.walk(database.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#load()}. + * @throws InterruptedException + */ + @Test + public void testLoad() throws InterruptedException { + verify(addon).log("Loading challenges..."); + verify(addon, never()).logError(anyString()); + this.testSaveLevel(); + this.testSaveChallenge(); + cm.load(); + verify(addon, times(2)).log("Loading challenges..."); + verify(addon, never()).logError(anyString()); + assertTrue(cm.containsChallenge(cName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#reload()}. + * @throws InterruptedException + */ + @Test + public void testReload() throws InterruptedException { + cm.reload(); + verify(addon).log("Reloading challenges..."); + this.testSaveLevel(); + this.testSaveChallenge(); + cm.reload(); + verify(addon, times(2)).log("Reloading challenges..."); + verify(addon, never()).logError(anyString()); + assertTrue(cm.containsChallenge(cName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadChallengeNoOverwriteSilent() { + // load once + assertTrue(cm.loadChallenge(challenge, false, user, true)); + // load twice - no overwrite + assertFalse(cm.loadChallenge(challenge, false, user, true)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadChallengeNoOverwriteNotSilent() { + // load once + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadChallengeOverwriteSilent() { + // load once + assertTrue(cm.loadChallenge(challenge, false, user, true)); + // overwrite + assertTrue(cm.loadChallenge(challenge, true, user, true)); + verify(user, never()).sendMessage(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 + public void testLoadChallengeOverwriteNotSilent() { + // load once + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadLevelNoOverwriteSilent() { + // load once + assertTrue(cm.loadLevel(level, false, user, true)); + // load twice - no overwrite + assertFalse(cm.loadLevel(level, false, user, true)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadLevelNoOverwriteNotSilent() { + // load once + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadLevelOverwriteSilent() { + // load once + assertTrue(cm.loadLevel(level, false, user, true)); + // overwrite + assertTrue(cm.loadLevel(level, true, user, true)); + verify(user, never()).sendMessage(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 + public void testLoadLevelOverwriteNotSilent() { + // load once + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#removeFromCache(java.util.UUID)}. + */ + @Test + public void testRemoveFromCache() { + cm.removeFromCache(playerID); + verify(settings).isStoreAsIslandData(); + // TODO there should be a test where isStoreAsIslandData returns true + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#wipeDatabase(boolean)}. + * @throws InterruptedException + */ + @Test + public void testWipeDatabase() throws InterruptedException { + // Create some database + this.testLoad(); + + // Verify file exists + File chDir = new File(database, "Challenge"); + File check = new File(chDir, cName + ".json"); + assertTrue(check.exists()); + + File lvDir = new File(database, "ChallengeLevel"); + File checkLv = new File(lvDir, levelName + ".json"); + assertTrue(checkLv.exists()); + + cm.setChallengeComplete(user, world, challenge, 20); + //cm.save(); + File plData = new File(database, "ChallengesPlayerData"); + File checkPd = new File(plData, playerID.toString() + ".json"); + assertTrue(checkPd.exists()); + + // Wipe it + cm.wipeDatabase(false); + + // Verify + assertFalse(check.exists()); + assertFalse(checkLv.exists()); + assertTrue(checkPd.exists()); + + cm.wipeDatabase(true); + assertFalse(checkPd.exists()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#wipePlayers()}. + * @throws InterruptedException + */ + @Test + public void testWipePlayers() throws InterruptedException { + this.testLoad(); + cm.setChallengeComplete(user, world, challenge, 20); + cm.save(); + File plData = new File(database, "ChallengesPlayerData"); + File checkLv = new File(plData, playerID.toString() + ".json"); + assertTrue(checkLv.exists()); + cm.wipePlayers(); + assertFalse(checkLv.exists()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#migrateDatabase(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + */ + @Test + public void testMigrateDatabase() { + cm.migrateDatabase(user, world); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#save()}. + */ + @Test + public void testSave() { + cm.save(); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#saveChallenge(world.bentobox.challenges.database.object.Challenge)}. + * @throws InterruptedException + */ + @Test + public void testSaveChallenge() throws InterruptedException { + // Async - may not happen quickly + cm.saveChallenge(challenge); + Thread.sleep(500); + File chDir = new File(database, "Challenge"); + assertTrue(chDir.exists()); + File check = new File(chDir, cName + ".json"); + assertTrue(check.exists()); + // Remove icon becauseit has mockito meta in it + removeLine(check); + } + + private boolean removeLine(File inputFile) { + File tempFile = new File("myTempFile.json"); + + try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) { + + String lineToRemove = "\"icon"; + String currentLine; + + while((currentLine = reader.readLine()) != null) { + // trim newline when comparing with lineToRemove + String trimmedLine = currentLine.trim(); + if(trimmedLine.startsWith(lineToRemove)) continue; + writer.write(currentLine + System.getProperty("line.separator")); + } + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return tempFile.renameTo(inputFile); + } + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#saveLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testSaveLevel() throws InterruptedException { + cm.saveLevel(level); + Thread.sleep(500); + File chDir = new File(database, "ChallengeLevel"); + assertTrue(chDir.exists()); + File check = new File(chDir, GAME_MODE_NAME + "_novice.json"); + assertTrue(check.exists()); + // Remove icon becauseit has mockito meta in it + removeLine(check); + } + + /** + * 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 + public void testIsChallengeCompleteUserWorldChallenge() { + assertFalse(cm.isChallengeComplete(user, world, challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + */ + @Test + public void testIsChallengeCompleteUUIDWorldChallenge() { + assertFalse(cm.isChallengeComplete(playerID, world, challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, java.lang.String)}. + */ + @Test + public void testIsChallengeCompleteUUIDWorldString() { + assertFalse(cm.isChallengeComplete(playerID, world, "Novice")); + } + + /** + * 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 + public void testSetChallengeCompleteUserWorldChallengeInt() { + cm.setChallengeComplete(user, world, challenge, 3); + assertTrue(cm.isChallengeComplete(user, world, challenge)); + verify(pim).callEvent(any(ChallengeCompletedEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. + */ + @Test + public void testSetChallengeCompleteUUIDWorldChallengeInt() { + cm.setChallengeComplete(playerID, world, challenge, 3); + assertTrue(cm.isChallengeComplete(playerID, world, challenge)); + verify(pim).callEvent(any(ChallengeCompletedEvent.class)); + } + + /** + * 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 + public void testSetChallengeCompleteUUIDWorldChallengeUUID() { + UUID adminID = UUID.randomUUID(); + cm.setChallengeComplete(playerID, world, challenge, adminID); + assertTrue(cm.isChallengeComplete(playerID, world, challenge)); + verify(pim).callEvent(any(ChallengeCompletedEvent.class)); + } + + /** + * 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 + public void testResetChallenge() { + cm.setChallengeComplete(user, world, challenge, 3); + assertTrue(cm.isChallengeComplete(user, world, challenge)); + cm.resetChallenge(playerID, world, challenge, playerID); + assertFalse(cm.isChallengeComplete(user, world, challenge)); + verify(pim).callEvent(any(ChallengeResetEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#resetAllChallenges(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + */ + @Test + public void testResetAllChallengesUserWorld() { + cm.setChallengeComplete(user, world, challenge, 3); + assertTrue(cm.isChallengeComplete(user, world, challenge)); + cm.resetAllChallenges(user, world); + assertFalse(cm.isChallengeComplete(user, world, challenge)); + verify(pim).callEvent(any(ChallengeResetAllEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#resetAllChallenges(java.util.UUID, org.bukkit.World, java.util.UUID)}. + */ + @Test + public void testResetAllChallengesUUIDWorldUUID() { + cm.setChallengeComplete(user, world, challenge, 3); + assertTrue(cm.isChallengeComplete(user, world, challenge)); + cm.resetAllChallenges(playerID, world, playerID); + assertFalse(cm.isChallengeComplete(user, world, challenge)); + verify(pim).callEvent(any(ChallengeResetAllEvent.class)); + } + + /** + * 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 + public void testGetChallengeTimesUserWorldChallenge() { + assertEquals(0L, cm.getChallengeTimes(user, world, challenge)); + cm.setChallengeComplete(user, world, challenge, 6); + assertEquals(6L, cm.getChallengeTimes(user, world, challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, java.lang.String)}. + */ + @Test + public void testGetChallengeTimesUserWorldString() { + assertEquals(0L, cm.getChallengeTimes(user, world, cName)); + cm.setChallengeComplete(user, world, challenge, 6); + assertEquals(6L, cm.getChallengeTimes(user, world, cName)); + } + + /** + * 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 + public void testIsLevelCompleted() { + assertFalse(cm.isLevelCompleted(user, world, level)); + } + + /** + * 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 + public void testIsLevelUnlocked() { + assertFalse(cm.isLevelUnlocked(user, world, level)); + this.testLoadLevelNoOverwriteSilent(); + assertTrue(cm.isLevelUnlocked(user, world, level)); + } + + /** + * 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 + public void testSetLevelComplete() { + assertFalse(cm.isLevelCompleted(user, world, level)); + cm.setLevelComplete(user, world, level); + assertTrue(cm.isLevelCompleted(user, world, level)); + verify(pim).callEvent(any(LevelCompletedEvent.class)); + } + + /** + * 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 + public void testValidateLevelCompletion() { + assertTrue(cm.validateLevelCompletion(user, world, level)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeLevelStatus(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + */ + @Test + public void testGetChallengeLevelStatus() { + this.testLoadLevelNoOverwriteSilent(); + LevelStatus cls = cm.getChallengeLevelStatus(playerID, world, level); + assertTrue(cls.getNumberOfChallengesStillToDo() == 0); + assertEquals(level, cls.getLevel()); + assertTrue(cls.isComplete()); + assertTrue(cls.isUnlocked()); + assertEquals("BSkyBlock_novice", cls.getLevel().getUniqueId()); + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallengeLevelStatus(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + */ + @Test + public void testGetAllChallengeLevelStatus() { + this.testLoadLevelNoOverwriteSilent(); + List list = cm.getAllChallengeLevelStatus(user, world); + assertTrue(list.size() == 1); + LevelStatus cls = list.get(0); + assertTrue(cls.getNumberOfChallengesStillToDo() == 0); + assertEquals(level, cls.getLevel()); + assertTrue(cls.isComplete()); + assertTrue(cls.isUnlocked()); + assertEquals("BSkyBlock_novice", cls.getLevel().getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallengesNames(org.bukkit.World)}. + */ + @Test + public void testGetAllChallengesNames() { + assertTrue(cm.getAllChallengesNames(world).isEmpty()); + cm.saveChallenge(challenge); + cm.loadChallenge(challenge, false, user, true); + List list = cm.getAllChallengesNames(world); + assertFalse(list.isEmpty()); + assertEquals(cName, list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallenges(org.bukkit.World)}. + */ + @Test + public void testGetAllChallenges() { + assertTrue(cm.getAllChallenges(world).isEmpty()); + cm.saveChallenge(challenge); + cm.loadChallenge(challenge, false, user, true); + List list = cm.getAllChallenges(world); + assertFalse(list.isEmpty()); + assertEquals(challenge, list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getFreeChallenges(org.bukkit.World)}. + */ + @Test + public void testGetFreeChallenges() { + // Empty + assertTrue(cm.getFreeChallenges(world).isEmpty()); + // One normal + cm.saveChallenge(challenge); + cm.loadChallenge(challenge, false, user, true); + assertTrue(cm.getFreeChallenges(world).isEmpty()); + // One free + challenge.setLevel(""); + cm.saveChallenge(challenge); + cm.loadChallenge(challenge, false, user, true); + List list = cm.getFreeChallenges(world); + assertFalse(list.isEmpty()); + assertEquals(challenge, list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevelChallenges(world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testGetLevelChallenges() throws InterruptedException { + assertTrue(cm.getLevelChallenges(level).isEmpty()); + // make some challenges + this.testSaveLevel(); + this.testSaveChallenge(); + level.setChallenges(Collections.singleton(challenge.getUniqueId())); + // Test again + List list = cm.getLevelChallenges(level); + assertFalse(list.isEmpty()); + assertEquals(challenge, list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallenge(java.lang.String)}. + * @throws InterruptedException + */ + @Test + public void testGetChallenge() throws InterruptedException { + assertNull(cm.getChallenge(cName)); + this.testSaveLevel(); + this.testSaveChallenge(); + Challenge ch = cm.getChallenge(cName); + assertNotNull(ch); + assertEquals(cName, ch.getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#containsChallenge(java.lang.String)}. + */ + @Test + public void testContainsChallenge() { + assertFalse(cm.containsChallenge("no-such-challenge")); + } + + /** + * 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 + public void testCreateChallenge() { + @Nullable + Challenge ch = cm.createChallenge("newChal", ChallengeType.ISLAND, null); + assertEquals(ChallengeType.ISLAND, ch.getChallengeType()); + assertEquals("newChal", ch.getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#deleteChallenge(world.bentobox.challenges.database.object.Challenge)}. + * @throws InterruptedException + */ + @Test + public void testDeleteChallenge() throws InterruptedException { + this.testSaveLevel(); + this.testSaveChallenge(); + Challenge ch = cm.getChallenge(cName); + assertNotNull(ch); + assertEquals(cName, ch.getUniqueId()); + cm.deleteChallenge(challenge); + ch = cm.getChallenge(cName); + assertNull(ch); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevels(org.bukkit.World)}. + */ + @Test + public void testGetLevels() { + this.testGetLevelString(); + List lvs = cm.getLevels(world); + assertFalse(lvs.isEmpty()); + assertEquals(level, lvs.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevel(world.bentobox.challenges.database.object.Challenge)}. + */ + @Test + public void testGetLevelChallenge() { + this.testGetLevelString(); + assertEquals(level, cm.getLevel(challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevel(java.lang.String)}. + */ + @Test + public void testGetLevelString() { + assertNull(cm.getLevel("dss")); + cm.saveLevel(level); + cm.loadLevel(level, false, user, true); + assertEquals(level, cm.getLevel(levelName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#containsLevel(java.lang.String)}. + */ + @Test + public void testContainsLevel() { + this.testGetLevelString(); + assertFalse(cm.containsLevel("sdsd")); + assertTrue(cm.containsLevel(levelName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#addChallengeToLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testAddChallengeToLevel() throws InterruptedException { + this.testLoad(); + cm.deleteChallenge(challenge); + assertFalse(cm.containsChallenge(cName)); + cm.addChallengeToLevel(challenge, level); + assertEquals(level, cm.getLevel(challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#removeChallengeFromLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testRemoveChallengeFromLevel() throws InterruptedException { + this.testAddChallengeToLevel(); + cm.removeChallengeFromLevel(challenge, level); + assertFalse(cm.containsChallenge(cName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#createLevel(java.lang.String, org.bukkit.World)}. + */ + @Test + public void testCreateLevel() { + @Nullable + ChallengeLevel cl = cm.createLevel("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)}. + * @throws InterruptedException + */ + @Test + public void testDeleteChallengeLevel() throws InterruptedException { + this.testAddChallengeToLevel(); + assertTrue(cm.containsLevel(levelName)); + cm.deleteChallengeLevel(level); + assertFalse(cm.containsLevel(levelName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#hasAnyChallengeData(org.bukkit.World)}. + * @throws InterruptedException + */ + @Test + public void testHasAnyChallengeDataWorld() throws InterruptedException { + assertFalse(cm.hasAnyChallengeData(world)); + this.testLoad(); + assertTrue(cm.hasAnyChallengeData(world)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#hasAnyChallengeData(java.lang.String)}. + * @throws InterruptedException + */ + @Test + public void testHasAnyChallengeDataString() throws InterruptedException { + assertFalse(cm.hasAnyChallengeData("BSkyBlock")); + this.testLoad(); + assertTrue(cm.hasAnyChallengeData("BSkyBlock")); + } + +} diff --git a/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java b/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java new file mode 100644 index 0000000..657a495 --- /dev/null +++ b/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java @@ -0,0 +1,268 @@ +package world.bentobox.challenges.commands; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +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.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +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.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +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.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +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.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class}) +public class ChallengesCommandTest { + + @Mock + private CompositeCommand ic; + private UUID uuid; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private ChallengesAddon addon; + private ChallengesCommand cc; + @Mock + private World world; + @Mock + private ChallengesManager chm; + @Mock + private IslandWorldManager iwm; + @Mock + private GameModeAddon gameModeAddon; + @Mock + private Settings settings; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + User.setPlugin(plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + // Addon + when(ic.getAddon()).thenReturn(addon); + when(ic.getPermissionPrefix()).thenReturn("bskyblock."); + when(ic.getLabel()).thenReturn("island"); + when(ic.getTopLabel()).thenReturn("island"); + when(ic.getWorld()).thenReturn(world); + when(ic.getTopLabel()).thenReturn("bsb"); + + // IWM friendly name + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + when(iwm.inWorld(any(World.class))).thenReturn(true); + Optional optionalAddon = Optional.of(gameModeAddon); + when(iwm.getAddon(any())).thenReturn(optionalAddon); + when(plugin.getIWM()).thenReturn(iwm); + + // Game Mode Addon + @NonNull + Optional optionalAdmin = Optional.of(ic); + when(gameModeAddon.getAdminCommand()).thenReturn(optionalAdmin); + + // World + when(world.toString()).thenReturn("world"); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + 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); + + // Mock item factory (for itemstacks) + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + ItemMeta itemMeta = mock(ItemMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(itemMeta); + + // Addon + when(addon.getChallengesManager()).thenReturn(chm); + when(chm.getAllChallengeLevelStatus(any(), any())).thenReturn(Collections.emptyList()); + // Challenges exist + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(true); + + // 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); + + // Island + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(any(), any(User.class))).thenReturn(island); + + // Command under test + cc = new ChallengesCommand(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 + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoChallenges() { + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoChallengesOp() { + when(user.isOp()).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-admin", "[command]", "bsb challenges"); + verify(user, never()).sendMessage("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 + public void testCanExecuteNoChallengesHasPerm() { + when(user.hasPermission(anyString())).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-admin", "[command]", "bsb challenges"); + verify(user, never()).sendMessage("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 + public void testCanExecuteNoAdminCommand() { + when(gameModeAddon.getAdminCommand()).thenReturn(Optional.empty()); + when(user.isOp()).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-admin", "[command]", "bsb challenges"); + verify(user, never()).sendMessage("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 + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#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()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringConsole() { + User console = mock(User.class); + assertFalse(cc.execute(console, "challenges", Collections.emptyList())); + verify(console).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringUser() { + assertTrue(cc.execute(user, "challenges", Collections.emptyList())); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("bskyblock." + ChallengesCommand.CHALLENGE_COMMAND, cc.getPermission()); + assertEquals("challenges.commands.user.main.parameters", cc.getParameters()); + assertEquals("challenges.commands.user.main.description", cc.getDescription()); + assertTrue(cc.isOnlyPlayer()); + // CompleteChallengeCommand + assertEquals(1, cc.getSubCommands(true).size()); + } + +} diff --git a/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java b/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java new file mode 100644 index 0000000..d35a363 --- /dev/null +++ b/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java @@ -0,0 +1,342 @@ +package world.bentobox.challenges.commands; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +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.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +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; +import org.mockito.Mock; +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.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +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.config.Settings; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.tasks.TryToComplete; +import world.bentobox.challenges.utils.Utils; + +/** + * @author tastybento + * + */ +@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 + private IslandsManager im; + @Mock + private Island island; + @Mock + private ChallengesAddon addon; + + private CompleteChallengeCommand cc; + @Mock + private World world; + @Mock + private ChallengesManager chm; + @Mock + 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 { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + User.setPlugin(plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + // Addon + when(ic.getAddon()).thenReturn(addon); + when(ic.getPermissionPrefix()).thenReturn("bskyblock."); + when(ic.getLabel()).thenReturn("island"); + when(ic.getTopLabel()).thenReturn("island"); + when(ic.getWorld()).thenReturn(world); + when(ic.getTopLabel()).thenReturn("bsb"); + + // IWM friendly name + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + when(iwm.inWorld(any(World.class))).thenReturn(true); + Optional optionalAddon = Optional.of(gameModeAddon); + when(iwm.getAddon(any())).thenReturn(optionalAddon); + when(plugin.getIWM()).thenReturn(iwm); + + // Game Mode Addon + @NonNull + Optional optionalAdmin = Optional.of(ic); + when(gameModeAddon.getAdminCommand()).thenReturn(optionalAdmin); + + // World + when(world.toString()).thenReturn("world"); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + 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); + + // Mock item factory (for itemstacks) + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + ItemMeta itemMeta = mock(ItemMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(itemMeta); + + // Addon & Challenge Manager + when(addon.getChallengesManager()).thenReturn(chm); + when(chm.getAllChallengeLevelStatus(any(), any())).thenReturn(Collections.emptyList()); + // Challenges exist + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(true); + // Challenges + 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 + when(addon.getChallengesSettings()).thenReturn(settings); + when(settings.getVisibilityMode()).thenReturn(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); + // 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)}. + */ + @Test + public void testCompleteChallengeCommand() { + assertEquals("complete", cc.getLabel()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("bskyblock.complete", cc.getPermission()); + assertEquals("challenges.commands.user.complete.parameters", cc.getParameters()); + assertEquals("challenges.commands.user.complete.description", cc.getDescription()); + assertTrue(cc.isOnlyPlayer()); + // No sub commands + assertEquals(0, cc.getSubCommands(true).size()); + } + + /** + * 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 testExecuteUserStringListOfStringNoArgs() { + assertFalse(cc.execute(user, "complete", Collections.emptyList())); + verify(user).sendMessage(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)}. + */ + @Test + 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).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)}. + */ + @Test + public void testExecuteUserStringListOfStringKnownChallengeFail() { + when(TryToComplete.complete(any(), any(), any(), any(), anyString(), anyString(), anyInt())).thenReturn(false); + 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)}. + */ + @Test + public void testExecuteUserStringListOfStringKnownChallengeSuccess() { + 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")); + } + + /** + * 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 testExecuteUserStringListOfStringKnownChallengeSuccessMultipleTimesHasPerm() { + when(user.hasPermission(anyString())).thenReturn(true); + 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)}. + */ + @Test + 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)}. + */ + @Test + public void testTabCompleteUserStringListOfStringOneArg() { + List list = cc.tabComplete(user, "complete", Collections.singletonList("arg")).get(); + 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)}. + */ + @Test + public void testTabCompleteUserStringListOfStringTwoArgs() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2")).get(); + 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)}. + */ + @Test + public void testTabCompleteUserStringListOfStringThreeArgs() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg3")).get(); + assertFalse(list.isEmpty()); + assertEquals("maker", list.get(0)); + 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)}. + */ + @Test + public void testTabCompleteUserStringListOfStringFourArgs() { + 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)}. + */ + @Test + public void testTabCompleteUserStringListOfStringFourArgsNumber() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg3", "4")).get(); + 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)}. + */ + @Test + public void testTabCompleteUserStringListOfStringFiveArgs() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg23", "arg4", "arg5")).get(); + assertFalse(list.isEmpty()); + assertEquals("help", list.get(0)); + } + +} diff --git a/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java b/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java new file mode 100644 index 0000000..dcf0de5 --- /dev/null +++ b/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java @@ -0,0 +1,392 @@ +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.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()}. + */ + @Test + 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 083b699..9f72b31 100644 --- a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java +++ b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java @@ -1,231 +1,649 @@ package world.bentobox.challenges.tasks; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.logging.Logger; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemStack; 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; +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.challenges.ChallengesAddon; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.AddonsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +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.config.Settings; +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.tasks.TryToComplete.ChallengeResult; +import world.bentobox.challenges.utils.Utils; + /** * @author tastybento + * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class}) +@PrepareForTest({Bukkit.class, BentoBox.class, Util.class, Utils.class}) public class TryToCompleteTest { - private User user; - ItemStack[] stacks = { new ItemStack(Material.PAPER, 32), - new ItemStack(Material.ACACIA_BOAT), - null, - null, - new ItemStack(Material.CACTUS, 32), - new ItemStack(Material.CACTUS, 32), - new ItemStack(Material.CACTUS, 32), - new ItemStack(Material.BRICK_STAIRS, 64), - new ItemStack(Material.BRICK_STAIRS, 64), - new ItemStack(Material.BRICK_STAIRS, 5), - new ItemStack(Material.GOLD_BLOCK, 32) - }; - List required; - private ChallengesAddon addon; - private PlayerInventory inv; + // Constants + private static final String GAME_MODE_NAME = "BSkyBlock"; + private static final String[] NAMES = {"adam", "ben", "cara", "dave", "ed", "frank", "freddy", "george", "harry", "ian", "joe"}; - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - Server server = mock(Server.class); - PowerMockito.mockStatic(Bukkit.class); - when(Bukkit.getServer()).thenReturn(server); - when(Bukkit.getBukkitVersion()).thenReturn("1.13.2"); + 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."; - user = mock(User.class); - inv = mock(PlayerInventory.class); - when(inv.getContents()).thenReturn(stacks); - when(user.getInventory()).thenReturn(inv); - addon = mock(ChallengesAddon.class); - required = new ArrayList<>(); + private String levelName; + @Mock + private ChallengesManager cm; + @Mock + private BentoBox plugin; + @Mock + private GameModeAddon gameMode; + @Mock + private AddonsManager am; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private Player player; + @Mock + private Settings settings; + @Mock + private WorldSettings mySettings; + private Map map; + @Mock + private @Nullable PlayerInventory inv; + private ItemStack[] contents = {}; + @Mock + private BoundingBox bb; - ItemFactory itemFactory = mock(ItemFactory.class); - when(server.getItemFactory()).thenReturn(itemFactory); + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(addon.getPlugin()).thenReturn(plugin); + // World + when(user.getWorld()).thenReturn(world); + when(world.getName()).thenReturn("world"); + when(world.getEnvironment()).thenReturn(Environment.NORMAL); - // Test will not work with items that has meta data. - when(itemFactory.getItemMeta(any())).thenReturn(null); - when(itemFactory.equals(null, null)).thenReturn(true); + // Addons manager + when(plugin.getAddonsManager()).thenReturn(am); + // One game mode + when(am.getGameModeAddons()).thenReturn(Collections.singletonList(gameMode)); + AddonDescription desc2 = new AddonDescription.Builder("bentobox", GAME_MODE_NAME, "1.3").description("test").authors("tasty").build(); + when(gameMode.getDescription()).thenReturn(desc2); - when(Bukkit.getItemFactory()).thenReturn(itemFactory); - when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); - } + // Challenge Level + level = new ChallengeLevel(); + levelName = GAME_MODE_NAME + "_novice"; + level.setUniqueId(levelName); + level.setFriendlyName("Novice"); + // Set up challenge + String uuid = UUID.randomUUID().toString(); + challenge = new Challenge(); + challenge.setUniqueId(GAME_MODE_NAME + "_" + uuid); + challenge.setFriendlyName("name"); + challenge.setLevel(GAME_MODE_NAME + "_novice"); + challenge.setDescription(Collections.singletonList("A description")); + challenge.setChallengeType(ChallengeType.INVENTORY); + challenge.setDeployed(true); + challenge.setIcon(new ItemStack(Material.EMERALD)); + challenge.setEnvironment(Collections.singleton(World.Environment.NORMAL)); + challenge.setLevel(levelName); + challenge.setRepeatable(true); + challenge.setMaxTimes(10); + InventoryRequirements req = new InventoryRequirements(); - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsSuccess() { - Material requiredMaterial = Material.PAPER; - int requiredQuantity = 21; + challenge.setRequirements(req); + // Util + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(any())).thenReturn(world); + when(Util.prettifyText(anyString())).thenCallRealMethod(); - this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); - TryToComplete x = new TryToComplete(this.addon); - x.user(this.user); - Map removed = x.removeItems(this.required, 1); + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + Optional optionalGameMode = Optional.of(gameMode); + when(iwm.getAddon(any())).thenReturn(optionalGameMode); + when(iwm.getIslandDistance(any())).thenReturn(400); - assertEquals((int) removed.getOrDefault(new ItemStack(requiredMaterial, 1), 0), requiredQuantity); - } + // Island Manager + when(addon.getIslands()).thenReturn(im); + Optional opIsland = Optional.of(island); + when(im.getIslandAt(any())).thenReturn(opIsland); + when(im.getIsland(any(), any(User.class))).thenReturn(island); + // Player is on island + when(im.locationIsOnIsland(any(), any())).thenReturn(true); + // Island flags - everything is allowed by default + when(island.isAllowed(any(), any())).thenReturn(true); + // Island - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsMax() { - Material requiredMaterial = Material.PAPER; - int requiredQuantity = 50; + @Nullable + Location loc = mock(Location.class); + when(loc.toString()).thenReturn("center"); + when(island.getCenter()).thenReturn(loc); - this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); - TryToComplete x = new TryToComplete(this.addon); - x.user(this.user); - Map removed = x.removeItems(this.required, 1); + // Challenges Manager + when(addon.getChallengesManager()).thenReturn(cm); + // All levels unlocked by default + when(cm.isLevelUnlocked(any(), any(), any())).thenReturn(true); + // Player has done this challenge 3 times (default max is 10) + when(cm.getChallengeTimes(any(), any(), any(Challenge.class))).thenReturn(3L); - assertNotEquals((int) removed.getOrDefault(new ItemStack(requiredMaterial, 1), 0), requiredQuantity); - } + // 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)); + when(user.getName()).thenReturn("tastybento"); + @Nullable + Location userLoc = mock(Location.class); + when(userLoc.toString()).thenReturn("location"); + when(user.getLocation()).thenReturn(userLoc); + when(user.getInventory()).thenReturn(inv); + when(inv.getContents()).thenReturn(contents); + when(player.getBoundingBox()).thenReturn(bb); + when(bb.clone()).thenReturn(bb); + when(bb.toString()).thenReturn("BoundingBox"); + // Locales + User.setPlugin(plugin); + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + PlaceholdersManager phm = mock(PlaceholdersManager.class); + when(plugin.getPlaceholdersManager()).thenReturn(phm); + when(phm.replacePlaceholders(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsZero() { - Material requiredMaterial = Material.PAPER; - int requiredQuantity = 0; + // Survival by default + when(player.getGameMode()).thenReturn(GameMode.SURVIVAL); - this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); - TryToComplete x = new TryToComplete(this.addon); - x.user(this.user); - Map removed = x.removeItems(this.required, 1); + // Addon + when(addon.getChallengesSettings()).thenReturn(settings); + when(settings.isBroadcastMessages()).thenReturn(true); - assertTrue(removed.isEmpty()); - } + // Bukkit - online players + Map online = new HashMap<>(); - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsSuccessMultiple() { - required.add(new ItemStack(Material.PAPER, 11)); - required.add(new ItemStack(Material.PAPER, 5)); - required.add(new ItemStack(Material.PAPER, 5)); - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); + Set onlinePlayers = new HashSet<>(); + for (int j = 0; j < NAMES.length; j++) { + 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]); + onlinePlayers.add(p1); + } + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getOnlinePlayers()).then((Answer>) invocation -> onlinePlayers); - assertEquals((int) removed.getOrDefault(new ItemStack(Material.PAPER, 1), 0), 21); - } + // World settings + map = new HashMap<>(); + when(mySettings.getWorldFlags()).thenReturn(map); + when(iwm.getWorldSettings(any())).thenReturn(mySettings); + ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, true); - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsSuccessMultipleOther() { - required.add(new ItemStack(Material.CACTUS, 5)); - required.add(new ItemStack(Material.PAPER, 11)); - required.add(new ItemStack(Material.PAPER, 5)); - required.add(new ItemStack(Material.PAPER, 5)); - required.add(new ItemStack(Material.CACTUS, 5)); - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); + // ItemFactory + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); - assertEquals((int) removed.getOrDefault(new ItemStack(Material.PAPER, 1), 0), 21); - assertEquals((int) removed.getOrDefault(new ItemStack(Material.CACTUS, 1), 0), 10); - } + } - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsMultipleOtherFail() { - required.add(new ItemStack(Material.ACACIA_FENCE, 5)); - required.add(new ItemStack(Material.ARROW, 11)); - required.add(new ItemStack(Material.STONE, 5)); - required.add(new ItemStack(Material.BAKED_POTATO, 5)); - required.add(new ItemStack(Material.GHAST_SPAWN_EGG, 5)); - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); - assertTrue(removed.isEmpty()); - } + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsFail() { - ItemStack input = new ItemStack(Material.GOLD_BLOCK, 55); - required.add(input); - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#TryToComplete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testTryToCompleteChallengesAddonUserChallengeWorldStringString() { + ttc = new TryToComplete(addon, + user, + challenge, + world, + topLabel, + permissionPrefix); + verify(addon).getChallengesManager(); - // It will remove 32, but not any more - assertEquals((int) removed.getOrDefault(new ItemStack(Material.GOLD_BLOCK, 1), 0), 32); + } - // An error will be thrown - Mockito.verify(addon, Mockito.times(1)).logError(Mockito.anyString()); - } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotDeployed() { + challenge.setDeployed(false); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.not-deployed"); + } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringWrongWorld() { + challenge.setUniqueId("test"); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("general.errors.wrong-world"); + } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotOnIsland() { + 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"); + } - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRequireTwoStacks() { - required.add(new ItemStack(Material.BRICK_STAIRS, 64)); - required.add(new ItemStack(Material.BRICK_STAIRS, 64)); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotOnIslandButOk() { + 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"); + } - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + 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"); + } - // It should remove both stacks - assertEquals((int) removed.getOrDefault(new ItemStack(Material.BRICK_STAIRS, 1), 0), 128); - } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotRepeatable() { + 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"); + } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotRepeatableFirstTime() { + challenge.setRepeatable(false); + 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"); + } - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testFactorStacks() { - required.add(new ItemStack(Material.BRICK_STAIRS, 32)); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + 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"); + } - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 4); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIntZero() { + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 0)); + verify(user).sendMessage("challenges.errors.not-valid-integer"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIntNegative() { + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, -10)); + verify(user).sendMessage("challenges.errors.not-valid-integer"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIntPositiveNoPerm() { + InventoryRequirements req = new InventoryRequirements(); + req.setRequiredPermissions(Collections.singleton("perm-you-dont-have")); + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccess() { + assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccessSingleReq() { + InventoryRequirements req = new InventoryRequirements(); + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccessMultipleReq() { + + InventoryRequirements req = new InventoryRequirements(); + ItemStack itemStackMock = mock(ItemStack.class); + when(itemStackMock.getAmount()).thenReturn(3); + when(itemStackMock.getType()).thenReturn(Material.EMERALD_BLOCK); + when(itemStackMock.clone()).thenReturn(itemStackMock); + + ItemStack itemStackMock2 = mock(ItemStack.class); + when(itemStackMock2.getType()).thenReturn(Material.ENCHANTED_BOOK); + when(itemStackMock2.getAmount()).thenReturn(10); + when(itemStackMock2.clone()).thenReturn(itemStackMock2); + + ItemStack itemStackMock3 = mock(ItemStack.class); + when(itemStackMock3.getType()).thenReturn(Material.EMERALD_BLOCK); + when(itemStackMock3.getAmount()).thenReturn(15); + when(itemStackMock3.clone()).thenReturn(itemStackMock3); + // itemStackMock and 3 are same type + when(itemStackMock3.isSimilar(eq(itemStackMock))).thenReturn(true); + when(itemStackMock.isSimilar(eq(itemStackMock3))).thenReturn(true); + + req.setRequiredItems(Arrays.asList(itemStackMock , itemStackMock2)); + challenge.setRequirements(req); + ItemStack[] newContents = {itemStackMock3}; + when(inv.getContents()).thenReturn(newContents); + + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + // Sufficient emerald blocks + verify(user, never()).sendMessage("challenges.errors.not-enough-items", "[items]", "Emerald Block"); + // Not enough books + verify(user).sendMessage("challenges.errors.not-enough-items", "[items]", "Enchanted Book"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandBBTooLarge() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + req.setSearchRadius(1); + challenge.setRequirements(req); + // Trigger big bounding box error + when(bb.getWidthX()).thenReturn(50000D); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(addon).logError("BoundingBox is larger than SearchRadius. | BoundingBox: BoundingBox | Search Distance: 1 | Location: location | Center: center | Range: 0"); + verify(bb).expand(1); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandSuccessNoEntities() { + challenge.setChallengeType(ChallengeType.ISLAND); + 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"); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailEntities() { + challenge.setChallengeType(ChallengeType.ISLAND); + 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"); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailMultipleEntities() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + Map requiredEntities = new HashMap<>(); + requiredEntities.put(EntityType.GHAST, 3); + requiredEntities.put(EntityType.CHICKEN, 5); + requiredEntities.put(EntityType.PUFFERFISH, 1); + 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).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); + verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "5", "[item]", "Chicken"); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailPartialMultipleEntities() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + Map requiredEntities = new HashMap<>(); + requiredEntities.put(EntityType.GHAST, 3); + requiredEntities.put(EntityType.CHICKEN, 5); + requiredEntities.put(EntityType.PUFFERFISH, 1); + req.setRequiredEntities(requiredEntities); + req.setSearchRadius(1); + challenge.setRequirements(req); + Entity ent = mock(Entity.class); + when(ent.getType()).thenReturn(EntityType.PUFFERFISH); + Location loc = mock(Location.class); + when(ent.getLocation()).thenReturn(loc); + 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"); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandSuccess() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + Map requiredEntities = new HashMap<>(); + requiredEntities.put(EntityType.PUFFERFISH, 1); + req.setRequiredEntities(requiredEntities); + req.setSearchRadius(1); + challenge.setRequirements(req); + Entity ent = mock(Entity.class); + when(ent.getType()).thenReturn(EntityType.PUFFERFISH); + Location loc = mock(Location.class); + when(ent.getLocation()).thenReturn(loc); + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandPlayerInOtherEnvironment() { + challenge.setEnvironment(Collections.singleton(Environment.NETHER)); + World netherWorld = mock(World.class); + when(user.getWorld()).thenReturn(netherWorld); + when(netherWorld.getName()).thenReturn("world_nether"); + when(netherWorld.getEnvironment()).thenReturn(Environment.NETHER); + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + Map requiredEntities = new HashMap<>(); + requiredEntities.put(EntityType.PUFFERFISH, 1); + req.setRequiredEntities(requiredEntities); + req.setSearchRadius(1); + challenge.setRequirements(req); + Entity ent = mock(Entity.class); + when(ent.getType()).thenReturn(EntityType.PUFFERFISH); + Location loc = mock(Location.class); + when(ent.getLocation()).thenReturn(loc); + List list = Collections.singletonList(ent); + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + 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"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#build(int)}. + */ + @Test + public void testBuild() { + this.testTryToCompleteChallengesAddonUserChallengeWorldStringString(); + ChallengeResult result = this.ttc.build(10); + assertTrue(result.isMeetsRequirements()); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItemsNothing() { + this.testTryToCompleteChallengesAddonUserChallengeWorldStringString(); + assertTrue(ttc.removeItems(Collections.emptyList(), 1).isEmpty()); + + } - // It should remove both stacks - assertEquals((int) removed.getOrDefault(new ItemStack(Material.BRICK_STAIRS, 1), 0), 128); - } } - diff --git a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java new file mode 100644 index 0000000..a4e5b4a --- /dev/null +++ b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java @@ -0,0 +1,242 @@ +package world.bentobox.challenges.tasks; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; + +/** + * @author tastybento + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class}) +public class TryToCompleteTestOld { + + private User user; + ItemStack[] stacks = { new ItemStack(Material.PAPER, 32), + new ItemStack(Material.ACACIA_BOAT), + null, + null, + new ItemStack(Material.CACTUS, 32), + new ItemStack(Material.CACTUS, 32), + new ItemStack(Material.CACTUS, 32), + new ItemStack(Material.BRICK_STAIRS, 64), + new ItemStack(Material.BRICK_STAIRS, 64), + new ItemStack(Material.BRICK_STAIRS, 5), + new ItemStack(Material.GOLD_BLOCK, 32) + }; + List required; + private ChallengesAddon addon; + private PlayerInventory inv; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + 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); + when(inv.getContents()).thenReturn(stacks); + when(user.getInventory()).thenReturn(inv); + addon = mock(ChallengesAddon.class); + required = new ArrayList<>(); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + // Test will not work with items that has meta data. + when(itemFactory.getItemMeta(any())).thenReturn(null); + when(itemFactory.equals(null, null)).thenReturn(true); + + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testRemoveItemsSuccess() { + Material requiredMaterial = Material.PAPER; + int requiredQuantity = 21; + + this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); + TryToComplete x = new TryToComplete(this.addon); + x.user(this.user); + Map removed = x.removeItems(this.required, 1); + + assertEquals((int) removed.getOrDefault(new ItemStack(requiredMaterial, 1), 0), requiredQuantity); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testRemoveItemsMax() { + Material requiredMaterial = Material.PAPER; + int requiredQuantity = 50; + + this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); + TryToComplete x = new TryToComplete(this.addon); + x.user(this.user); + Map removed = x.removeItems(this.required, 1); + + assertNotEquals((int) removed.getOrDefault(new ItemStack(requiredMaterial, 1), 0), requiredQuantity); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testRemoveItemsZero() { + Material requiredMaterial = Material.PAPER; + int requiredQuantity = 0; + + this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); + TryToComplete x = new TryToComplete(this.addon); + x.user(this.user); + Map removed = x.removeItems(this.required, 1); + + assertTrue(removed.isEmpty()); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testRemoveItemsSuccessMultiple() { + required.add(new ItemStack(Material.PAPER, 11)); + required.add(new ItemStack(Material.PAPER, 5)); + required.add(new ItemStack(Material.PAPER, 5)); + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + + assertEquals((int) removed.getOrDefault(new ItemStack(Material.PAPER, 1), 0), 21); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testRemoveItemsSuccessMultipleOther() { + required.add(new ItemStack(Material.CACTUS, 5)); + required.add(new ItemStack(Material.PAPER, 11)); + required.add(new ItemStack(Material.PAPER, 5)); + required.add(new ItemStack(Material.PAPER, 5)); + required.add(new ItemStack(Material.CACTUS, 5)); + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + + assertEquals((int) removed.getOrDefault(new ItemStack(Material.PAPER, 1), 0), 21); + assertEquals((int) removed.getOrDefault(new ItemStack(Material.CACTUS, 1), 0), 10); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testRemoveItemsMultipleOtherFail() { + required.add(new ItemStack(Material.ACACIA_FENCE, 5)); + required.add(new ItemStack(Material.ARROW, 11)); + required.add(new ItemStack(Material.STONE, 5)); + required.add(new ItemStack(Material.BAKED_POTATO, 5)); + required.add(new ItemStack(Material.GHAST_SPAWN_EGG, 5)); + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + assertTrue(removed.isEmpty()); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testRemoveItemsFail() { + ItemStack input = new ItemStack(Material.GOLD_BLOCK, 55); + required.add(input); + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + + // It will remove 32, but not any more + assertEquals((int) removed.getOrDefault(new ItemStack(Material.GOLD_BLOCK, 1), 0), 32); + + // An error will be thrown + Mockito.verify(addon, Mockito.times(1)).logError(Mockito.anyString()); + } + + + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testRequireTwoStacks() { + required.add(new ItemStack(Material.BRICK_STAIRS, 64)); + required.add(new ItemStack(Material.BRICK_STAIRS, 64)); + + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + + // It should remove both stacks + assertEquals((int) removed.getOrDefault(new ItemStack(Material.BRICK_STAIRS, 1), 0), 128); + } + + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @SuppressWarnings("deprecation") + @Test + public void testFactorStacks() { + required.add(new ItemStack(Material.BRICK_STAIRS, 32)); + + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 4); + + // It should remove both stacks + assertEquals((int) removed.getOrDefault(new ItemStack(Material.BRICK_STAIRS, 1), 0), 128); + } +} + diff --git a/src/test/java/world/bentobox/challenges/utils/UtilsTest.java b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java new file mode 100644 index 0000000..a89873f --- /dev/null +++ b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java @@ -0,0 +1,199 @@ +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; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +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 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; +import org.mockito.Mock; +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.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class}) +public class UtilsTest { + + @Mock + private IslandWorldManager iwm; + @Mock + private GameModeAddon gameModeAddon; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Mock item factory (for itemstacks) + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + + // IWM getAddon + AddonDescription desc = new AddonDescription.Builder("main", "name", "1.0").build(); + when(gameModeAddon.getDescription()).thenReturn(desc); + Optional optionalAddon = Optional.of(gameModeAddon); + when(iwm.getAddon(any())).thenReturn(optionalAddon); + when(plugin.getIWM()).thenReturn(iwm); + + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + */ + @Test + public void testGroupEqualItemsEmpty() { + assertTrue(Utils.groupEqualItems(Collections.emptyList()).isEmpty()); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + */ + @Test + public void testGroupEqualItems() { + List requiredItems = new ArrayList<>(); + // First item + ItemStack is = mock(ItemStack.class); + when(is.getAmount()).thenReturn(1); + when(is.getType()).thenReturn(Material.ACACIA_FENCE); + when(is.getMaxStackSize()).thenReturn(64); + when(is.isSimilar(any())).thenReturn(true); + when(is.clone()).thenReturn(is); + requiredItems.add(is); + for (int i = 0; i < 9; i++) { + ItemStack is2 = mock(ItemStack.class); + when(is2.getAmount()).thenReturn(1); + when(is2.getType()).thenReturn(Material.ACACIA_FENCE); + when(is2.getMaxStackSize()).thenReturn(64); + when(is2.isSimilar(any())).thenReturn(true); + when(is2.clone()).thenReturn(is); + requiredItems.add(is2); + } + List list = Utils.groupEqualItems(requiredItems); + // 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 + public void testGroupEqualItemsUnique() { + List requiredItems = new ArrayList<>(); + // First item + ItemStack is = mock(ItemStack.class); + when(is.getAmount()).thenReturn(1); + when(is.getType()).thenReturn(Material.ACACIA_FENCE); + when(is.getMaxStackSize()).thenReturn(64); + when(is.isSimilar(any())).thenReturn(false); + when(is.clone()).thenReturn(is); + requiredItems.add(is); + for (int i = 0; i < 9; i++) { + ItemStack is2 = mock(ItemStack.class); + when(is2.getAmount()).thenReturn(1); + when(is2.getType()).thenReturn(Material.values()[i+20]); + when(is2.getMaxStackSize()).thenReturn(64); + when(is2.isSimilar(any())).thenReturn(false); + when(is2.clone()).thenReturn(is); + requiredItems.add(is2); + } + List list = Utils.groupEqualItems(requiredItems); + // 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)}. + */ + @Test + public void testGetGameModeNoGameMode() { + when(iwm.getAddon(any())).thenReturn(Optional.empty()); + assertNull(Utils.getGameMode(mock(World.class))); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#getGameMode(org.bukkit.World)}. + */ + @Test + public void testGetGameMode() { + assertEquals("name", Utils.getGameMode(mock(World.class))); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#getNextValue(T[], java.lang.Object)}. + */ + @Test + public void testGetNextValue() { + assertEquals(VisibilityMode.HIDDEN, Utils.getNextValue(VisibilityMode.values(), VisibilityMode.VISIBLE)); + assertEquals(VisibilityMode.TOGGLEABLE, Utils.getNextValue(VisibilityMode.values(), VisibilityMode.HIDDEN)); + assertEquals(VisibilityMode.VISIBLE, Utils.getNextValue(VisibilityMode.values(), VisibilityMode.TOGGLEABLE)); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#getPreviousValue(T[], java.lang.Object)}. + */ + @Test + public void testGetPreviousValue() { + assertEquals(VisibilityMode.TOGGLEABLE, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.VISIBLE)); + assertEquals(VisibilityMode.VISIBLE, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.HIDDEN)); + assertEquals(VisibilityMode.HIDDEN, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.TOGGLEABLE)); + } + +}