From 8383c93cf95429d5a4cd2002c6b4ebac0f4f7bd1 Mon Sep 17 00:00:00 2001 From: BONNe Date: Wed, 22 Apr 2020 01:19:46 +0300 Subject: [PATCH 1/3] 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)); + } + +} From 7ac1ab4a618e31b6fa7e70a690a9622b29605bc9 Mon Sep 17 00:00:00 2001 From: BONNe Date: Fri, 6 May 2022 19:51:54 +0300 Subject: [PATCH 2/3] Prepare 1.0 Release (#287) * Version 0.8.5 * Retranslated zh-CN.yml (#273) * 50% completed. * 60% completed. * 63% completed. * Completed. * Change the indentation, some improvements. Co-authored-by: zhangYi * Updated german language file (#278) fixed double & and double whitespace * Fixes a mistaken permission for completing multiple challenges at once. * Fixes translated placeholders in PL translation. Note: translation looks bad. * Update CompleteChallengeCommandTest.java * Upgrade to BentoBox 1.17 API changes. Implement Pladdon functionality. Compile against java 16 and Spigot 1.17 * Fix Java 16 compilation. * Use BentoBox 1.17.0 * Update pom.xml * Create Statistic Requirement for Challenges addon. Statistic requirement is a new type of challenge that is based on Statistic page for clients. * Switch to annotations instead of plugin.yml file. * Move managers to a separate directory. * Add mojang authLib instead of NMS. * Rename classes to Selectors. Split single and multiple item selectors for easier implementation. Update proper locales. * Implement customizable user panels. Server owners can customize 3 panels: - main panel - gamemode selector - multiple completions Panel functions will be explained in docs later. * Update all admin panels. Admin panels will not contain better locales codding and easier-to-improve design. Remove old and unused GUIs. * Remove unused adapters. Updates Challenges and ChallengesLevel objects. Add TypeMigrationAdapter that will fix issue with renamed challenge type. * Update commands. Commands will now call correct GUI. * Update Settings file. Remove unused parts. * Fixes ChallengesManager and Completer. * Adds panel saving to the `/challenges/panels` directory. * Updates locales file. Complete rework of the locales file. Very sorry translators :( no migration. * Updates pom.xml * Updates tests. ChallengesGUITest is removed because GUI is removed. * Update default.json Split text into multiple lines. * Create template YAML file. This file format is for people who has an alergy with ingame GUI. * Implements Template reading. Add template loading via Admin Panel. Improve LibraryPanel so it could find json and yml files. * Improve coloring scheme a bit. * Change settings file. Add ability to change commands for addon. Change default mode from player challenges to island challenges. * Update Main addon class. Move vault and level detection after everything is loaded. Update command names. * Update all commands. Commands now will have an option to change their call values. * Update default config value. * Fixes #264 Challenges Menu will be opened only if player is in correct world. * Changes User#sendMessage to Utils#sendMessage This allows add "prefix" to all messages send from Challenges addon. * Separate singe and multiple listings. * Clean up Constants a bit. * Add meta for items translations. * Fix permission link. * Translates color codes for database texts. * Fixes a bug when global commands does not displays in tab-complete. Remove DefaultsCommand.java as it is not used anymore. * Fixes small bugs in translation. * Remove unnecessary "admin" tag. * Update default locale. * Update latvian locale to the latest version. * Implement multi-linguistic server support. Now server owners can specify different name, description and reward text for each challenge and level via locales file. Add showcase example. * Comment out showcase translation. * Update BentoBox version * Update missing icons for blocks. Some blocks cannot be displayed in GUI's, and were leaving empty spaces. This replaces their icon with a close representative. Fixes #286 * Add missing mob heads. * Fixes illegal stack issues in default challenges. #249 * Change from click-to-select to a proper next/previous page tooltip * Add search field to the PagedSelectors. Add missing tooltips. * Change download icon from hopper to cobweb. * Add missing tooltips to the CommonPagedPanel * Add search button to the CommonPagedPanel. Search button will allow to search elements if there are more than displayed elements. * Add missing strings into locale. * Reorder dependencies The Mojang dependency was blocking out the needed Google common packages. * Prevent errors in TryToCompleteTest Note - tests still fail. * Fixed errors and tests for CompleteChallengeCommandTest * Fixed ChallengesCommandTest tests * Fixes tests * Fix JavaDoc, Shade plugin settings * Updated .gitignore * Try different spigot API version * Remove Vault repo because it is not needed. * Excluded unnecessary files from shading. * Fixes #253 Adds TeamKick and TeamLeave events to the reset check. Do not reset challenges if data is stored per island. As in that case, they will already lose their data. * Fixes #187 Add a new method that updates unlocked level list without changing active level. This method returns if last unlocked level was changed, and in that case it triggers whole gui rebuilding. * Fixes #269 Disable waiver amount message for last challenge level. * Add timeout for repeatable challenges. Relates #71 * Implement timeout respecting in challenges completion. Implement timeout in GUI's. Relates #71 * Implement changing Timeout in the Challenge Edit GUI. Relates #71 * Implement an option to set which item type will ignore metadata per challenge. Fixes #261 Fixes #252 * Fixes failing unit-test * Removed shade plugin from POM * Replace GuiUtils and HeadLib to the PanelUtils library. * Link templates to the docs. * Remove unnecessary NMS dependency. NMS code was used for Player Heads, but instead of NMS now it uses public mojang lib. * Address some code quality reports from SonarCloud. Most of the errors are just sanity checks, as the most of null-pointers were already checked in other ways. * Fixes incorrect NEXT and PREVIOUS button descriptions. Fixes #289 * Implement MetaData ignoring for rewards. While required items had a metadata grouping, reward items did not have it. This will fix that. Fixes #289 * Fix an issue when edit menu did not display item amount. * Update lv translation. * Fixes some small bugs with translation potion base effect. There was an issue that it tried to translate extra effects and ignored main one. Relates to #290 * Fix a bug with completion broadcasting Reported via Discord. * Update pom.xml * Fixes a bug with `-1` repeat-times There was a bug that prevented the challenge to be completed if negative numbers were set in the "max-repeats" value. * Improve equal item listing. Change when items should be grouped. Instead of relaying strictly from ignoreMetaData set, now try to group equal elements without durability check, and use set only if that fails. * Update German translation (#295) * Translate de.yml via GitLocalize * Translate de.yml via GitLocalize * Translate de.yml via GitLocalize Co-authored-by: Patrick Co-authored-by: Michael F Co-authored-by: DAge030 * Fix NPEs when running tests. Note that there are still test failures, but these are assertions and not errors. * Fix error in test class. Note this does not fix the failing assertion. * Fix failing test. Make player default to being on island. * Fixed test failures. * Avoid potential call with a null parameter to User.getInstance * Check for null world * Null check * Added null check * Require non-nulls. getInventory never returns null. * Remove various code smells. Co-authored-by: tastybento Co-authored-by: apachezy <50116371+apachezy@users.noreply.github.com> Co-authored-by: zhangYi Co-authored-by: Qumoo <76853697+Qumoo@users.noreply.github.com> Co-authored-by: tastybento Co-authored-by: gitlocalize-app[bot] <55277160+gitlocalize-app[bot]@users.noreply.github.com> Co-authored-by: Patrick Co-authored-by: Michael F Co-authored-by: DAge030 --- .github/workflows/build.yml | 4 +- pom.xml | 198 +- src/.gitignore | 1 + .../bentobox/challenges/ChallengesAddon.java | 200 +- .../challenges/ChallengesImportManager.java | 572 ----- .../challenges/ChallengesPladdon.java | 30 + .../commands/ChallengesCommand.java | 96 - ...ava => ChallengesGlobalPlayerCommand.java} | 52 +- .../commands/ChallengesPlayerCommand.java | 110 + .../commands/CompleteChallengeCommand.java | 45 +- .../challenges/commands/admin/Challenges.java | 68 - .../admin/ChallengesAdminCommand.java | 121 +- .../admin/ChallengesGlobalAdminCommand.java | 87 + .../commands/admin/CompleteCommand.java | 47 +- .../commands/admin/DefaultsCommand.java | 165 -- .../commands/admin/MigrateCommand.java | 23 +- .../commands/admin/ReloadChallenges.java | 20 +- .../commands/admin/ResetCommand.java | 54 +- .../commands/admin/ShowChallenges.java | 25 +- .../bentobox/challenges/config/Settings.java | 288 +-- .../challenges/database/object/Challenge.java | 437 +--- .../database/object/ChallengeLevel.java | 45 +- .../database/object/ChallengesPlayerData.java | 20 +- .../object/adapters/ChallengeLoreAdapter.java | 63 - .../adapters/EntityCompatibilityAdapter.java | 6 +- .../object/adapters/LevelLoreAdapter.java | 63 - .../object/adapters/TypeMigrationAdapter.java | 51 + .../requirements/InventoryRequirements.java | 47 +- .../requirements/IslandRequirements.java | 10 +- .../requirements/OtherRequirements.java | 6 +- .../object/requirements/Requirements.java | 7 +- .../requirements/StatisticRequirements.java | 228 ++ .../handlers/ChallengeDataRequestHandler.java | 2 +- .../handlers/ChallengeListRequestHandler.java | 19 +- .../CompletedChallengesRequestHandler.java | 25 +- .../handlers/LevelDataRequestHandler.java | 2 +- .../handlers/LevelListRequestHandler.java | 20 +- .../challenges/listeners/ResetListener.java | 87 +- .../challenges/listeners/SaveListener.java | 2 +- .../managers/ChallengesImportManager.java | 1348 +++++++++++ .../{ => managers}/ChallengesManager.java | 408 ++-- .../bentobox/challenges/panel/CommonGUI.java | 1106 --------- .../challenges/panel/CommonPagedPanel.java | 267 +++ .../challenges/panel/CommonPanel.java | 1103 +++++++++ .../challenges/panel/ConversationUtils.java | 564 +++++ .../challenges/panel/GameModesGUI.java | 143 -- .../challenges/panel/admin/AdminGUI.java | 736 ------ .../challenges/panel/admin/AdminPanel.java | 593 +++++ .../panel/admin/EditChallengeGUI.java | 1531 ------------ .../panel/admin/EditChallengePanel.java | 2121 +++++++++++++++++ .../challenges/panel/admin/EditLevelGUI.java | 764 ------ .../panel/admin/EditLevelPanel.java | 998 ++++++++ .../challenges/panel/admin/EditLoreGUI.java | 638 ----- .../panel/admin/EditSettingsGUI.java | 606 ----- .../panel/admin/EditSettingsPanel.java | 576 +++++ .../challenges/panel/admin/LibraryPanel.java | 471 ++++ .../panel/admin/ListChallengesGUI.java | 207 -- .../panel/admin/ListChallengesPanel.java | 225 ++ .../challenges/panel/admin/ListLevelsGUI.java | 209 -- .../panel/admin/ListLevelsPanel.java | 224 ++ .../panel/admin/ListLibraryGUI.java | 313 --- .../challenges/panel/admin/ListUsersGUI.java | 334 --- .../panel/admin/ListUsersPanel.java | 408 ++++ .../panel/admin/ManageBlocksGUI.java | 241 -- .../panel/admin/ManageBlocksPanel.java | 312 +++ .../panel/admin/ManageEntitiesGUI.java | 258 -- .../panel/admin/ManageEntitiesPanel.java | 330 +++ .../challenges/panel/user/ChallengesGUI.java | 528 ---- .../panel/user/ChallengesPanel.java | 818 +++++++ .../challenges/panel/user/GameModePanel.java | 385 +++ .../challenges/panel/user/MultipleGUI.java | 215 -- .../challenges/panel/user/MultiplePanel.java | 282 +++ .../panel/util/ChallengeSelector.java | 253 ++ .../panel/util/ChallengeTypeGUI.java | 146 -- .../panel/util/ChallengeTypeSelector.java | 133 ++ .../panel/util/ConfirmationGUI.java | 114 - .../panel/util/EnvironmentSelector.java | 195 ++ .../{ItemSwitchGUI.java => ItemSelector.java} | 109 +- .../panel/util/MultiBlockSelector.java | 294 +++ .../panel/util/MultiEntitySelector.java | 282 +++ .../challenges/panel/util/NumberGUI.java | 559 ----- .../challenges/panel/util/PagedSelector.java | 262 ++ .../panel/util/SelectBlocksGUI.java | 276 --- .../panel/util/SelectChallengeGUI.java | 215 -- .../panel/util/SelectEntityGUI.java | 237 -- .../panel/util/SelectEnvironmentGUI.java | 148 -- .../panel/util/SingleBlockSelector.java | 241 ++ .../panel/util/SingleEntitySelector.java | 238 ++ .../panel/util/StatisticSelector.java | 190 ++ .../challenges/panel/util/StringListGUI.java | 366 --- .../challenges/tasks/TryToComplete.java | 625 +++-- .../bentobox/challenges/utils/Constants.java | 226 ++ .../bentobox/challenges/utils/GuiUtils.java | 439 ---- .../bentobox/challenges/utils/HeadLib.java | 285 --- .../challenges/utils/LevelStatus.java | 2 +- .../bentobox/challenges/utils/Utils.java | 821 ++++++- .../bentobox/challenges/web/WebManager.java | 12 +- .../challenges/web/object/LibraryEntry.java | 186 +- src/main/resources/addon.yml | 2 +- src/main/resources/config.yml | 125 +- src/main/resources/default.json | 514 ++-- src/main/resources/locales/de.yml | 1615 ++++++++----- src/main/resources/locales/en-US.yml | 1707 +++++++++---- src/main/resources/locales/lv.yml | 1782 ++++++++++---- src/main/resources/locales/pl.yml | 210 +- src/main/resources/locales/zh-CN.yml | 1051 ++++---- src/main/resources/panels/gamemode_panel.yml | 53 + src/main/resources/panels/main_panel.yml | 114 + src/main/resources/panels/multiple_panel.yml | 62 + src/main/resources/template.yml | 562 +++++ .../challenges/ChallengesAddonTest.java | 39 +- .../challenges/ChallengesManagerTest.java | 147 +- .../commands/ChallengesCommandTest.java | 82 +- .../CompleteChallengeCommandTest.java | 72 +- .../challenges/commands/TestWorldSetting.java | 404 ++++ .../panel/user/ChallengesGUITest.java | 396 --- .../challenges/tasks/TryToCompleteTest.java | 120 +- .../tasks/TryToCompleteTestOld.java | 6 +- .../bentobox/challenges/utils/UtilsTest.java | 43 +- 119 files changed, 22142 insertions(+), 16426 deletions(-) create mode 100644 src/.gitignore delete mode 100644 src/main/java/world/bentobox/challenges/ChallengesImportManager.java create mode 100644 src/main/java/world/bentobox/challenges/ChallengesPladdon.java delete mode 100644 src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java rename src/main/java/world/bentobox/challenges/commands/{ChallengesUserCommand.java => ChallengesGlobalPlayerCommand.java} (52%) create mode 100644 src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java delete mode 100644 src/main/java/world/bentobox/challenges/commands/admin/Challenges.java create mode 100644 src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java delete mode 100644 src/main/java/world/bentobox/challenges/commands/admin/DefaultsCommand.java delete mode 100644 src/main/java/world/bentobox/challenges/database/object/adapters/ChallengeLoreAdapter.java delete mode 100644 src/main/java/world/bentobox/challenges/database/object/adapters/LevelLoreAdapter.java create mode 100644 src/main/java/world/bentobox/challenges/database/object/adapters/TypeMigrationAdapter.java create mode 100644 src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java create mode 100644 src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java rename src/main/java/world/bentobox/challenges/{ => managers}/ChallengesManager.java (87%) delete mode 100644 src/main/java/world/bentobox/challenges/panel/CommonGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/CommonPagedPanel.java create mode 100644 src/main/java/world/bentobox/challenges/panel/CommonPanel.java create mode 100644 src/main/java/world/bentobox/challenges/panel/ConversationUtils.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/GameModesGUI.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/AdminPanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ListChallengesPanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ListLevelsPanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ListUsersPanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksPanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesPanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java create mode 100644 src/main/java/world/bentobox/challenges/panel/user/GameModePanel.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/user/MultiplePanel.java create mode 100644 src/main/java/world/bentobox/challenges/panel/util/ChallengeSelector.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeSelector.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/util/EnvironmentSelector.java rename src/main/java/world/bentobox/challenges/panel/util/{ItemSwitchGUI.java => ItemSelector.java} (66%) create mode 100644 src/main/java/world/bentobox/challenges/panel/util/MultiBlockSelector.java create mode 100644 src/main/java/world/bentobox/challenges/panel/util/MultiEntitySelector.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/util/PagedSelector.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java create mode 100644 src/main/java/world/bentobox/challenges/panel/util/SingleBlockSelector.java create mode 100644 src/main/java/world/bentobox/challenges/panel/util/SingleEntitySelector.java create mode 100644 src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java delete mode 100644 src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java create mode 100644 src/main/java/world/bentobox/challenges/utils/Constants.java delete mode 100644 src/main/java/world/bentobox/challenges/utils/GuiUtils.java delete mode 100644 src/main/java/world/bentobox/challenges/utils/HeadLib.java create mode 100644 src/main/resources/panels/gamemode_panel.yml create mode 100644 src/main/resources/panels/main_panel.yml create mode 100644 src/main/resources/panels/multiple_panel.yml create mode 100644 src/main/resources/template.yml create mode 100644 src/test/java/world/bentobox/challenges/commands/TestWorldSetting.java delete mode 100644 src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9e16544..b9cf60c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 11 + - name: Set up JDK 16 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 16 - name: Cache SonarCloud packages uses: actions/cache@v1 with: diff --git a/pom.xml b/pom.xml index 9e27c11..dc1f977 100644 --- a/pom.xml +++ b/pom.xml @@ -32,17 +32,19 @@ UTF-8 UTF-8 - 1.8 - 2.0.4 + 16 + 2.0.9 - 1.15.2-R0.1-SNAPSHOT - 1.15.4 - 2.5.0 + 1.17.1-R0.1-SNAPSHOT + 1.2.3-SNAPSHOT + 1.20.0 + 2.6.3 1.7 + 1.0.0 ${build.version}-SNAPSHOT - 0.8.4 + 1.0.0 -LOCAL BentoBoxWorld_Challenges @@ -92,35 +94,33 @@ + + + apache.snapshots + https://repository.apache.org/snapshots/ + + + + spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots - - spigotmc-public - https://hub.spigotmc.org/nexus/content/groups/public/ - + codemc-repo - https://repo.codemc.org/repository/maven-public/ + https://repo.codemc.io/repository/maven-public + - codemc-nms - https://repo.codemc.org/repository/nms/ - - - - vault-repo - http://nexus.hc.to/content/repositories/pub_releases - - - jitpack.io - https://jitpack.io + minecraft-repo + https://libraries.minecraft.net/ + org.spigotmc spigot-api @@ -129,15 +129,49 @@ org.spigotmc - spigot - ${spigot.version} + plugin-annotations + 1.2.3-SNAPSHOT + provided + + + + net.milkbowl.vault + VaultAPI + ${vault.version} + provided + + + + world.bentobox + bentobox + ${bentobox.version} + provided + + + world.bentobox + level + ${level.version} + provided + + + + lv.id.bonne + panelutils + ${panelutils.version} + compile + + + + org.jetbrains + annotations + 22.0.0 provided org.mockito mockito-core - 3.1.0 + 3.11.2 test @@ -152,22 +186,11 @@ ${powermock.version} test + - world.bentobox - bentobox - ${bentobox.version} - provided - - - world.bentobox - level - ${level.version} - provided - - - net.milkbowl.vault - VaultAPI - ${vault.version} + com.mojang + authlib + 1.5.21 provided @@ -213,16 +236,49 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.7.0 ${java.version} ${java.version} + ${java.version} org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.0.0-M5 + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.math=ALL-UNNAMED + --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens + java.base/java.util.stream=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED + --add-opens + java.base/java.util.regex=ALL-UNNAMED + --add-opens + java.base/java.nio.channels.spi=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED + --add-opens java.base/java.net=ALL-UNNAMED + --add-opens + java.base/java.util.concurrent=ALL-UNNAMED + --add-opens java.base/sun.nio.fs=ALL-UNNAMED + --add-opens java.base/sun.nio.cs=ALL-UNNAMED + --add-opens java.base/java.nio.file=ALL-UNNAMED + --add-opens + java.base/java.nio.charset=ALL-UNNAMED + --add-opens + java.base/java.lang.reflect=ALL-UNNAMED + --add-opens + java.logging/java.util.logging=ALL-UNNAMED + --add-opens java.base/java.lang.ref=ALL-UNNAMED + --add-opens java.base/java.util.jar=ALL-UNNAMED + --add-opens java.base/java.util.zip=ALL-UNNAMED + + + org.apache.maven.plugins @@ -234,12 +290,10 @@ maven-javadoc-plugin 3.1.1 - 8 public false -Xdoclint:none - - + ${java.home}/bin/javadoc @@ -263,28 +317,6 @@ - - org.apache.maven.plugins - maven-shade-plugin - 3.2.1 - - true - - - io.github.TheBusyBiscuit.GitHubWebAPI4Java - world.bentobox.bentobox.api.github - - - - - - package - - shade - - - - org.apache.maven.plugins maven-install-plugin @@ -322,6 +354,38 @@ + + org.apache.maven.plugins + maven-shade-plugin + 3.3.1-SNAPSHOT + + true + + + lv.id.bonne:panelutils:* + + + + + + MANIFEST.MF + + + + META-INF/MANIFEST.MF + src/main/resources/META-INF/MANIFEST.MF + + + + + + package + + shade + + + + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..9bb88d3 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +/.DS_Store diff --git a/src/main/java/world/bentobox/challenges/ChallengesAddon.java b/src/main/java/world/bentobox/challenges/ChallengesAddon.java index da875a8..1882d57 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesAddon.java +++ b/src/main/java/world/bentobox/challenges/ChallengesAddon.java @@ -3,7 +3,6 @@ package world.bentobox.challenges; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -16,10 +15,10 @@ import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; import world.bentobox.bentobox.hooks.VaultHook; import world.bentobox.bentobox.managers.RanksManager; -import world.bentobox.challenges.commands.ChallengesCommand; -import world.bentobox.challenges.commands.ChallengesUserCommand; -import world.bentobox.challenges.commands.admin.Challenges; +import world.bentobox.challenges.commands.ChallengesPlayerCommand; +import world.bentobox.challenges.commands.ChallengesGlobalPlayerCommand; import world.bentobox.challenges.commands.admin.ChallengesAdminCommand; +import world.bentobox.challenges.commands.admin.ChallengesGlobalAdminCommand; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.database.object.ChallengeLevel; import world.bentobox.challenges.handlers.ChallengeDataRequestHandler; @@ -29,6 +28,8 @@ import world.bentobox.challenges.handlers.LevelDataRequestHandler; import world.bentobox.challenges.handlers.LevelListRequestHandler; import world.bentobox.challenges.listeners.ResetListener; import world.bentobox.challenges.listeners.SaveListener; +import world.bentobox.challenges.managers.ChallengesImportManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.web.WebManager; import world.bentobox.level.Level; @@ -72,6 +73,12 @@ public class ChallengesAddon extends Addon { */ private boolean levelProvided; + /** + * List of hooked gamemode addons. + */ + private final List hookedGameModes = new ArrayList<>(); + + // --------------------------------------------------------------------- // Section: Constants // --------------------------------------------------------------------- @@ -112,6 +119,12 @@ public class ChallengesAddon extends Addon { this.saveDefaultConfig(); // Load the plugin's config this.loadSettings(); + + if (this.settings.isUseCommonGUI()) + { + new ChallengesGlobalPlayerCommand(this, this.hookedGameModes); + new ChallengesGlobalAdminCommand(this, this.hookedGameModes); + } } @@ -150,77 +163,28 @@ public class ChallengesAddon extends Addon { // Web content loading this.webManager = new WebManager(this); - List hookedGameModes = new ArrayList<>(); + this.hookedGameModes.clear(); this.getPlugin().getAddonsManager().getGameModeAddons().forEach(gameModeAddon -> { if (!this.settings.getDisabledGameModes().contains( gameModeAddon.getDescription().getName())) { - if (gameModeAddon.getPlayerCommand().isPresent()) - { - new ChallengesCommand(this, gameModeAddon.getPlayerCommand().get()); - this.hooked = true; + gameModeAddon.getPlayerCommand().ifPresent(command -> + new ChallengesPlayerCommand(this, command)); + gameModeAddon.getAdminCommand().ifPresent(command -> + new ChallengesAdminCommand(this, command)); - hookedGameModes.add(gameModeAddon); - } - - if (gameModeAddon.getAdminCommand().isPresent()) - { - new Challenges(this, gameModeAddon.getAdminCommand().get()); - this.hooked = true; - } + this.hooked = true; + this.hookedGameModes.add(gameModeAddon); CHALLENGES_WORLD_PROTECTION.addGameModeAddon(gameModeAddon); CHALLENGES_ISLAND_PROTECTION.addGameModeAddon(gameModeAddon); this.registerPlaceholders(gameModeAddon); - - // TODO: this is old placeholders. Remove when backward compatibility ends. - this.registerPlaceholdersOld(gameModeAddon); } }); if (this.hooked) { - - // Create general challenge commands - - if (this.settings.isUseCommonGUI()) - { - new ChallengesUserCommand(this, - this.settings.getUserCommand(), - hookedGameModes); - new ChallengesAdminCommand(this, - this.settings.getAdminCommand(), - hookedGameModes); - } - - // Try to find Level addon and if it does not exist, display a warning - - Optional level = this.getAddonByName("Level"); - - if (!level.isPresent()) - { - this.logWarning("Level add-on not found so level challenges will not work!"); - this.levelAddon = null; - } - else - { - this.levelProvided = true; - this.levelAddon = (Level) level.get(); - } - - Optional vault = this.getPlugin().getVault(); - - if (!vault.isPresent() || !vault.get().hook()) - { - this.vaultHook = null; - this.logWarning("Vault plugin not found. Economy will not work!"); - } - else - { - this.vaultHook = vault.get(); - } - // Register the reset listener this.registerListener(new ResetListener(this)); // Register the autosave listener. @@ -255,6 +219,44 @@ public class ChallengesAddon extends Addon { } + /** + * Process Level addon and Vault Hook when everything is loaded. + */ + @Override + public void allLoaded() + { + super.allLoaded(); + + // Try to find Level addon and if it does not exist, display a warning + this.getAddonByName("Level").ifPresentOrElse(addon -> { + this.levelAddon = (Level) addon; + this.levelProvided = true; + this.log("Challenges Addon hooked into Level addon."); + }, () -> { + this.levelAddon = null; + this.logWarning("Level add-on not found so level challenges will not work!"); + }); + + + // Try to find Vault Plugin and if it does not exist, display a warning + this.getPlugin().getVault().ifPresentOrElse(hook -> { + this.vaultHook = hook; + + if (this.vaultHook.hook()) + { + this.log("Challenges Addon hooked into Economy."); + } + else + { + this.logWarning("Challenges Addon could not hook into valid Economy."); + } + }, () -> { + this.vaultHook = null; + this.logWarning("Vault plugin not found. Economy will not work!"); + }); + } + + /** * {@inheritDoc} */ @@ -306,6 +308,15 @@ public class ChallengesAddon extends Addon { this.logError("Challenges settings could not load! Addon disabled."); this.setState(State.DISABLED); } + + // Save existing panels. + this.saveResource("panels/main_panel.yml", false); + this.saveResource("panels/multiple_panel.yml",false); + this.saveResource("panels/gamemode_panel.yml",false); + + // Save template + this.saveResource("template.yml",false); + this.saveResource("default.json",false); } @@ -403,75 +414,6 @@ public class ChallengesAddon extends Addon { } - /** - * This method registers placeholders into GameMode addon. - * @param gameModeAddon GameMode addon where placeholders must be hooked in. - * @since 0.8.1 - * @deprecated remove after 0.9.0 - */ - @Deprecated - private void registerPlaceholdersOld(GameModeAddon gameModeAddon) - { - final String gameMode = gameModeAddon.getDescription().getName().toLowerCase(); - final World world = gameModeAddon.getOverWorld(); - - // Number of completions for all challenges placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_total_completion_count", - user -> String.valueOf(this.challengesManager.getTotalChallengeCompletionCount(user, world))); - - // Completed challenge count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_completed_count", - user -> String.valueOf(this.challengesManager.getCompletedChallengeCount(user, world))); - - // Uncompleted challenge count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_uncompleted_count", - user -> String.valueOf(this.challengesManager.getChallengeCount(world) - - this.challengesManager.getCompletedChallengeCount(user, world))); - - // Completed challenge level count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_completed_level_count", - user -> String.valueOf(this.challengesManager.getCompletedLevelCount(user, world))); - - // Uncompleted challenge level count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_uncompleted_level_count", - user -> String.valueOf(this.challengesManager.getLevelCount(world) - - this.challengesManager.getCompletedLevelCount(user, world))); - - // Unlocked challenge level count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_unlocked_level_count", - user -> String.valueOf(this.challengesManager.getLevelCount(world) - - this.challengesManager.getUnlockedLevelCount(user, world))); - - // Locked challenge level count placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_locked_level_count", - user -> String.valueOf(this.challengesManager.getLevelCount(world) - - this.challengesManager.getUnlockedLevelCount(user, world))); - - // Latest challenge level name placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_latest_level_name", - user -> { - ChallengeLevel level = this.challengesManager.getLatestUnlockedLevel(user, world); - return level != null ? level.getFriendlyName() : ""; - }); - - // Latest challenge level id placeholder - this.getPlugin().getPlaceholdersManager().registerPlaceholder(this, - gameMode + "_challenge_latest_level_id", - user -> { - ChallengeLevel level = this.challengesManager.getLatestUnlockedLevel(user, world); - return level != null ? level.getUniqueId() : ""; - }); - } - - // --------------------------------------------------------------------- // Section: Getters // --------------------------------------------------------------------- diff --git a/src/main/java/world/bentobox/challenges/ChallengesImportManager.java b/src/main/java/world/bentobox/challenges/ChallengesImportManager.java deleted file mode 100644 index 47e0fca..0000000 --- a/src/main/java/world/bentobox/challenges/ChallengesImportManager.java +++ /dev/null @@ -1,572 +0,0 @@ -package world.bentobox.challenges; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.bukkit.World; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.Expose; - -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory; -import world.bentobox.bentobox.database.objects.DataObject; -import world.bentobox.bentobox.util.Util; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.ChallengeLevel; -import world.bentobox.challenges.utils.Utils; - - -/** - * Imports challenges - * @author BONNe1704 - * - */ -public class ChallengesImportManager -{ - /** - * Import challenges from default.json - * @param challengesAddon - */ - public ChallengesImportManager(ChallengesAddon challengesAddon) - { - this.addon = challengesAddon; - } - - - // --------------------------------------------------------------------- - // Section: Default Challenge Loader - // --------------------------------------------------------------------- - - - /** - * This method loads default challenges into memory. - * @param user User who calls default challenge loading - * @param world Target world. - * @return true if everything was successful, otherwise false. - */ - public boolean loadDefaultChallenges(User user, World world) - { - ChallengesManager manager = this.addon.getChallengesManager(); - - // If exist any challenge or level that is bound to current world, then do not load default challenges. - if (manager.hasAnyChallengeData(world.getName())) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.exist-challenges-or-levels"); - } - else - { - this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); - } - - return false; - } - - // default configuration should be removed. - // user made configuration should not!. - boolean removeAtEnd = - !Files.exists(Paths.get(this.addon.getDataFolder().getPath() + "/default.json")); - - // Safe json configuration to Challenges folder. - this.addon.saveResource("default.json", false); - - try - { - // This prefix will be used to all challenges. That is a unique way how to separate challenged for - // each game mode. - String uniqueIDPrefix = Utils.getGameMode(world) + "_"; - DefaultDataHolder defaultChallenges = new DefaultJSONHandler(this.addon).loadObject(); - if (defaultChallenges != null) { - // All new challenges should get correct ID. So we need to map it to loaded challenges. - defaultChallenges.getChallengeList().forEach(challenge -> { - // Set correct challenge ID - challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); - // Set up correct level ID if it is necessary - if (!challenge.getLevel().isEmpty()) - { - challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); - } - // Load challenge in memory - manager.loadChallenge(challenge, false, user, user == null); - }); - - defaultChallenges.getLevelList().forEach(challengeLevel -> { - // Set correct level ID - challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); - // Set correct world name - challengeLevel.setWorld(Util.getWorld(world).getName()); - // Reset names for all challenges. - challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> uniqueIDPrefix + challenge). - collect(Collectors.toSet())); - // Load level in memory - manager.loadLevel(challengeLevel, false, user, user == null); - }); - } - } - catch (Exception e) - { - e.printStackTrace(); - return false; - } - - this.addon.getChallengesManager().saveChallenges(); - this.addon.getChallengesManager().saveLevels(); - - if (removeAtEnd) - { - // Remove default.yml file from resources to avoid interacting with it. - return new File(this.addon.getDataFolder(), "default.json").delete(); - } - - return true; - } - - - /** - * This method loads downloaded challenges into memory. - * @param user User who calls downloaded challenge loading - * @param world Target world. - * @param downloadString String that need to be loaded via DefaultDataHolder. - * @return true if everything was successful, otherwise false. - */ - public boolean loadDownloadedChallenges(User user, World world, String downloadString) - { - ChallengesManager manager = this.addon.getChallengesManager(); - - // If exist any challenge or level that is bound to current world, then do not load default challenges. - if (manager.hasAnyChallengeData(world.getName())) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.exist-challenges-or-levels"); - } - else - { - this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); - } - - return false; - } - - try - { - // This prefix will be used to all challenges. That is a unique way how to separate challenged for - // each game mode. - String uniqueIDPrefix = Utils.getGameMode(world) + "_"; - DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadWebObject(downloadString); - - // All new challenges should get correct ID. So we need to map it to loaded challenges. - downloadedChallenges.getChallengeList().forEach(challenge -> { - // Set correct challenge ID - challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); - // Set up correct level ID if it is necessary - if (!challenge.getLevel().isEmpty()) - { - challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); - } - // Load challenge in memory - manager.loadChallenge(challenge, false, user, user == null); - }); - - downloadedChallenges.getLevelList().forEach(challengeLevel -> { - // Set correct level ID - challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); - // Set correct world name - challengeLevel.setWorld(Util.getWorld(world).getName()); - // Reset names for all challenges. - challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> uniqueIDPrefix + challenge). - collect(Collectors.toSet())); - // Load level in memory - manager.loadLevel(challengeLevel, false, user, user == null); - }); - } - catch (Exception e) - { - addon.getPlugin().logStacktrace(e); - return false; - } - - this.addon.getChallengesManager().saveChallenges(); - this.addon.getChallengesManager().saveLevels(); - - return true; - } - - - // --------------------------------------------------------------------- - // Section: Default generation - // --------------------------------------------------------------------- - - - /** - * Create method that can generate default challenge file from existing challenges in given world. - * This method will create default.json file in Challenges folder. - * @param user User who calls this method. - * @param world from which challenges must be stored. - * @param overwrite indicates if existing default.json file can be overwritten. - * @return true if everything was successful, otherwise false - */ - public boolean generateDefaultChallengeFile(User user, World world, boolean overwrite) - { - File defaultFile = new File(this.addon.getDataFolder(), "default.json"); - - if (defaultFile.exists()) - { - if (overwrite) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.messages.defaults-file-overwrite"); - } - else - { - this.addon.logWarning("challenges.messages.defaults-file-overwrite"); - } - - if (!defaultFile.delete()) { - this.addon.logError("Could not delete file: " + defaultFile.getAbsolutePath()); - } - } - else - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.defaults-file-exist"); - } - else - { - this.addon.logWarning("challenges.errors.defaults-file-exist"); - } - - return false; - } - } - - try - { - if (defaultFile.createNewFile()) - { - String replacementString = Utils.getGameMode(world) + "_"; - ChallengesManager manager = this.addon.getChallengesManager(); - - List challengeList = manager.getAllChallenges(world). - stream(). - map(challenge -> { - // Use clone to avoid any changes in existing challenges. - Challenge clone = challenge.clone(); - // Remove world name from challenge id. - clone.setUniqueId(challenge.getUniqueId().replaceFirst(replacementString, "")); - // Remove world name from level id. - clone.setLevel(challenge.getLevel().replaceFirst(replacementString, "")); - - return clone; - }). - collect(Collectors.toList()); - - List levelList = manager.getLevels(world). - stream(). - map(challengeLevel -> { - // Use clone to avoid any changes in existing levels. - ChallengeLevel clone = challengeLevel.clone(); - // Remove world name from level ID. - clone.setUniqueId(challengeLevel.getUniqueId().replaceFirst(replacementString, "")); - // Remove world name. - clone.setWorld(""); - // Challenges must be reassign, as they also contains world name. - clone.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> challenge.replaceFirst(replacementString, "")). - collect(Collectors.toSet())); - - return clone; - }). - collect(Collectors.toList()); - - DefaultDataHolder defaultChallenges = new DefaultDataHolder(); - defaultChallenges.setChallengeList(challengeList); - defaultChallenges.setLevelList(levelList); - defaultChallenges.setVersion(this.addon.getDescription().getVersion()); - - try (BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(defaultFile), StandardCharsets.UTF_8))) { - writer.write(Objects.requireNonNull( - new DefaultJSONHandler(this.addon).toJsonString(defaultChallenges))); - } - } - } - catch (IOException e) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.defaults-file-error"); - } - - this.addon.logError("Could not save json file: " + e.getMessage()); - return false; - } - finally - { - if (user.isPlayer()) - { - user.sendMessage("challenges.messages.defaults-file-completed", "[world]", world.getName()); - } - else - { - this.addon.logWarning("challenges.messages.defaults-file-completed"); - } - } - - return true; - } - - - // --------------------------------------------------------------------- - // Section: Private classes for default challenges - // --------------------------------------------------------------------- - - - /** - * This Class allows to load default challenges and their levels as objects much easier. - */ - private static final class DefaultJSONHandler - { - /** - * This constructor inits JSON builder that will be used to parse challenges. - * @param addon Challenges Adddon - */ - DefaultJSONHandler(ChallengesAddon addon) - { - GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); - // Register adapters - builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(addon.getPlugin())); - // Keep null in the database - builder.serializeNulls(); - // Allow characters like < or > without escaping them - builder.disableHtmlEscaping(); - - this.addon = addon; - this.gson = builder.setPrettyPrinting().create(); - } - - - /** - * This method returns json object that is parsed to string. Json object is made from given instance. - * @param instance Instance that must be parsed to json string. - * @return String that contains JSON information from instance object. - */ - String toJsonString(DefaultDataHolder instance) - { - // Null check - if (instance == null) - { - this.addon.logError("JSON database request to store a null. "); - return null; - } - - return this.gson.toJson(instance); - } - - - /** - * This method creates and adds to list all objects from default.json file. - * @return List of all objects from default.json that is with T instance. - */ - DefaultDataHolder loadObject() - { - File defaultFile = new File(this.addon.getDataFolder(), "default.json"); - - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(defaultFile), StandardCharsets.UTF_8)) - { - DefaultDataHolder object = this.gson.fromJson(reader, DefaultDataHolder.class); - - reader.close(); // NOSONAR Required to keep OS file handlers low and not rely on GC - - return object; - } - catch (FileNotFoundException e) - { - this.addon.logError("Could not load file '" + defaultFile.getName() + "': File not found."); - } - catch (Exception e) - { - this.addon.logError("Could not load objects " + defaultFile.getName() + " " + e.getMessage()); - } - - return null; - } - - - /** - * This method creates and adds to list all objects from default.json file. - * @return List of all objects from default.json that is with T instance. - */ - DefaultDataHolder loadWebObject(String downloadedObject) - { - return this.gson.fromJson(downloadedObject, DefaultDataHolder.class); - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * Holds JSON builder object. - */ - private Gson gson; - - /** - * Holds ChallengesAddon object. - */ - private ChallengesAddon addon; - } - - - /** - * This is simple object that will allow to store all current challenges and levels - * in single file. - */ - private static final class DefaultDataHolder implements DataObject - { - /** - * Default constructor. Creates object with empty lists. - */ - DefaultDataHolder() - { - this.challengeList = Collections.emptyList(); - this.challengeLevelList = Collections.emptyList(); - this.version = ""; - } - - - /** - * This method returns stored challenge list. - * @return list that contains default challenges. - */ - List getChallengeList() - { - return challengeList; - } - - - /** - * This method sets given list as default challenge list. - * @param challengeList new default challenge list. - */ - void setChallengeList(List challengeList) - { - this.challengeList = challengeList; - } - - - /** - * This method returns list of default challenge levels. - * @return List that contains default challenge levels. - */ - List getLevelList() - { - return challengeLevelList; - } - - - /** - * This method sets given list as default challenge level list. - * @param levelList new default challenge level list. - */ - void setLevelList(List levelList) - { - this.challengeLevelList = levelList; - } - - - /** - * This method returns the version value. - * @return the value of version. - */ - public String getVersion() - { - return version; - } - - - /** - * This method sets the version value. - * @param version the version new value. - * - */ - public void setVersion(String version) - { - this.version = version; - } - - - /** - * @return default.json - */ - @Override - public String getUniqueId() - { - return "default.json"; - } - - - /** - * @param uniqueId - unique ID the uniqueId to set - */ - @Override - public void setUniqueId(String uniqueId) - { - // method not used. - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * Holds a list with default challenges. - */ - @Expose - private List challengeList; - - /** - * Holds a list with default levels. - */ - @Expose - private List challengeLevelList; - - /** - * Holds a variable that stores in which addon version file was made. - */ - @Expose - private String version; - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - private ChallengesAddon addon; -} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/ChallengesPladdon.java b/src/main/java/world/bentobox/challenges/ChallengesPladdon.java new file mode 100644 index 0000000..b636f7a --- /dev/null +++ b/src/main/java/world/bentobox/challenges/ChallengesPladdon.java @@ -0,0 +1,30 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges; + + +import org.bukkit.plugin.java.annotation.dependency.Dependency; +import org.bukkit.plugin.java.annotation.plugin.ApiVersion; +import org.bukkit.plugin.java.annotation.plugin.Plugin; + +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.addons.Pladdon; + +/** + * @author tastybento + */ +@Plugin(name="Pladdon", version="1.0") +@ApiVersion(ApiVersion.Target.v1_17) +@Dependency(value = "BentoBox") +public class ChallengesPladdon extends Pladdon +{ + @Override + public Addon getAddon() + { + return new ChallengesAddon(); + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java deleted file mode 100644 index 7012c9b..0000000 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java +++ /dev/null @@ -1,96 +0,0 @@ -package world.bentobox.challenges.commands; - -import java.util.List; - -import world.bentobox.bentobox.api.addons.GameModeAddon; -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.user.ChallengesGUI; - - -public class ChallengesCommand extends CompositeCommand -{ - public static final String CHALLENGE_COMMAND = "challenges"; - - - public ChallengesCommand(ChallengesAddon addon, CompositeCommand cmd) - { - super(addon, cmd, CHALLENGE_COMMAND); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean canExecute(User user, String label, List args) - { - if (!getIWM().inWorld(getWorld())) { - // Not a GameMode world. - user.sendMessage("general.errors.wrong-world"); - return false; - } - - if (!((ChallengesAddon) this.getAddon()).getChallengesManager().hasAnyChallengeData(this.getWorld())) - { - // Do not open gui if there is no challenges. - this.getAddon().logError("There are no challenges set up in " + this.getWorld() + "!"); - - // Show admin better explanation. - if (user.isOp() || user.hasPermission(this.getPermissionPrefix() + "admin.challenges")) - { - String topLabel = getIWM().getAddon(this.getWorld()) - .map(GameModeAddon::getAdminCommand) - .map(optionalAdminCommand -> optionalAdminCommand.map(ac -> ac.getTopLabel()).orElse(this.getTopLabel())).orElse(this.getTopLabel()); - user.sendMessage("challenges.errors.no-challenges-admin", "[command]", topLabel + " challenges"); - } - else - { - user.sendMessage("challenges.errors.no-challenges"); - } - - return false; - } - - if (this.getIslands().getIsland(this.getWorld(), user) == null) - { - // Do not open gui if there is no island for this player. - user.sendMessage("general.errors.no-island"); - return false; - } - - return true; - } - - - @Override - public boolean execute(User user, String label, List args) - { - // Open up the challenges GUI - if (user.isPlayer()) - { - new ChallengesGUI((ChallengesAddon) this.getAddon(), - this.getWorld(), - user, - this.getTopLabel(), - this.getPermissionPrefix()).build(); - return true; - } - // Show help - showHelp(this, user); - return false; - } - - - @Override - public void setup() - { - this.setPermission(CHALLENGE_COMMAND); - this.setParametersHelp("challenges.commands.user.main.parameters"); - this.setDescription("challenges.commands.user.main.description"); - - new CompleteChallengeCommand(this.getAddon(), this); - this.setOnlyPlayer(true); - } -} diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java similarity index 52% rename from src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java rename to src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java index 4c90d22..6583866 100644 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesUserCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java @@ -8,27 +8,27 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.config.SettingsUtils.GuiMode; -import world.bentobox.challenges.panel.GameModesGUI; +import world.bentobox.challenges.panel.user.GameModePanel; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; /** * This class provides all necessary thing to implement challenges user command */ -public class ChallengesUserCommand extends CompositeCommand +public class ChallengesGlobalPlayerCommand extends CompositeCommand { /** - * Constructor that inits command with given string. + * Constructor that init command with given string. * @param addon Challenges Addon - * @param commands String that contains main command and its alias separated via whitespace. * @param gameModeAddons List with GameModes where challenges addon operates. */ - public ChallengesUserCommand(ChallengesAddon addon, String commands, List gameModeAddons) + public ChallengesGlobalPlayerCommand(ChallengesAddon addon, List gameModeAddons) { super(addon, - commands.split(" ")[0], - commands.split(" ")); + addon.getChallengesSettings().getPlayerGlobalCommand().split(" ")[0], + addon.getChallengesSettings().getPlayerGlobalCommand().split(" ")); this.gameModeAddons = gameModeAddons; - this.addon = addon; } @@ -39,7 +39,7 @@ public class ChallengesUserCommand extends CompositeCommand public void setup() { this.setOnlyPlayer(true); - this.setPermission("challenges"); + this.setPermission("addon.challenges"); this.setParametersHelp("challenges.commands.user.main.parameters"); this.setDescription("challenges.commands.user.main.description"); } @@ -53,13 +53,19 @@ public class ChallengesUserCommand extends CompositeCommand { // It is not necessary to check 0, as in that case addon will not be hooked. - if (this.gameModeAddons.size() == 1) + if (this.gameModeAddons.isEmpty()) + { + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "not-hooked")); + return false; + } + else if (this.gameModeAddons.size() == 1) { this.gameModeAddons.get(0).getPlayerCommand().ifPresent(compositeCommand -> - user.performCommand(compositeCommand.getTopLabel() + " challenges")); + user.performCommand(compositeCommand.getTopLabel() + " " + + this.getAddon().getChallengesSettings().getPlayerMainCommand().split(" ")[0])); return true; } - else if (this.addon.getChallengesSettings().getUserGuiMode() == GuiMode.CURRENT_WORLD) + else if (this.getAddon().getChallengesSettings().getUserGuiMode() == GuiMode.CURRENT_WORLD) { // Find GameMode and run command for (GameModeAddon addon : this.gameModeAddons) @@ -67,21 +73,22 @@ public class ChallengesUserCommand extends CompositeCommand if (addon.inWorld(user.getWorld())) { addon.getPlayerCommand().ifPresent(compositeCommand -> - user.performCommand(compositeCommand.getTopLabel() + " challenges")); + user.performCommand(compositeCommand.getTopLabel() + " " + + this.getAddon().getChallengesSettings().getPlayerMainCommand().split(" ")[0])); return true; } } + + Utils.sendMessage(user, user.getTranslation("general.errors.wrong-world")); } - else if (this.addon.getChallengesSettings().getUserGuiMode() == GuiMode.GAMEMODE_LIST) + else if (this.getAddon().getChallengesSettings().getUserGuiMode() == GuiMode.GAMEMODE_LIST) { - new GameModesGUI(this.addon, + GameModePanel.open(this.getAddon(), this.getWorld(), user, - this.getTopLabel(), - this.getPermissionPrefix(), - false, - this.gameModeAddons).build(); + this.gameModeAddons, + false); return true; } @@ -97,10 +104,5 @@ public class ChallengesUserCommand extends CompositeCommand /** * List with hooked GameMode addons. */ - private List gameModeAddons; - - /** - * Challenges addon for easier operations. - */ - private ChallengesAddon addon; + private final List gameModeAddons; } diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java new file mode 100644 index 0000000..fe2fd6e --- /dev/null +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java @@ -0,0 +1,110 @@ +package world.bentobox.challenges.commands; + +import java.util.List; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.user.ChallengesPanel; +import world.bentobox.challenges.utils.Utils; + + +public class ChallengesPlayerCommand extends CompositeCommand +{ + public ChallengesPlayerCommand(ChallengesAddon addon, CompositeCommand cmd) + { + super(addon, + cmd, + addon.getChallengesSettings().getPlayerMainCommand().split(" ")[0], + addon.getChallengesSettings().getPlayerMainCommand().split(" ")); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean canExecute(User user, String label, List args) + { + if (!this.getIWM().inWorld(user.getWorld()) || + !Util.sameWorld(this.getWorld(), user.getWorld())) { + // Not a GameMode world. + Utils.sendMessage(user, user.getTranslation("general.errors.wrong-world")); + return false; + } + + if (!((ChallengesAddon) this.getAddon()).getChallengesManager().hasAnyChallengeData(this.getWorld())) + { + // Do not open gui if there is no challenges. + this.getAddon().logError("There are no challenges set up in " + this.getWorld() + "!"); + + // Show admin better explanation. + if (user.isOp() || user.hasPermission(this.getPermissionPrefix() + "admin.challenges")) + { + String topLabel = this.getIWM().getAddon(this.getWorld()). + map(GameModeAddon::getAdminCommand). + map(optionalAdminCommand -> optionalAdminCommand.map(CompositeCommand::getTopLabel).orElse(this.getTopLabel())). + orElse(this.getTopLabel()); + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-challenges-admin", + "[command]", + topLabel + " " + this.getAddon().getChallengesSettings().getAdminMainCommand().split(" ")[0])); + } + else + { + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-challenges")); + } + + return false; + } + + if (this.getIslands().getIsland(this.getWorld(), user) == null) + { + // Do not open gui if there is no island for this player. + Utils.sendMessage(user, user.getTranslation("general.errors.no-island")); + return false; + } else if (ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.getWorld()) && + !this.getIslands().locationIsOnIsland(user.getPlayer(), user.getLocation())) + { + // Do not open gui if player is not on the island, but challenges requires island for + // completion. + Utils.sendMessage(user, user.getTranslation("challenges.errors.not-on-island")); + return false; + } + + return true; + } + + + @Override + public boolean execute(User user, String label, List args) + { + // Open up the challenges GUI + if (user.isPlayer()) + { + ChallengesPanel.open(this.getAddon(), + this.getWorld(), + user, + this.getTopLabel(), + this.getPermissionPrefix()); + + return true; + } + // Show help + showHelp(this, user); + return false; + } + + + @Override + public void setup() + { + this.setPermission("challenges"); + this.setParametersHelp("challenges.commands.user.main.parameters"); + this.setDescription("challenges.commands.user.main.description"); + + new CompleteChallengeCommand(this.getAddon(), this); + this.setOnlyPlayer(true); + } +} diff --git a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java index 840759f..f59a1d5 100644 --- a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java @@ -4,9 +4,7 @@ package world.bentobox.challenges.commands; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; -import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; @@ -17,7 +15,7 @@ import world.bentobox.challenges.utils.Utils; /** - * This command allows to complete challenges without a gui. + * This command allows completing challenges without a gui. */ public class CompleteChallengeCommand extends CompositeCommand { @@ -26,10 +24,12 @@ public class CompleteChallengeCommand extends CompositeCommand * @param addon Challenges addon. * @param cmd Parent Command. */ - public CompleteChallengeCommand(Addon addon, CompositeCommand cmd) + public CompleteChallengeCommand(ChallengesAddon addon, CompositeCommand cmd) { - super(addon, cmd, "complete"); - this.addon = (ChallengesAddon) addon; + super(addon, + cmd, + addon.getChallengesSettings().getPlayerCompleteCommand().split(" ")[0], + addon.getChallengesSettings().getPlayerCompleteCommand().split(" ")); } @@ -40,7 +40,7 @@ public class CompleteChallengeCommand extends CompositeCommand public void setup() { this.setOnlyPlayer(true); - this.setPermission("complete"); + this.setPermission("challenges"); this.setParametersHelp("challenges.commands.user.complete.parameters"); this.setDescription("challenges.commands.user.complete.description"); } @@ -54,7 +54,7 @@ public class CompleteChallengeCommand extends CompositeCommand { if (args.isEmpty()) { - user.sendMessage("challenges.errors.no-name"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); this.showHelp(this, user); return false; } @@ -62,22 +62,22 @@ public class CompleteChallengeCommand extends CompositeCommand { // Add world name back at the start String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(0); - Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName); + Challenge challenge = this.getAddon().getChallengesManager().getChallenge(challengeName); if (challenge != null) { - int count = args.size() == 2 ? Integer.valueOf(args.get(1)) : 1; + int count = args.size() == 2 ? Integer.parseInt(args.get(1)) : 1; boolean canMultipleTimes = user.hasPermission(this.getPermission() + ".multiple"); if (!canMultipleTimes && count > 1) { - user.sendMessage("challenges.error.no-multiple-permission"); + Utils.sendMessage(user, user.getTranslation("challenges.error.no-multiple-permission")); count = 1; } - return TryToComplete.complete(this.addon, + return TryToComplete.complete(this.getAddon(), user, challenge, this.getWorld(), @@ -87,7 +87,7 @@ public class CompleteChallengeCommand extends CompositeCommand } else { - user.sendMessage("challenges.errors.unknown-challenge"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); this.showHelp(this, user); return false; } @@ -113,10 +113,11 @@ public class CompleteChallengeCommand extends CompositeCommand case 3: // Create suggestions with all challenges that is available for users. - returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream(). - filter(challenge -> challenge.startsWith(Utils.getGameMode(this.getWorld()) + "_")). - map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)). - collect(Collectors.toList())); + returnList.addAll(this.getAddon().getChallengesManager().getAllChallengesNames(this.getWorld()). + stream(). + filter(challenge -> challenge.startsWith(Utils.getGameMode(this.getWorld()) + "_") || + challenge.startsWith(Utils.getGameMode(this.getWorld()).toLowerCase() + "_")). + map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).toList()); break; case 4: // Suggest a number of completions. @@ -135,14 +136,4 @@ public class CompleteChallengeCommand extends CompositeCommand return Optional.of(Util.tabLimit(returnList, lastString)); } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * Variable that holds challenge addon. Single casting. - */ - private ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java b/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java deleted file mode 100644 index 1567503..0000000 --- a/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java +++ /dev/null @@ -1,68 +0,0 @@ -package world.bentobox.challenges.commands.admin; - -import java.util.List; - -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.admin.AdminGUI; - - -public class Challenges extends CompositeCommand -{ - - /** - * Admin command for challenges - * - * @param parent - */ - public Challenges(ChallengesAddon addon, CompositeCommand parent) - { - super(addon, parent, "challenges"); - } - - - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.main.parameters"); - this.setDescription("challenges.commands.admin.main.description"); - - // Register sub commands - - // This method reloads challenges addon - new ReloadChallenges(getAddon(), this); - - // Defaults processing command - new DefaultsCommand(this.getAddon(), this); - - // Complete challenge command - new CompleteCommand(this.getAddon(), this); - - // Reset challenge command - new ResetCommand(this.getAddon(), this); - - new ShowChallenges(this.getAddon(), this); - - new MigrateCommand(this.getAddon(), this); - } - - - @Override - public boolean execute(User user, String label, List args) - { - // Open up the admin challenges GUI - if (user.isPlayer()) - { - new AdminGUI((ChallengesAddon) this.getAddon(), - this.getWorld(), - user, - this.getTopLabel(), - this.getPermissionPrefix()).build(); - - return true; - } - return false; - } -} diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ChallengesAdminCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesAdminCommand.java index 3137d8c..57f9efc 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ChallengesAdminCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesAdminCommand.java @@ -1,87 +1,68 @@ package world.bentobox.challenges.commands.admin; - import java.util.List; -import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.GameModesGUI; +import world.bentobox.challenges.panel.admin.AdminPanel; -/** - * This class provides all necessary thing to implement challenges admin command - */ public class ChallengesAdminCommand extends CompositeCommand { - /** - * Constructor that inits command with given string. - * @param addon Challenges Addon - * @param commands String that contains main command and its alias separated via whitespace. - * @param gameModeAddons List with GameModes where challenges addon operates. - */ - public ChallengesAdminCommand(ChallengesAddon addon, String commands, List gameModeAddons) - { - super(addon, - commands.split(" ")[0], - commands.split(" ")); - this.gameModeAddons = gameModeAddons; - this.addon = addon; - } + /** + * Instantiates a new Challenges' admin command. + * + * @param addon the addon + * @param parent the parent + */ + public ChallengesAdminCommand(ChallengesAddon addon, CompositeCommand parent) + { + super(addon, + parent, + addon.getChallengesSettings().getAdminMainCommand().split(" ")[0], + addon.getChallengesSettings().getAdminMainCommand().split(" ")); + } - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.main.parameters"); - this.setDescription("challenges.commands.admin.main.description"); - } + @Override + public void setup() + { + this.setPermission("admin.challenges"); + this.setParametersHelp("challenges.commands.admin.main.parameters"); + this.setDescription("challenges.commands.admin.main.description"); + + // Register sub commands + + // This method reloads challenges addon + new ReloadChallenges(getAddon(), this); + + // Complete challenge command + new CompleteCommand(this.getAddon(), this); + + // Reset challenge command + new ResetCommand(this.getAddon(), this); + + new ShowChallenges(this.getAddon(), this); + + new MigrateCommand(this.getAddon(), this); + } - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - // For single game mode just open correct gui. + @Override + public boolean execute(User user, String label, List args) + { + // Open up the admin challenges GUI + if (user.isPlayer()) + { + AdminPanel.open(this.getAddon(), + this.getWorld(), + user, + this.getTopLabel(), + this.getPermissionPrefix()); - if (this.gameModeAddons.size() == 1) - { - this.gameModeAddons.get(0).getAdminCommand().ifPresent(compositeCommand -> - user.performCommand(compositeCommand.getTopLabel() + " challenges")); - } - else - { - new GameModesGUI(this.addon, - this.getWorld(), - user, - this.getTopLabel(), - this.getPermissionPrefix(), - true, - this.gameModeAddons).build(); - } - - return true; - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * This variable stores challenges addon. - */ - private ChallengesAddon addon; - - /** - * This variable stores List with game modes where challenges addon are hooked in. - */ - private List gameModeAddons; + return true; + } + return false; + } } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java new file mode 100644 index 0000000..0e09c2b --- /dev/null +++ b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java @@ -0,0 +1,87 @@ +package world.bentobox.challenges.commands.admin; + + +import java.util.List; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.user.GameModePanel; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class provides all necessary thing to implement challenges admin command + */ +public class ChallengesGlobalAdminCommand extends CompositeCommand +{ + /** + * Constructor that init command with given string. + * @param addon Challenges Addon + * @param gameModeAddons List with GameModes where challenges addon operates. + */ + public ChallengesGlobalAdminCommand(ChallengesAddon addon, List gameModeAddons) + { + super(addon, + addon.getChallengesSettings().getAdminGlobalCommand().split(" ")[0], + addon.getChallengesSettings().getAdminGlobalCommand().split(" ")); + this.gameModeAddons = gameModeAddons; + } + + + /** + * {@inheritDoc} + */ + @Override + public void setup() + { + this.setPermission("addon.admin.challenges"); + this.setParametersHelp("challenges.commands.admin.main.parameters"); + this.setDescription("challenges.commands.admin.main.description"); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean execute(User user, String label, List args) + { + // For single game mode just open correct gui. + + if (this.gameModeAddons.isEmpty()) + { + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "not-hooked")); + return false; + } + else if (this.gameModeAddons.size() == 1) + { + this.gameModeAddons.get(0).getAdminCommand().ifPresent(compositeCommand -> + user.performCommand(compositeCommand.getTopLabel() + " " + + this.getAddon().getChallengesSettings().getAdminMainCommand().split(" ")[0])); + } + else + { + GameModePanel.open(this.getAddon(), + this.getWorld(), + user, + this.gameModeAddons, + true); + } + + return true; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * This variable stores List with game modes where challenges addon are hooked in. + */ + private final List gameModeAddons; +} diff --git a/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java index d0d3f67..24f8913 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java @@ -2,11 +2,9 @@ package world.bentobox.challenges.commands.admin; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; -import java.util.stream.Collectors; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.commands.CompositeCommand; @@ -15,11 +13,12 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; /** - * This command allows to complete challenges without a gui. + * This command allows completing challenges without a gui. */ public class CompleteCommand extends CompositeCommand { @@ -57,7 +56,7 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.no-name"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); } else { @@ -68,7 +67,7 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.missing-arguments"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.missing-arguments")); } else { @@ -83,9 +82,9 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("general.errors.unknown-player", + Utils.sendMessage(user, user.getTranslation("general.errors.unknown-player", TextVariables.NAME, - args.get(0)); + args.get(0))); } else { @@ -98,6 +97,7 @@ public class CompleteCommand extends CompositeCommand // Add world name back at the start String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(1); Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName); + User target = User.getInstance(targetUUID); if (challenge != null) { @@ -106,23 +106,24 @@ public class CompleteCommand extends CompositeCommand this.addon.getChallengesManager().setChallengeComplete( targetUUID, this.getWorld(), challenge, user.getUniqueId()); + if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.completed", - "[name]", challenge.getFriendlyName(), - "[player]", User.getInstance(targetUUID).getName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.completed", + Constants.PARAMETER_NAME, challenge.getFriendlyName(), + Constants.PARAMETER_PLAYER, target.getName())); } else { this.addon.log("Challenge " + challenge.getFriendlyName() + " completed for player " + - User.getInstance(targetUUID).getName()); + target.getName()); } } else { if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.already-completed"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.already-completed")); } else { @@ -136,7 +137,7 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.unknown-challenge"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); } else { @@ -165,23 +166,15 @@ public class CompleteCommand extends CompositeCommand switch (size) { - case 3: + case 3 -> // Create suggestions with all challenges that is available for users. - returnList.addAll(Util.getOnlinePlayerList(user)); - break; - case 4: + case 4 -> // Create suggestions with all challenges that is available for users. returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream(). - map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)). - collect(Collectors.toList())); - - break; - default: - { - returnList.addAll(Collections.singletonList("help")); - break; - } + map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).toList()); + default -> + returnList.add("help"); } return Optional.of(Util.tabLimit(returnList, lastString)); @@ -195,5 +188,5 @@ public class CompleteCommand extends CompositeCommand /** * Variable that holds challenge addon. Single casting. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/DefaultsCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/DefaultsCommand.java deleted file mode 100644 index df9d7a9..0000000 --- a/src/main/java/world/bentobox/challenges/commands/admin/DefaultsCommand.java +++ /dev/null @@ -1,165 +0,0 @@ -package world.bentobox.challenges.commands.admin; - - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import world.bentobox.bentobox.api.addons.Addon; -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.util.Util; -import world.bentobox.challenges.ChallengesAddon; - - -/** - * This method generates default challenges file. - */ -public class DefaultsCommand extends CompositeCommand -{ - - /** - * Constructor that inits generate defaults command. - * - * @param addon Addon that inits this command - * @param cmd Parent command - */ - public DefaultsCommand(Addon addon, CompositeCommand cmd) - { - super(addon, cmd, "defaults"); - this.addon = (ChallengesAddon) addon; - } - - - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.defaults.parameters"); - this.setDescription("challenges.commands.admin.defaults.description"); - - // Register sub commands - // This method reloads challenges addon - new ImportCommand(this); - // Import ASkyBlock Challenges - new GenerateCommand(this); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - return this.showHelp(this, user); - } - - -// --------------------------------------------------------------------- -// Section: Private Classes -// --------------------------------------------------------------------- - - - /** - * This class allows to process import command. - */ - private class ImportCommand extends CompositeCommand - { - /** - * Default constructor for import method. - * @param parent composite command - */ - private ImportCommand(CompositeCommand parent) - { - super(DefaultsCommand.this.addon, parent, "import"); - } - - - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.defaults-import.parameters"); - this.setDescription("challenges.commands.admin.defaults-import.description"); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - return DefaultsCommand.this.addon.getImportManager().loadDefaultChallenges(user, this.getWorld()); - } - } - - - /** - * This class allows to process generate command. - */ - private class GenerateCommand extends CompositeCommand - { - /** - * Default constructor for generate method. - * @param parent composite command - */ - private GenerateCommand(CompositeCommand parent) - { - super(DefaultsCommand.this.addon, parent, "generate"); - } - - - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setPermission("admin.challenges"); - this.setParametersHelp("challenges.commands.admin.defaults-generate.parameters"); - this.setDescription("challenges.commands.admin.defaults-generate.description"); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - return DefaultsCommand.this.addon.getImportManager().generateDefaultChallengeFile( - user, - this.getWorld(), - !args.isEmpty() && args.get(0).equalsIgnoreCase("overwrite")); - } - - - /** - * {@inheritDoc} - */ - @Override - public Optional> tabComplete(User user, String alias, List args) - { - String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : ""; - return Optional.of(Util.tabLimit(Collections.singletonList("overwrite"), lastArg)); - } - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * Holds challenges addon as variable. - */ - private ChallengesAddon addon; -} diff --git a/src/main/java/world/bentobox/challenges/commands/admin/MigrateCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/MigrateCommand.java index cfcadfd..17ed6d5 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/MigrateCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/MigrateCommand.java @@ -9,27 +9,32 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; -public class MigrateCommand extends CompositeCommand { - +public class MigrateCommand extends CompositeCommand +{ /** - * Migrates challenges - * @param addon - * @param cmd + * Instantiates a new Migrate command command. + * + * @param addon the addon + * @param cmd the cmd */ - public MigrateCommand(Addon addon, CompositeCommand cmd) { + public MigrateCommand(Addon addon, CompositeCommand cmd) + { super(addon, cmd, "migrate"); } + @Override - public boolean execute(User user, String label, List args) { - ((ChallengesAddon)getAddon()).getChallengesManager().migrateDatabase(user, getWorld()); + public boolean execute(User user, String label, List args) + { + ((ChallengesAddon) getAddon()).getChallengesManager().migrateDatabase(user, getWorld()); return true; } @Override - public void setup() { + public void setup() + { this.setPermission("challenges.admin"); this.setParametersHelp("challenges.commands.admin.migrate.parameters"); this.setDescription("challenges.commands.admin.migrate.description"); diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java b/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java index 6022895..0dd7ea9 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java @@ -6,17 +6,20 @@ import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; +import world.bentobox.challenges.utils.Utils; /** - * This class allows to reload challenges addon. + * This class allows reloading challenges addon. */ public class ReloadChallenges extends CompositeCommand { /** - * Admin command to reloads challenges addon. - * @param parent + * Instantiates a new Reload challenges command. + * + * @param addon the addon + * @param parent the parent */ public ReloadChallenges(Addon addon, CompositeCommand parent) { @@ -46,13 +49,13 @@ public class ReloadChallenges extends CompositeCommand if (args.isEmpty()) { this.manager.load(); - user.sendMessage("general.success"); + Utils.sendMessage(user, user.getTranslation("general.success")); return true; } else if (args.get(0).equalsIgnoreCase("hard")) { this.manager.reload(); - user.sendMessage("general.success"); + Utils.sendMessage(user, user.getTranslation("general.success")); return true; } else @@ -68,5 +71,8 @@ public class ReloadChallenges extends CompositeCommand // --------------------------------------------------------------------- - private ChallengesManager manager; + /** + * Addon Manager instance. + */ + private final ChallengesManager manager; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java index 649b728..8955da5 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java @@ -2,11 +2,9 @@ package world.bentobox.challenges.commands.admin; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; -import java.util.stream.Collectors; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.commands.CompositeCommand; @@ -15,11 +13,12 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; /** - * This command allows to reset challenges without a gui. + * This command allows resetting challenges without a gui. */ public class ResetCommand extends CompositeCommand { @@ -57,7 +56,7 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.no-name"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); } else { @@ -68,7 +67,7 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.missing-arguments"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.missing-arguments")); } else { @@ -83,7 +82,8 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); + Utils.sendMessage(user, user.getTranslation("general.errors.unknown-player", + TextVariables.NAME, args.get(0))); } else { @@ -92,7 +92,8 @@ public class ResetCommand extends CompositeCommand return false; } - + + User target = User.getInstance(targetUUID); // Add world name back at the start if (args.get(1).equals("all")) @@ -101,13 +102,12 @@ public class ResetCommand extends CompositeCommand if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.reset-all", - "[player]", User.getInstance(targetUUID).getName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.reset-all", + Constants.PARAMETER_PLAYER, target.getName())); } else { - this.addon.log("All challenges for user " + - User.getInstance(targetUUID).getName() + " was reset!"); + this.addon.log("All challenges for user " + target.getName() + " was reset!"); } return true; @@ -125,21 +125,21 @@ public class ResetCommand extends CompositeCommand if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.reset", - "[name]", challenge.getFriendlyName(), - "[player]", User.getInstance(targetUUID).getName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.reset", + Constants.PARAMETER_NAME, challenge.getFriendlyName(), + Constants.PARAMETER_PLAYER, target.getName())); } else { this.addon.log("Challenge " + challenge.getFriendlyName() + " was reset for player " + - User.getInstance(targetUUID).getName()); + target.getName()); } } else { if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.not-completed"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.not-completed")); } else { @@ -153,7 +153,7 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - user.sendMessage("challenges.errors.unknown-challenge"); + Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); } else { @@ -183,25 +183,17 @@ public class ResetCommand extends CompositeCommand switch (size) { - case 3: + case 3 -> // Create suggestions with all challenges that is available for users. - returnList.addAll(Util.getOnlinePlayerList(user)); - break; - case 4: + case 4 -> { // Create suggestions with all challenges that is available for users. returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream(). - map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)). - collect(Collectors.toList())); - + map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)).toList()); returnList.add("all"); - - break; - default: - { - returnList.addAll(Collections.singletonList("help")); - break; } + default -> + returnList.add("help"); } return Optional.of(Util.tabLimit(returnList, lastString)); @@ -215,5 +207,5 @@ public class ResetCommand extends CompositeCommand /** * Variable that holds challenge addon. Single casting. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java b/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java index a735dc3..3171d63 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java @@ -7,27 +7,32 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; -public class ShowChallenges extends CompositeCommand { - - +public class ShowChallenges extends CompositeCommand +{ /** - * Admin command to show challenges and manage them - * @param parent + * Instantiates a new Show challenges command. + * + * @param addon the addon + * @param parent the parent */ - public ShowChallenges(Addon addon, CompositeCommand parent) { + public ShowChallenges(Addon addon, CompositeCommand parent) + { super(addon, parent, "show"); } + @Override - public void setup() { + public void setup() + { this.setPermission("admin.challenges"); this.setParametersHelp("challenges.commands.admin.show.parameters"); this.setDescription("challenges.commands.admin.show.description"); - } + @Override - public boolean execute(User user, String label, List args) { + public boolean execute(User user, String label, List args) + { if (user.isPlayer()) { ((ChallengesAddon) getAddon()).getChallengesManager(). @@ -40,7 +45,5 @@ public class ShowChallenges extends CompositeCommand { } return true; - } - } diff --git a/src/main/java/world/bentobox/challenges/config/Settings.java b/src/main/java/world/bentobox/challenges/config/Settings.java index 2332b46..237fe12 100644 --- a/src/main/java/world/bentobox/challenges/config/Settings.java +++ b/src/main/java/world/bentobox/challenges/config/Settings.java @@ -1,9 +1,7 @@ package world.bentobox.challenges.config; -import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.bukkit.Material; @@ -13,15 +11,13 @@ import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.api.configuration.ConfigEntry; import world.bentobox.bentobox.api.configuration.ConfigObject; import world.bentobox.bentobox.api.configuration.StoreAt; -import world.bentobox.bentobox.database.objects.adapters.Adapter; -import world.bentobox.challenges.config.SettingsUtils.ChallengeLore; import world.bentobox.challenges.config.SettingsUtils.GuiMode; -import world.bentobox.challenges.config.SettingsUtils.LevelLore; import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; -import world.bentobox.challenges.database.object.adapters.ChallengeLoreAdapter; -import world.bentobox.challenges.database.object.adapters.LevelLoreAdapter; +/** + * The type Settings. + */ @StoreAt(filename="config.yml", path="addons/Challenges") @ConfigComment("Challenges [version] Configuration") @ConfigComment("This config file is dynamic and saved when the server is shutdown.") @@ -30,37 +26,60 @@ import world.bentobox.challenges.database.object.adapters.LevelLoreAdapter; @ConfigComment("") public class Settings implements ConfigObject { - @ConfigComment("") - @ConfigComment("Allows to define common challenges command that will open User GUI") - @ConfigComment("with all GameMode selection or Challenges from user world.") - @ConfigComment("This will not affect /{gamemode_user} challenges command.") - @ConfigEntry(path = "commands.user", needsRestart = true) - private String userCommand = "challenges c"; - - @ConfigComment("") - @ConfigComment("Allows to define common challenges command that will open Admin GUI") - @ConfigComment("with all GameMode selection.") - @ConfigComment("This will not affect /{gamemode_admin} challenges command.") - @ConfigEntry(path = "commands.admin", needsRestart = true) - private String adminCommand = "challengesadmin chadmin"; - @ConfigComment("") @ConfigComment("This enables/disables common command that will be independent from") @ConfigComment("all GameModes. For admins it will open selection with all GameModes") @ConfigComment("(unless there is one), but for users it will open GUI that corresponds") @ConfigComment("to their world (unless it is specified other way in Admin GUI).") - @ConfigEntry(path = "commands.single-gui", needsRestart = true) + @ConfigComment("This means that writing `/[user_global]` will open Challenges GUI's") + @ConfigComment("and `/[admin_global]` will open Admin GUI's") + @ConfigEntry(path = "commands.global-command", needsRestart = true) private boolean useCommonGUI = false; @ConfigComment("") - @ConfigComment("This allows for admins to define which GUI will be opened for admins") - @ConfigComment("when users calls single-gui command.") + @ConfigComment("This allows to define which GUI will be opened when `single-gui` is enabled.") + @ConfigComment("This option is ignored if `single-gui` is disabled.") @ConfigComment("Acceptable values:") @ConfigComment(" - CURRENT_WORLD - will open GUI that corresponds to user location.") @ConfigComment(" - GAMEMODE_LIST - will open GUI with all installed game modes.") - @ConfigEntry(path = "commands.single-gamemode") + @ConfigEntry(path = "commands.global-view-mode") private GuiMode userGuiMode = GuiMode.CURRENT_WORLD; + @ConfigComment("") + @ConfigComment("Allows to define a global challenges user command. This command will work") + @ConfigComment("only if `global-commands` is enabled. This allows to execute `/challenges`") + @ConfigComment("without referring to the gamemode.") + @ConfigEntry(path = "commands.player.global", needsRestart = true) + private String playerGlobalCommand = "challenges c"; + + @ConfigComment("") + @ConfigComment("Allows to define user command for opening challenges GUI's.") + @ConfigComment("Unlike `global` command, this requires to have gamemode player command before it.") + @ConfigComment("This will look like: `/[player_cmd] challenges`") + @ConfigEntry(path = "commands.player.main", needsRestart = true) + private String playerMainCommand = "challenges"; + + @ConfigComment("") + @ConfigComment("Allows to define complete command.") + @ConfigComment("This will look like: `/[player_cmd] challenges complete`") + @ConfigEntry(path = "commands.player.complete", needsRestart = true) + private String playerCompleteCommand = "complete"; + + @ConfigComment("") + @ConfigComment("Allows to define a global challenges admin command. This command will work") + @ConfigComment("only if `global-commands` is enabled. This allows to execute `/chadmin`") + @ConfigComment("without referring to the gamemode.") + @ConfigComment("Note, this must not be the same as user global command.") + @ConfigEntry(path = "commands.admin.global", needsRestart = true) + private String adminGlobalCommand = "challengesadmin chadmin"; + + @ConfigComment("") + @ConfigComment("Allows to define admin command for opening challenges GUI's.") + @ConfigComment("Unlike `global` command, this requires to have gamemode admin command before it.") + @ConfigComment("This will look like: `/[admin_cmd] challenges`") + @ConfigEntry(path = "commands.admin.main", needsRestart = true) + private String adminMainCommand = "challenges"; + @ConfigComment("") @ConfigComment("This indicate if player challenges data history will be stored or not.") @ConfigEntry(path = "history.store-history-data") @@ -99,62 +118,10 @@ public class Settings implements ConfigObject @ConfigEntry(path = "gui-settings.locked-level-icon") private ItemStack lockedLevelIcon = new ItemStack(Material.BOOK); - @ConfigComment("") - @ConfigComment("This indicate if free challenges must be at the start (true) or at the end (false) of list.") - @ConfigEntry(path = "gui-settings.free-challenges-first") - private boolean freeChallengesFirst = true; - - @ConfigComment("") - @ConfigComment("This allows to change lore description line length. By default it is 25, but some server") - @ConfigComment("owners may like it to be larger.") - @ConfigEntry(path = "gui-settings.lore-length") - private int loreLineLength = 25; - - @ConfigComment("") - @ConfigComment("This string allows to change element order in Challenge description. Each letter represents") - @ConfigComment("one object from challenge description. If letter is not used, then its represented part") - @ConfigComment("will not be in description. If use any letter that is not recognized, then it will be") - @ConfigComment("ignored. Some strings can be customized via lang file under 'challenges.gui.challenge-description'.") - @ConfigComment("List of values and their meaning: ") - @ConfigComment(" - LEVEL - Level String: '*.level'") - @ConfigComment(" - STATUS - Status String: '*.completed'") - @ConfigComment(" - COUNT - Times String: '*.completed-times', '*.completed-times-of' or '*.maxed-reached'") - @ConfigComment(" - DESCRIPTION - Description String: defined in challenge object - challenge.description") - @ConfigComment(" - WARNINGS - Warning String: '*.warning-items-take', '*.objects-close-by', '*.warning-entities-kill', '*.warning-blocks-remove'") - @ConfigComment(" - ENVIRONMENT - Environment String: defined in challenge object - challenge.environment") - @ConfigComment(" - REQUIREMENTS - Requirement String: '*.required-level', '*.required-money', '*.required-experience' and items, blocks or entities") - @ConfigComment(" - REWARD_TEXT - Reward String: message that is defined in challenge.rewardTest and challenge.repeatRewardText") - @ConfigComment(" - REWARD_OTHER - Reward extra String: '*.experience-reward', '*.money-reward', '*.not-repeatable'") - @ConfigComment(" - REWARD_ITEMS - Reward Items: List of items that will be rewarded.") - @ConfigComment(" - REWARD_COMMANDS - Reward Commands: List of commands that will be rewarded.") - @ConfigComment("Requirement and reward items, blocks and entities that are defined in challenge and can be customized under 'challenges.gui.item-description.*'") - @ConfigEntry(path = "gui-settings.challenge-lore") - @Adapter(ChallengeLoreAdapter.class) - private List challengeLoreMessage = Arrays.asList(ChallengeLore.values()); - - @ConfigComment("") - @ConfigComment("This string allows to change element order in Level description. Each letter represents") - @ConfigComment("one object from level description. If letter is not used, then its represented part") - @ConfigComment("will not be in description. If use any letter that is not recognized, then it will be") - @ConfigComment("ignored. Some strings can be customized via lang file under 'challenges.gui.level-description'.") - @ConfigComment("List of values and their meaning: ") - @ConfigComment(" - LEVEL_STATUS - Status String: '*.completed'") - @ConfigComment(" - CHALLENGE_COUNT - Count of completed challenges String: '*.completed-challenges-of'") - @ConfigComment(" - UNLOCK_MESSAGE - Description String: defined in level object - challengeLevel.unlockMessage") - @ConfigComment(" - WAIVER_AMOUNT - WaiverAmount String: '*.waver-amount'") - @ConfigComment(" - LEVEL_REWARD_TEXT - Reward String: message that is defined in challengeLevel.rewardText.") - @ConfigComment(" - LEVEL_REWARD_OTHER - Reward extra String: '*.experience-reward', '*.money-reward'") - @ConfigComment(" - LEVEL_REWARD_ITEMS - Reward Items: List of items that will be rewarded.") - @ConfigComment(" - LEVEL_REWARD_COMMANDS - Reward Commands: List of commands that will be rewarded.") - @ConfigComment("Reward items that are defined in challenge level and can be customized under 'challenges.gui.item-description.*'") - @ConfigEntry(path = "gui-settings.level-lore") - @Adapter(LevelLoreAdapter.class) - private List levelLoreMessage = Arrays.asList(LevelLore.values()); - @ConfigComment("") @ConfigComment("This indicate if challenges data will be stored per island (true) or per player (false).") @ConfigEntry(path = "store-island-data") - private boolean storeAsIslandData = false; + private boolean storeAsIslandData = true; @ConfigComment("") @ConfigComment("Reset Challenges - if this is true, player's challenges will reset when users") @@ -205,17 +172,6 @@ public class Settings implements ConfigObject // Section: Getters // --------------------------------------------------------------------- - - /** - * This method returns the challengeLoreMessage object. - * @return the challengeLoreMessage object. - */ - public List getChallengeLoreMessage() - { - return challengeLoreMessage; - } - - /** * This method returns the configVersion object. * @return the configVersion object. @@ -271,35 +227,6 @@ public class Settings implements ConfigObject } - /** - * @return freeChallengesFirst value. - */ - public boolean isFreeChallengesFirst() - { - return this.freeChallengesFirst; - } - - - /** - * This method returns the loreLineLength object. - * @return the loreLineLength object. - */ - public int getLoreLineLength() - { - return loreLineLength; - } - - - /** - * This method returns the levelLoreMessage object. - * @return the levelLoreMessage object. - */ - public List getLevelLoreMessage() - { - return levelLoreMessage; - } - - /** * This method returns the storeAsIslandData object. * @return the storeAsIslandData object. @@ -324,9 +251,42 @@ public class Settings implements ConfigObject * This method returns the userCommand value. * @return the value of userCommand. */ - public String getUserCommand() + public String getPlayerGlobalCommand() { - return userCommand; + return playerGlobalCommand; + } + + + /** + * Gets main user command. + * + * @return the main user command + */ + public String getPlayerMainCommand() + { + return playerMainCommand; + } + + + /** + * Gets complete user command. + * + * @return the complete user command + */ + public String getPlayerCompleteCommand() + { + return playerCompleteCommand; + } + + + /** + * Gets main admin command. + * + * @return the main admin command + */ + public String getAdminMainCommand() + { + return adminMainCommand; } @@ -334,9 +294,9 @@ public class Settings implements ConfigObject * This method returns the adminCommand value. * @return the value of adminCommand. */ - public String getAdminCommand() + public String getAdminGlobalCommand() { - return adminCommand; + return adminGlobalCommand; } @@ -489,26 +449,6 @@ public class Settings implements ConfigObject } - /** - * This method sets the challengeLoreMessage object value. - * @param challengeLoreMessage the challengeLoreMessage object new value. - */ - public void setChallengeLoreMessage(List challengeLoreMessage) - { - this.challengeLoreMessage = challengeLoreMessage; - } - - - /** - * This method sets the levelLoreMessage object value. - * @param levelLoreMessage the levelLoreMessage object new value. - */ - public void setLevelLoreMessage(List levelLoreMessage) - { - this.levelLoreMessage = levelLoreMessage; - } - - /** * @param resetChallenges new resetChallenges value. */ @@ -554,25 +494,6 @@ public class Settings implements ConfigObject } - /** - * @param freeChallengesFirst new freeChallengesFirst value. - */ - public void setFreeChallengesFirst(boolean freeChallengesFirst) - { - this.freeChallengesFirst = freeChallengesFirst; - } - - - /** - * This method sets the loreLineLength object value. - * @param loreLineLength the loreLineLength object new value. - */ - public void setLoreLineLength(int loreLineLength) - { - this.loreLineLength = loreLineLength; - } - - /** * This method sets the storeAsIslandData object value. * @param storeAsIslandData the storeAsIslandData object new value. @@ -595,21 +516,54 @@ public class Settings implements ConfigObject /** * This method sets the userCommand value. - * @param userCommand the userCommand new value. + * @param playerGlobalCommand the userCommand new value. */ - public void setUserCommand(String userCommand) + public void setPlayerGlobalCommand(String playerGlobalCommand) { - this.userCommand = userCommand; + this.playerGlobalCommand = playerGlobalCommand; + } + + + /** + * Sets main user command. + * + * @param playerMainCommand the main user command + */ + public void setPlayerMainCommand(String playerMainCommand) + { + this.playerMainCommand = playerMainCommand; + } + + + /** + * Sets complete user command. + * + * @param playerCompleteCommand the complete user command + */ + public void setPlayerCompleteCommand(String playerCompleteCommand) + { + this.playerCompleteCommand = playerCompleteCommand; + } + + + /** + * Sets main admin command. + * + * @param adminMainCommand the main admin command + */ + public void setAdminMainCommand(String adminMainCommand) + { + this.adminMainCommand = adminMainCommand; } /** * This method sets the adminCommand value. - * @param adminCommand the adminCommand new value. + * @param adminGlobalCommand the adminCommand new value. */ - public void setAdminCommand(String adminCommand) + public void setAdminGlobalCommand(String adminGlobalCommand) { - this.adminCommand = adminCommand; + this.adminGlobalCommand = adminGlobalCommand; } diff --git a/src/main/java/world/bentobox/challenges/database/object/Challenge.java b/src/main/java/world/bentobox/challenges/database/object/Challenge.java index fb14d8a..c595a0a 100644 --- a/src/main/java/world/bentobox/challenges/database/object/Challenge.java +++ b/src/main/java/world/bentobox/challenges/database/object/Challenge.java @@ -6,7 +6,6 @@ import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.NonNull; @@ -16,8 +15,8 @@ import com.google.gson.annotations.JsonAdapter; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.Table; -import world.bentobox.challenges.database.object.adapters.EntityCompatibilityAdapter; import world.bentobox.challenges.database.object.adapters.RequirementsAdapter; +import world.bentobox.challenges.database.object.adapters.TypeMigrationAdapter; import world.bentobox.challenges.database.object.requirements.Requirements; @@ -45,18 +44,23 @@ public class Challenge implements DataObject /** * The player must have the items on them. */ - INVENTORY, + INVENTORY_TYPE, /** * Items or required entities have to be within x blocks of the player. */ - ISLAND, + ISLAND_TYPE, /** * Other type, like required money / experience or island level. This my request * other plugins to be setup before it could work. */ - OTHER, + OTHER_TYPE, + + /** + * Challenge based on player statistic data. + */ + STATISTIC_TYPE } @@ -106,7 +110,8 @@ public class Challenge implements DataObject * Challenge type can be INVENTORY, OTHER or ISLAND. */ @Expose - private ChallengeType challengeType = ChallengeType.INVENTORY; + @JsonAdapter(TypeMigrationAdapter.class) + private ChallengeType challengeType = ChallengeType.INVENTORY_TYPE; /** * List of environments where this challenge will occur: NETHER, NORMAL, THE_END. Leave blank for all. @@ -137,63 +142,6 @@ public class Challenge implements DataObject @JsonAdapter(RequirementsAdapter.class) private Requirements requirements; - // --------------------------------------------------------------------- - // Section: Deprecated Requirements - // --------------------------------------------------------------------- - - @Deprecated - @Expose - private Set requiredPermissions = new HashSet<>(); - - @Deprecated - @Expose - private Map requiredBlocks = new EnumMap<>(Material.class); - - @Deprecated - @Expose - private boolean removeBlocks; - - @Deprecated - @Expose - @JsonAdapter(EntityCompatibilityAdapter.class) - private Map requiredEntities = new EnumMap<>(EntityType.class); - - @Deprecated - @Expose - private boolean removeEntities; - - @Deprecated - @Expose - private List requiredItems = new ArrayList<>(); - - @Deprecated - @Expose - private boolean takeItems = true; - - @Deprecated - @Expose - private int requiredExperience = 0; - - @Deprecated - @Expose - private boolean takeExperience; - - @Deprecated - @Expose - private int requiredMoney = 0; - - @Deprecated - @Expose - private boolean takeMoney; - - @Deprecated - @Expose - private long requiredIslandLevel; - - @Deprecated - @Expose - private int searchRadius = 10; - // --------------------------------------------------------------------- // Section: Rewards @@ -221,7 +169,7 @@ public class Challenge implements DataObject * Money reward. Economy plugin or addon required for this option. */ @Expose - private int rewardMoney = 0; + private double rewardMoney = 0; /** * Commands to run when the player completes the challenge for the first time. String List @@ -229,6 +177,11 @@ public class Challenge implements DataObject @Expose private List rewardCommands = new ArrayList<>(); + /** + * Set of item stacks that should ignore metadata. + */ + @Expose + private Set ignoreRewardMetaData = new HashSet<>(); // --------------------------------------------------------------------- // Section: Repeat Rewards @@ -240,6 +193,12 @@ public class Challenge implements DataObject @Expose private boolean repeatable; + /** + * Timeout for repeatable challenge before it can be completed again. + */ + @Expose + private long timeout; + /** * Description of the repeat rewards. If blank, it will be autogenerated */ @@ -268,7 +227,7 @@ public class Challenge implements DataObject * Repeat money reward. Economy plugin or addon required for this option. */ @Expose - private int repeatMoneyReward; + private double repeatMoneyReward; /** * Commands to run when challenge is repeated. String List. @@ -384,136 +343,6 @@ public class Challenge implements DataObject } - /** - * @return the requiredPermissions - */ - @Deprecated - public Set getRequiredPermissions() - { - return requiredPermissions; - } - - - /** - * @return the requiredBlocks - */ - @Deprecated - public Map getRequiredBlocks() - { - return requiredBlocks; - } - - - /** - * @return the removeBlocks - */ - @Deprecated - public boolean isRemoveBlocks() - { - return removeBlocks; - } - - - /** - * @return the requiredEntities - */ - @Deprecated - public Map getRequiredEntities() - { - return requiredEntities; - } - - - /** - * @return the removeEntities - */ - @Deprecated - public boolean isRemoveEntities() - { - return removeEntities; - } - - - /** - * @return the requiredItems - */ - @Deprecated - public List getRequiredItems() - { - return requiredItems; - } - - - /** - * @return the takeItems - */ - @Deprecated - public boolean isTakeItems() - { - return takeItems; - } - - - /** - * @return the requiredExperience - */ - @Deprecated - public int getRequiredExperience() - { - return requiredExperience; - } - - - /** - * @return the takeExperience - */ - @Deprecated - public boolean isTakeExperience() - { - return takeExperience; - } - - - /** - * @return the requiredMoney - */ - @Deprecated - public int getRequiredMoney() - { - return requiredMoney; - } - - - /** - * @return the takeMoney - */ - @Deprecated - public boolean isTakeMoney() - { - return takeMoney; - } - - - /** - * @return the requiredIslandLevel - */ - @Deprecated - public long getRequiredIslandLevel() - { - return requiredIslandLevel; - } - - - /** - * @return the searchRadius - */ - @Deprecated - public int getSearchRadius() - { - return searchRadius; - } - - /** * @return the rewardText */ @@ -544,7 +373,7 @@ public class Challenge implements DataObject /** * @return the rewardMoney */ - public int getRewardMoney() + public double getRewardMoney() { return rewardMoney; } @@ -607,7 +436,7 @@ public class Challenge implements DataObject /** * @return the repeatMoneyReward */ - public int getRepeatMoneyReward() + public double getRepeatMoneyReward() { return repeatMoneyReward; } @@ -622,6 +451,28 @@ public class Challenge implements DataObject } + /** + * Gets timeout. + * + * @return the timeout + */ + public long getTimeout() + { + return timeout; + } + + + /** + * Gets ignore reward meta data. + * + * @return the ignore reward meta data + */ + public Set getIgnoreRewardMetaData() + { + return ignoreRewardMetaData; + } + + // --------------------------------------------------------------------- // Section: Setters // --------------------------------------------------------------------- @@ -735,162 +586,6 @@ public class Challenge implements DataObject } - /** - * This method sets the requiredPermissions value. - * @param requiredPermissions the requiredPermissions new value. - * - */ - @Deprecated - public void setRequiredPermissions(Set requiredPermissions) - { - this.requiredPermissions = requiredPermissions; - } - - - /** - * This method sets the requiredBlocks value. - * @param requiredBlocks the requiredBlocks new value. - * - */ - @Deprecated - public void setRequiredBlocks(Map requiredBlocks) - { - this.requiredBlocks = requiredBlocks; - } - - - /** - * This method sets the removeBlocks value. - * @param removeBlocks the removeBlocks new value. - * - */ - @Deprecated - public void setRemoveBlocks(boolean removeBlocks) - { - this.removeBlocks = removeBlocks; - } - - - /** - * This method sets the requiredEntities value. - * @param requiredEntities the requiredEntities new value. - * - */ - @Deprecated - public void setRequiredEntities(Map requiredEntities) - { - this.requiredEntities = requiredEntities; - } - - - /** - * This method sets the removeEntities value. - * @param removeEntities the removeEntities new value. - * - */ - @Deprecated - public void setRemoveEntities(boolean removeEntities) - { - this.removeEntities = removeEntities; - } - - - /** - * This method sets the requiredItems value. - * @param requiredItems the requiredItems new value. - * - */ - @Deprecated - public void setRequiredItems(List requiredItems) - { - this.requiredItems = requiredItems; - } - - - /** - * This method sets the takeItems value. - * @param takeItems the takeItems new value. - * - */ - @Deprecated - public void setTakeItems(boolean takeItems) - { - this.takeItems = takeItems; - } - - - /** - * This method sets the requiredExperience value. - * @param requiredExperience the requiredExperience new value. - * - */ - @Deprecated - public void setRequiredExperience(int requiredExperience) - { - this.requiredExperience = requiredExperience; - } - - - /** - * This method sets the takeExperience value. - * @param takeExperience the takeExperience new value. - * - */ - @Deprecated - public void setTakeExperience(boolean takeExperience) - { - this.takeExperience = takeExperience; - } - - - /** - * This method sets the requiredMoney value. - * @param requiredMoney the requiredMoney new value. - * - */ - @Deprecated - public void setRequiredMoney(int requiredMoney) - { - this.requiredMoney = requiredMoney; - } - - - /** - * This method sets the takeMoney value. - * @param takeMoney the takeMoney new value. - * - */ - @Deprecated - public void setTakeMoney(boolean takeMoney) - { - this.takeMoney = takeMoney; - } - - - /** - * This method sets the requiredIslandLevel value. - * @param requiredIslandLevel the requiredIslandLevel new value. - * - */ - @Deprecated - public void setRequiredIslandLevel(long requiredIslandLevel) - { - this.requiredIslandLevel = requiredIslandLevel; - } - - - /** - * This method sets the searchRadius value. - * @param searchRadius the searchRadius new value. - * - */ - @Deprecated - public void setSearchRadius(int searchRadius) - { - this.searchRadius = searchRadius; - } - - /** * This method sets the rewardText value. * @param rewardText the rewardText new value. @@ -929,7 +624,7 @@ public class Challenge implements DataObject * @param rewardMoney the rewardMoney new value. * */ - public void setRewardMoney(int rewardMoney) + public void setRewardMoney(double rewardMoney) { this.rewardMoney = rewardMoney; } @@ -1006,7 +701,7 @@ public class Challenge implements DataObject * @param repeatMoneyReward the repeatMoneyReward new value. * */ - public void setRepeatMoneyReward(int repeatMoneyReward) + public void setRepeatMoneyReward(double repeatMoneyReward) { this.repeatMoneyReward = repeatMoneyReward; } @@ -1034,6 +729,28 @@ public class Challenge implements DataObject } + /** + * Sets timeout. + * + * @param timeout the timeout + */ + public void setTimeout(long timeout) + { + this.timeout = timeout; + } + + + /** + * Sets ignore reward meta data. + * + * @param ignoreRewardMetaData the ignore reward meta data + */ + public void setIgnoreRewardMetaData(Set ignoreRewardMetaData) + { + this.ignoreRewardMetaData = ignoreRewardMetaData; + } + + // --------------------------------------------------------------------- // Section: Other methods // --------------------------------------------------------------------- @@ -1051,7 +768,7 @@ public class Challenge implements DataObject public boolean matchGameMode(String gameMode) { return gameMode != null && - this.uniqueId.regionMatches(true, 0, gameMode, 0, gameMode.length()); + this.uniqueId.regionMatches(true, 0, gameMode, 0, gameMode.length()); } @@ -1082,13 +799,11 @@ public class Challenge implements DataObject return true; } - if (!(obj instanceof Challenge)) + if (!(obj instanceof Challenge other)) { return false; } - Challenge other = (Challenge) obj; - if (uniqueId == null) { return other.uniqueId == null; @@ -1148,7 +863,7 @@ public class Challenge implements DataObject clone.setEnvironment(new HashSet<>(this.environment)); clone.setLevel(this.level); clone.setRemoveWhenCompleted(this.removeWhenCompleted); - clone.setRequirements(this.requirements.clone()); + clone.setRequirements(this.requirements.copy()); clone.setRewardText(this.rewardText); clone.setRewardItems( this.rewardItems.stream(). @@ -1167,6 +882,8 @@ public class Challenge implements DataObject collect(Collectors.toCollection(() -> new ArrayList<>(this.repeatItemReward.size())))); clone.setRepeatMoneyReward(this.repeatMoneyReward); clone.setRepeatRewardCommands(new ArrayList<>(this.repeatRewardCommands)); + clone.setTimeout(this.timeout); + clone.setIgnoreRewardMetaData(new HashSet<>(this.ignoreRewardMetaData)); } catch (Exception e) { diff --git a/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java b/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java index d5baf35..f885632 100644 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import com.google.gson.annotations.Expose; @@ -13,7 +14,7 @@ import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.Table; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; /** @@ -100,7 +101,7 @@ public class ChallengeLevel implements DataObject, Comparable @ConfigComment("") @ConfigComment("Money reward. Economy plugin or addon required for this option.") @Expose - private int rewardMoney = 0; + private double rewardMoney = 0; @ConfigComment("") @ConfigComment("Commands to run when the player completes all challenges in current") @@ -114,6 +115,10 @@ public class ChallengeLevel implements DataObject, Comparable @Expose private Set challenges = new HashSet<>(); + @ConfigComment("") + @ConfigComment("Set of materials which metadata can be ignored.") + @Expose + private Set ignoreRewardMetaData = new HashSet<>(); // --------------------------------------------------------------------- // Section: Getters @@ -236,7 +241,7 @@ public class ChallengeLevel implements DataObject, Comparable * This method returns the rewardMoney value. * @return the value of rewardMoney. */ - public int getRewardMoney() + public double getRewardMoney() { return rewardMoney; } @@ -262,6 +267,17 @@ public class ChallengeLevel implements DataObject, Comparable } + /** + * Gets ignore reward meta data. + * + * @return the ignore reward meta data + */ + public Set getIgnoreRewardMetaData() + { + return ignoreRewardMetaData; + } + + // --------------------------------------------------------------------- // Section: Setters // --------------------------------------------------------------------- @@ -395,7 +411,7 @@ public class ChallengeLevel implements DataObject, Comparable * @param rewardMoney the rewardMoney new value. * */ - public void setRewardMoney(int rewardMoney) + public void setRewardMoney(double rewardMoney) { this.rewardMoney = rewardMoney; } @@ -423,6 +439,17 @@ public class ChallengeLevel implements DataObject, Comparable } + /** + * Sets ignore reward meta data. + * + * @param ignoreRewardMetaData the ignore reward meta data + */ + public void setIgnoreRewardMetaData(Set ignoreRewardMetaData) + { + this.ignoreRewardMetaData = ignoreRewardMetaData; + } + + // --------------------------------------------------------------------- // Section: Other methods // --------------------------------------------------------------------- @@ -449,7 +476,7 @@ public class ChallengeLevel implements DataObject, Comparable * {@inheritDoc} */ @Override - public int compareTo(ChallengeLevel o) + public int compareTo(@NotNull ChallengeLevel o) { if (this.equals(o)) { @@ -501,13 +528,11 @@ public class ChallengeLevel implements DataObject, Comparable return true; } - if (!(obj instanceof ChallengeLevel)) + if (!(obj instanceof ChallengeLevel other)) { return false; } - ChallengeLevel other = (ChallengeLevel) obj; - if (uniqueId == null) { return other.uniqueId == null; @@ -542,8 +567,7 @@ public class ChallengeLevel implements DataObject, Comparable * Clone method that returns clone of current challengeLevel. * @return ChallengeLevel that is cloned from current object. */ - @Override - public ChallengeLevel clone() + public ChallengeLevel copy() { ChallengeLevel clone = new ChallengeLevel(); @@ -566,6 +590,7 @@ public class ChallengeLevel implements DataObject, Comparable clone.setRewardMoney(this.rewardMoney); clone.setRewardCommands(new ArrayList<>(this.rewardCommands)); clone.setChallenges(new HashSet<>(this.challenges)); + clone.setIgnoreRewardMetaData(new HashSet<>(this.ignoreRewardMetaData)); } catch (Exception e) { diff --git a/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java b/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java index c1de447..8a8106e 100644 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java @@ -65,7 +65,7 @@ public class ChallengesPlayerData implements DataObject private Map challengeStatus = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); /** - * Map of challenges completion time where key is challenges unique id and value is + * Map of challenges completion time when key is challenges unique id and value is * timestamp when challenge was completed last time. */ @Expose @@ -251,13 +251,25 @@ public class ChallengesPlayerData implements DataObject * @param challengeName - unique challenge name * @param times - the number of times to set */ - public void setChallengeTimes(@NonNull String challengeName, @NonNull int times) + public void setChallengeTimes(@NonNull String challengeName, int times) { challengeStatus.put(challengeName, times); challengesTimestamp.put(challengeName, System.currentTimeMillis()); } + /** + * Gets last completion time. + * + * @param challengeName the unique id + * @return the last completion time + */ + public long getLastCompletionTime(@NonNull String challengeName) + { + return this.challengesTimestamp.getOrDefault(challengeName, 0L); + } + + /** * Check if a challenge has been done * @@ -341,13 +353,11 @@ public class ChallengesPlayerData implements DataObject return true; } - if (!(obj instanceof ChallengesPlayerData)) + if (!(obj instanceof ChallengesPlayerData other)) { return false; } - ChallengesPlayerData other = (ChallengesPlayerData) obj; - if (uniqueId == null) { return other.uniqueId == null; diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/ChallengeLoreAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/ChallengeLoreAdapter.java deleted file mode 100644 index 450ca72..0000000 --- a/src/main/java/world/bentobox/challenges/database/object/adapters/ChallengeLoreAdapter.java +++ /dev/null @@ -1,63 +0,0 @@ -// -// Created by BONNe -// Copyright - 2019 -// - - -package world.bentobox.challenges.database.object.adapters; - - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import world.bentobox.bentobox.database.objects.adapters.AdapterInterface; -import world.bentobox.challenges.config.SettingsUtils.ChallengeLore; - - -/** - * This adapter allows to serialize and deserialize ChallengeLore object. - */ -public class ChallengeLoreAdapter implements AdapterInterface, List> -{ - @SuppressWarnings("unchecked") - @Override - public List deserialize(Object from) - { - List result; - - if (from instanceof List) - { - result = ((List) from).stream(). - map(ChallengeLore::valueOf). - collect(Collectors.toCollection(ArrayList::new)); - } - else - { - result = new ArrayList<>(0); - } - - return result; - } - - - @SuppressWarnings("unchecked") - @Override - public List serialize(Object to) - { - List result; - - if (to instanceof List) - { - result = ((List) to).stream(). - map(ChallengeLore::name). - collect(Collectors.toCollection(ArrayList::new)); - } - else - { - result = new ArrayList<>(0); - } - - return result; - } -} diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/EntityCompatibilityAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/EntityCompatibilityAdapter.java index b64a32f..8abcf5f 100644 --- a/src/main/java/world/bentobox/challenges/database/object/adapters/EntityCompatibilityAdapter.java +++ b/src/main/java/world/bentobox/challenges/database/object/adapters/EntityCompatibilityAdapter.java @@ -33,10 +33,7 @@ public class EntityCompatibilityAdapter implements { JsonObject jsonArray = new JsonObject(); - src.forEach((entity, number) -> - { - jsonArray.addProperty(entity.name(), number); - }); + src.forEach((entity, number) -> jsonArray.addProperty(entity.name(), number)); return jsonArray; } @@ -46,7 +43,6 @@ public class EntityCompatibilityAdapter implements * This method deserializes json object that stores Entity Name and amount as integer. * @param json Json element that must be parsed. * @return EnumMap that contains EntityType as key and Integer as value. - * @throws JsonParseException */ @Override public Map deserialize(JsonElement json, diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/LevelLoreAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/LevelLoreAdapter.java deleted file mode 100644 index 6c60245..0000000 --- a/src/main/java/world/bentobox/challenges/database/object/adapters/LevelLoreAdapter.java +++ /dev/null @@ -1,63 +0,0 @@ -// -// Created by BONNe -// Copyright - 2019 -// - - -package world.bentobox.challenges.database.object.adapters; - - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import world.bentobox.bentobox.database.objects.adapters.AdapterInterface; -import world.bentobox.challenges.config.SettingsUtils.LevelLore; - - -/** - * This adapter allows to serialize and deserialize LevelLore object. - */ -public class LevelLoreAdapter implements AdapterInterface, List> -{ - @SuppressWarnings("unchecked") - @Override - public List deserialize(Object from) - { - List result; - - if (from instanceof List) - { - result = ((List) from).stream(). - map(LevelLore::valueOf). - collect(Collectors.toCollection(ArrayList::new)); - } - else - { - result = new ArrayList<>(0); - } - - return result; - } - - - @SuppressWarnings("unchecked") - @Override - public List serialize(Object to) - { - List result; - - if (to instanceof List) - { - result = ((List) to).stream(). - map(LevelLore::name). - collect(Collectors.toCollection(ArrayList::new)); - } - else - { - result = new ArrayList<>(0); - } - - return result; - } -} diff --git a/src/main/java/world/bentobox/challenges/database/object/adapters/TypeMigrationAdapter.java b/src/main/java/world/bentobox/challenges/database/object/adapters/TypeMigrationAdapter.java new file mode 100644 index 0000000..2c8c434 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/database/object/adapters/TypeMigrationAdapter.java @@ -0,0 +1,51 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.challenges.database.object.adapters; + + +import com.google.gson.*; +import java.lang.reflect.Type; + +import world.bentobox.challenges.database.object.Challenge; + + +/** + * This is a generic JSON serializer and deserializer for abstract classes. + * It store target class in class object, and instance variables in variables object. + */ +public class TypeMigrationAdapter implements JsonSerializer, JsonDeserializer +{ + /** + * Use default enum name serialization. + */ + @Override + public JsonElement serialize(Challenge.ChallengeType src, Type typeOfSrc, JsonSerializationContext context) + { + return new JsonPrimitive(src.name()); + } + + + /** + * Deserialize enum with old type format. + */ + @Override + public Challenge.ChallengeType deserialize(JsonElement json, + Type typeOfT, + JsonDeserializationContext context) + throws JsonParseException + { + JsonPrimitive primitive = json.getAsJsonPrimitive(); + + return switch (primitive.getAsString()) + { + case "INVENTORY", "INVENTORY_TYPE" -> Challenge.ChallengeType.INVENTORY_TYPE; + case "OTHER", "OTHER_TYPE" -> Challenge.ChallengeType.OTHER_TYPE; + case "STATISTIC", "STATISTIC_TYPE" -> Challenge.ChallengeType.STATISTIC_TYPE; + default -> Challenge.ChallengeType.ISLAND_TYPE; + }; + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java index 64741b4..1f3686e 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java @@ -7,12 +7,10 @@ package world.bentobox.challenges.database.object.requirements; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; +import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import com.google.gson.annotations.Expose; @@ -81,6 +79,34 @@ public class InventoryRequirements extends Requirements } + /** + * Gets ignore meta data. + * + * @return the ignore meta data + */ + public Set getIgnoreMetaData() + { + if (this.ignoreMetaData == null) + { + // Fixes null-pointer, that should not be possible, but may be. + this.ignoreMetaData = new HashSet<>(); + } + + return this.ignoreMetaData; + } + + + /** + * Sets ignore meta data. + * + * @param ignoreMetaData the ignore meta data + */ + public void setIgnoreMetaData(Set ignoreMetaData) + { + this.ignoreMetaData = ignoreMetaData; + } + + // --------------------------------------------------------------------- // Section: Other methods // --------------------------------------------------------------------- @@ -100,12 +126,12 @@ public class InventoryRequirements extends Requirements /** - * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary + * Method Requirements#copy allows copies Requirements object, to avoid changing content when it is necessary * to use it. - * @return InventoryRequirements clone + * @return InventoryRequirements copy */ @Override - public Requirements clone() + public Requirements copy() { InventoryRequirements clone = new InventoryRequirements(); clone.setRequiredPermissions(new HashSet<>(this.getRequiredPermissions())); @@ -114,6 +140,7 @@ public class InventoryRequirements extends Requirements map(ItemStack::clone). collect(Collectors.toCollection(() -> new ArrayList<>(this.requiredItems.size())))); clone.setTakeItems(this.takeItems); + clone.setIgnoreMetaData(new HashSet<>(this.ignoreMetaData)); return clone; } @@ -129,6 +156,12 @@ public class InventoryRequirements extends Requirements @Expose private List requiredItems = new ArrayList<>(); + /** + * Set of item stacks that should ignore metadata. + */ + @Expose + private Set ignoreMetaData = new HashSet<>(); + /** * Boolean that indicate if challenge completion should remove items from inventory. */ diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/IslandRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/IslandRequirements.java index 1de6737..407c28b 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/IslandRequirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/IslandRequirements.java @@ -39,7 +39,7 @@ public class IslandRequirements extends Requirements /** * Method IslandRequirements#getRequiredBlocks returns the requiredBlocks of this object. * - * @return the requiredBlocks (type Map) of this object. + * @return the requiredBlocks (type {@code Map}) of this object. */ public Map getRequiredBlocks() { @@ -83,7 +83,7 @@ public class IslandRequirements extends Requirements /** * Method IslandRequirements#getRequiredEntities returns the requiredEntities of this object. * - * @return the requiredEntities (type Map) of this object. + * @return the requiredEntities (type {@code Map}) of this object. */ public Map getRequiredEntities() { @@ -165,12 +165,12 @@ public class IslandRequirements extends Requirements /** - * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary + * Method Requirements#copy allows copies Requirements object, to avoid changing content when it is necessary * to use it. - * @return IslandRequirements clone + * @return IslandRequirements copy */ @Override - public Requirements clone() + public Requirements copy() { IslandRequirements clone = new IslandRequirements(); clone.setRequiredPermissions(new HashSet<>(this.getRequiredPermissions())); diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/OtherRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/OtherRequirements.java index fb1bf44..5f59e7d 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/OtherRequirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/OtherRequirements.java @@ -147,12 +147,12 @@ public class OtherRequirements extends Requirements /** - * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary + * Method Requirements#copy allows copies Requirements object, to avoid changing content when it is necessary * to use it. - * @return OtherRequirements clone + * @return OtherRequirements copy */ @Override - public Requirements clone() + public Requirements copy() { OtherRequirements clone = new OtherRequirements(); clone.setRequiredPermissions(new HashSet<>(this.getRequiredPermissions())); diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/Requirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/Requirements.java index 1b292b7..ce8279a 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/Requirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/Requirements.java @@ -70,12 +70,11 @@ public abstract class Requirements /** - * Method Requirements#clone allows to clone Requirements object, to avoid changing content when it is necessary + * Method Requirements#copy allows to copy Requirements object, to avoid changing content when it is necessary * to use it. - * @return Requirements clone + * @return Requirements copy */ - @Override - public abstract Requirements clone(); + public abstract Requirements copy(); // --------------------------------------------------------------------- diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java new file mode 100644 index 0000000..b90ec70 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java @@ -0,0 +1,228 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.database.object.requirements; + + +import com.google.gson.annotations.Expose; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.entity.EntityType; +import org.eclipse.jdt.annotation.Nullable; + + +public class StatisticRequirements extends Requirements +{ + /** + * Constructor Requirements creates a new Requirements instance. + */ + public StatisticRequirements() + { + // Empty constructor + } + + + /** + * This method copies given statistic object. + * @return Copy of this object. + */ + @Override + public Requirements copy() + { + StatisticRequirements requirements = new StatisticRequirements(); + requirements.setStatistic(this.statistic); + requirements.setEntity(this.entity); + requirements.setMaterial(this.material); + requirements.setAmount(this.amount); + requirements.setReduceStatistic(this.reduceStatistic); + + return requirements; + } + + + @Override + public boolean isValid() + { + if (!super.isValid()) + { + return false; + } + + if (this.statistic == null) + { + return false; + } + + return switch (this.statistic.getType()) + { + case ITEM -> this.material != null && this.material.isItem(); + + case BLOCK -> this.material != null && this.material.isBlock(); + + case ENTITY -> this.entity != null; + + case UNTYPED -> true; + + }; + + } + + + // --------------------------------------------------------------------- +// Section: Getters and setters +// --------------------------------------------------------------------- + + + /** + * Gets statistic. + * + * @return the statistic + */ + @Nullable + public Statistic getStatistic() + { + return statistic; + } + + + /** + * Sets statistic. + * + * @param statistic the statistic + */ + public void setStatistic(@Nullable Statistic statistic) + { + this.statistic = statistic; + } + + + /** + * Gets entity. + * + * @return the entity + */ + @Nullable + public EntityType getEntity() + { + return entity; + } + + + /** + * Sets entity. + * + * @param entity the entity + */ + public void setEntity(@Nullable EntityType entity) + { + this.entity = entity; + } + + + /** + * Gets material. + * + * @return the material + */ + @Nullable + public Material getMaterial() + { + return material; + } + + + /** + * Sets material. + * + * @param material the material + */ + public void setMaterial(@Nullable Material material) + { + this.material = material; + } + + + /** + * Gets amount. + * + * @return the amount + */ + public int getAmount() + { + return amount; + } + + + /** + * Sets amount. + * + * @param amount the amount + */ + public void setAmount(int amount) + { + this.amount = amount; + } + + + /** + * Is reduce statistic boolean. + * + * @return the boolean + */ + public boolean isReduceStatistic() + { + return reduceStatistic; + } + + + /** + * Sets reduce statistic. + * + * @param reduceStatistic the reduce statistic + */ + public void setReduceStatistic(boolean reduceStatistic) + { + this.reduceStatistic = reduceStatistic; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Type of the statistic field. + */ + @Expose + @Nullable + private Statistic statistic; + + /** + * Type of entity for entity related statistics. + */ + @Expose + @Nullable + private EntityType entity; + + /** + * Type of material for block and item related statistics. + */ + @Expose + @Nullable + private Material material; + + /** + * Amount of the stats. + */ + @Expose + private int amount; + + /** + * Indicate that player statistic fields must be adjusted after completing challenges. + */ + @Expose + private boolean reduceStatistic; +} diff --git a/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java index c099961..58af0c0 100644 --- a/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/ChallengeDataRequestHandler.java @@ -101,5 +101,5 @@ public class ChallengeDataRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java index 5c067cf..9222c0b 100644 --- a/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/ChallengeListRequestHandler.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.Map; import org.bukkit.Bukkit; +import org.bukkit.World; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; @@ -43,15 +44,21 @@ public class ChallengeListRequestHandler extends AddonRequestHandler */ if (metaData == null || - metaData.isEmpty() || - metaData.get("world-name") == null || - !(metaData.get("world-name") instanceof String) || - Bukkit.getWorld((String) metaData.get("world-name")) == null) + metaData.isEmpty() || + metaData.get("world-name") == null || + !(metaData.get("world-name") instanceof String)) { return Collections.emptyList(); } - return this.addon.getChallengesManager().getAllChallengesNames(Bukkit.getWorld((String) metaData.get("world-name"))); + World world = Bukkit.getWorld((String) metaData.get("world-name")); + + if (world == null) + { + return Collections.emptyList(); + } + + return this.addon.getChallengesManager().getAllChallengesNames(world); } @@ -63,5 +70,5 @@ public class ChallengeListRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java index 3ea05bb..dc0f9f0 100644 --- a/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/CompletedChallengesRequestHandler.java @@ -11,7 +11,7 @@ import org.bukkit.World; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; /** @@ -50,24 +50,27 @@ public class CompletedChallengesRequestHandler extends AddonRequestHandler */ if (metaData == null || - metaData.isEmpty() || - metaData.get("world-name") == null || - !(metaData.get("world-name") instanceof String) || - metaData.get("player") == null || - !(metaData.get("player") instanceof UUID) || - Bukkit.getWorld((String) metaData.get("world-name")) == null) + metaData.isEmpty() || + metaData.get("world-name") == null || + !(metaData.get("world-name") instanceof String) || + metaData.get("player") == null || + !(metaData.get("player") instanceof UUID player)) { return Collections.emptySet(); } World world = Bukkit.getWorld((String) metaData.get("world-name")); - UUID player = (UUID) metaData.get("player"); + + if (world == null) + { + return Collections.emptySet(); + } ChallengesManager manager = this.addon.getChallengesManager(); return manager.getAllChallengesNames(world).stream(). - filter(challenge -> manager.isChallengeComplete(player, world, challenge)). - collect(Collectors.toSet()); + filter(challenge -> manager.isChallengeComplete(player, world, challenge)). + collect(Collectors.toSet()); } @@ -79,5 +82,5 @@ public class CompletedChallengesRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/handlers/LevelDataRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/LevelDataRequestHandler.java index 10bf01d..492148d 100644 --- a/src/main/java/world/bentobox/challenges/handlers/LevelDataRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/LevelDataRequestHandler.java @@ -94,5 +94,5 @@ public class LevelDataRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java b/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java index f449297..290f1d9 100644 --- a/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java +++ b/src/main/java/world/bentobox/challenges/handlers/LevelListRequestHandler.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.Map; import org.bukkit.Bukkit; +import org.bukkit.World; import world.bentobox.bentobox.api.addons.request.AddonRequestHandler; import world.bentobox.challenges.ChallengesAddon; @@ -44,16 +45,21 @@ public class LevelListRequestHandler extends AddonRequestHandler */ if (metaData == null || - metaData.isEmpty() || - metaData.get("world-name") == null || - !(metaData.get("world-name") instanceof String) || - Bukkit.getWorld((String) metaData.get("world-name")) == null) + metaData.isEmpty() || + metaData.get("world-name") == null || + !(metaData.get("world-name") instanceof String)) { return Collections.emptyList(); } - return this.addon.getChallengesManager().getLevelNames( - Bukkit.getWorld((String) metaData.get("world-name"))); + World world = Bukkit.getWorld((String) metaData.get("world-name")); + + if (world == null) + { + return Collections.emptyList(); + } + + return this.addon.getChallengesManager().getLevelNames(world); } @@ -65,5 +71,5 @@ public class LevelListRequestHandler extends AddonRequestHandler /** * Variable stores challenges addon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/listeners/ResetListener.java b/src/main/java/world/bentobox/challenges/listeners/ResetListener.java index 2106e03..1f961bf 100644 --- a/src/main/java/world/bentobox/challenges/listeners/ResetListener.java +++ b/src/main/java/world/bentobox/challenges/listeners/ResetListener.java @@ -1,6 +1,3 @@ -/** - * - */ package world.bentobox.challenges.listeners; import org.bukkit.event.EventHandler; @@ -10,22 +7,18 @@ import org.bukkit.event.Listener; import world.bentobox.bentobox.api.events.island.IslandCreatedEvent; import world.bentobox.bentobox.api.events.island.IslandRegisteredEvent; import world.bentobox.bentobox.api.events.island.IslandResettedEvent; +import world.bentobox.bentobox.api.events.team.TeamKickEvent; +import world.bentobox.bentobox.api.events.team.TeamLeaveEvent; import world.bentobox.challenges.ChallengesAddon; + /** * Resets challenges when the island is reset - * @author tastybento * + * @author tastybento */ -public class ResetListener implements Listener { - - private ChallengesAddon addon; - - public ResetListener(ChallengesAddon addon) { - this.addon = addon; - } - - +public record ResetListener(ChallengesAddon addon) implements Listener +{ /** * This method handles Island Created event. * @@ -34,7 +27,13 @@ public class ResetListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onIslandCreated(IslandCreatedEvent e) { - addon.getChallengesManager().resetAllChallenges(e.getOwner(), e.getLocation().getWorld(), e.getOwner()); + // Reset any challenges that can be assigned to the island or its owner. + if (this.addon.getChallengesSettings().isResetChallenges()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getOwner(), + e.getLocation().getWorld(), + e.getOwner()); + } } @@ -44,9 +43,16 @@ public class ResetListener implements Listener { * @param e Event that must be handled. */ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onIslandCreated(IslandResettedEvent e) + public void onIslandResetted(IslandResettedEvent e) { - addon.getChallengesManager().resetAllChallenges(e.getOwner(), e.getLocation().getWorld(), e.getOwner()); + // Reset owner challenges only if data is stored per player. + if (this.addon.getChallengesSettings().isResetChallenges() && + !this.addon.getChallengesSettings().isStoreAsIslandData()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getOwner(), + e.getLocation().getWorld(), + e.getOwner()); + } } @@ -56,8 +62,53 @@ public class ResetListener implements Listener { * @param e Event that must be handled. */ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onIslandCreated(IslandRegisteredEvent e) + public void onIslandRegistered(IslandRegisteredEvent e) { - addon.getChallengesManager().resetAllChallenges(e.getOwner(), e.getLocation().getWorld(), e.getOwner()); + // Reset owner challenges only if data is stored per player. + if (this.addon.getChallengesSettings().isResetChallenges() && + !this.addon.getChallengesSettings().isStoreAsIslandData()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getOwner(), + e.getLocation().getWorld(), + e.getOwner()); + } + } + + + /** + * This method handles Island Registered event. + * + * @param e Event that must be handled. + */ + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onTeamLeave(TeamLeaveEvent e) + { + // Reset player challenges only if data is stored per player. + if (this.addon.getChallengesSettings().isResetChallenges() && + !this.addon.getChallengesSettings().isStoreAsIslandData()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getPlayerUUID(), + e.getLocation().getWorld(), + e.getOwner()); + } + } + + + /** + * This method handles Island Registered event. + * + * @param e Event that must be handled. + */ + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onTeamKick(TeamKickEvent e) + { + // Reset player challenges only if data is stored per player. + if (this.addon.getChallengesSettings().isResetChallenges() && + !this.addon.getChallengesSettings().isStoreAsIslandData()) + { + this.addon.getChallengesManager().resetAllChallenges(e.getPlayerUUID(), + e.getLocation().getWorld(), + e.getOwner()); + } } } diff --git a/src/main/java/world/bentobox/challenges/listeners/SaveListener.java b/src/main/java/world/bentobox/challenges/listeners/SaveListener.java index f20e588..a718772 100644 --- a/src/main/java/world/bentobox/challenges/listeners/SaveListener.java +++ b/src/main/java/world/bentobox/challenges/listeners/SaveListener.java @@ -50,5 +50,5 @@ public class SaveListener implements Listener // --------------------------------------------------------------------- - private ChallengesAddon addon; + private final ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java b/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java new file mode 100644 index 0000000..aa76502 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java @@ -0,0 +1,1348 @@ +package world.bentobox.challenges.managers; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory; +import world.bentobox.bentobox.database.objects.DataObject; +import world.bentobox.bentobox.util.ItemParser; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.database.object.requirements.InventoryRequirements; +import world.bentobox.challenges.database.object.requirements.IslandRequirements; +import world.bentobox.challenges.database.object.requirements.OtherRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * Imports challenges + * @author BONNe1704 + * + */ +public class ChallengesImportManager +{ + /** + * Import challenges from file or link. + * @param challengesAddon Challenges addon. + */ + public ChallengesImportManager(ChallengesAddon challengesAddon) + { + this.addon = challengesAddon; + } + + + // --------------------------------------------------------------------- + // Section: YAML Importers + // --------------------------------------------------------------------- + + + /** + * This method imports generator tiers from template + * + * @param user - user + * @param world - world to import into + * @param file - file that must be imported + */ + public void importFile(@Nullable User user, World world, String file) + { + File generatorFile = new File(this.addon.getDataFolder(), file.endsWith(".yml") ? file : file + ".yml"); + + if (!generatorFile.exists()) + { + if (user != null) + { + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-file", Constants.PARAMETER_FILE, file)); + } + + return; + } + + YamlConfiguration config = new YamlConfiguration(); + + try + { + config.load(generatorFile); + } + catch (IOException | InvalidConfigurationException e) + { + if (user != null) + { + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-load", + Constants.PARAMETER_FILE, file, TextVariables.DESCRIPTION, e.getMessage())); + } + else + { + this.addon.logError("Exception when loading file. " + e.getMessage()); + } + + return; + } + + Optional optional = this.addon.getPlugin().getIWM().getAddon(world); + + if (optional.isEmpty()) + { + if (user != null) + { + Utils.sendMessage(user, + user.getTranslation(Constants.ERRORS + "not-a-gamemode-world", + Constants.PARAMETER_WORLD, world.getName())); + } + else + { + this.addon.logWarning("Given world is not a gamemode world."); + } + + return; + } + + this.addon.getChallengesManager().wipeDatabase(optional.get().getDescription().getName().toLowerCase()); + this.createChallenges(config, user, optional.get(), world); + } + + + /** + * This method creates generator tier object from config file. + * + * @param config YamlConfiguration that contains all generators. + * @param user User who calls reading. + * @param gameMode GameMode in which generator tiers must be imported + */ + private void createChallenges(YamlConfiguration config, @Nullable User user, GameModeAddon gameMode, World world) + { + final String prefix = gameMode.getDescription().getName().toLowerCase() + "_"; + + long challengeCount = 0; + long levelCount = 0; + + if (config.contains("challenges")) + { + ConfigurationSection reader = config.getConfigurationSection("challenges"); + + if (reader != null) + { + challengeCount = reader.getKeys(false).stream(). + mapToInt(challengeId -> this.createChallenge(challengeId, + prefix, + reader.getConfigurationSection(challengeId))). + sum(); + } + } + + if (config.contains("levels")) + { + ConfigurationSection reader = config.getConfigurationSection("levels"); + + if (reader != null) + { + levelCount = reader.getKeys(false).stream(). + mapToInt(levelId -> this.createLevel(levelId, + prefix, + world, + reader.getConfigurationSection(levelId))). + sum(); + } + } + + if (user != null) + { + Utils.sendMessage(user, + user.getTranslation(Constants.MESSAGES + "import-count", + "[levels]", String.valueOf(levelCount), + "[challenges]", String.valueOf(challengeCount))); + } + + this.addon.log("Imported " + challengeCount + " challenges and " + + levelCount + " levels into database."); + } + + + /** + * This method creates challenge from given config section. + * @param challengeId Challenge ID. + * @param prefix GameMode prefix. + * @param section Configuration Section that contains information. + * @return 1 if challenge is created, otherwise 0. + */ + private int createChallenge(String challengeId, + String prefix, + @Nullable ConfigurationSection section) + { + if (section == null) + { + return 0; + } + + try + { + Challenge challenge = new Challenge(); + challenge.setUniqueId(prefix + challengeId); + + challenge.setFriendlyName(section.getString("name", challengeId)); + challenge.setIcon(matchIcon(section.getString("icon"), new ItemStack(Material.PAPER))); + + // Read description + if (section.isList("description")) + { + challenge.setDescription(section.getStringList("description")); + } + else if (section.isString("description")) + { + String description = section.getString("description"); + + if (description != null) + { + // Define as list. + challenge.setDescription(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + + challenge.setDeployed(section.getBoolean("deployed", true)); + challenge.setOrder(section.getInt("order", 0)); + challenge.setChallengeType(matchChallengeType(section.getString("type"), + Challenge.ChallengeType.ISLAND_TYPE)); + + // Read environment + Set environments = new HashSet<>(); + challenge.setEnvironment(environments); + + if (section.isList("environments")) + { + section.getStringList("environments"). + forEach(text -> environments.add(matchEnvironment(text, + World.Environment.NORMAL))); + } + else if (section.isString("environments")) + { + environments.add(matchEnvironment(section.getString("environments"), + World.Environment.NORMAL)); + } + + challenge.setRemoveWhenCompleted(section.getBoolean("remove-completed", false)); + + // Read Requirements + this.populateRequirements(challenge, section.getConfigurationSection("requirements")); + // Read Rewards + this.populateRewards(challenge, section.getConfigurationSection("rewards")); + + // Check Repeating status + challenge.setRepeatable(section.getBoolean("repeatable", false)); + challenge.setMaxTimes(section.getInt("repeat-times", -1)); + + if (challenge.isRepeatable()) + { + // Read Repeat Rewards + this.populateRepeatRewards(challenge, + section.getConfigurationSection("repeat-rewards")); + } + + this.addon.getChallengesManager().saveChallenge(challenge); + this.addon.getChallengesManager().loadChallenge(challenge, true, null, true); + } + catch (Exception e) + { + return 0; + } + + return 1; + } + + + /** + * Populates requirements for the given challenge. + * + * @param challenge the challenge + * @param section the section + */ + private void populateRequirements(Challenge challenge, ConfigurationSection section) + { + switch (challenge.getChallengeType()) + { + case INVENTORY_TYPE -> { + InventoryRequirements requirements = new InventoryRequirements(); + challenge.setRequirements(requirements); + + requirements.setTakeItems(section.getBoolean("take-items", false)); + List requiredItems = new ArrayList<>(); + requirements.setRequiredItems(requiredItems); + + if (section.isList("items")) + { + section.getStringList("items"). + forEach(text -> { + ItemStack itemStack = ItemParser.parse(text); + + if (itemStack != null) + { + requiredItems.add(itemStack); + } + }); + } + } + case ISLAND_TYPE -> { + IslandRequirements requirements = new IslandRequirements(); + challenge.setRequirements(requirements); + + requirements.setRemoveBlocks(section.getBoolean("remove-blocks", false)); + requirements.setRequiredBlocks(this.createMaterialMap(section.getConfigurationSection("blocks"))); + + requirements.setRemoveEntities(section.getBoolean("remove-entities", false)); + requirements.setRequiredEntities(this.createEntityMap(section.getConfigurationSection("entities"))); + + requirements.setSearchRadius(section.getInt("search-distance", 10)); + } + case OTHER_TYPE -> { + OtherRequirements requirements = new OtherRequirements(); + challenge.setRequirements(requirements); + + requirements.setTakeMoney(section.getBoolean("take-money", false)); + requirements.setRequiredMoney(section.getDouble("money", 0)); + + requirements.setTakeExperience(section.getBoolean("take-experience", false)); + requirements.setRequiredExperience(section.getInt("experience", 0)); + + requirements.setRequiredIslandLevel(section.getInt("level", 0)); + } + case STATISTIC_TYPE -> { + StatisticRequirements requirements = new StatisticRequirements(); + challenge.setRequirements(requirements); + + requirements.setAmount(section.getInt("amount", 0)); + requirements.setReduceStatistic(section.getBoolean("reduce", false)); + + requirements.setStatistic(matchStatistic(section.getString("statistic"))); + requirements.setEntity(matchEntity(section.getString("entity"))); + requirements.setMaterial(matchMaterial(section.getString("material"))); + } + } + + // Read permissions + if (challenge.getRequirements() != null) + { + Set permissions = new HashSet<>(); + challenge.getRequirements().setRequiredPermissions(permissions); + + if (section.isList("permissions")) + { + permissions.addAll(section.getStringList("permissions")); + } + else if (section.isString("permissions")) + { + String description = section.getString("permissions"); + + if (description != null) + { + // Define as list. + permissions.addAll(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + } + } + + + /** + * This method populates material map from given section field. + * @param section Section that contains material. + * @return Map that links material and number. + */ + private Map createMaterialMap(ConfigurationSection section) + { + Map materialMaps = new HashMap<>(); + + if (section != null) + { + for (String materialKey : section.getKeys(false)) + { + Material material = matchMaterial(materialKey); + + if (material != null) + { + materialMaps.put(material, section.getInt(materialKey, 0)); + } + } + } + + return materialMaps; + } + + + /** + * This method populates entity map from given section field. + * @param section Section that contains material. + * @return Map that links entity and number. + */ + private Map createEntityMap(ConfigurationSection section) + { + Map entityMap = new HashMap<>(); + + if (section != null) + { + for (String EntityType : section.getKeys(false)) + { + EntityType entity = matchEntity(EntityType); + + if (entity != null) + { + entityMap.put(entity, section.getInt(EntityType, 0)); + } + } + } + + return entityMap; + } + + + /** + * This method populates rewards for a challenge. + * @param challenge Challenge + * @param section Section that contains rewards + */ + private void populateRewards(Challenge challenge, @Nullable ConfigurationSection section) + { + List rewardItems = new ArrayList<>(); + challenge.setRewardItems(rewardItems); + + if (section == null) + { + return; + } + + challenge.setRewardText(section.getString("text", "")); + + if (section.isList("items")) + { + section.getStringList("items"). + forEach(text -> { + ItemStack itemStack = ItemParser.parse(text); + + if (itemStack != null) + { + rewardItems.add(itemStack); + } + }); + } + + challenge.setRewardExperience(section.getInt("experience", 0)); + challenge.setRewardMoney(section.getDouble("money", 0)); + + if (section.isList("commands")) + { + challenge.setRewardCommands(section.getStringList("commands")); + } + else if (section.isString("commands")) + { + String description = section.getString("commands"); + + if (description != null) + { + // Define as list. + challenge.setRewardCommands(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + } + + + /** + * This method populates repeat rewards for a challenge. + * @param challenge Challenge + * @param section Section that contains rewards + */ + private void populateRepeatRewards(Challenge challenge, @Nullable ConfigurationSection section) + { + List rewardItems = new ArrayList<>(); + challenge.setRepeatItemReward(rewardItems); + + if (section == null) + { + return; + } + + challenge.setRepeatRewardText(section.getString("text", "")); + + if (section.isList("items")) + { + section.getStringList("items"). + forEach(text -> { + ItemStack itemStack = ItemParser.parse(text); + + if (itemStack != null) + { + rewardItems.add(itemStack); + } + }); + } + + challenge.setRepeatExperienceReward(section.getInt("experience", 0)); + challenge.setRepeatMoneyReward(section.getDouble("money", 0)); + + if (section.isList("commands")) + { + challenge.setRepeatRewardCommands(section.getStringList("commands")); + } + else if (section.isString("commands")) + { + String description = section.getString("commands"); + + if (description != null) + { + // Define as list. + challenge.setRepeatRewardCommands(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + } + + + /** + * This method populates rewards for a level. + * @param level level + * @param section Section that contains rewards + */ + private void populateRewards(ChallengeLevel level, @Nullable ConfigurationSection section) + { + List rewardItems = new ArrayList<>(); + level.setRewardItems(rewardItems); + + if (section == null) + { + return; + } + + level.setRewardText(section.getString("text", "")); + + if (section.isList("items")) + { + section.getStringList("items"). + forEach(text -> { + ItemStack itemStack = ItemParser.parse(text); + + if (itemStack != null) + { + rewardItems.add(itemStack); + } + }); + } + + level.setRewardExperience(section.getInt("experience", 0)); + level.setRewardMoney(section.getDouble("money", 0)); + + if (section.isList("commands")) + { + level.setRewardCommands(section.getStringList("commands")); + } + else if (section.isString("commands")) + { + String description = section.getString("commands"); + + if (description != null) + { + // Define as list. + level.setRewardCommands(Arrays.asList( + description.replaceAll("\\|", "\n"). + split("\n").clone())); + } + } + } + + + /** + * This method creates Level + * @param levelId Level Id + * @param prefix Gamemode prefix + * @param world World where level operates. + * @param section Section that contains level info. + * @return 1 if level created, 0 otherwise. + */ + private int createLevel(String levelId, + String prefix, + World world, + @Nullable ConfigurationSection section) + { + if (section == null) + { + return 0; + } + + try + { + ChallengeLevel level = new ChallengeLevel(); + level.setUniqueId(prefix + levelId); + + level.setFriendlyName(section.getString("name", levelId)); + level.setIcon(matchIcon(section.getString("icon"), new ItemStack(Material.PAPER))); + level.setLockedIcon(matchIcon(section.getString("icon"))); + + level.setWorld(world.getName()); + + level.setOrder(section.getInt("order", 0)); + level.setWaiverAmount(section.getInt("waiver", 0)); + + level.setUnlockMessage(section.getString("description", "")); + + this.populateRewards(level, section.getConfigurationSection("rewards")); + + Set challenges = new HashSet<>(); + level.setChallenges(challenges); + + if (section.isList("challenges")) + { + section.getStringList("challenges").forEach(text -> { + Challenge challenge = this.addon.getChallengesManager().getChallenge(prefix + text); + + if (challenge != null) + { + challenges.add(challenge.getUniqueId()); + this.addon.getChallengesManager().addChallengeToLevel(challenge, level); + } + }); + } + + this.addon.getChallengesManager().saveLevel(level); + this.addon.getChallengesManager().loadLevel(level, true, null, true); + } + catch (Exception ignored) + { + return 0; + } + + return 1; + } + + + // --------------------------------------------------------------------- + // Section: JSON Importers + // --------------------------------------------------------------------- + + + /** + * Import database file from local storage. + * + * @param user the user + * @param world the world + * @param fileName the file name + */ + public void importDatabaseFile(User user, World world, String fileName) + { + World correctWorld = Util.getWorld(world); + + if (correctWorld == null) + { + this.addon.logError("Given world is not part of BentoBox"); + return; + } + + ChallengesManager manager = this.addon.getChallengesManager(); + + // If exist any generator that is bound to current world, then do not load generators. + if (manager.hasAnyChallengeData(world.getName())) + { + this.addon.getPlugin().getIWM().getAddon(world).ifPresent(gameModeAddon -> + manager.wipeDatabase(gameModeAddon.getDescription().getName().toLowerCase())); + } + + try + { + // This prefix will be used to all generators. That is a unique way how to separate generators for + // each game mode. + String uniqueIDPrefix = Utils.getGameMode(world).toLowerCase() + "_"; + DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadObject(fileName); + + if (downloadedChallenges == null) + { + return; + } + + // All new challenges should get correct ID. So we need to map it to loaded challenges. + downloadedChallenges.getChallengeList().forEach(challenge -> { + // Set correct challenge ID + challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); + // Set up correct level ID if it is necessary + if (!challenge.getLevel().isEmpty()) + { + challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); + } + // Load challenge in memory + manager.loadChallenge(challenge, false, user, user == null); + }); + + downloadedChallenges.getLevelList().forEach(challengeLevel -> { + // Set correct level ID + challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); + // Set correct world name + challengeLevel.setWorld(correctWorld.getName()); + // Reset names for all challenges. + challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> uniqueIDPrefix + challenge). + collect(Collectors.toSet())); + // Load level in memory + manager.loadLevel(challengeLevel, false, user, user == null); + }); + } + catch (Exception e) + { + this.addon.getPlugin().logStacktrace(e); + return; + } + + manager.saveChallenges(); + manager.saveLevels(); + } + + + /** + * This method loads downloaded challenges into memory. + * @param user User who calls downloaded challenge loading + * @param world Target world. + * @param downloadString String that need to be loaded via DefaultDataHolder. + */ + public void loadDownloadedChallenges(User user, World world, String downloadString) + { + World correctWorld = Util.getWorld(world); + + if (correctWorld == null) + { + this.addon.logError("Given world is not part of BentoBox"); + return; + } + + ChallengesManager manager = this.addon.getChallengesManager(); + + // If exist any challenge or level that is bound to current world, then do not load default challenges. + if (manager.hasAnyChallengeData(world.getName())) + { + if (user.isPlayer()) + { + Utils.sendMessage(user, user.getTranslation("challenges.errors.exist-challenges-or-levels")); + } + else + { + this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); + } + + return; + } + + try + { + // This prefix will be used to all challenges. That is a unique way how to separate challenged for + // each game mode. + String uniqueIDPrefix = Utils.getGameMode(world).toLowerCase() + "_"; + DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadWebObject(downloadString); + + // All new challenges should get correct ID. So we need to map it to loaded challenges. + downloadedChallenges.getChallengeList().forEach(challenge -> { + // Set correct challenge ID + challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); + // Set up correct level ID if it is necessary + if (!challenge.getLevel().isEmpty()) + { + challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); + } + // Load challenge in memory + manager.loadChallenge(challenge, false, user, user == null); + }); + + downloadedChallenges.getLevelList().forEach(challengeLevel -> { + // Set correct level ID + challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); + // Set correct world name + challengeLevel.setWorld(correctWorld.getName()); + // Reset names for all challenges. + challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> uniqueIDPrefix + challenge). + collect(Collectors.toSet())); + // Load level in memory + manager.loadLevel(challengeLevel, false, user, user == null); + }); + } + catch (Exception e) + { + this.addon.getPlugin().logStacktrace(e); + return; + } + + this.addon.getChallengesManager().saveChallenges(); + this.addon.getChallengesManager().saveLevels(); + } + + + // --------------------------------------------------------------------- + // Section: Default generation + // --------------------------------------------------------------------- + + + public void generateDatabaseFile(User user, World world, String fileName) + { + File defaultFile = new File(this.addon.getDataFolder(), + fileName.endsWith(".json") ? fileName : fileName + ".json"); + + if (defaultFile.exists()) + { + if (user.isPlayer()) + { + Utils.sendMessage(user, + user.getTranslation(Constants.ERRORS + "file-exist", + Constants.PARAMETER_FILE, fileName)); + } + else + { + this.addon.logWarning(Constants.ERRORS + "file-exist"); + } + + return; + } + + try + { + if (defaultFile.createNewFile()) + { + String replacementString = Utils.getGameMode(world).toLowerCase() + "_"; + ChallengesManager manager = this.addon.getChallengesManager(); + + List challengeList = manager.getAllChallenges(world). + stream(). + map(challenge -> { + // Use clone to avoid any changes in existing challenges. + Challenge clone = challenge.clone(); + // Remove world name from challenge id. + clone.setUniqueId(challenge.getUniqueId().replaceFirst(replacementString, "")); + // Remove world name from level id. + clone.setLevel(challenge.getLevel().replaceFirst(replacementString, "")); + + return clone; + }). + collect(Collectors.toList()); + + List levelList = manager.getLevels(world). + stream(). + map(challengeLevel -> { + // Use clone to avoid any changes in existing levels. + ChallengeLevel clone = challengeLevel.copy(); + // Remove world name from level ID. + clone.setUniqueId(challengeLevel.getUniqueId().replaceFirst(replacementString, "")); + // Remove world name. + clone.setWorld(""); + // Challenges must be reassign, as they also contains world name. + clone.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> challenge.replaceFirst(replacementString, "")). + collect(Collectors.toSet())); + + return clone; + }). + collect(Collectors.toList()); + + DefaultDataHolder defaultChallenges = new DefaultDataHolder(); + defaultChallenges.setChallengeList(challengeList); + defaultChallenges.setLevelList(levelList); + defaultChallenges.setVersion(this.addon.getDescription().getVersion()); + + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(defaultFile), StandardCharsets.UTF_8))) { + writer.write(Objects.requireNonNull( + new DefaultJSONHandler(this.addon).toJsonString(defaultChallenges))); + } + } + } + catch (IOException e) + { + if (user.isPlayer()) + { + Utils.sendMessage(user, + user.getTranslation(Constants.ERRORS + "no-load", + Constants.PARAMETER_FILE, fileName, + TextVariables.DESCRIPTION, e.getMessage())); + } + + this.addon.logError("Could not save json file: " + e.getMessage()); + } + finally + { + if (user.isPlayer()) + { + Utils.sendMessage(user, + user.getTranslation(Constants.CONVERSATIONS + "database-export-completed", + Constants.PARAMETER_WORLD, world.getName(), + Constants.PARAMETER_FILE, fileName)); + } + else + { + this.addon.logWarning("Database Export Completed"); + } + } + } + + + // --------------------------------------------------------------------- + // Section: Static Methods + // --------------------------------------------------------------------- + + + /** + * Match item stack. + * + * @param text the text + * @return the item stack + */ + @Nullable + private static ItemStack matchIcon(@Nullable String text) + { + if (text == null || text.isBlank()) + { + return new ItemStack(Material.PAPER); + } + else + { + return ItemParser.parse(text, new ItemStack(Material.PAPER)); + } + } + + + /** + * Match item stack. + * + * @param text the text + * @param defaultItem the default item + * @return the item stack + */ + @NonNull + private static ItemStack matchIcon(@Nullable String text, ItemStack defaultItem) + { + ItemStack item = matchIcon(text); + return item == null ? defaultItem : item; + } + + + /** + * Match material. + * + * @param text the text + * @return the material + */ + @Nullable + private static Material matchMaterial(@Nullable String text) + { + if (text == null || text.isBlank()) + { + return null; + } + else + { + return Material.getMaterial(text.toUpperCase()); + } + } + + + /** + * Match material. + * + * @param text the text + * @param defaultItem the default item + * @return the material + */ + @NonNull + private static Material matchMaterial(@Nullable String text, Material defaultItem) + { + Material item = matchMaterial(text); + return item == null ? defaultItem : item; + } + + + /** + * Match entity type. + * + * @param text the text + * @return the entity type + */ + @Nullable + private static EntityType matchEntity(@Nullable String text) + { + if (text == null || text.isBlank()) + { + return null; + } + else + { + try + { + return EntityType.valueOf(text.toUpperCase()); + } + catch (Exception e) + { + return null; + } + } + } + + + /** + * Match entity type. + * + * @param text the text + * @param defaultItem the default item + * @return the entity type + */ + @NonNull + private static EntityType matchEntity(@Nullable String text, EntityType defaultItem) + { + EntityType item = matchEntity(text); + return item == null ? defaultItem : item; + } + + + /** + * Match statistic value. + * + * @param text the text + * @return the statistic + */ + @Nullable + private static Statistic matchStatistic(@Nullable String text) + { + if (text == null || text.isBlank()) + { + return null; + } + else + { + try + { + return Statistic.valueOf(text.toUpperCase()); + } + catch (Exception e) + { + return null; + } + } + } + + + /** + * Match challenge type + * + * @param text the text + * @param defaultType default type + * @return the challenge type + */ + private static Challenge.ChallengeType matchChallengeType(@Nullable String text, Challenge.ChallengeType defaultType) + { + if (text == null || text.isBlank()) + { + return defaultType; + } + else + { + try + { + return Challenge.ChallengeType.valueOf(text.toUpperCase()); + } + catch (Exception e) + { + return defaultType; + } + } + } + + + /** + * Match world environment. + * + * @param text the text + * @param defaultType the default type + * @return the world environment + */ + private static World.Environment matchEnvironment(@Nullable String text, World.Environment defaultType) + { + if (text == null || text.isBlank()) + { + return defaultType; + } + else + { + try + { + return World.Environment.valueOf(text.toUpperCase()); + } + catch (Exception e) + { + return defaultType; + } + } + } + + + // --------------------------------------------------------------------- + // Section: Private classes for default challenges + // --------------------------------------------------------------------- + + + /** + * This Class allows to load default challenges and their levels as objects much easier. + */ + private static final class DefaultJSONHandler + { + /** + * This constructor inits JSON builder that will be used to parse challenges. + * @param addon Challenges Adddon + */ + DefaultJSONHandler(ChallengesAddon addon) + { + GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); + // Register adapters + builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(addon.getPlugin())); + // Keep null in the database + builder.serializeNulls(); + // Allow characters like < or > without escaping them + builder.disableHtmlEscaping(); + + this.addon = addon; + this.gson = builder.setPrettyPrinting().create(); + } + + + /** + * This method returns json object that is parsed to string. Json object is made from given instance. + * @param instance Instance that must be parsed to json string. + * @return String that contains JSON information from instance object. + */ + String toJsonString(DefaultDataHolder instance) + { + // Null check + if (instance == null) + { + this.addon.logError("JSON database request to store a null. "); + return null; + } + + return this.gson.toJson(instance); + } + + + /** + * This method creates and adds to list all objects from default.json file. + * @return List of all objects from default.json that is with T instance. + */ + DefaultDataHolder loadObject(String fileName) + { + if (!fileName.endsWith(".json")) + { + fileName = fileName + ".json"; + } + + File defaultFile = new File(this.addon.getDataFolder(), fileName); + + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(defaultFile), StandardCharsets.UTF_8)) + { + DefaultDataHolder object = this.gson.fromJson(reader, DefaultDataHolder.class); + + reader.close(); // NOSONAR Required to keep OS file handlers low and not rely on GC + + return object; + } + catch (FileNotFoundException e) + { + this.addon.logError("Could not load file '" + defaultFile.getName() + "': File not found."); + } + catch (Exception e) + { + this.addon.logError("Could not load objects " + defaultFile.getName() + " " + e.getMessage()); + } + + return null; + } + + + /** + * This method creates and adds to list all objects from default.json file. + * @return List of all objects from default.json that is with T instance. + */ + DefaultDataHolder loadWebObject(String downloadedObject) + { + return this.gson.fromJson(downloadedObject, DefaultDataHolder.class); + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * Holds JSON builder object. + */ + private final Gson gson; + + /** + * Holds ChallengesAddon object. + */ + private final ChallengesAddon addon; + } + + + /** + * This is simple object that will allow to store all current challenges and levels + * in single file. + */ + private static final class DefaultDataHolder implements DataObject + { + /** + * Default constructor. Creates object with empty lists. + */ + DefaultDataHolder() + { + this.challengeList = Collections.emptyList(); + this.challengeLevelList = Collections.emptyList(); + this.version = ""; + } + + + /** + * This method returns stored challenge list. + * @return list that contains default challenges. + */ + List getChallengeList() + { + return challengeList; + } + + + /** + * This method sets given list as default challenge list. + * @param challengeList new default challenge list. + */ + void setChallengeList(List challengeList) + { + this.challengeList = challengeList; + } + + + /** + * This method returns list of default challenge levels. + * @return List that contains default challenge levels. + */ + List getLevelList() + { + return challengeLevelList; + } + + + /** + * This method sets given list as default challenge level list. + * @param levelList new default challenge level list. + */ + void setLevelList(List levelList) + { + this.challengeLevelList = levelList; + } + + + /** + * This method returns the version value. + * @return the value of version. + */ + public String getVersion() + { + return version; + } + + + /** + * This method sets the version value. + * @param version the version new value. + * + */ + public void setVersion(String version) + { + this.version = version; + } + + + /** + * @return default.json + */ + @Override + public String getUniqueId() + { + return "default.json"; + } + + + /** + * @param uniqueId - unique ID the uniqueId to set + */ + @Override + public void setUniqueId(String uniqueId) + { + // method not used. + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * Holds a list with default challenges. + */ + @Expose + private List challengeList; + + /** + * Holds a list with default levels. + */ + @Expose + private List challengeLevelList; + + /** + * Holds a variable that stores in which addon version file was made. + */ + @Expose + private String version; + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + private final ChallengesAddon addon; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/ChallengesManager.java b/src/main/java/world/bentobox/challenges/managers/ChallengesManager.java similarity index 87% rename from src/main/java/world/bentobox/challenges/ChallengesManager.java rename to src/main/java/world/bentobox/challenges/managers/ChallengesManager.java index 6624dc5..6e19df9 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesManager.java +++ b/src/main/java/world/bentobox/challenges/managers/ChallengesManager.java @@ -1,23 +1,14 @@ -package world.bentobox.challenges; +package world.bentobox.challenges.managers; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Statistic; import org.bukkit.World; +import org.bukkit.entity.EntityType; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; @@ -27,13 +18,11 @@ import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.database.object.Challenge; import world.bentobox.challenges.database.object.ChallengeLevel; import world.bentobox.challenges.database.object.ChallengesPlayerData; -import world.bentobox.challenges.database.object.requirements.InventoryRequirements; -import world.bentobox.challenges.database.object.requirements.IslandRequirements; -import world.bentobox.challenges.database.object.requirements.OtherRequirements; import world.bentobox.challenges.database.object.requirements.Requirements; import world.bentobox.challenges.events.ChallengeCompletedEvent; import world.bentobox.challenges.events.ChallengeResetAllEvent; @@ -56,47 +45,47 @@ public class ChallengesManager /** * This config object stores structures for challenge objects. */ - private Database challengeDatabase; + private final Database challengeDatabase; /** * This config object stores structures for challenge level objects. */ - private Database levelDatabase; + private final Database levelDatabase; /** - * This database allows to access player challenge data. + * This database allows accessing player challenge data. */ - private Database playersDatabase; + private final Database playersDatabase; /** * This is local cache that links challenge unique id with challenge object. */ - private Map challengeCacheData; + private final Map challengeCacheData; /** * This is local cache that links level unique id with level object. */ - private Map levelCacheData; + private final Map levelCacheData; /** * This is local cache that links UUID with corresponding player challenge data. */ - private Map playerCacheData; + private final Map playerCacheData; /** - * This variable allows to access ChallengesAddon. + * This variable allows accessing ChallengesAddon. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; /** - * This variable allows to access ChallengesAddon settings. + * This variable allows accessing ChallengesAddon settings. */ - private Settings settings; + private final Settings settings; /** - * Island world manager allows to detect which world refferes to which gamemode addon. + * Island world manager allows detecting which world refers to which gamemode addon. */ - private IslandWorldManager islandWorldManager; + private final IslandWorldManager islandWorldManager; // --------------------------------------------------------------------- @@ -128,7 +117,7 @@ public class ChallengesManager { if (o1.getOrder() == o2.getOrder()) { - // If orders are equal, sort by unique Id + // If orders are equal, sort by unique id return o1.getUniqueId().compareToIgnoreCase(o2.getUniqueId()); } else @@ -146,9 +135,18 @@ public class ChallengesManager } else { - // Sort by challenges level order numbers - return Integer.compare(this.getLevel(o1.getLevel()).getOrder(), - this.getLevel(o2.getLevel()).getOrder()); + ChallengeLevel o1Level = this.getLevel(o1.getLevel()); + ChallengeLevel o2Level = this.getLevel(o2.getLevel()); + + if (o1Level == null || o2Level == null) + { + return Boolean.compare(o1Level == null, o2Level == null); + } + else + { + // Sort by challenges level order numbers + return Integer.compare(o1Level.getOrder(), o2Level.getOrder()); + } } } }; @@ -173,7 +171,7 @@ public class ChallengesManager // Set up the configs this.challengeDatabase = new Database<>(addon, Challenge.class); this.levelDatabase = new Database<>(addon, ChallengeLevel.class); - // Players is where all the player history will be stored + // playersDatabase is where all the player history will be stored this.playersDatabase = new Database<>(addon, ChallengesPlayerData.class); // Init all cache objects. @@ -233,9 +231,6 @@ public class ChallengesManager // store player data before cleaning. this.savePlayersData(); } - //this.challengeDatabase = new Database<>(addon, Challenge.class); - //this.levelDatabase = new Database<>(addon, ChallengeLevel.class); - //this.playersDatabase = new Database<>(addon, ChallengesPlayerData.class); this.loadAndValidate(); } @@ -245,7 +240,6 @@ public class ChallengesManager * Load challenge silently. Used when loading. * * @param challenge Challenge that must be loaded. - * @return true if successful */ private void loadChallenge(@NonNull Challenge challenge) { @@ -273,7 +267,7 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("load-error", "[value]", "NULL"); + Utils.sendMessage(user, user.getTranslation("load-error", "[value]", "NULL")); } return false; @@ -283,11 +277,14 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.errors.invalid-challenge", "[challenge]", challenge.getUniqueId()); + Utils.sendMessage(user, user.getTranslation("challenges.errors.invalid-challenge", + "[challenge]", challenge.getUniqueId())); } this.addon.logWarning("Data for challenge `" + challenge.getUniqueId() + "` is not valid. It could be NULL element in item-stack!"); - return false; + + // Load the challenge but set it as "undeployed" + challenge.setDeployed(false); } if (this.challengeCacheData.containsKey(challenge.getUniqueId())) @@ -296,8 +293,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-skipping", - VALUE, challenge.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-skipping", + VALUE, challenge.getFriendlyName())); } return false; @@ -306,8 +303,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-overwriting", - VALUE, challenge.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-overwriting", + VALUE, challenge.getFriendlyName())); } } } @@ -315,8 +312,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-add", - VALUE, challenge.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-add", + VALUE, challenge.getFriendlyName())); } } @@ -357,7 +354,7 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("load-error", "[value]", "NULL"); + Utils.sendMessage(user, user.getTranslation("load-error", "[value]", "NULL")); } return false; @@ -367,7 +364,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.errors.invalid-level", "[level]", level.getUniqueId()); + Utils.sendMessage(user, user.getTranslation("challenges.errors.invalid-level", + "[level]", level.getUniqueId())); } this.addon.logWarning("Data for level `" + level.getUniqueId() + "` is not valid. It could be NULL element in item-stack!"); @@ -378,13 +376,12 @@ public class ChallengesManager { if (user != null) { - user.sendMessage("challenges.errors.load-error", - VALUE, level.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.errors.load-error", + VALUE, level.getFriendlyName())); } else { - this.addon.logError( - "Challenge Level '" + level.getUniqueId() + "' is not valid and skipped"); + this.addon.logError("Challenge Level '" + level.getUniqueId() + "' is not valid and skipped"); } return false; @@ -396,8 +393,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-skipping", - VALUE, level.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-skipping", + VALUE, level.getFriendlyName())); } return false; @@ -406,8 +403,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-overwriting", - VALUE, level.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-overwriting", + VALUE, level.getFriendlyName())); } } } @@ -415,8 +412,8 @@ public class ChallengesManager { if (!silent) { - user.sendMessage("challenges.messages.load-add", - VALUE, level.getFriendlyName()); + Utils.sendMessage(user, user.getTranslation("challenges.messages.load-add", + VALUE, level.getFriendlyName())); } } @@ -425,26 +422,6 @@ public class ChallengesManager } - /** - * This method stores PlayerData into local cache. - * - * @param playerData ChallengesPlayerData that must be loaded. - * - * TODO: Remove this unused method? - */ - private void loadPlayerData(@NonNull ChallengesPlayerData playerData) - { - try - { - this.playerCacheData.put(playerData.getUniqueId(), playerData); - } - catch (Exception e) - { - this.addon.getLogger().severe("UUID for player in challenge data file is invalid!"); - } - } - - /** * This method removes given player from cache data. * @@ -453,12 +430,6 @@ public class ChallengesManager public void removeFromCache(UUID playerID) { // Remove due possible issues with saving... (#246) -// if (!this.settings.isStoreAsIslandData() && this.playerCacheData.containsKey(playerID.toString())) -// { -// // save before remove -// this.savePlayerData(playerID.toString()); -// this.playerCacheData.remove(playerID.toString()); -// } this.savePlayerData(playerID.toString()); @@ -588,29 +559,45 @@ public class ChallengesManager /** * This method removes all challenges addon data from Database. * @param complete Remove also user data. + * @param name Name of the GameMode. */ - public void wipeDatabase(boolean complete) + public void wipeDatabase(boolean complete, String name) { - this.wipeLevels(); - this.wipeChallenges(); + this.wipeLevels(name); + this.wipeChallenges(name); if (complete) { - this.wipePlayers(); + this.wipePlayers(name); } } + /** + * This method removes all challenges addon data from Database. + * @param name Name of the GameMode. + */ + public void wipeDatabase(String name) + { + this.wipeDatabase(false, name); + } + + /** * This method collects all data from levels database and removes them. * Also clears levels cache data. */ - private void wipeLevels() + private void wipeLevels(String gamemode) { List levelList = this.levelDatabase.loadObjects(); - levelList.forEach(level -> this.levelDatabase.deleteID(level.getUniqueId())); - this.levelCacheData.clear(); + levelList.stream(). + filter(level -> level.getUniqueId().startsWith(gamemode.toLowerCase()) || + level.getUniqueId().startsWith(gamemode)). + forEach(level -> { + this.levelDatabase.deleteID(level.getUniqueId()); + this.levelCacheData.remove(level.getUniqueId()); + }); } @@ -618,12 +605,17 @@ public class ChallengesManager * This method collects all data from challenges database and removes them. * Also clears challenges cache data. */ - private void wipeChallenges() + private void wipeChallenges(String gamemode) { List challengeList = this.challengeDatabase.loadObjects(); - challengeList.forEach(challenge -> this.challengeDatabase.deleteID(challenge.getUniqueId())); - this.challengeCacheData.clear(); + challengeList.stream(). + filter(challenge -> challenge.getUniqueId().startsWith(gamemode.toLowerCase()) || + challenge.getUniqueId().startsWith(gamemode)). + forEach(challenge -> { + this.challengeDatabase.deleteID(challenge.getUniqueId()); + this.challengeCacheData.remove(challenge.getUniqueId()); + }); } @@ -631,12 +623,15 @@ public class ChallengesManager * This method collects all data from players database and removes them. * Also clears players cache data. */ - public void wipePlayers() + public void wipePlayers(String gamemode) { - List playerDataList = this.playersDatabase.loadObjects(); - - playerDataList.forEach(playerData -> this.playersDatabase.deleteID(playerData.getUniqueId())); this.playerCacheData.clear(); + + List playerDataList = this.playersDatabase.loadObjects(); + playerDataList.forEach(playerData -> { + playerData.reset(gamemode); + this.playersDatabase.saveObjectAsync(playerData); + }); } @@ -651,10 +646,14 @@ public class ChallengesManager public void migrateDatabase(User user, World world) { world = Util.getWorld(world); - + if (world == null) { + this.addon.logError("No such world!"); + return; + } + if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.migrate-start"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-start")); } else { @@ -670,7 +669,7 @@ public class ChallengesManager if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.migrate-end"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-end")); } else { @@ -681,7 +680,7 @@ public class ChallengesManager { if (user.isPlayer()) { - user.sendMessage("challenges.messages.admin.migrate-not"); + Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-not")); } else { @@ -735,7 +734,6 @@ public class ChallengesManager /** * This method collects all data from challenges database and migrates them. */ - @SuppressWarnings("deprecation") private boolean migrateChallenges(World world) { String addonName = Utils.getGameMode(world); @@ -768,49 +766,6 @@ public class ChallengesManager this.challengeDatabase.saveObjectAsync(challenge); this.challengeCacheData.put(challenge.getUniqueId(), challenge); } - - // Migrate Requirements. - if (challenge.getRequirements() == null) - { - switch (challenge.getChallengeType()) - { - case INVENTORY: - InventoryRequirements inventoryRequirements = new InventoryRequirements(); - inventoryRequirements.setRequiredItems(challenge.getRequiredItems()); - inventoryRequirements.setTakeItems(challenge.isTakeItems()); - - inventoryRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(inventoryRequirements); - break; - case ISLAND: - IslandRequirements islandRequirements = new IslandRequirements(); - islandRequirements.setRemoveBlocks(challenge.isRemoveBlocks()); - islandRequirements.setRemoveEntities(challenge.isRemoveEntities()); - islandRequirements.setRequiredBlocks(challenge.getRequiredBlocks()); - islandRequirements.setRequiredEntities(challenge.getRequiredEntities()); - islandRequirements.setSearchRadius(challenge.getSearchRadius()); - - islandRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(islandRequirements); - break; - case OTHER: - OtherRequirements otherRequirements = new OtherRequirements(); - otherRequirements.setRequiredExperience(challenge.getRequiredExperience()); - otherRequirements.setRequiredIslandLevel(challenge.getRequiredIslandLevel()); - otherRequirements.setRequiredMoney(challenge.getRequiredMoney()); - otherRequirements.setTakeExperience(challenge.isTakeExperience()); - otherRequirements.setTakeMoney(challenge.isTakeMoney()); - - otherRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(otherRequirements); - break; - } - - // This save should not involve any upgrades in other parts. - - this.challengeDatabase.saveObjectAsync(challenge); - this.challengeCacheData.put(challenge.getUniqueId(), challenge); - } } return updated; @@ -876,8 +831,6 @@ public class ChallengesManager { // Challenges and Levels are saved on modifications only to avoid issues with // NULL's in data after interrupting server while in saving stage. - // this.saveChallenges(); - // this.saveLevels(); this.savePlayersData(); } @@ -1287,6 +1240,101 @@ public class ChallengesManager // --------------------------------------------------------------------- + /** + * Gets statistic data. + * + * @param user the user + * @param world the world + * @param statistic the statistic + * @return the statistic data + */ + public int getStatisticData(User user, World world, Statistic statistic) + { + if (this.settings.isStoreAsIslandData()) + { + Island island = this.addon.getIslands().getIsland(world, user); + + if (island == null) + { + return 0; + } + + return island.getMemberSet().stream().map(Bukkit::getPlayer). + filter(Objects::nonNull). + mapToInt(player -> player.getStatistic(statistic)). + sum(); + } + else + { + return user.getPlayer().getStatistic(statistic); + } + } + + + /** + * Gets statistic data. + * + * @param user the user + * @param world the world + * @param statistic the statistic + * @param material the material + * @return the statistic data + */ + public int getStatisticData(User user, World world, Statistic statistic, Material material) + { + if (this.settings.isStoreAsIslandData()) + { + Island island = this.addon.getIslands().getIsland(world, user); + + if (island == null) + { + return 0; + } + + return island.getMemberSet().stream().map(Bukkit::getPlayer). + filter(Objects::nonNull). + mapToInt(player -> player.getStatistic(statistic, material)). + sum(); + } + else + { + return user.getPlayer().getStatistic(statistic, material); + } + } + + + /** + * Gets statistic data. + * + * @param user the user + * @param world the world + * @param statistic the statistic + * @param entity the entity + * @return the statistic data + */ + public int getStatisticData(User user, World world, Statistic statistic, EntityType entity) + { + if (this.settings.isStoreAsIslandData()) + { + Island island = this.addon.getIslands().getIsland(world, user); + + if (island == null) + { + return 0; + } + + return island.getMemberSet().stream().map(Bukkit::getPlayer). + filter(Objects::nonNull). + mapToInt(player -> player.getStatistic(statistic, entity)). + sum(); + } + else + { + return user.getPlayer().getStatistic(statistic, entity); + } + } + + /** * This method returns if given user has completed given challenge in world. * @param user - User that must be checked. @@ -1327,6 +1375,43 @@ public class ChallengesManager } + /** + * This method returns if given user breached timeout for given challenge. + * @param user - User that must be checked. + * @param world - World where challenge operates. + * @param challenge - Challenge that must be checked. + * @return True, if challenge is breached timeout, otherwise - false. + */ + public boolean isBreachingTimeOut(User user, World world, Challenge challenge) + { + if (challenge.getTimeout() <= 0) + { + // Challenge does not have a timeout. + return false; + } + + return System.currentTimeMillis() < + this.getLastCompletionDate(user, world, challenge) + challenge.getTimeout(); + } + + + /** + * Gets last completion date for given challenge. + * + * @param user the user + * @param world the world + * @param challenge the challenge + * @return the last completion date + */ + public long getLastCompletionDate(User user, World world, Challenge challenge) + { + String userId = this.getDataUniqueID(user, Util.getWorld(world)); + this.addPlayerData(userId); + + return this.playerCacheData.get(userId).getLastCompletionTime(challenge.getUniqueId()); + } + + /** * This method sets given challenge as completed. * @param user - Targeted user. @@ -1607,6 +1692,21 @@ public class ChallengesManager } + /** + * Returns if the given level is last leve in given world. + * + * @param level the level + * @param world the world + * @return the boolean + */ + public boolean isLastLevel(ChallengeLevel level, World world) + { + List levels = this.getLevels(world); + + return levels.get(levels.size() - 1) == level; + } + + // --------------------------------------------------------------------- // Section: Challenges related methods // --------------------------------------------------------------------- @@ -1735,12 +1835,13 @@ public class ChallengesManager * @return Challenge that is currently created. */ @Nullable - public Challenge createChallenge(String uniqueID, Challenge.ChallengeType type, Requirements requirements) + public Challenge createChallenge(String uniqueID, String name, Challenge.ChallengeType type, Requirements requirements) { if (!this.containsChallenge(uniqueID)) { Challenge challenge = new Challenge(); challenge.setUniqueId(uniqueID); + challenge.setFriendlyName(name); challenge.setRequirements(requirements); challenge.setChallengeType(type); @@ -2030,15 +2131,18 @@ public class ChallengesManager /** * This method creates and returns new challenges level with given uniqueID. * @param uniqueID - new ID for challenge level. + * @param name Name - name of the level. + * @param world World where level is created. * @return ChallengeLevel that is currently created. */ @Nullable - public ChallengeLevel createLevel(String uniqueID, World world) + public ChallengeLevel createLevel(String uniqueID, String name, World world) { if (!this.containsLevel(uniqueID)) { ChallengeLevel level = new ChallengeLevel(); level.setUniqueId(uniqueID); + level.setFriendlyName(name); level.setWorld(world.getName()); this.saveLevel(level); diff --git a/src/main/java/world/bentobox/challenges/panel/CommonGUI.java b/src/main/java/world/bentobox/challenges/panel/CommonGUI.java deleted file mode 100644 index e3d69af..0000000 --- a/src/main/java/world/bentobox/challenges/panel/CommonGUI.java +++ /dev/null @@ -1,1106 +0,0 @@ -package world.bentobox.challenges.panel; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.conversations.Conversation; -import org.bukkit.conversations.ConversationContext; -import org.bukkit.conversations.ConversationFactory; -import org.bukkit.conversations.Prompt; -import org.bukkit.conversations.StringPrompt; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BookMeta; -import org.bukkit.inventory.meta.EnchantmentStorageMeta; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.KnowledgeBookMeta; -import org.bukkit.inventory.meta.LeatherArmorMeta; -import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.inventory.meta.SkullMeta; -import org.bukkit.inventory.meta.SpawnEggMeta; -import org.bukkit.inventory.meta.TropicalFishBucketMeta; -import org.bukkit.potion.PotionData; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; - -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.TextComponent; -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.util.Util; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.ChallengeLevel; -import world.bentobox.challenges.database.object.requirements.InventoryRequirements; -import world.bentobox.challenges.database.object.requirements.IslandRequirements; -import world.bentobox.challenges.database.object.requirements.OtherRequirements; -import world.bentobox.challenges.utils.LevelStatus; -import world.bentobox.challenges.utils.Utils; - - -/** - * This class contains common methods that will be used over all GUIs. It also allows - * easier navigation between different GUIs. - */ -public abstract class CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * This variable stores parent gui. - */ - protected CommonGUI parentGUI; - - /** - * Variable stores Challenges addon. - */ - protected ChallengesAddon addon; - - /** - * Variable stores world in which panel is referred to. - */ - protected World world; - - /** - * Variable stores user who created this panel. - */ - protected User user; - - /** - * Variable stores top label of command from which panel was called. - */ - protected String topLabel; - - /** - * Variable stores permission prefix of command from which panel was called. - */ - protected String permissionPrefix; - - /** - * Variable stores any value. - */ - protected Object valueObject; - - /** - * This object holds current page index. - */ - protected int pageIndex; - - /** - * This object holds PanelItem that allows to return to previous panel. - */ - protected PanelItem returnButton; - - - /** - * This enum contains buttons that is offten used in multiple GUIs. - */ - protected enum CommonButtons - { - NEXT, - PREVIOUS, - RETURN - } - - - // --------------------------------------------------------------------- - // Section: Constants - // --------------------------------------------------------------------- - - - protected static final String ADMIN = "admin"; - - protected static final String CHALLENGES = "challenges"; - - protected static final String IMPORT = "import"; - - protected static final String DEFAULT = "defaults"; - - protected static final String GENERATE = "generate"; - - protected static final String SETTINGS = "settings"; - - protected static final String DELETE = "delete"; - - protected static final String WIPE = "wipe"; - - protected static final String EDIT = "edit"; - - protected static final String ADD = "add"; - - protected static final String RESET = "reset"; - - protected static final String COMPLETE = "complete"; - - protected static final String DOWNLOAD = "download"; - - // --------------------------------------------------------------------- - // Section: Constructors - // --------------------------------------------------------------------- - - - /** - * Default constructor that inits panels with minimal requirements, without parent panel. - * - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - */ - public CommonGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix) - { - this(addon, world, user, topLabel, permissionPrefix, null); - } - - - /** - * Default constructor that inits panels with minimal requirements. - * - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param parentGUI Parent panel for current panel. - */ - public CommonGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - this.addon = addon; - this.world = world; - this.user = user; - - this.topLabel = topLabel; - this.permissionPrefix = permissionPrefix; - - this.parentGUI = parentGUI; - - this.pageIndex = 0; - - this.returnButton = new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.return")). - icon(Material.OAK_DOOR). - clickHandler((panel, user1, clickType, i) -> { - if (this.parentGUI == null) - { - this.user.closeInventory(); - return true; - } - - this.parentGUI.build(); - return true; - }).build(); - } - - - /** - * Default constructor that inits panels with minimal requirements. - * @param parentGUI Parent panel for current panel. - */ - public CommonGUI(CommonGUI parentGUI) - { - this.addon = parentGUI.addon; - this.world = parentGUI.world; - this.user = parentGUI.user; - - this.topLabel = parentGUI.topLabel; - this.permissionPrefix = parentGUI.permissionPrefix; - - this.parentGUI = parentGUI; - - this.pageIndex = 0; - - this.returnButton = new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.return")). - icon(Material.OAK_DOOR). - clickHandler((panel, user1, clickType, i) -> { - - if (this.parentGUI == null) - { - this.user.closeInventory(); - return true; - } - - this.parentGUI.build(); - return true; - }).build(); - } - - - // --------------------------------------------------------------------- - // Section: Common methods - // --------------------------------------------------------------------- - - - /** - * This method builds all necessary elements in GUI panel. - */ - public abstract void build(); - - - /** - * This method returns PanelItem that represents given Button. - * @param button Button that must be returned. - * @return PanelItem with requested functionality. - */ - protected PanelItem getButton(CommonButtons button) - { - ItemStack icon; - String name; - List description; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case NEXT: - { - name = this.user.getTranslation("challenges.gui.buttons.next"); - description = Collections.emptyList(); - icon = new ItemStack(Material.OAK_SIGN); - clickHandler = (panel, user, clickType, slot) -> { - this.pageIndex++; - this.build(); - return true; - }; - - break; - } - case PREVIOUS: - { - name = this.user.getTranslation("challenges.gui.buttons.previous"); - description = Collections.emptyList(); - icon = new ItemStack(Material.OAK_SIGN); - clickHandler = (panel, user, clickType, slot) -> { - this.pageIndex--; - this.build(); - return true; - }; - - break; - } - case RETURN: - return this.returnButton; - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(description). - glow(false). - clickHandler(clickHandler). - build(); - } - - - /** - * This method sets new value to ValueObject variable. - * @param value new Value of valueObject. - */ - public void setValue(Object value) - { - this.valueObject = value; - } - - - // --------------------------------------------------------------------- - // Section: Generate Challenge Description - // --------------------------------------------------------------------- - - - /** - * This method generates and returns given challenge description. It is used here to avoid multiple - * duplicates, as it would be nice to have single place where challenge could be generated. - * @param challenge Challenge which description must be generated. - * @return List of strings that will be used in challenges description. - */ - protected List generateChallengeDescription(Challenge challenge, Player user) - { - List result = new ArrayList<>(); - - // Some values to avoid overchecking. - ChallengesManager manager = this.addon.getChallengesManager(); - - final boolean isCompletedOnce = - manager.isChallengeComplete(user.getUniqueId(), world, challenge); - final long doneTimes = challenge.isRepeatable() ? - manager.getChallengeTimes(this.user, this.world, challenge) : isCompletedOnce ? 0 : 1; - - boolean isCompletedAll = isCompletedOnce && challenge.isRepeatable() && - challenge.getMaxTimes() > 0 && - doneTimes >= challenge.getMaxTimes(); - - this.addon.getChallengesSettings().getChallengeLoreMessage().forEach(messagePart -> { - switch (messagePart) - { - case LEVEL: - { - ChallengeLevel level = manager.getLevel(challenge); - - if (level == null) - { - result.add(this.user.getTranslation("challenges.errors.missing-level", - "[level]", challenge.getLevel())); - } - else - { - result.add(this.user - .getTranslation("challenges.gui.challenge-description.level", - "[level]", level.getFriendlyName())); - } - break; - } - case STATUS: - { - if (isCompletedOnce) - { - result.add(this.user - .getTranslation("challenges.gui.challenge-description.completed")); - } - break; - } - case COUNT: - { - if (challenge.isRepeatable()) - { - if (challenge.getMaxTimes() > 0) - { - if (isCompletedAll) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.maxed-reached", - "[donetimes]", - String.valueOf(doneTimes), - "[maxtimes]", - String.valueOf(challenge.getMaxTimes()))); - } - else - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.completed-times-of", - "[donetimes]", - String.valueOf(doneTimes), - "[maxtimes]", - String.valueOf(challenge.getMaxTimes()))); - } - } - else - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.completed-times", - "[donetimes]", - String.valueOf(doneTimes))); - } - } - break; - } - case DESCRIPTION: - { - result.addAll(challenge.getDescription()); - break; - } - case WARNINGS: - { - if (!isCompletedAll) - { - if (challenge.getChallengeType().equals(Challenge.ChallengeType.INVENTORY)) - { - if (challenge.getRequirements().isTakeItems()) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.warning-items-take")); - } - } - else if (challenge.getChallengeType().equals(Challenge.ChallengeType.ISLAND)) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.objects-close-by")); - - IslandRequirements requirements = challenge.getRequirements(); - - if (requirements.isRemoveEntities() && !requirements.getRequiredEntities().isEmpty()) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.warning-entities-kill")); - } - - if (requirements.isRemoveBlocks() && !requirements.getRequiredBlocks().isEmpty()) - { - result.add(this.user.getTranslation( - "challenges.gui.challenge-description.warning-blocks-remove")); - } - } - } - break; - } - case ENVIRONMENT: - { - // Display only if there are limited environments - - if (!isCompletedAll && - !challenge.getEnvironment().isEmpty() && - challenge.getEnvironment().size() != 3) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.environment")); - - if (challenge.getEnvironment().contains(World.Environment.NORMAL)) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.normal")); - } - - if (challenge.getEnvironment().contains(World.Environment.NETHER)) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.nether")); - } - - if (challenge.getEnvironment().contains(World.Environment.THE_END)) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.the-end")); - } - } - break; - } - case REQUIREMENTS: - { - if (!isCompletedAll) - { - switch (challenge.getChallengeType()) - { - case INVENTORY: - result.addAll(this.getInventoryRequirements(challenge.getRequirements())); - break; - case ISLAND: - result.addAll(this.getIslandRequirements(challenge.getRequirements())); - break; - case OTHER: - result.addAll(this.getOtherRequirements(challenge.getRequirements())); - break; - } - } - - break; - } - case REWARD_TEXT: - { - if (isCompletedAll) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.not-repeatable")); - } - else - { - // Show a title to the rewards - result.add(this.user.getTranslation("challenges.gui.challenge-description.rewards-title")); - if (isCompletedOnce) - { - result.add(challenge.getRepeatRewardText()); - } - else - { - result.add(challenge.getRewardText()); - } - } - break; - } - case REWARD_OTHER: - { - if (!isCompletedAll) - { - result.addAll(this.getChallengeRewardOthers(challenge, isCompletedOnce)); - } - break; - } - case REWARD_ITEMS: - { - if (!isCompletedAll) - { - result.addAll(this.getChallengeRewardItems(challenge, isCompletedOnce)); - } - break; - } - case REWARD_COMMANDS: - { - if (!isCompletedAll) - { - result.addAll(this.getChallengeRewardCommands(challenge, isCompletedOnce, user)); - } - break; - } - } - }); - - result.replaceAll(x -> x.replace("[label]", this.topLabel)); - - return result; - } - - - /** - * This method returns list of strings that contains basic information about challenge rewards. - * @param challenge which reward message must be created. - * @param isCompletedOnce indicate if must use repeat rewards - * @return list of strings that contains rewards message. - */ - private List getChallengeRewardOthers(Challenge challenge, boolean isCompletedOnce) - { - double rewardMoney; - int rewardExperience; - - - if (!isCompletedOnce) - { - rewardMoney = challenge.getRewardMoney(); - rewardExperience = challenge.getRewardExperience(); - } - else - { - rewardMoney = challenge.getRepeatMoneyReward(); - rewardExperience = challenge.getRepeatExperienceReward(); - } - - List result = new ArrayList<>(); - - // Add message about reward XP - if (rewardExperience > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.experience-reward", - "[value]", Integer.toString(rewardExperience))); - } - - // Add message about reward money - if (this.addon.getPlugin().getSettings().isUseEconomy() && rewardMoney > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.money-reward", - "[value]", Double.toString(rewardMoney))); - } - - return result; - } - - - /** - * This method returns list of strings that contains reward items from given challenge. - * @param challenge Challenge which reward items and commands must be returned. - * @param isCompletedOnce Boolean that indicate if must use repeat rewards. - * @return List of strings that contains message from challenges. - */ - private List getChallengeRewardItems(Challenge challenge, boolean isCompletedOnce) - { - List result = new ArrayList<>(); - - List rewardItems; - - if (isCompletedOnce) - { - rewardItems = challenge.getRepeatItemReward(); - } - else - { - rewardItems = challenge.getRewardItems(); - } - - // Add message about reward items - if (!rewardItems.isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.reward-items")); - - Utils.groupEqualItems(rewardItems).forEach(itemStack -> - result.addAll(this.generateItemStackDescription(itemStack))); - } - - return result; - } - - - /** - * This method returns list of strings that contains reward commands from given challenge. - * @param challenge Challenge which reward items and commands must be returned. - * @param isCompletedOnce Boolean that indicate if must use repeat rewards. - * @param user Target user for command string. - * @return List of strings that contains message from challenges. - */ - private List getChallengeRewardCommands(Challenge challenge, boolean isCompletedOnce, Player user) - { - List result = new ArrayList<>(); - - List rewardCommands; - - if (isCompletedOnce) - { - rewardCommands = challenge.getRepeatRewardCommands(); - } - else - { - rewardCommands = challenge.getRewardCommands(); - } - - // Add message about reward commands - if (!rewardCommands.isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.reward-commands")); - - for (String command : rewardCommands) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command.replace("[player]", user.getName()).replace("[SELF]", ""))); - } - } - - return result; - } - - - /** - * This method returns list of strings that contains basic information about requirements. - * @param requirements which requirements message must be created. - * @return list of strings that contains requirements message. - */ - private List getOtherRequirements(OtherRequirements requirements) - { - List result = new ArrayList<>(); - - // Add message about required exp - if (requirements.getRequiredExperience() > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-experience", - "[value]", Integer.toString(requirements.getRequiredExperience()))); - } - - // Add message about required money - if (this.addon.isEconomyProvided() && requirements.getRequiredMoney() > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-money", - "[value]", Double.toString(requirements.getRequiredMoney()))); - } - - // Add message about required island level - if (this.addon.isLevelProvided() && requirements.getRequiredIslandLevel() > 0) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-island-level", - "[value]", Long.toString(requirements.getRequiredIslandLevel()))); - } - - return result; - } - - - /** - * This method returns list of strings that contains basic information about requirements. - * @param requirements which requirements message must be created. - * @return list of strings that contains requirements message. - */ - private List getInventoryRequirements(InventoryRequirements requirements) - { - List result = new ArrayList<>(); - - // Add message about required items - if (!requirements.getRequiredItems().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-items")); - - Utils.groupEqualItems(requirements.getRequiredItems()).forEach(itemStack -> - result.addAll(this.generateItemStackDescription(itemStack))); - } - - return result; - } - - - /** - * This method returns list of strings that contains required items, entities and blocks from given challenge. - * @param challenge Challenge which requirement items, entities and blocks must be returned. - * @return List of strings that contains message from challenges. - */ - private List getIslandRequirements(IslandRequirements challenge) - { - List result = new ArrayList<>(); - - if (!challenge.getRequiredBlocks().isEmpty() || !challenge.getRequiredEntities().isEmpty()) - { - // Add required blocks - if (!challenge.getRequiredBlocks().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-blocks")); - - for (Map.Entry entry : challenge.getRequiredBlocks().entrySet()) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.block", - "[block]", Util.prettifyText(entry.getKey().name()), - "[count]", Integer.toString(entry.getValue()))); - } - } - - // Add required entities - if (!challenge.getRequiredEntities().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.challenge-description.required-entities")); - - for (Map.Entry entry : challenge.getRequiredEntities().entrySet()) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.entity", - "[entity]", Util.prettifyText(entry.getKey().name()), - "[count]", Integer.toString(entry.getValue()))); - } - } - } - - return result; - } - - - // --------------------------------------------------------------------- - // Section: Generate Level Description - // --------------------------------------------------------------------- - - - /** - * This method generates level description string. - * @param level Level which string must be generated. - * @param user User who calls generation. - * @return List with generated description. - */ - protected List generateLevelDescription(ChallengeLevel level, Player user) - { - List result = new ArrayList<>(); - - ChallengesManager manager = this.addon.getChallengesManager(); - LevelStatus status = manager.getChallengeLevelStatus(user.getUniqueId(), this.world, level); - - // Check if unlock message should appear. - boolean hasCompletedOne = status.isComplete() || status.isUnlocked() && - level.getChallenges().stream().anyMatch(challenge -> - this.addon.getChallengesManager().isChallengeComplete(user.getUniqueId(), world, challenge)); - - this.addon.getChallengesSettings().getLevelLoreMessage().forEach(messagePart -> { - switch (messagePart) - { - case LEVEL_STATUS: - { - if (status.isComplete()) - { - result.add(this.user.getTranslation("challenges.gui.level-description.completed")); - } - break; - } - case CHALLENGE_COUNT: - { - if (!status.isComplete() && status.isUnlocked()) - { - int doneChallengeCount = (int) level.getChallenges().stream(). - filter(challenge -> this.addon.getChallengesManager().isChallengeComplete(user.getUniqueId(), world, challenge)). - count(); - - result.add(this.user.getTranslation("challenges.gui.level-description.completed-challenges-of", - "[number]", Integer.toString(doneChallengeCount), - "[max]", Integer.toString(level.getChallenges().size()))); - } - - break; - } - case UNLOCK_MESSAGE: - { - if (!hasCompletedOne) - { - result.add(level.getUnlockMessage()); - } - - break; - } - case WAIVER_AMOUNT: - { - if (status.isUnlocked() && !status.isComplete()) - { - result.add(this.user.getTranslation("challenges.gui.level-description.waver-amount", - "[value]", Integer.toString(level.getWaiverAmount()))); - } - - break; - } - case LEVEL_REWARD_TEXT: - { - if (status.isUnlocked() && !status.isComplete()) - { - result.add(level.getRewardText()); - } - break; - } - case LEVEL_REWARD_OTHER: - { - if (status.isUnlocked() && !status.isComplete()) - { - if (level.getRewardExperience() > 0) - { - result.add(this.user.getTranslation("challenges.gui.level-description.experience-reward", - "[value]", Integer.toString(level.getRewardExperience()))); - } - - if (this.addon.isEconomyProvided() && level.getRewardMoney() > 0) - { - result.add(this.user.getTranslation("challenges.gui.level-description.money-reward", - "[value]", Integer.toString(level.getRewardMoney()))); - } - } - break; - } - case LEVEL_REWARD_ITEMS: - { - if (status.isUnlocked() && !status.isComplete() && !level.getRewardItems().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.level-description.reward-items")); - - Utils.groupEqualItems(level.getRewardItems()).forEach(itemStack -> - result.addAll(this.generateItemStackDescription(itemStack))); - } - break; - } - case LEVEL_REWARD_COMMANDS: - { - if (status.isUnlocked() && !status.isComplete() && !level.getRewardCommands().isEmpty()) - { - result.add(this.user.getTranslation("challenges.gui.level-description.reward-commands")); - - for (String command : level.getRewardCommands()) - { - result.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command.replace("[player]", user.getName()).replace("[SELF]", ""))); - } - } - break; - } - } - }); - - result.replaceAll(x -> x.replace("[label]", this.topLabel)); - - return result; - } - - - // --------------------------------------------------------------------- - // Section: ItemStack Description - // --------------------------------------------------------------------- - - - /** - * This method generates description for given item stack object. - * @param itemStack Object which lore must be generated - * @return List with generated description - */ - @SuppressWarnings("deprecation") - protected List generateItemStackDescription(ItemStack itemStack) - { - List result = new ArrayList<>(); - - result.add(this.user.getTranslation("challenges.gui.item-description.item", - "[item]", Util.prettifyText(itemStack.getType().name()), - "[count]", Integer.toString(itemStack.getAmount()))); - - if (itemStack.hasItemMeta()) - { - ItemMeta meta = itemStack.getItemMeta(); - - if (meta.hasDisplayName()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.item-name", - "[name]", meta.getDisplayName())); - } - - if (meta.hasLore()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.item-lore")); - result.addAll(meta.getLore()); - } - - if (meta instanceof BookMeta) - { - result.add(this.user.getTranslation("challenges.gui.item-description.book-meta", - "[author]", ((BookMeta) meta).getAuthor(), - "[title]", ((BookMeta) meta).getTitle())); - } - else if (meta instanceof EnchantmentStorageMeta) - { - ((EnchantmentStorageMeta) meta).getStoredEnchants().forEach(((enchantment, level) -> { - result.add(this.user.getTranslation("challenges.gui.item-description.item-enchant", - "[enchant]", enchantment.getKey().getKey(), "[level]", Integer.toString(level))); - })); - } - else if (meta instanceof KnowledgeBookMeta) - { - result.add(this.user.getTranslation("challenges.gui.item-description.recipe-count", - "[count]", Integer.toString(((KnowledgeBookMeta) meta).getRecipes().size()))); - } - else if (meta instanceof LeatherArmorMeta) - { - result.add(this.user.getTranslation("challenges.gui.item-description.armor-color", - "[color]", ((LeatherArmorMeta) meta).getColor().toString())); - } - else if (meta instanceof PotionMeta) - { - PotionData data = ((PotionMeta) meta).getBasePotionData(); - - if (data.isExtended() && data.isUpgraded()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.potion-type-extended-upgraded", - "[name]", Util.prettifyText(data.getType().name()))); - } - else if (data.isUpgraded()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.potion-type-upgraded", - "[name]", Util.prettifyText(data.getType().name()))); - } - else if (data.isExtended()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.potion-type-extended", - "[name]", Util.prettifyText(data.getType().name()))); - } - else - { - result.add(this.user.getTranslation("challenges.gui.item-description.potion-type", - "[name]", Util.prettifyText(data.getType().name()))); - } - - if (((PotionMeta) meta).hasCustomEffects()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.custom-effects")); - - ((PotionMeta) meta).getCustomEffects().forEach(potionEffect -> - result.add(this.user.getTranslation("challenges.gui.item-description.potion-effect", - "[effect]", Util.prettifyText(potionEffect.getType().getName()), - "[duration]", Integer.toString(potionEffect.getDuration()), - "[amplifier]", Integer.toString(potionEffect.getAmplifier())))); - } - } - else if (meta instanceof SkullMeta) - { - if (((SkullMeta) meta).getOwningPlayer() != null) - { - result.add(this.user.getTranslation("challenges.gui.item-description.skull-owner", - "[owner]", ((SkullMeta) meta).getOwningPlayer().getName())); - } - } - else if (meta instanceof SpawnEggMeta) - { - result.add(this.user.getTranslation("challenges.gui.item-description.egg-meta", - "[mob]", Util.prettifyText(((SpawnEggMeta) meta).getSpawnedType().name()))); - } - else if (meta instanceof TropicalFishBucketMeta) - { - if (((TropicalFishBucketMeta) meta).hasVariant()) - { - result.add(this.user.getTranslation("challenges.gui.item-description.fish-meta", - "[pattern]", Util.prettifyText(((TropicalFishBucketMeta) meta).getPattern().name()), - "[pattern-color]", Util.prettifyText(((TropicalFishBucketMeta) meta).getPatternColor().name()), - "[body-color]", Util.prettifyText(((TropicalFishBucketMeta) meta).getBodyColor().name()))); - } - } - - if (meta.hasEnchants()) - { - itemStack.getEnchantments().forEach((enchantment, level) -> { - result.add(this.user.getTranslation("challenges.gui.item-description.item-enchant", - "[enchant]", - enchantment.getKey().getKey(), - "[level]", - Integer.toString(level))); - }); - } - } - - return result; - } - - - // --------------------------------------------------------------------- - // Section: Chat Input Methods - // --------------------------------------------------------------------- - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on inputText in - * chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - * @param message Message that will be set in player text field when clicked on question. - */ - protected void getFriendlyName(Consumer consumer, @NonNull String question, @Nullable String message) - { - final User user = this.user; - - Conversation conversation = - new ConversationFactory(BentoBox.getInstance()).withFirstPrompt( - new StringPrompt() - { - /** - * @see Prompt#getPromptText(ConversationContext) - */ - @Override - public String getPromptText(ConversationContext conversationContext) - { - // Close input GUI. - user.closeInventory(); - - if (message != null) - { - // Create Edit Text message. - TextComponent component = new TextComponent(user.getTranslation("challenges.gui.descriptions.admin.click-to-edit")); - component.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, message)); - // Send question and message to player. - user.getPlayer().spigot().sendMessage(component); - } - - // There are no editable message. Just return question. - return question; - } - - - /** - * @see Prompt#acceptInput(ConversationContext, String) - */ - @Override - public Prompt acceptInput(ConversationContext conversationContext, String answer) - { - // Add answer to consumer. - consumer.accept(answer); - // End conversation - return Prompt.END_OF_CONVERSATION; - } - }). - withLocalEcho(false). - // On cancel conversation will be closed. - withEscapeSequence("cancel"). - // Use null value in consumer to detect if user has abandoned conversation. - addConversationAbandonedListener(abandonedEvent -> - { - if (!abandonedEvent.gracefulExit()) - { - consumer.accept(null); - } - }). - withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). - buildConversation(user.getPlayer()); - - conversation.begin(); - } -} - diff --git a/src/main/java/world/bentobox/challenges/panel/CommonPagedPanel.java b/src/main/java/world/bentobox/challenges/panel/CommonPagedPanel.java new file mode 100644 index 0000000..947c10e --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/CommonPagedPanel.java @@ -0,0 +1,267 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.utils.Constants; + + +/** + * This panel implements common things for Paged pages. + */ +public abstract class CommonPagedPanel extends CommonPanel +{ + /** + * Instantiates a new Common paged panel. + * + * @param addon the addon + * @param user the user + * @param world the world + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + protected CommonPagedPanel(ChallengesAddon addon, + User user, + World world, String topLabel, String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + } + + + /** + * Instantiates a new Common paged panel. + * + * @param parentPanel the parent panel + */ + protected CommonPagedPanel(@NonNull CommonPanel parentPanel) + { + super(parentPanel); + } + + + /** + * This method is called when filter value is updated. + */ + protected abstract void updateFilters(); + + + /** + * Create element button panel item. + * + * @param object the object + * @return the panel item + */ + protected abstract PanelItem createElementButton(T object); + + + /** + * Populate elements. + * + * @param panelBuilder the panel builder + * @param objectList the object list + */ + protected void populateElements(PanelBuilder panelBuilder, List objectList) + { + final int MAX_ELEMENTS = 21; + final int size = objectList.size(); + + if (this.pageIndex < 0) + { + this.pageIndex = size / MAX_ELEMENTS; + } + else if (this.pageIndex > (size / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int objectIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (objectIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + objectIndex < size && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createElementButton(objectList.get(objectIndex++))); + } + + index++; + } + + if (size > MAX_ELEMENTS && !(1.0 * size / MAX_ELEMENTS <= this.pageIndex + 1)) + { + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + + } + + if (this.pageIndex > 0) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + } + + // Add search button only if there is more than MAX_ELEMENTS objects or searchString + // is not blank. + if (!this.searchString.isBlank() || objectList.size() > MAX_ELEMENTS) + { + panelBuilder.item(40, this.getButton(CommonButtons.SEARCH)); + } + } + + + /** + * This method returns PanelItem that represents given Button. + * @param button Button that must be returned. + * @return PanelItem with requested functionality. + */ + protected PanelItem getButton(CommonButtons button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + if (button == CommonButtons.NEXT) + { + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_NUMBER, String.valueOf(this.pageIndex + 2))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-next")); + + icon = new ItemStack(Material.OAK_SIGN, this.pageIndex + 2); + clickHandler = (panel, user, clickType, slot) -> + { + this.pageIndex++; + this.build(); + return true; + }; + } + else if (button == CommonButtons.PREVIOUS) + { + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_NUMBER, String.valueOf(this.pageIndex))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-previous")); + + icon = new ItemStack(Material.OAK_SIGN, Math.max(1, this.pageIndex)); + clickHandler = (panel, user, clickType, slot) -> + { + this.pageIndex--; + this.build(); + return true; + }; + } + else if (button == CommonButtons.SEARCH) + { + description.add(this.user.getTranslation(reference + "description")); + + if (this.searchString != null && !this.searchString.isEmpty()) + { + description.add(this.user.getTranslation(reference + "search", + Constants.PARAMETER_VALUE, this.searchString)); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-edit")); + + if (this.searchString != null && !this.searchString.isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-clear")); + } + + icon = new ItemStack(Material.ANVIL); + + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + // Clear string. + this.searchString = ""; + this.updateFilters(); + // Rebuild gui. + this.build(); + } + else + { + // Create consumer that process description change + Consumer consumer = value -> + { + if (value != null) + { + this.searchString = value; + this.updateFilters(); + } + + this.build(); + }; + + // start conversation + ConversationUtils.createStringInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-search"), + user.getTranslation(Constants.CONVERSATIONS + "search-updated")); + } + + return true; + }; + } + else + { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * Next and Previous Buttons. + */ + private enum CommonButtons + { + NEXT, + PREVIOUS, + SEARCH + } + + + /** + * Current page index. + */ + private int pageIndex; + + /** + * Text that contains filter string. + */ + protected String searchString = ""; +} diff --git a/src/main/java/world/bentobox/challenges/panel/CommonPanel.java b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java new file mode 100644 index 0000000..83f2ecd --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java @@ -0,0 +1,1103 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import java.time.*; +import java.util.*; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.database.object.requirements.*; +import world.bentobox.challenges.managers.ChallengesManager; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.LevelStatus; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains common methods for all panels. + */ +public abstract class CommonPanel +{ + /** + * This is default constructor for all classes that extends CommonPanel. + * + * @param addon ChallengesAddon instance. + * @param user User who opens panel. + */ + protected CommonPanel(ChallengesAddon addon, User user, World world, String topLabel, String permissionPrefix) + { + this.addon = addon; + this.world = world; + this.manager = addon.getChallengesManager(); + this.user = user; + + this.topLabel = topLabel; + this.permissionPrefix = permissionPrefix; + + this.parentPanel = null; + + this.returnButton = new PanelItemBuilder(). + name(this.user.getTranslation(Constants.BUTTON + "quit.name")). + description(this.user.getTranslationOrNothing(Constants.BUTTON + "quit.description")). + description(""). + description(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-quit")). + icon(Material.OAK_DOOR). + clickHandler((panel, user1, clickType, i) -> { + this.user.closeInventory(); + return true; + }).build(); + } + + + /** + * This is default constructor for all classes that extends CommonPanel. + * + * @param parentPanel Parent panel of current panel. + */ + protected CommonPanel(@NonNull CommonPanel parentPanel) + { + this.addon = parentPanel.addon; + this.manager = parentPanel.manager; + this.user = parentPanel.user; + this.world = parentPanel.world; + + this.topLabel = parentPanel.topLabel; + this.permissionPrefix = parentPanel.permissionPrefix; + + this.parentPanel = parentPanel; + + this.returnButton = new PanelItemBuilder(). + name(this.user.getTranslation(Constants.BUTTON + "return.name")). + description(this.user.getTranslationOrNothing(Constants.BUTTON + "return.description")). + description(""). + description(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-return")). + icon(Material.OAK_DOOR). + clickHandler((panel, user1, clickType, i) -> { + this.parentPanel.build(); + return true; + }).build(); + } + + + /** + * This method allows building panel. + */ + protected abstract void build(); + + + /** + * This method reopens given panel. + * @param panel Panel that must be reopened. + */ + public static void reopen(CommonPanel panel) + { + panel.build(); + } + + +// --------------------------------------------------------------------- +// Section: Common methods +// --------------------------------------------------------------------- + + + /** + * This method generates and returns given challenge description. It is used here to avoid multiple + * duplicates, as it would be nice to have single place where challenge could be generated. + * @param challenge Challenge which description must be generated. + * @param target target player. + * @return List of strings that will be used in challenges description. + */ + protected List generateChallengeDescription(Challenge challenge, @Nullable User target) + { + // Some values to avoid over checking. + final boolean isCompletedOnce = target != null && + this.manager.isChallengeComplete(target.getUniqueId(), this.world, challenge); + + final long doneTimes = target != null && challenge.isRepeatable() ? + this.manager.getChallengeTimes(target, this.world, challenge) : (isCompletedOnce ? 0 : 1); + + boolean isCompletedAll = isCompletedOnce && + (!challenge.isRepeatable() || + challenge.getMaxTimes() > 0 && doneTimes >= challenge.getMaxTimes()); + + final String reference = Constants.DESCRIPTIONS + "challenge."; + + // Get description from custom translations + String description = this.user.getTranslationOrNothing( + "challenges.challenges." + challenge.getUniqueId() + ".description"); + + if (description.isEmpty()) + { + // Get data from object in single string. + description = Util.translateColorCodes(String.join("\n", challenge.getDescription())); + } + + // Non-memory optimal code used for easier debugging and nicer code layout for my eye :) + // Get status in single string + String status = this.generateChallengeStatus(isCompletedOnce, + isCompletedAll, + doneTimes, + challenge.getMaxTimes()); + // Get requirements in single string + String requirements = isCompletedAll ? "" : this.generateRequirements(challenge, target); + // Get rewards in single string + String rewards = isCompletedAll ? "" : this.generateRewards(challenge, isCompletedOnce); + // Get coolDown in singe string + String coolDown = isCompletedAll || challenge.getTimeout() <= 0 ? "" : + this.generateCoolDown(challenge, target); + + if (!description.replaceAll("(?m)^[ \\t]*\\r?\\n", "").isEmpty()) + { + String returnString = this.user.getTranslationOrNothing(reference + "lore", + "[requirements]", requirements, + "[rewards]", rewards, + "[status]", status, + "[cooldown]", coolDown); + + // remove empty lines from the generated text. + List collect = + Arrays.stream(returnString.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). + split("\n")). + collect(Collectors.toList()); + + // find and replace description from collected blocks. + + for (int i = 0; i < collect.size(); i++) + { + if (collect.get(i).contains(Constants.PARAMETER_DESCRIPTION)) + { + collect.set(i, collect.get(i).replace(Constants.PARAMETER_DESCRIPTION, description)); + } + } + + return collect; + } + else + { + String returnString = this.user.getTranslationOrNothing(reference + "lore", + Constants.PARAMETER_DESCRIPTION, description, + "[requirements]", requirements, + "[rewards]", rewards, + "[status]", status, + "[cooldown]", coolDown); + + // Remove empty lines and returns as a list. + + return Arrays.stream(returnString.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). + split("\n")). + collect(Collectors.toList()); + } + } + + + /** + * Generate cool down string. + * + * @param challenge the challenge + * @param target the target + * @return the string + */ + private String generateCoolDown(Challenge challenge, @Nullable User target) + { + final String reference = Constants.DESCRIPTIONS + "challenge.cooldown."; + + String coolDown; + + if (target != null && this.manager.isBreachingTimeOut(target, this.world, challenge)) + { + long missing = this.manager.getLastCompletionDate(this.user, this.world, challenge) + + challenge.getTimeout() - System.currentTimeMillis(); + + coolDown = this.user.getTranslation(reference + "wait-time", + "[time]", + Utils.parseDuration(Duration.ofMillis(missing), this.user)); + } + else + { + coolDown = ""; + } + + String timeout = this.user.getTranslation(reference + "timeout", + "[time]", + Utils.parseDuration(Duration.ofMillis(challenge.getTimeout()), this.user)); + + return this.user.getTranslation(reference + "lore", + "[timeout]", timeout, + "[wait-time]", coolDown); + } + + + /** + * This method generate requirements description for given challenge. + * @param challenge Challenge which requirements must be generated. + * @return Lore message with requirements. + */ + private String generateRequirements(Challenge challenge, @Nullable User target) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements."; + + String environment; + + if (challenge.getEnvironment().isEmpty() || challenge.getEnvironment().size() == 3) + { + // If challenge can be completed everywhere, do not display requirement. + environment = ""; + } + else if (challenge.getEnvironment().size() == 1) + { + environment = this.user.getTranslationOrNothing(reference + "environment-single", + Constants.PARAMETER_ENVIRONMENT, + Utils.prettifyObject(challenge.getEnvironment().iterator().next(), this.user)); + } + else + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "environment-title")); + challenge.getEnvironment().stream().sorted().forEach(en -> + { + builder.append("\n"); + builder.append(this.user.getTranslationOrNothing(reference + "environment-single", + Constants.PARAMETER_ENVIRONMENT, + Utils.prettifyObject(en, this.user))); + }); + + environment = builder.toString(); + } + + String permissions; + + if (!challenge.getRequirements().getRequiredPermissions().isEmpty()) + { + // Yes list duplication for complete menu. + List missingPermissions = challenge.getRequirements().getRequiredPermissions().stream(). + filter(permission -> target == null || !target.hasPermission(permission)). + sorted().toList(); + + StringBuilder permissionBuilder = new StringBuilder(); + + if (missingPermissions.size() == 1) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "permission-single", + Constants.PARAMETER_PERMISSION, missingPermissions.get(0))); + } + else if (!missingPermissions.isEmpty()) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "permissions-title")); + missingPermissions.forEach(permission -> + { + permissionBuilder.append("\n"); + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "permissions-list", + Constants.PARAMETER_PERMISSION, permission)); + }); + } + + permissions = permissionBuilder.toString(); + } + else + { + permissions = ""; + } + + String typeRequirement = switch (challenge.getChallengeType()) { + case INVENTORY_TYPE -> this.generateInventoryChallenge(challenge.getRequirements()); + case ISLAND_TYPE -> this.generateIslandChallenge(challenge.getRequirements()); + case OTHER_TYPE -> this.generateOtherChallenge(challenge.getRequirements()); + case STATISTIC_TYPE -> this.generateStatisticChallenge(challenge.getRequirements()); + }; + + return this.user.getTranslationOrNothing(reference + "lore", + Constants.PARAMETER_ENVIRONMENT, environment, + "[type-requirement]", typeRequirement, + "[permissions]", permissions); + } + + + /** + * This method generates lore message for island requirement. + * @param requirement Island Requirement. + * @return Requirement lore message. + */ + private String generateIslandChallenge(IslandRequirements requirement) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements.island."; + + String blocks; + + if (!requirement.getRequiredBlocks().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "blocks-title")); + requirement.getRequiredBlocks().entrySet().stream(). + sorted(Map.Entry.comparingByKey()). + forEach(entry -> + { + builder.append("\n"); + + if (entry.getValue() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "blocks-value", + Constants.PARAMETER_NUMBER, String.valueOf(entry.getValue()), + Constants.PARAMETER_MATERIAL, Utils.prettifyObject(entry.getKey(), this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "block-value", + Constants.PARAMETER_MATERIAL, Utils.prettifyObject(entry.getKey(), this.user))); + } + }); + + blocks = builder.toString(); + } + else + { + blocks = ""; + } + + String entities; + + if (!requirement.getRequiredEntities().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "entities-title")); + requirement.getRequiredEntities().entrySet().stream(). + sorted(Map.Entry.comparingByKey()). + forEach(entry -> + { + builder.append("\n"); + + if (entry.getValue() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "entities-value", + Constants.PARAMETER_NUMBER, String.valueOf(entry.getValue()), + Constants.PARAMETER_ENTITY, Utils.prettifyObject(entry.getKey(), this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "entity-value", + Constants.PARAMETER_ENTITY, Utils.prettifyObject(entry.getKey(), this.user))); + } + }); + + entities = builder.toString(); + } + else + { + entities = ""; + } + + String searchRadius = this.user.getTranslationOrNothing(reference + "search-radius", + Constants.PARAMETER_NUMBER, String.valueOf(requirement.getSearchRadius())); + + String warningBlocks = requirement.isRemoveBlocks() ? + this.user.getTranslationOrNothing(reference + "warning-block") : ""; + String warningEntities = requirement.isRemoveEntities() ? + this.user.getTranslationOrNothing(reference + "warning-entity") : ""; + + return this.user.getTranslationOrNothing(reference + "lore", + "[blocks]", blocks, + "[entities]", entities, + "[warning-block]", warningBlocks, + "[warning-entity]", warningEntities, + "[search-radius]", searchRadius); + } + + + /** + * This method generates lore message for inventory requirement. + * @param requirement Inventory Requirement. + * @return Requirement lore message. + */ + private String generateInventoryChallenge(InventoryRequirements requirement) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements.inventory."; + + String items; + + if (!requirement.getRequiredItems().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "item-title")); + Utils.groupEqualItems(requirement.getRequiredItems(), requirement.getIgnoreMetaData()).stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + { + builder.append("\n"); + + if (itemStack.getAmount() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "items-value", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "item-value", + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + }); + + items = builder.toString(); + } + else + { + items = ""; + } + + String warning = requirement.isTakeItems() ? + this.user.getTranslationOrNothing(reference + "warning") : ""; + + return this.user.getTranslationOrNothing(reference + "lore", + "[items]", items, + "[warning]", warning); + } + + + /** + * This method generates lore message for other requirement. + * @param requirement Other Requirement. + * @return Requirement lore message. + */ + private String generateOtherChallenge(OtherRequirements requirement) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements.other."; + + String experience = requirement.getRequiredExperience() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "experience", + "[number]", String.valueOf(requirement.getRequiredExperience())); + + String experienceWarning = requirement.getRequiredExperience() > 0 && requirement.isTakeExperience() ? + this.user.getTranslationOrNothing(reference + "experience-warning") : ""; + + String money = !this.addon.isEconomyProvided() || requirement.getRequiredMoney() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "money", + "[number]", String.valueOf(requirement.getRequiredMoney())); + + String moneyWarning = this.addon.isEconomyProvided() && + requirement.getRequiredMoney() > 0 && + requirement.isTakeMoney() ? + this.user.getTranslationOrNothing(reference + "money-warning") : ""; + + String level = !this.addon.isLevelProvided() || requirement.getRequiredIslandLevel() <= 0 ? "" : + this.user.getTranslationOrNothing(reference, + "[number]", String.valueOf(requirement.getRequiredIslandLevel())); + + return this.user.getTranslationOrNothing(reference + "lore", + "[experience]", experience, + "[experience-warning]", experienceWarning, + "[money]", money, + "[money-warning]", moneyWarning, + "[level]", level); + } + + + /** + * This method generates lore message for Statistic requirement. + * @param requirement Statistic Requirement. + * @return Requirement lore message. + */ + private String generateStatisticChallenge(StatisticRequirements requirement) + { + final String reference = Constants.DESCRIPTIONS + "challenge.requirements.statistic."; + + String statistic; + + if (requirement.getStatistic() == null) + { + // Challenges by default comes with empty statistic field. + return ""; + } + + switch (requirement.getStatistic().getType()) + { + case UNTYPED -> statistic = this.user.getTranslationOrNothing(reference + "statistic", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[number]", String.valueOf(requirement.getAmount())); + case ITEM, BLOCK -> { + if (requirement.getAmount() > 1) + { + statistic = this.user.getTranslationOrNothing(reference + "multiple-target", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[number]", String.valueOf(requirement.getAmount()), + "[target]", Utils.prettifyObject(requirement.getMaterial(), this.user)); + } + else + { + statistic = this.user.getTranslationOrNothing(reference + "single-target", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[target]", Utils.prettifyObject(requirement.getMaterial(), this.user)); + } + } + case ENTITY -> { + if (requirement.getAmount() > 1) + { + statistic = this.user.getTranslationOrNothing(reference + "multiple-target", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[number]", String.valueOf(requirement.getAmount()), + "[target]", Utils.prettifyObject(requirement.getEntity(), this.user)); + } + else + { + statistic = this.user.getTranslationOrNothing(reference + "single-target", + "[statistic]", Utils.prettifyObject(requirement.getStatistic(), this.user), + "[target]", Utils.prettifyObject(requirement.getEntity(), this.user)); + } + } + default -> statistic = ""; + } + + String warning = requirement.isReduceStatistic() ? + this.user.getTranslationOrNothing(reference + "warning") : ""; + + return this.user.getTranslationOrNothing(reference + "lore", + "[statistic]", statistic, + "[warning]", warning); + } + + + /** + * This message generates challenge status description. + * @param completedOnce Indicate that challenge is completed at least one time. + * @param completedAll Indicate that challenge is not repeatable anymore. + * @param completionCount Number of completion count. + * @param maxCompletions Number of max completion count. + * @return String with a text that will be generated for status. + */ + private String generateChallengeStatus(boolean completedOnce, + boolean completedAll, + long completionCount, + int maxCompletions) + { + final String reference = Constants.DESCRIPTIONS + "challenge.status."; + + if (completedAll) + { + if (maxCompletions > 1) + { + return this.user.getTranslationOrNothing(reference + "completed-times-reached", + Constants.PARAMETER_MAX, String.valueOf(maxCompletions)); + } + else + { + return this.user.getTranslationOrNothing(reference + "completed"); + } + } + else if (completedOnce) + { + if (maxCompletions > 0) + { + return this.user.getTranslationOrNothing(reference + "completed-times-of", + Constants.PARAMETER_MAX, String.valueOf(maxCompletions), + Constants.PARAMETER_NUMBER, String.valueOf(completionCount)); + } + else + { + return this.user.getTranslationOrNothing(reference + "completed-times", + Constants.PARAMETER_NUMBER, String.valueOf(completionCount)); + } + } + else + { + return ""; + } + } + + + /** + * This method creates reward lore text. + * @param challenge Challenge which reward lore must be generated. + * @param isRepeating Boolean that indicate if it is repeating reward or first time. + * @return Reward text. + */ + private String generateRewards(Challenge challenge, boolean isRepeating) + { + if (isRepeating) + { + return this.generateRepeatReward(challenge); + } + else + { + return this.generateReward(challenge); + } + } + + + /** + * This method creates repeat reward lore text. + * @param challenge Challenge which reward lore must be generated. + * @return Reward text. + */ + private String generateRepeatReward(Challenge challenge) + { + final String reference = Constants.DESCRIPTIONS + "challenge.rewards."; + + String items; + + if (!challenge.getRepeatItemReward().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "item-title")); + Utils.groupEqualItems(challenge.getRepeatItemReward(), challenge.getIgnoreRewardMetaData()).stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + { + builder.append("\n"); + + if (itemStack.getAmount() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "items-value", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "item-value", + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + }); + + items = builder.toString(); + } + else + { + items = ""; + } + + String experience = challenge.getRepeatExperienceReward() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "experience", + "[number]", String.valueOf(challenge.getRepeatExperienceReward())); + + String money = !this.addon.isEconomyProvided() || challenge.getRepeatMoneyReward() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "money", + "[number]", String.valueOf(challenge.getRepeatMoneyReward())); + + String commands; + + if (!challenge.getRepeatRewardCommands().isEmpty()) + { + StringBuilder permissionBuilder = new StringBuilder(); + + if (!challenge.getRepeatRewardCommands().isEmpty()) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "commands-title")); + + challenge.getRepeatRewardCommands().forEach(command -> + { + permissionBuilder.append("\n"); + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "command", + "[command]", command)); + }); + } + + commands = permissionBuilder.toString(); + } + else + { + commands = ""; + } + + if (challenge.getRepeatRewardText().isEmpty() && + items.isEmpty() && + experience.isEmpty() && + money.isEmpty() && + commands.isEmpty()) + { + // If everything is empty, do not return anything. + return ""; + } + + String rewardText = this.user.getTranslationOrNothing( + "challenges.challenges." + challenge.getUniqueId() + ".repeat-reward-text"); + + if (rewardText.isEmpty()) + { + rewardText = Util.translateColorCodes(String.join("\n", challenge.getRepeatRewardText())); + } + + return this.user.getTranslationOrNothing(reference + "lore", + "[text]", rewardText, + "[items]", items, + "[experience]", experience, + "[money]", money, + "[commands]", commands); + } + + + /** + * This method creates reward lore text. + * @param challenge Challenge which reward lore must be generated. + * @return Reward text. + */ + private String generateReward(Challenge challenge) + { + final String reference = Constants.DESCRIPTIONS + "challenge.rewards."; + + String items; + + if (!challenge.getRewardItems().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "item-title")); + Utils.groupEqualItems(challenge.getRewardItems(), challenge.getIgnoreRewardMetaData()).stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + { + builder.append("\n"); + + if (itemStack.getAmount() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "items-value", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "item-value", + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + }); + + items = builder.toString(); + } + else + { + items = ""; + } + + String experience = challenge.getRewardExperience() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "experience", + "[number]", String.valueOf(challenge.getRewardExperience())); + + String money = !this.addon.isEconomyProvided() || challenge.getRewardMoney() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "money", + "[number]", String.valueOf(challenge.getRewardMoney())); + + String commands; + + if (!challenge.getRewardCommands().isEmpty()) + { + StringBuilder permissionBuilder = new StringBuilder(); + + if (!challenge.getRewardCommands().isEmpty()) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "commands-title")); + + challenge.getRewardCommands().forEach(command -> + { + permissionBuilder.append("\n"); + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "command", + "[command]", command)); + }); + } + + commands = permissionBuilder.toString(); + } + else + { + commands = ""; + } + + if (challenge.getRewardText().isEmpty() && + items.isEmpty() && + experience.isEmpty() && + money.isEmpty() && + commands.isEmpty()) + { + // If everything is empty, do not return anything. + return ""; + } + + String rewardText = this.user.getTranslationOrNothing( + "challenges.challenges." + challenge.getUniqueId() + ".reward-text"); + + if (rewardText.isEmpty()) + { + rewardText = Util.translateColorCodes(String.join("\n", challenge.getRewardText())); + } + + return this.user.getTranslationOrNothing(reference + "lore", + "[text]", rewardText, + "[items]", items, + "[experience]", experience, + "[money]", money, + "[commands]", commands); + } + + + /** + * This method generates level description string. + * @param level Level which string must be generated. + * @return List with generated description. + */ + protected List generateLevelDescription(ChallengeLevel level) + { + final String reference = Constants.DESCRIPTIONS + "level."; + + // Non-memory optimal code used for easier debugging and nicer code layout for my eye :) + // Get status in single string + String status = ""; + // Get requirements in single string + String waiver = this.manager.isLastLevel(level, this.world) ? "" : + this.user.getTranslationOrNothing(reference + "waiver", + "[number]", String.valueOf(level.getWaiverAmount())); + // Get rewards in single string + String rewards = this.generateReward(level); + + String returnString = this.user.getTranslation(reference + "lore", + "[text]", Util.translateColorCodes(level.getUnlockMessage()), + "[waiver]", waiver, + "[rewards]", rewards, + "[status]", status); + + // Remove empty lines and returns as a list. + + return Arrays.stream(returnString.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). + split("\n")). + collect(Collectors.toList()); + } + + + /** + * This method generates level description string. + * @param levelStatus Level which string must be generated. + * @param user User who calls generation. + * @return List with generated description. + */ + protected List generateLevelDescription(LevelStatus levelStatus, User user) + { + ChallengeLevel level = levelStatus.getLevel(); + + final String reference = Constants.DESCRIPTIONS + "level."; + + // Non-memory optimal code used for easier debugging and nicer code layout for my eye :) + // Get status in single string + String status = this.generateLevelStatus(levelStatus); + // Get requirements in single string + String waiver = this.manager.isLastLevel(level, this.world) || + !levelStatus.isUnlocked() || + levelStatus.isComplete() ? + "" : this.user.getTranslationOrNothing(reference + "waiver", + "[number]", String.valueOf(level.getWaiverAmount())); + // Get rewards in single string + String rewards = !levelStatus.isUnlocked() ? "" : this.generateReward(level); + + String description = this.user.getTranslationOrNothing( + "challenges.levels." + level.getUniqueId() + ".description"); + + if (description.isEmpty()) + { + description = Util.translateColorCodes(String.join("\n", level.getUnlockMessage())); + } + + String returnString = this.user.getTranslation(reference + "lore", + "[text]", description, + "[waiver]", waiver, + "[rewards]", rewards, + "[status]", status); + + // Remove empty lines and returns as a list. + + return Arrays.stream(returnString.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). + split("\n")). + collect(Collectors.toList()); + } + + + /** + * This method generates level status description. + * @param levelStatus Level status which description must be generated. + * @return Level status text. + */ + private String generateLevelStatus(LevelStatus levelStatus) + { + final String reference = Constants.DESCRIPTIONS + "level.status."; + + if (!levelStatus.isUnlocked()) + { + return this.user.getTranslationOrNothing(reference + "locked") + "\n" + + this.user.getTranslationOrNothing(reference + "missing-challenges", + "[number]", String.valueOf(levelStatus.getNumberOfChallengesStillToDo())); + } + else if (levelStatus.isComplete()) + { + return this.user.getTranslationOrNothing(reference + "completed"); + } + else + { + ChallengeLevel level = levelStatus.getLevel(); + // Check if unlock message should appear. + int doneChallenges = (int) level.getChallenges().stream(). + filter(challenge -> this.addon.getChallengesManager().isChallengeComplete(user.getUniqueId(), world, challenge)). + count(); + + return this.user.getTranslation(reference + "completed-challenges-of", + "[number]", String.valueOf(doneChallenges), + "[max]", String.valueOf(level.getChallenges().size())); + } + } + + + /** + * This method creates reward lore text. + * @param level ChallengeLevel which reward lore must be generated. + * @return Reward text. + */ + private String generateReward(ChallengeLevel level) + { + final String reference = Constants.DESCRIPTIONS + "level.rewards."; + + String items; + + if (!level.getRewardItems().isEmpty()) + { + StringBuilder builder = new StringBuilder(); + builder.append(this.user.getTranslationOrNothing(reference + "item-title")); + Utils.groupEqualItems(level.getRewardItems(), level.getIgnoreRewardMetaData()).stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + { + builder.append("\n"); + + if (itemStack.getAmount() > 1) + { + builder.append(this.user.getTranslationOrNothing(reference + "items-value", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + else + { + builder.append(this.user.getTranslationOrNothing(reference + "item-value", + "[item]", Utils.prettifyObject(itemStack, this.user))); + } + }); + + items = builder.toString(); + } + else + { + items = ""; + } + + String experience = level.getRewardExperience() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "experience", + "[number]", String.valueOf(level.getRewardExperience())); + + String money = !this.addon.isEconomyProvided() || level.getRewardMoney() <= 0 ? "" : + this.user.getTranslationOrNothing(reference + "money", + "[number]", String.valueOf(level.getRewardMoney())); + + String commands; + + if (!level.getRewardCommands().isEmpty()) + { + StringBuilder permissionBuilder = new StringBuilder(); + + if (!level.getRewardCommands().isEmpty()) + { + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "commands-title")); + + level.getRewardCommands().forEach(command -> + { + permissionBuilder.append("\n"); + permissionBuilder.append(this.user.getTranslationOrNothing(reference + "command", + "[command]", command)); + }); + } + + commands = permissionBuilder.toString(); + } + else + { + commands = ""; + } + + if (level.getRewardText().isEmpty() && + items.isEmpty() && + experience.isEmpty() && + money.isEmpty() && + commands.isEmpty()) + { + // If everything is empty, do not return anything. + return ""; + } + + String rewardText = this.user.getTranslationOrNothing( + "challenges.levels." + level.getUniqueId() + ".reward-text"); + + if (rewardText.isEmpty()) + { + rewardText = Util.translateColorCodes(String.join("\n", level.getRewardText())); + } + + return this.user.getTranslationOrNothing(reference + "lore", + "[text]", rewardText, + "[items]", items, + "[experience]", experience, + "[money]", money, + "[commands]", commands); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * This variable stores parent gui. + */ + @Nullable + protected final CommonPanel parentPanel; + + /** + * Variable stores Challenges addon. + */ + protected final ChallengesAddon addon; + + /** + * Variable stores Challenges addon manager. + */ + protected final ChallengesManager manager; + + /** + * Variable stores world in which panel is referred to. + */ + protected final World world; + + /** + * Variable stores user who created this panel. + */ + protected final User user; + + /** + * Variable stores top label of command from which panel was called. + */ + protected final String topLabel; + + /** + * Variable stores permission prefix of command from which panel was called. + */ + protected final String permissionPrefix; + + /** + * This object holds PanelItem that allows to return to previous panel. + */ + protected PanelItem returnButton; +} diff --git a/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java b/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java new file mode 100644 index 0000000..d343906 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java @@ -0,0 +1,564 @@ +// +// Created by BONNe +// Copyright - 2020 +// + + +package world.bentobox.challenges.panel; + + +import org.apache.commons.lang.ArrayUtils; +import org.bukkit.ChatColor; +import org.bukkit.conversations.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +public class ConversationUtils +{ + // --------------------------------------------------------------------- + // Section: Conversation API implementation + // --------------------------------------------------------------------- + + + /** + * This method will close opened gui and writes question in chat. After players answers on question in chat, message + * will trigger consumer and gui will reopen. Success and fail messages can be implemented like that, as user's chat + * options are disabled while it is in conversation. + * + * @param consumer Consumer that accepts player output text. + * @param question Message that will be displayed in chat when player triggers conversion. + * @param successMessage Message that will be displayed on successful operation. + * @param user User who is targeted with current confirmation. + */ + public static void createConfirmation(Consumer consumer, + User user, + @NotNull String question, + @Nullable String successMessage) + { + ValidatingPrompt confirmationPrompt = new ValidatingPrompt() + { + /** + * Is input valid boolean. + * + * @param context the context + * @param input the input + * @return the boolean + */ + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) + { + // Get valid strings from translations + String validEntry = user.getTranslation(Constants.CONVERSATIONS + "confirm-string") + + "," + user.getTranslation(Constants.CONVERSATIONS + "deny-string") + + "," + user.getTranslation(Constants.CONVERSATIONS + "exit-string") + + "," + user.getTranslation(Constants.CONVERSATIONS + "cancel-string"); + + // Split and check if they exist in valid entries. + String[] accepted = validEntry.toLowerCase().replaceAll("\\s", "").split(","); + return ArrayUtils.contains(accepted, input.toLowerCase()); + } + + + /** + * Accept validated input prompt. + * + * @param context the context + * @param input the input + * @return the prompt + */ + @Override + protected Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) + { + String validEntry = user.getTranslation(Constants.CONVERSATIONS + "confirm-string").toLowerCase(); + + if (ArrayUtils.contains(validEntry.replaceAll("\\s", "").split(","), input.toLowerCase())) + { + // Add answer to consumer. + consumer.accept(true); + // Return message about success. + return ConversationUtils.endMessagePrompt(successMessage); + } + else + { + // Add answer to consumer. + consumer.accept(false); + + // Return message about failed operation. + return ConversationUtils.endMessagePrompt( + user.getTranslation(Constants.CONVERSATIONS + "cancelled")); + } + } + + + /** + * @see Prompt#getPromptText(ConversationContext) + */ + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext conversationContext) + { + // Close input GUI. + user.closeInventory(); + // There are no editable message. Just return question. + return question; + } + }; + + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(confirmationPrompt). + withLocalEcho(false). + withTimeout(90). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This method will close opened gui and writes question in chat. After players answers on question in chat, message + * will trigger consumer and gui will reopen. Be aware, consumer does not return (and validate) sanitized value, + * while sanitization is done in failure for better informing. Proper implementation would be with adding new + * consumer for failure message. + * + * @param consumer Consumer that accepts player output text. + * @param validation Function that validates if input value is acceptable. + * @param question Message that will be displayed in chat when player triggers conversion. + * @param failTranslationLocation Message that will be displayed on failed operation. + * @param user User who is targeted with current confirmation. + */ + public static void createIDStringInput(Consumer consumer, + Function validation, + User user, + @NotNull String question, + @Nullable String successMessage, + @Nullable String failTranslationLocation) + { + ValidatingPrompt validatingPrompt = new ValidatingPrompt() + { + /** + * Gets the text to display to the user when + * this prompt is first presented. + * + * @param context Context information about the + * conversation. + * @return The text to display. + */ + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + // Close input GUI. + user.closeInventory(); + + // There are no editable message. Just return question. + return question; + } + + + /** + * Override this method to check the validity of + * the player's input. + * + * @param context Context information about the + * conversation. + * @param input The player's raw console input. + * @return True or false depending on the + * validity of the input. + */ + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) + { + return validation.apply(input); + } + + + /** + * Optionally override this method to + * display an additional message if the + * user enters an invalid input. + * + * @param context Context information + * about the conversation. + * @param invalidInput The invalid input + * provided by the user. + * @return A message explaining how to + * correct the input. + */ + @Override + protected String getFailedValidationText(@NotNull ConversationContext context, + @NotNull String invalidInput) + { + return user.getTranslation(failTranslationLocation, + Constants.PARAMETER_ID, + Utils.sanitizeInput(invalidInput)); + } + + + /** + * Override this method to accept and processes + * the validated input from the user. Using the + * input, the next Prompt in the prompt graph + * should be returned. + * + * @param context Context information about the + * conversation. + * @param input The validated input text from + * the user. + * @return The next Prompt in the prompt graph. + */ + @Override + protected Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) + { + // Add answer to consumer. + consumer.accept(input); + // Send message that it is accepted. + return ConversationUtils.endMessagePrompt(successMessage); + } + }; + + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(validatingPrompt). + withLocalEcho(false). + withTimeout(90). + // On cancel conversation will be closed. + withEscapeSequence(user.getTranslation(Constants.CONVERSATIONS + "cancel-string")). + // Use null value in consumer to detect if user has abandoned conversation. + addConversationAbandonedListener(ConversationUtils.getAbandonListener(consumer, user)). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This method will close opened gui and writes inputText in chat. After players answers on inputText in chat, + * message will trigger consumer and gui will reopen. + * + * @param consumer Consumer that accepts player output text. + * @param question Message that will be displayed in chat when player triggers conversion. + */ + public static void createNumericInput(Consumer consumer, + @NotNull User user, + @NotNull String question, + Number minValue, + Number maxValue) + { + // Create NumericPromt instance that will validate and process input. + NumericPrompt numberPrompt = new NumericPrompt() + { + /** + * Override this method to perform some action + * with the user's integer response. + * + * @param context Context information about the + * conversation. + * @param input The user's response as a {@link + * Number}. + * @return The next {@link Prompt} in the prompt + * graph. + */ + @Override + protected Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull Number input) + { + // Add answer to consumer. + consumer.accept(input); + // End conversation + return Prompt.END_OF_CONVERSATION; + } + + + /** + * Override this method to do further validation on the numeric player + * input after the input has been determined to actually be a number. + * + * @param context Context information about the conversation. + * @param input The number the player provided. + * @return The validity of the player's input. + */ + @Override + protected boolean isNumberValid(@NotNull ConversationContext context, Number input) + { + return input.doubleValue() >= minValue.doubleValue() && + input.doubleValue() <= maxValue.doubleValue(); + } + + + /** + * Optionally override this method to display an additional message if the + * user enters an invalid number. + * + * @param context Context information about the conversation. + * @param invalidInput The invalid input provided by the user. + * @return A message explaining how to correct the input. + */ + @Override + protected String getInputNotNumericText(@NotNull ConversationContext context, @NotNull String invalidInput) + { + return user.getTranslation(Constants.CONVERSATIONS + "numeric-only", Constants.PARAMETER_VALUE, invalidInput); + } + + + /** + * Optionally override this method to display an additional message if the + * user enters an invalid numeric input. + * + * @param context Context information about the conversation. + * @param invalidInput The invalid input provided by the user. + * @return A message explaining how to correct the input. + */ + @Override + protected String getFailedValidationText(@NotNull ConversationContext context, Number invalidInput) + { + return user.getTranslation(Constants.CONVERSATIONS + "not-valid-value", + Constants.PARAMETER_VALUE, invalidInput.toString(), + Constants.PARAMETER_MIN, Double.toString(minValue.doubleValue()), + Constants.PARAMETER_MAX, Double.toString(maxValue.doubleValue())); + } + + + /** + * @see Prompt#getPromptText(ConversationContext) + */ + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext conversationContext) + { + // Close input GUI. + user.closeInventory(); + // There are no editable message. Just return question. + return question; + } + }; + + // Init conversation api. + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(numberPrompt). + withLocalEcho(false). + withTimeout(90). + withEscapeSequence(user.getTranslation(Constants.CONVERSATIONS + "cancel-string")). + // Use null value in consumer to detect if user has abandoned conversation. + addConversationAbandonedListener(ConversationUtils.getAbandonListener(consumer, user)). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This method will close opened gui and writes question in chat. After players answers on question in chat, message + * will trigger consumer and gui will reopen. Be aware, consumer does not return (and validate) sanitized value, + * while sanitization is done in failure for better informing. Proper implementation would be with adding new + * consumer for failure message. + * + * @param consumer Consumer that accepts player output text. + * @param question Message that will be displayed in chat when player triggers conversion. + * @param user User who is targeted with current confirmation. + */ + public static void createStringListInput(Consumer> consumer, + User user, + @NotNull String question, + @NotNull String successMessage) + { + final String SESSION_CONSTANT = Constants.CONVERSATIONS + user.getUniqueId(); + + // Successful message about completing. + MessagePrompt messagePrompt = new MessagePrompt() + { + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + if (context.getSessionData(SESSION_CONSTANT) instanceof List description) + { + consumer.accept((List) description); + return successMessage; + } + else + { + return user.getTranslation(Constants.CONVERSATIONS + "cancelled"); + } + } + + + @Override + protected @Nullable Prompt getNextPrompt(@NotNull ConversationContext context) + { + return Prompt.END_OF_CONVERSATION; + } + }; + + // Text input message. + StringPrompt stringPrompt = new StringPrompt() + { + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + user.closeInventory(); + + if (context.getSessionData(SESSION_CONSTANT) != null) + { + StringBuilder sb = new StringBuilder(); + sb.append(user.getTranslation(Constants.CONVERSATIONS + "written-text")); + sb.append(System.getProperty("line.separator")); + + for (String line : ((List) context.getSessionData(SESSION_CONSTANT))) + { + sb.append(line); + sb.append(System.getProperty("line.separator")); + } + + return sb.toString(); + } + + return question; + } + + + @Override + public Prompt acceptInput(@NotNull ConversationContext context, @Nullable String input) + { + String[] exit = user.getTranslation(Constants.CONVERSATIONS + "exit-string"). + toLowerCase().replaceAll("\\s", ""). + split(","); + + if (input != null && ArrayUtils.contains(exit, input.toLowerCase())) + { + return messagePrompt; + } + + List desc = new ArrayList<>(); + + if (context.getSessionData(SESSION_CONSTANT) instanceof List list) + { + desc = (List) list; + } + if (input != null) { + desc.add(ChatColor.translateAlternateColorCodes('&', input)); + } + context.setSessionData(SESSION_CONSTANT, desc); + return this; + } + }; + + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(stringPrompt). + withModality(true). + withLocalEcho(false). + withTimeout(90). + withEscapeSequence(user.getTranslation(Constants.CONVERSATIONS + "cancel-string")). + addConversationAbandonedListener(ConversationUtils.getAbandonListener(consumer, user)). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This method will close opened gui and writes question in chat. After players answers on question in chat, message + * will trigger consumer and gui will reopen. + * + * @param consumer Consumer that accepts player output text. + * @param question Message that will be displayed in chat when player triggers conversion. + * @param user User who is targeted with current confirmation. + */ + public static void createStringInput(Consumer consumer, + User user, + @NotNull String question, + @Nullable String successMessage) + { + // Text input message. + StringPrompt stringPrompt = new StringPrompt() + { + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + user.closeInventory(); + return question; + } + + + @Override + @NotNull + public Prompt acceptInput(@NotNull ConversationContext context, @Nullable String input) + { + consumer.accept(input); + return ConversationUtils.endMessagePrompt(successMessage); + } + }; + + new ConversationFactory(BentoBox.getInstance()). + withPrefix(context -> user.getTranslation(Constants.CONVERSATIONS + "prefix")). + withFirstPrompt(stringPrompt). + // On cancel conversation will be closed. + withLocalEcho(false). + withTimeout(90). + withEscapeSequence(user.getTranslation(Constants.CONVERSATIONS + "cancel-string")). + // Use null value in consumer to detect if user has abandoned conversation. + addConversationAbandonedListener(ConversationUtils.getAbandonListener(consumer, user)). + buildConversation(user.getPlayer()). + begin(); + } + + + /** + * This is just a simple end message prompt that displays requested message. + * + * @param message Message that will be displayed. + * @return MessagePrompt that displays given message and exists from conversation. + */ + private static MessagePrompt endMessagePrompt(@Nullable String message) + { + return new MessagePrompt() + { + @Override + @NotNull + public String getPromptText(@NotNull ConversationContext context) + { + return message == null ? "" : message; + } + + + @Override + @Nullable + protected Prompt getNextPrompt(@NotNull ConversationContext context) + { + return Prompt.END_OF_CONVERSATION; + } + }; + } + + + /** + * This method creates and returns abandon listener for every conversation. + * + * @param consumer Consumer which must return null value. + * @param user User who was using conversation. + * @return ConversationAbandonedListener instance. + */ + private static ConversationAbandonedListener getAbandonListener(Consumer consumer, User user) + { + return abandonedEvent -> + { + if (!abandonedEvent.gracefulExit()) + { + consumer.accept(null); + // send cancell message + abandonedEvent.getContext().getForWhom().sendRawMessage( + user.getTranslation(Constants.CONVERSATIONS + "prefix") + + user.getTranslation(Constants.CONVERSATIONS + "cancelled")); + } + }; + } +} diff --git a/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java b/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java deleted file mode 100644 index d60e7a4..0000000 --- a/src/main/java/world/bentobox/challenges/panel/GameModesGUI.java +++ /dev/null @@ -1,143 +0,0 @@ -package world.bentobox.challenges.panel; - - -import java.util.List; -import java.util.Optional; - -import org.bukkit.Material; -import org.bukkit.World; - -import world.bentobox.bentobox.api.addons.GameModeAddon; -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class process GameModeGui opening. - */ -public class GameModesGUI extends CommonGUI -{ - /** - * @param adminMode - boolean that indicate if Gui is in admin mode. - * @param gameModeAddons - List with GameModes where Challenges addon is integrated. - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - - */ - public GameModesGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix, - boolean adminMode, - List gameModeAddons) - { - super(addon, world, user, topLabel, permissionPrefix); - this.adminMode = adminMode; - this.gameModeAddons = gameModeAddons; - } - - - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user). - name("challenges.gui.title.game-modes"); - - GuiUtils.fillBorder(panelBuilder, this.adminMode ? - Material.BLACK_STAINED_GLASS_PANE : - Material.BLUE_STAINED_GLASS_PANE); - - int elementIndex; - - if (this.gameModeAddons.size() < 8) - { - if (this.gameModeAddons.size() == 7) - { - elementIndex = 19; - } - else - { - elementIndex = 22 - this.gameModeAddons.size() / 2; - } - } - else - { - elementIndex = 10; - } - - for (GameModeAddon gameModeAddon : this.gameModeAddons) - { - if (!panelBuilder.slotOccupied(elementIndex)) - { - panelBuilder.item(elementIndex++, this.createGameModeIcon(gameModeAddon)); - } - else - { - // Find first open slot - while (panelBuilder.slotOccupied(elementIndex)) - { - elementIndex++; - } - } - } - - panelBuilder.build(); - } - - - /** - * This method creates icon that will display given GameMode addon. - * @param gameModeAddon GameMode addon. - * @return PanelItem that acts as icon for given GameMode. - */ - private PanelItem createGameModeIcon(GameModeAddon gameModeAddon) - { - return new PanelItemBuilder(). - name(gameModeAddon.getDescription().getName()). - description(gameModeAddon.getDescription().getDescription()). - icon(Material.PAPER). - clickHandler((panel, user, clickType, slot) -> { - Optional command; - - if (this.adminMode) - { - command = gameModeAddon.getAdminCommand(); - } - else - { - command = gameModeAddon.getPlayerCommand(); - } - - command.ifPresent(compositeCommand -> - user.performCommand(compositeCommand.getTopLabel() + " challenges")); - - return true; - }). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * List with game mode addons which must be showed in current GUI. - */ - private List gameModeAddons; - - /** - * Stores if current GUI is in Admin Mode or not. - */ - private boolean adminMode; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java deleted file mode 100644 index 216d6f1..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java +++ /dev/null @@ -1,736 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.Locale; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.conversations.*; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; - -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.ChallengeTypeGUI; -import world.bentobox.challenges.panel.util.ConfirmationGUI; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.utils.Utils; -import world.bentobox.challenges.web.WebManager; - - -/** - * This class contains Main - */ -public class AdminGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * This boolean holds if import should overwrite existing challenges. - */ - private boolean overwriteMode; - - /** - * This indicate if Reset Challenges must work as reset all. - */ - private boolean resetAllMode; - - /** - * This indicate if wipe button should clear all data, or only challenges. - */ - private boolean wipeAll; - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * This enum contains all button variations. Just for cleaner code. - */ - private enum Button - { - COMPLETE_USER_CHALLENGES, - RESET_USER_CHALLENGES, - ADD_CHALLENGE, - ADD_LEVEL, - EDIT_CHALLENGE, - EDIT_LEVEL, - DELETE_CHALLENGE, - DELETE_LEVEL, - EDIT_SETTINGS, - DEFAULT_IMPORT_CHALLENGES, - DEFAULT_EXPORT_CHALLENGES, - /** - * Allows to remove whole database - */ - COMPLETE_WIPE, - /** - * Allows to remove only challenges and levels - */ - CHALLENGE_WIPE, - /** - * Allows to remove only players data - */ - USER_WIPE, - /** - * Allows to access Web Library - */ - LIBRARY - } - - - // --------------------------------------------------------------------- - // Section: Constructor - // --------------------------------------------------------------------- - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - */ - public AdminGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix) - { - super(addon, world, user, topLabel, permissionPrefix); - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * {@inheritDoc} - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.gui-title")); - - GuiUtils.fillBorder(panelBuilder); - - panelBuilder.item(10, this.createButton(Button.COMPLETE_USER_CHALLENGES)); - panelBuilder.item(19, this.createButton(Button.RESET_USER_CHALLENGES)); - - // Add All Player Data removal. - panelBuilder.item(28, this.createButton(Button.USER_WIPE)); - - // Add Challenges - panelBuilder.item(12, this.createButton(Button.ADD_CHALLENGE)); - panelBuilder.item(13, this.createButton(Button.ADD_LEVEL)); - - // Edit Challenges - panelBuilder.item(21, this.createButton(Button.EDIT_CHALLENGE)); - panelBuilder.item(22, this.createButton(Button.EDIT_LEVEL)); - - // Remove Challenges - panelBuilder.item(30, this.createButton(Button.DELETE_CHALLENGE)); - panelBuilder.item(31, this.createButton(Button.DELETE_LEVEL)); - - - // Import Challenges - panelBuilder.item(15, this.createButton(Button.DEFAULT_IMPORT_CHALLENGES)); - panelBuilder.item(24, this.createButton(Button.LIBRARY)); - - // Not added as I do not think admins should use it. It still will be able via command. - // panelBuilder.item(33, this.createButton(Button.DEFAULT_EXPORT_CHALLENGES)); - - // Edit Addon Settings - panelBuilder.item(16, this.createButton(Button.EDIT_SETTINGS)); - - // Button that deletes everything from challenges addon - - if (this.wipeAll) - { - panelBuilder.item(34, this.createButton(Button.COMPLETE_WIPE)); - } - else - { - panelBuilder.item(34, this.createButton(Button.CHALLENGE_WIPE)); - } - - panelBuilder.item(44, this.returnButton); - - panelBuilder.build(); - } - - - /** - * This method is used to create PanelItem for each button type. - * @param button Button which must be created. - * @return PanelItem with necessary functionality. - */ - private PanelItem createButton(Button button) - { - ItemStack icon; - String name; - String description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - String permissionSuffix; - - switch (button) - { - case COMPLETE_USER_CHALLENGES: - permissionSuffix = COMPLETE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.complete"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.complete"); - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new ListUsersGUI(this.addon, - this.world, - this.user, - ListUsersGUI.Mode.COMPLETE, - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - - break; - case RESET_USER_CHALLENGES: - permissionSuffix = RESET; - - name = this.user.getTranslation("challenges.gui.buttons.admin.reset"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.reset"); - icon = new ItemStack(Material.WRITABLE_BOOK); - - glow = this.resetAllMode; - - clickHandler = (panel, user, clickType, slot) -> { - if (clickType.isRightClick()) - { - this.resetAllMode = !this.resetAllMode; - this.build(); - } - else - { - new ListUsersGUI(this.addon, - this.world, - this.user, - this.resetAllMode ? ListUsersGUI.Mode.RESET_ALL : ListUsersGUI.Mode.RESET, - this.topLabel, - this.permissionPrefix, - this).build(); - } - - return true; - }; - - break; - case ADD_CHALLENGE: - permissionSuffix = ADD; - - name = this.user.getTranslation("challenges.gui.buttons.admin.create-challenge"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.create-challenge"); - icon = new ItemStack(Material.BOOK); - clickHandler = (panel, user, clickType, slot) -> { - - this.getNewUniqueID(challenge -> - { - if (challenge == null) - { - // Build Admin Gui if input is null. - this.build(); - } - else - { - String uniqueId = Utils.getGameMode(this.world) + "_" + challenge; - - ChallengeTypeGUI.open(user, - this.addon.getChallengesSettings().getLoreLineLength(), - (type, requirements) -> new EditChallengeGUI(this.addon, - this.world, - this.user, - this.addon.getChallengesManager().createChallenge(uniqueId, type, requirements), - this.topLabel, - this.permissionPrefix, - this).build()); - } - }, - input -> { - String uniqueId = Utils.getGameMode(this.world) + "_" + input; - return !this.addon.getChallengesManager().containsChallenge(uniqueId); - }, - this.user.getTranslation("challenges.gui.questions.admin.unique-id") - ); - - return true; - }; - glow = false; - - break; - case ADD_LEVEL: - permissionSuffix = ADD; - - name = this.user.getTranslation("challenges.gui.buttons.admin.create-level"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.create-level"); - icon = new ItemStack(Material.BOOK); - clickHandler = (panel, user, clickType, slot) -> { - - this.getNewUniqueID(level -> - { - if (level == null) - { - // Build Admin Gui if input is null. - this.build(); - } - else - { - String newName = Utils.getGameMode(this.world) + "_" + level; - - new EditLevelGUI(this.addon, - this.world, - this.user, - this.addon.getChallengesManager().createLevel(newName, this.world), - this.topLabel, - this.permissionPrefix, - this).build(); - } - }, - input -> { - String newName = Utils.getGameMode(this.world) + "_" + input; - return !this.addon.getChallengesManager().containsLevel(newName); - }, - this.user.getTranslation("challenges.gui.questions.admin.unique-id") - ); - - return true; - }; - glow = false; - - break; - case EDIT_CHALLENGE: - permissionSuffix = EDIT; - - name = this.user.getTranslation("challenges.gui.buttons.admin.edit-challenge"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.edit-challenge"); - icon = new ItemStack(Material.ANVIL); - clickHandler = (panel, user, clickType, slot) -> { - new ListChallengesGUI(this.addon, - this.world, - this.user, - ListChallengesGUI.Mode.EDIT, - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - - break; - case EDIT_LEVEL: - { - permissionSuffix = EDIT; - - name = this.user.getTranslation("challenges.gui.buttons.admin.edit-level"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.edit-level"); - icon = new ItemStack(Material.ANVIL); - clickHandler = (panel, user, clickType, slot) -> { - new ListLevelsGUI(this.addon, - this.world, - this.user, - ListLevelsGUI.Mode.EDIT, - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - - break; - } - case DELETE_CHALLENGE: - { - permissionSuffix = DELETE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.delete-challenge"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.delete-challenge"); - icon = new ItemStack(Material.LAVA_BUCKET); - clickHandler = (panel, user, clickType, slot) -> { - new ListChallengesGUI(this.addon, - this.world, - this.user, - ListChallengesGUI.Mode.DELETE, - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - - break; - } - case DELETE_LEVEL: - { - permissionSuffix = DELETE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.delete-level"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.delete-level"); - icon = new ItemStack(Material.LAVA_BUCKET); - clickHandler = (panel, user, clickType, slot) -> { - new ListLevelsGUI(this.addon, - this.world, - this.user, - ListLevelsGUI.Mode.DELETE, - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - - break; - } - case DEFAULT_IMPORT_CHALLENGES: - { - permissionSuffix = DEFAULT; - - name = this.user.getTranslation("challenges.gui.buttons.admin.default-import"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.default-import"); - icon = new ItemStack(Material.HOPPER); - clickHandler = (panel, user, clickType, slot) -> { - // Run import command. - this.user.performCommand(this.topLabel + " " + CHALLENGES + " " + DEFAULT + " " + IMPORT); - - return true; - }; - glow = false; - - break; - } - case DEFAULT_EXPORT_CHALLENGES: - { - permissionSuffix = DEFAULT; - - name = this.user.getTranslation("challenges.gui.buttons.admin.default-export"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.default-export"); - icon = new ItemStack(Material.HOPPER); - clickHandler = (panel, user, clickType, slot) -> { - if (clickType.isRightClick()) - { - this.overwriteMode = !this.overwriteMode; - this.build(); - } - else - { - // Run import command. - this.user.performCommand(this.topLabel + " " + CHALLENGES + " " + DEFAULT + " " + GENERATE + - (this.overwriteMode ? " overwrite" : "")); - } - return true; - }; - glow = this.overwriteMode; - - break; - } - case EDIT_SETTINGS: - { - permissionSuffix = SETTINGS; - - name = this.user.getTranslation("challenges.gui.buttons.admin.settings"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.settings"); - icon = new ItemStack(Material.CRAFTING_TABLE); - clickHandler = (panel, user, clickType, slot) -> { - new EditSettingsGUI(this.addon, - this.world, - this.user, - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - - break; - } - case COMPLETE_WIPE: - { - permissionSuffix = WIPE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.complete-wipe"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.complete-wipe"); - icon = new ItemStack(Material.TNT); - clickHandler = (panel, user, clickType, slot) -> { - - if (clickType.isRightClick()) - { - this.wipeAll = false; - this.build(); - } - else - { - new ConfirmationGUI(this.user, value -> { - if (value) - { - this.addon.getChallengesManager().wipeDatabase(false); - this.user.sendMessage("challenges.messages.admin.complete-wipe"); - } - - this.build(); - }); - } - - return true; - }; - glow = true; - - break; - } - case CHALLENGE_WIPE: - { - permissionSuffix = WIPE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.challenge-wipe"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.challenge-wipe"); - icon = new ItemStack(Material.TNT); - clickHandler = (panel, user, clickType, slot) -> { - - if (clickType.isRightClick()) - { - this.wipeAll = true; - this.build(); - } - else - { - new ConfirmationGUI(this.user, value -> { - if (value) - { - this.addon.getChallengesManager().wipeDatabase(false); - this.user.sendMessage("challenges.messages.admin.challenge-wipe"); - } - - this.build(); - }); - } - - return true; - }; - glow = false; - - break; - } - case USER_WIPE: - { - permissionSuffix = WIPE; - - name = this.user.getTranslation("challenges.gui.buttons.admin.players-wipe"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.players-wipe"); - icon = new ItemStack(Material.TNT); - clickHandler = (panel, user, clickType, slot) -> { - - new ConfirmationGUI(this.user, value -> { - if (value) - { - this.addon.getChallengesManager().wipePlayers(); - this.user.sendMessage("challenges.messages.admin.players-wipe"); - } - - this.build(); - }); - - return true; - }; - glow = false; - - break; - } - case LIBRARY: - { - permissionSuffix = DOWNLOAD; - - name = this.user.getTranslation("challenges.gui.buttons.admin.library"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.library"); - - if (WebManager.isEnabled()) - { - icon = new ItemStack(Material.COBWEB); - } - else - { - description += "|" + this.user.getTranslation("challenges.gui.descriptions.admin.download-disabled"); - icon = new ItemStack(Material.STRUCTURE_VOID); - } - - clickHandler = (panel, user, clickType, slot) -> { - if (WebManager.isEnabled()) - { - ListLibraryGUI.open(this); - } - - return true; - }; - glow = false; - - break; - } - default: - // This should never happen. - return null; - } - - // If user does not have permission to run command, then change icon and clickHandler. - final String actionPermission = this.permissionPrefix + ADMIN + "." + CHALLENGES + "." + permissionSuffix; - - if (!this.user.hasPermission(actionPermission)) - { - icon = new ItemStack(Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - this.user.sendMessage("general.errors.no-permission", "[permission]", actionPermission); - return true; - }; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - glow(glow). - clickHandler(clickHandler). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Conversation -// --------------------------------------------------------------------- - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on - * inputText in chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - */ - private void getNewUniqueID(Consumer consumer, - Function stringValidation, - @NonNull String question) - { - final User user = this.user; - - Conversation conversation = - new ConversationFactory(BentoBox.getInstance()).withFirstPrompt( - new ValidatingPrompt() - { - - /** - * Gets the text to display to the user when - * this prompt is first presented. - * - * @param context Context information about the - * conversation. - * @return The text to display. - */ - @Override - public String getPromptText(ConversationContext context) - { - // Close input GUI. - user.closeInventory(); - - // There are no editable message. Just return question. - return question; - } - - - /** - * Override this method to check the validity of - * the player's input. - * - * @param context Context information about the - * conversation. - * @param input The player's raw console input. - * @return True or false depending on the - * validity of the input. - */ - @Override - protected boolean isInputValid(ConversationContext context, String input) - { - return stringValidation.apply(GuiUtils.sanitizeInput(input)); - } - - - /** - * Optionally override this method to - * display an additional message if the - * user enters an invalid input. - * - * @param context Context information - * about the conversation. - * @param invalidInput The invalid input - * provided by the user. - * @return A message explaining how to - * correct the input. - */ - @Override - protected String getFailedValidationText(ConversationContext context, - String invalidInput) - { - return user.getTranslation("challenges.errors.unique-id", "[id]", GuiUtils.sanitizeInput(invalidInput)); - } - - - /** - * Override this method to accept and processes - * the validated input from the user. Using the - * input, the next Prompt in the prompt graph - * should be returned. - * - * @param context Context information about the - * conversation. - * @param input The validated input text from - * the user. - * @return The next Prompt in the prompt graph. - */ - @Override - protected Prompt acceptValidatedInput(ConversationContext context, String input) - { - // Add answer to consumer. - consumer.accept(GuiUtils.sanitizeInput(input)); - // End conversation - return Prompt.END_OF_CONVERSATION; - } - }). - // On cancel conversation will be closed. - withEscapeSequence("cancel"). - // Use null value in consumer to detect if user has abandoned conversation. - addConversationAbandonedListener(abandonedEvent -> - { - if (!abandonedEvent.gracefulExit()) - { - consumer.accept(null); - } - }). - withLocalEcho(false). - withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). - buildConversation(user.getPlayer()); - - conversation.begin(); - } -} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/AdminPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/AdminPanel.java new file mode 100644 index 0000000..5a1dd21 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/AdminPanel.java @@ -0,0 +1,593 @@ +package world.bentobox.challenges.panel.admin; + + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.ChallengeTypeSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; +import world.bentobox.challenges.web.WebManager; + + +/** + * This class contains Main + */ +public class AdminPanel extends CommonPanel +{ + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- + + /** + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + */ + private AdminPanel(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + new AdminPanel(addon, world, user, topLabel, permissionPrefix).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "admin-gui")); + + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(10, this.createButton(Button.COMPLETE_USER_CHALLENGES)); + panelBuilder.item(19, this.createButton(Button.RESET_USER_CHALLENGES)); + + // Add All Player Data removal. + panelBuilder.item(28, this.createButton(Button.USER_WIPE)); + + // Add Challenges + panelBuilder.item(12, this.createButton(Button.ADD_CHALLENGE)); + panelBuilder.item(13, this.createButton(Button.ADD_LEVEL)); + + // Edit Challenges + panelBuilder.item(21, this.createButton(Button.EDIT_CHALLENGE)); + panelBuilder.item(22, this.createButton(Button.EDIT_LEVEL)); + + // Remove Challenges + panelBuilder.item(30, this.createButton(Button.DELETE_CHALLENGE)); + panelBuilder.item(31, this.createButton(Button.DELETE_LEVEL)); + + // Import Challenges + panelBuilder.item(14, this.createButton(Button.IMPORT_TEMPLATE)); + panelBuilder.item(15, this.createButton(Button.IMPORT_DATABASE)); + panelBuilder.item(33, this.createButton(Button.LIBRARY)); + // Export Challenges + panelBuilder.item(24, this.createButton(Button.EXPORT_CHALLENGES)); + + // Edit Addon Settings + panelBuilder.item(16, this.createButton(Button.EDIT_SETTINGS)); + + // Button that deletes everything from challenges addon + + if (this.wipeAll) + { + panelBuilder.item(34, this.createButton(Button.COMPLETE_WIPE)); + } + else + { + panelBuilder.item(34, this.createButton(Button.CHALLENGE_WIPE)); + } + + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method is used to create PanelItem for each button type. + * @param button Button which must be created. + * @return PanelItem with necessary functionality. + */ + private PanelItem createButton(Button button) + { + final String name = this.user.getTranslation(Constants.BUTTON + button.name().toLowerCase() + ".name"); + List description = new ArrayList<>(3); + description.add(this.user.getTranslation(Constants.BUTTON + button.name().toLowerCase() + ".description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + + switch (button) + { + case COMPLETE_USER_CHALLENGES -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + ListUsersPanel.open(this, ListUsersPanel.Mode.COMPLETE); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case RESET_USER_CHALLENGES -> { + icon = new ItemStack(Material.WRITABLE_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.resetAllMode = !this.resetAllMode; + this.build(); + } + else + { + ListUsersPanel.open(this, + this.resetAllMode ? ListUsersPanel.Mode.RESET_ALL : ListUsersPanel.Mode.RESET); + } + + return true; + }; + glow = this.resetAllMode; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "left-click-to-open")); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "right-click-to-reset-all")); + } + case ADD_CHALLENGE -> { + icon = new ItemStack(Material.BOOK); + clickHandler = (panel, user, clickType, slot) -> { + + String gameModePrefix = Utils.getGameMode(this.world).toLowerCase() + "_"; + + // This consumer process new bundle creating with a name and id from given + // consumer value.. + Consumer challengeIdConsumer = value -> { + if (value != null) + { + ChallengeTypeSelector.open(this.user, + (type, requirements) -> EditChallengePanel.open(this, + this.addon.getChallengesManager().createChallenge( + gameModePrefix + Utils.sanitizeInput(value), + value, + type, + requirements))); + } + else + { + // Operation is canceled. Open this panel again. + this.build(); + } + }; + + // This function checks if generator with a given ID already exist. + Function validationFunction = uniqueId -> + !this.addon.getChallengesManager().containsChallenge(gameModePrefix + Utils.sanitizeInput(uniqueId)); + + // Call a conversation API to get input string. + ConversationUtils.createIDStringInput(challengeIdConsumer, + validationFunction, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "write-name"), + this.user.getTranslation(Constants.CONVERSATIONS + "new-object-created", + Constants.PARAMETER_WORLD, this.world.getName()), + Constants.CONVERSATIONS + "object-already-exists"); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-create")); + } + case ADD_LEVEL -> { + icon = new ItemStack(Material.BOOK); + clickHandler = (panel, user, clickType, slot) -> { + + String gameModePrefix = Utils.getGameMode(this.world).toLowerCase() + "_"; + + // This consumer process new bundle creating with a name and id from given + // consumer value.. + Consumer levelIdConsumer = value -> { + if (value != null) + { + EditLevelPanel.open(this, + this.addon.getChallengesManager().createLevel( + gameModePrefix + Utils.sanitizeInput(value), + value, + world)); + } + else + { + // Operation is canceled. Open this panel again. + this.build(); + } + }; + + // This function checks if generator with a given ID already exist. + Function validationFunction = uniqueId -> + !this.addon.getChallengesManager().containsLevel(gameModePrefix + Utils.sanitizeInput(uniqueId)); + + // Call a conversation API to get input string. + ConversationUtils.createIDStringInput(levelIdConsumer, + validationFunction, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "write-name"), + this.user.getTranslation(Constants.CONVERSATIONS + "new-object-created", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + Constants.CONVERSATIONS + "object-already-exists"); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-create")); + } + case EDIT_CHALLENGE -> { + icon = new ItemStack(Material.ANVIL); + clickHandler = (panel, user, clickType, slot) -> { + ListChallengesPanel.open(this, ListChallengesPanel.Mode.EDIT); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case EDIT_LEVEL -> { + icon = new ItemStack(Material.ANVIL); + clickHandler = (panel, user, clickType, slot) -> { + ListLevelsPanel.open(this, ListLevelsPanel.Mode.EDIT); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case DELETE_CHALLENGE -> { + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ListChallengesPanel.open(this, ListChallengesPanel.Mode.DELETE); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case DELETE_LEVEL -> { + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ListLevelsPanel.open(this, ListLevelsPanel.Mode.DELETE); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case EDIT_SETTINGS -> { + icon = new ItemStack(Material.CRAFTING_TABLE); + clickHandler = (panel, user, clickType, slot) -> { + EditSettingsPanel.open(this); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case IMPORT_DATABASE -> { + icon = new ItemStack(Material.BOOKSHELF); + clickHandler = (panel, user, clickType, slot) -> { + LibraryPanel.open(this, LibraryPanel.Library.DATABASE); + return true; + }; + glow = true; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case IMPORT_TEMPLATE -> { + icon = new ItemStack(Material.BOOKSHELF); + clickHandler = (panel, user, clickType, slot) -> { + LibraryPanel.open(this, LibraryPanel.Library.TEMPLATE); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case EXPORT_CHALLENGES -> { + icon = new ItemStack(Material.HOPPER); + clickHandler = (panel, user, clickType, slot) -> { + + // This consumer process file exporting after user input is returned. + Consumer fileNameConsumer = value -> { + if (value != null) + { + this.addon.getImportManager().generateDatabaseFile(this.user, + this.world, + Utils.sanitizeInput(value)); + } + + this.build(); + }; + + // This function checks if file can be created. + Function validationFunction = fileName -> + { + String sanitizedName = Utils.sanitizeInput(fileName); + return !new File(this.addon.getDataFolder(), + sanitizedName.endsWith(".json") ? sanitizedName : sanitizedName + ".json").exists(); + }; + + // Call a conversation API to get input string. + ConversationUtils.createIDStringInput(fileNameConsumer, + validationFunction, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "exported-file-name"), + this.user.getTranslation(Constants.CONVERSATIONS + "database-export-completed", + Constants.PARAMETER_WORLD, world.getName()), + Constants.CONVERSATIONS + "file-name-exist"); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-export")); + } + case LIBRARY -> { + if (WebManager.isEnabled()) + { + icon = new ItemStack(Material.COBWEB); + } + else + { + icon = new ItemStack(Material.STRUCTURE_VOID); + } + + clickHandler = (panel, user, clickType, slot) -> { + if (WebManager.isEnabled()) + { + LibraryPanel.open(this, LibraryPanel.Library.WEB); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-open")); + } + case COMPLETE_WIPE -> { + icon = new ItemStack(Material.TNT); + clickHandler = (panel, user, clickType, slot) -> { + + if (clickType.isRightClick()) + { + this.wipeAll = false; + this.build(); + } + else + { + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().wipeDatabase(this.wipeAll, + Utils.getGameMode(this.world)); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-all-data-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + this.user.getTranslation(Constants.CONVERSATIONS + "all-data-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world))); + } + + return true; + }; + glow = true; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "left-click-to-wipe")); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "right-click-to-switch")); + } + case CHALLENGE_WIPE -> { + icon = new ItemStack(Material.TNT); + clickHandler = (panel, user, clickType, slot) -> { + + if (clickType.isRightClick()) + { + this.wipeAll = true; + this.build(); + } + else + { + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().wipeDatabase(this.wipeAll, + Utils.getGameMode(this.world)); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-challenge-data-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + this.user.getTranslation(Constants.CONVERSATIONS + "challenge-data-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world))); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "left-click-to-wipe")); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "right-click-to-switch")); + } + case USER_WIPE -> { + icon = new ItemStack(Material.TNT); + clickHandler = (panel, user, clickType, slot) -> { + + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().wipePlayers(Utils.getGameMode(this.world)); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-user-data-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + this.user.getTranslation(Constants.CONVERSATIONS + "user-data-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world))); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslationOrNothing(Constants.TIPS + "click-to-wipe")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + // --------------------------------------------------------------------- + // Section: Enums + // --------------------------------------------------------------------- + + + /** + * This enum contains all button variations. Just for cleaner code. + */ + private enum Button + { + COMPLETE_USER_CHALLENGES, + RESET_USER_CHALLENGES, + ADD_CHALLENGE, + ADD_LEVEL, + EDIT_CHALLENGE, + EDIT_LEVEL, + DELETE_CHALLENGE, + DELETE_LEVEL, + EDIT_SETTINGS, + IMPORT_DATABASE, + IMPORT_TEMPLATE, + EXPORT_CHALLENGES, + /** + * Allows to remove whole database + */ + COMPLETE_WIPE, + /** + * Allows to remove only challenges and levels + */ + CHALLENGE_WIPE, + /** + * Allows to remove only players data + */ + USER_WIPE, + /** + * Allows to access Web Library + */ + LIBRARY + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * This indicates if Reset Challenges must work as reset all. + */ + private boolean resetAllMode; + + /** + * This indicates if wipe button should clear all data, or only challenges. + */ + private boolean wipeAll; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java deleted file mode 100644 index 837e7ee..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java +++ /dev/null @@ -1,1531 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.util.Util; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.requirements.InventoryRequirements; -import world.bentobox.challenges.database.object.requirements.IslandRequirements; -import world.bentobox.challenges.database.object.requirements.OtherRequirements; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.ItemSwitchGUI; -import world.bentobox.challenges.panel.util.NumberGUI; -import world.bentobox.challenges.panel.util.SelectBlocksGUI; -import world.bentobox.challenges.panel.util.SelectEnvironmentGUI; -import world.bentobox.challenges.panel.util.StringListGUI; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.utils.Utils; - - -/** - * This class contains all necessary methods that creates GUI and allow to edit challenges - * properties. - */ -public class EditChallengeGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructors - // --------------------------------------------------------------------- - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param challenge - challenge that needs editing - */ - public EditChallengeGUI(ChallengesAddon addon, - World world, - User user, - Challenge challenge, - String topLabel, - String permissionPrefix) - { - this(addon, world, user, challenge, topLabel, permissionPrefix, null); - } - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param challenge challenge that needs editing. - */ - public EditChallengeGUI(ChallengesAddon addon, - World world, - User user, - Challenge challenge, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - super(addon, world, user, topLabel, permissionPrefix, parentGUI); - this.challenge = challenge; - - // Default panel should be Properties. - this.currentMenuType = MenuType.PROPERTIES; - - // Set line length. - this.lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - } - - - // --------------------------------------------------------------------- - // Section: Panel Creation related methods - // --------------------------------------------------------------------- - - - /** - * {@inheritDoc} - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.edit-challenge-title")); - - GuiUtils.fillBorder(panelBuilder); - - panelBuilder.item(2, this.createMenuButton(MenuType.PROPERTIES)); - panelBuilder.item(4, this.createMenuButton(MenuType.REQUIREMENTS)); - panelBuilder.item(6, this.createMenuButton(MenuType.REWARDS)); - - if (this.currentMenuType.equals(MenuType.PROPERTIES)) - { - this.buildMainPropertiesPanel(panelBuilder); - } - else if (this.currentMenuType.equals(MenuType.REQUIREMENTS)) - { - switch (this.challenge.getChallengeType()) - { - case INVENTORY: - this.buildInventoryRequirementsPanel(panelBuilder); - break; - case ISLAND: - this.buildIslandRequirementsPanel(panelBuilder); - break; - case OTHER: - this.buildOtherRequirementsPanel(panelBuilder); - break; - } - } - else if (this.currentMenuType.equals(MenuType.REWARDS)) - { - this.buildRewardsPanel(panelBuilder); - } - - panelBuilder.item(44, this.returnButton); - - // Every time when this GUI is build, save challenge - // This will ensure that all main things will be always stored - this.addon.getChallengesManager().saveChallenge(this.challenge); - - panelBuilder.build(); - } - - - /** - * This class populate ChallengesEditGUI with main challenge settings. - * @param panelBuilder PanelBuilder where icons must be added. - */ - private void buildMainPropertiesPanel(PanelBuilder panelBuilder) - { - panelBuilder.item(10, this.createButton(Button.NAME)); - panelBuilder.item(16, this.createButton(Button.DEPLOYED)); - - panelBuilder.item(19, this.createButton(Button.ICON)); - panelBuilder.item(22, this.createButton(Button.DESCRIPTION)); - panelBuilder.item(25, this.createButton(Button.ORDER)); - - panelBuilder.item(28, this.createButton(Button.ENVIRONMENT)); - panelBuilder.item(31, this.createButton(Button.REMOVE_ON_COMPLETE)); - } - - - /** - * This class populates ChallengesEditGUI with island challenges requirement elements. - * @param panelBuilder PanelBuilder where icons must be added. - */ - private void buildIslandRequirementsPanel(PanelBuilder panelBuilder) - { - panelBuilder.item(19, this.createRequirementButton(RequirementButton.REQUIRED_ENTITIES)); - panelBuilder.item(28, this.createRequirementButton(RequirementButton.REMOVE_ENTITIES)); - - panelBuilder.item(21, this.createRequirementButton(RequirementButton.REQUIRED_BLOCKS)); - panelBuilder.item(30, this.createRequirementButton(RequirementButton.REMOVE_BLOCKS)); - - panelBuilder.item(23, this.createRequirementButton(RequirementButton.SEARCH_RADIUS)); - panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); - } - - - /** - * This class populates ChallengesEditGUI with inventory challenges requirement elements. - * @param panelBuilder PanelBuilder where icons must be added. - */ - private void buildInventoryRequirementsPanel(PanelBuilder panelBuilder) - { - panelBuilder.item(10, this.createRequirementButton(RequirementButton.REQUIRED_ITEMS)); - panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_ITEMS)); - - panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); - } - - - /** - * This class populates ChallengesEditGUI with other challenges requirement elements. - * @param panelBuilder PanelBuilder where icons must be added. - */ - private void buildOtherRequirementsPanel(PanelBuilder panelBuilder) - { - panelBuilder.item(10, this.createRequirementButton(RequirementButton.REQUIRED_EXPERIENCE)); - panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_EXPERIENCE)); - - panelBuilder.item(12, this.createRequirementButton(RequirementButton.REQUIRED_MONEY)); - panelBuilder.item(21, this.createRequirementButton(RequirementButton.REMOVE_MONEY)); - - panelBuilder.item(23, this.createRequirementButton(RequirementButton.REQUIRED_LEVEL)); - - panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); - } - - - /** - * This class populates ChallengesEditGUI with challenges reward elements. - * @param panelBuilder PanelBuilder where icons must be added. - */ - private void buildRewardsPanel(PanelBuilder panelBuilder) - { - panelBuilder.item(10, this.createRewardButton(RewardButton.REWARD_TEXT)); - panelBuilder.item(19, this.createRewardButton(RewardButton.REWARD_COMMANDS)); - - panelBuilder.item(11, this.createRewardButton(RewardButton.REWARD_ITEM)); - panelBuilder.item(20, this.createRewardButton(RewardButton.REWARD_EXPERIENCE)); - panelBuilder.item(29, this.createRewardButton(RewardButton.REWARD_MONEY)); - - panelBuilder.item(22, this.createRewardButton(RewardButton.REPEATABLE)); - - if (this.challenge.isRepeatable()) - { - panelBuilder.item(31, this.createRewardButton(RewardButton.REPEAT_COUNT)); - - panelBuilder.item(15, this.createRewardButton(RewardButton.REPEAT_REWARD_TEXT)); - panelBuilder.item(24, this.createRewardButton(RewardButton.REPEAT_REWARD_COMMANDS)); - - panelBuilder.item(16, this.createRewardButton(RewardButton.REPEAT_REWARD_ITEM)); - panelBuilder.item(25, this.createRewardButton(RewardButton.REPEAT_REWARD_EXPERIENCE)); - panelBuilder.item(34, this.createRewardButton(RewardButton.REPEAT_REWARD_MONEY)); - } - } - - - // --------------------------------------------------------------------- - // Section: Other methods - // --------------------------------------------------------------------- - - - /** - * This method creates top menu buttons, that allows to switch "tabs". - * @param menuType Menu Type which button must be constructed. - * @return PanelItem that represents given menu type. - */ - private PanelItem createMenuButton(MenuType menuType) - { - ItemStack icon; - String name; - String description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (menuType) - { - case PROPERTIES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.properties"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.properties"); - icon = new ItemStack(Material.CRAFTING_TABLE); - clickHandler = (panel, user, clickType, slot) -> { - this.currentMenuType = MenuType.PROPERTIES; - this.build(); - - return true; - }; - glow = this.currentMenuType.equals(MenuType.PROPERTIES); - break; - } - case REQUIREMENTS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.requirements"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.requirements"); - icon = new ItemStack(Material.HOPPER); - clickHandler = (panel, user, clickType, slot) -> { - this.currentMenuType = MenuType.REQUIREMENTS; - this.build(); - - return true; - }; - glow = this.currentMenuType.equals(MenuType.REQUIREMENTS); - break; - } - case REWARDS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.rewards"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.rewards"); - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - this.currentMenuType = MenuType.REWARDS; - this.build(); - - return true; - }; - glow = this.currentMenuType.equals(MenuType.REWARDS); - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for default main menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createButton(Button button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case DEPLOYED: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.deployment"); - - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.deployment")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.challenge.isDeployed() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - this.challenge.setDeployed(!this.challenge.isDeployed()); - - this.build(); - return true; - }; - glow = this.challenge.isDeployed(); - break; - } - case ICON: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.icon"); - description = Collections.singletonList(this.user.getTranslation( - "challenges.gui.descriptions.admin.icon-challenge")); - icon = this.challenge.getIcon(); - clickHandler = (panel, user, clickType, slot) -> { - - new SelectBlocksGUI(this.user, true, (status, materials) -> { - if (status) - { - materials.forEach(material -> - this.challenge.setIcon(new ItemStack(material))); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case DESCRIPTION: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.description"); - description = Collections.singletonList( - this.user.getTranslation("challenges.gui.descriptions.admin.description")); - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getDescription(), - this.lineLength, - (status, value) -> { - if (status) - { - this.challenge.setDescription(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case ORDER: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.order"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.order")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getOrder()))); - - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getOrder(), - -1, - 9999, - this.lineLength, - (status, value) -> { - if (status) - { - this.challenge.setOrder(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case ENVIRONMENT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.environment"); - - description = new ArrayList<>(4); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.environment")); - - description.add((this.challenge.getEnvironment().contains(World.Environment.NORMAL) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.normal")); - description.add((this.challenge.getEnvironment().contains(World.Environment.NETHER) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.nether")); - description.add((this.challenge.getEnvironment().contains(World.Environment.THE_END) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.the-end")); - - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - new SelectEnvironmentGUI(this.user, - this.challenge.getEnvironment(), - (status, value) -> { - if (status) - { - this.challenge.setEnvironment(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REMOVE_ON_COMPLETE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-on-complete"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-on-complete")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.challenge.isRemoveWhenCompleted() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - if (this.challenge.isRemoveWhenCompleted()) - { - icon = new ItemStack(Material.LAVA_BUCKET); - } - else - { - icon = new ItemStack(Material.BUCKET); - } - - clickHandler = (panel, user, clickType, slot) -> { - this.challenge.setRemoveWhenCompleted(!this.challenge.isRemoveWhenCompleted()); - this.build(); - - return true; - }; - glow = this.challenge.isRemoveWhenCompleted(); - break; - } - case NAME: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.name"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.name-challenge")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", this.challenge.getFriendlyName())); - - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - - this.getFriendlyName(reply -> { - if (reply != null) - { - this.challenge.setFriendlyName(reply); - } - - this.build(); - }, - this.user.getTranslation("challenges.gui.questions.admin.challenge-name"), - this.challenge.getFriendlyName() - ); - - return true; - }; - glow = false; - break; - } - - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for requirements menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createRequirementButton(RequirementButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case REQUIRED_PERMISSIONS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-permissions"); - description = new ArrayList<>(this.challenge.getRequirements().getRequiredPermissions().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-permissions")); - - for (String permission : this.challenge.getRequirements().getRequiredPermissions()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.permission", - "[permission]", permission)); - } - - icon = new ItemStack(Material.REDSTONE_LAMP); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRequirements().getRequiredPermissions(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.getRequirements().setRequiredPermissions(new HashSet<>(value)); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case REQUIRED_ENTITIES: - case REMOVE_ENTITIES: - case REQUIRED_BLOCKS: - case REMOVE_BLOCKS: - case SEARCH_RADIUS: - { - return this.createIslandRequirementButton(button); - } - - case REQUIRED_ITEMS: - case REMOVE_ITEMS: - { - return this.createInventoryRequirementButton(button); - } - - case REQUIRED_EXPERIENCE: - case REMOVE_EXPERIENCE: - case REQUIRED_LEVEL: - case REQUIRED_MONEY: - case REMOVE_MONEY: - { - return this.createOtherRequirementButton(button); - } - - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for island requirements menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createIslandRequirementButton(RequirementButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - final IslandRequirements requirements = this.challenge.getRequirements(); - - switch (button) - { - case REQUIRED_ENTITIES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-entities"); - - description = new ArrayList<>(requirements.getRequiredEntities().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-entities")); - - for (Map.Entry entry : requirements.getRequiredEntities().entrySet()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.entity", - "[entity]", Util.prettifyText(entry.getKey().name()), - "[count]", Integer.toString(entry.getValue()))); - } - - icon = new ItemStack(Material.CREEPER_HEAD); - clickHandler = (panel, user, clickType, slot) -> { - new ManageEntitiesGUI(this.addon, - this.world, - this.user, - requirements.getRequiredEntities(), - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - break; - } - case REMOVE_ENTITIES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-entities"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-entities")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isRemoveEntities() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setRemoveEntities(!requirements.isRemoveEntities()); - - this.build(); - return true; - }; - glow = requirements.isRemoveEntities(); - break; - } - case REQUIRED_BLOCKS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-blocks"); - - description = new ArrayList<>(requirements.getRequiredBlocks().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-blocks")); - - for (Map.Entry entry : requirements.getRequiredBlocks().entrySet()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.block", - "[block]", entry.getKey().name(), - "[count]", Integer.toString(entry.getValue()))); - } - - icon = new ItemStack(Material.STONE); - clickHandler = (panel, user, clickType, slot) -> { - new ManageBlocksGUI(this.addon, - this.world, - this.user, - requirements.getRequiredBlocks(), - this.topLabel, - this.permissionPrefix, - this).build(); - - return true; - }; - glow = false; - break; - } - case REMOVE_BLOCKS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-blocks"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-blocks")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isRemoveBlocks() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setRemoveBlocks(!requirements.isRemoveBlocks()); - - this.build(); - return true; - }; - glow = requirements.isRemoveBlocks(); - break; - } - case SEARCH_RADIUS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.search-radius"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.search-radius")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(requirements.getSearchRadius()))); - - icon = new ItemStack(Material.COBBLESTONE_WALL); - - // Search radius should not be larger then island radius. - int maxSearchDistance = - this.addon.getPlugin().getIWM().getAddon(this.world).map(gameModeAddon -> - gameModeAddon.getWorldSettings().getIslandDistance()).orElse(100); - - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - requirements.getSearchRadius(), - 0, - maxSearchDistance, - this.lineLength, - (status, value) -> { - if (status) - { - requirements.setSearchRadius(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for inventory requirements menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createInventoryRequirementButton(RequirementButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - final InventoryRequirements requirements = this.challenge.getRequirements(); - - switch (button) - { - case REQUIRED_ITEMS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-items"); - - description = new ArrayList<>(requirements.getRequiredItems().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-items")); - - Utils.groupEqualItems(requirements.getRequiredItems()).forEach(itemStack -> - description.addAll(this.generateItemStackDescription(itemStack))); - - icon = new ItemStack(Material.CHEST); - clickHandler = (panel, user, clickType, slot) -> { - new ItemSwitchGUI(this.user, - requirements.getRequiredItems(), - this.lineLength, - (status, value) -> { - if (status) - { - requirements.setRequiredItems(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REMOVE_ITEMS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-items"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-items")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isTakeItems() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setTakeItems(!requirements.isTakeItems()); - - this.build(); - return true; - }; - glow = requirements.isTakeItems(); - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for other requirements menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createOtherRequirementButton(RequirementButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - final OtherRequirements requirements = this.challenge.getRequirements(); - - switch (button) - { - case REQUIRED_EXPERIENCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-experience"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-experience")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(requirements.getRequiredExperience()))); - - icon = new ItemStack(Material.EXPERIENCE_BOTTLE); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - requirements.getRequiredExperience(), - 0, - this.lineLength, - (status, value) -> { - if (status) - { - requirements.setRequiredExperience(value); - } - - this.build(); - }); - return true; - }; - glow = false; - break; - } - case REMOVE_EXPERIENCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-experience"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-experience")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isTakeExperience() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setTakeExperience(!requirements.isTakeExperience()); - - this.build(); - return true; - }; - glow = requirements.isTakeExperience(); - break; - } - case REQUIRED_LEVEL: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-level"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-level")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Long.toString(requirements.getRequiredIslandLevel()))); - - icon = new ItemStack(this.addon.isLevelProvided() ? Material.BEACON : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - (int) requirements.getRequiredIslandLevel(), - lineLength, - (status, value) -> { - if (status) - { - requirements.setRequiredIslandLevel(value); - } - - this.build(); - }); - - return true; - }; - - glow = false; - break; - } - case REQUIRED_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.required-money"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-money")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Double.toString(requirements.getRequiredMoney()))); - - icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - (int) requirements.getRequiredMoney(), - 0, - lineLength, - (status, value) -> { - if (status) - { - requirements.setRequiredMoney(value); - } - - this.build(); - }); - return true; - }; - - glow = false; - break; - } - case REMOVE_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-money"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-money")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - requirements.isTakeMoney() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(this.addon.isEconomyProvided() ? Material.LEVER : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setTakeMoney(!requirements.isTakeMoney()); - - this.build(); - return true; - }; - - glow = requirements.isTakeMoney(); - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates buttons for rewards menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createRewardButton(RewardButton button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case REWARD_TEXT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-text"); - description = new ArrayList<>(2); - description - .add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-text")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", "|" + this.challenge.getRewardText())); - - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRewardText(), - lineLength, - (status, value) -> { - if (status) - { - String singleLineMessage = value.stream(). - map(s -> s + "|"). - collect(Collectors.joining()); - - if (singleLineMessage.endsWith("|")) - { - singleLineMessage = singleLineMessage - .substring(0, singleLineMessage.length() - 1); - } - - this.challenge.setRewardText(singleLineMessage); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_ITEM: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-items"); - - description = new ArrayList<>(this.challenge.getRewardItems().size() + 1); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.reward-items")); - - Utils.groupEqualItems(this.challenge.getRewardItems()).forEach(itemStack -> - description.addAll(this.generateItemStackDescription(itemStack))); - - icon = new ItemStack(Material.CHEST); - clickHandler = (panel, user, clickType, slot) -> { - new ItemSwitchGUI(this.user, - this.challenge.getRewardItems(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRewardItems(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_EXPERIENCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-experience"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.reward-experience")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getRewardExperience()))); - icon = new ItemStack(Material.EXPERIENCE_BOTTLE); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getRewardExperience(), - 0, - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRewardExperience(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-money"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.reward-money")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getRewardMoney()))); - - icon = new ItemStack( - this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getRewardMoney(), - 0, - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRewardMoney(value); - } - - this.build(); - }); - - return true; - }; - - - glow = false; - break; - } - case REWARD_COMMANDS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-commands"); - description = new ArrayList<>(this.challenge.getRewardCommands().size() + 1); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.reward-commands")); - - for (String command : this.challenge.getRewardCommands()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command)); - } - - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRewardCommands(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRewardCommands(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case REPEATABLE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.repeatable"); - description = new ArrayList<>(2); - description - .add(this.user.getTranslation("challenges.gui.descriptions.admin.repeatable")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.challenge.isRepeatable() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - this.challenge.setRepeatable(!this.challenge.isRepeatable()); - - this.build(); - return true; - }; - glow = this.challenge.isRepeatable(); - break; - } - case REPEAT_COUNT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.repeat-count"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-count")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getMaxTimes()))); - - icon = new ItemStack(Material.COBBLESTONE_WALL); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getMaxTimes(), - 0, - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setMaxTimes(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case REPEAT_REWARD_TEXT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.repeat-reward-text"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-text")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", "|" + this.challenge.getRepeatRewardText())); - - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRepeatRewardText(), - lineLength, - (status, value) -> { - if (status) - { - String singleLineMessage = value.stream(). - map(s -> s + "|"). - collect(Collectors.joining()); - - if (singleLineMessage.endsWith("|")) - { - singleLineMessage = singleLineMessage - .substring(0, singleLineMessage.length() - 1); - } - - this.challenge.setRepeatRewardText(singleLineMessage); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REPEAT_REWARD_ITEM: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.repeat-reward-items"); - - description = new ArrayList<>(this.challenge.getRepeatItemReward().size() + 1); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-items")); - - Utils.groupEqualItems(this.challenge.getRepeatItemReward()).forEach(itemStack -> - description.addAll(this.generateItemStackDescription(itemStack))); - - icon = new ItemStack(Material.TRAPPED_CHEST); - clickHandler = (panel, user, clickType, slot) -> { - new ItemSwitchGUI(this.user, - this.challenge.getRepeatItemReward(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRepeatItemReward(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REPEAT_REWARD_EXPERIENCE: - { - name = this.user - .getTranslation("challenges.gui.buttons.admin.repeat-reward-experience"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-experience")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getRepeatExperienceReward()))); - - icon = new ItemStack(Material.GLASS_BOTTLE); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getRepeatExperienceReward(), - 0, - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRepeatExperienceReward(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REPEAT_REWARD_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.repeat-reward-money"); - description = new ArrayList<>(2); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-money")); - description - .add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challenge.getRepeatMoneyReward()))); - - icon = new ItemStack( - this.addon.isEconomyProvided() ? Material.GOLD_NUGGET : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, - this.challenge.getRepeatMoneyReward(), - 0, - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRepeatMoneyReward(value); - } - - this.build(); - }); - - return true; - }; - - glow = false; - break; - } - case REPEAT_REWARD_COMMANDS: - { - name = - this.user.getTranslation("challenges.gui.buttons.admin.repeat-reward-commands"); - description = new ArrayList<>(this.challenge.getRepeatRewardCommands().size() + 1); - description.add(this.user - .getTranslation("challenges.gui.descriptions.admin.repeat-reward-commands")); - - for (String command : this.challenge.getRepeatRewardCommands()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command)); - } - - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, - this.challenge.getRepeatRewardCommands(), - lineLength, - (status, value) -> { - if (status) - { - this.challenge.setRepeatRewardCommands(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * Represents different types of menus - */ - private enum MenuType - { - PROPERTIES, - REQUIREMENTS, - REWARDS - } - - - /** - * Represents different buttons that could be in menus. - */ - private enum Button - { - NAME, - DEPLOYED, - ICON, - DESCRIPTION, - ORDER, - ENVIRONMENT, - REMOVE_ON_COMPLETE, - } - - - /** - * Represents different rewards buttons that are used in menus. - */ - private enum RewardButton - { - REWARD_TEXT, - REWARD_ITEM, - REWARD_EXPERIENCE, - REWARD_MONEY, - REWARD_COMMANDS, - - REPEATABLE, - REPEAT_COUNT, - - REPEAT_REWARD_TEXT, - REPEAT_REWARD_ITEM, - REPEAT_REWARD_EXPERIENCE, - REPEAT_REWARD_MONEY, - REPEAT_REWARD_COMMANDS, - } - - - /** - * Represents different requirement buttons that are used in menus. - */ - private enum RequirementButton - { - REQUIRED_ENTITIES, - REMOVE_ENTITIES, - REQUIRED_BLOCKS, - REMOVE_BLOCKS, - SEARCH_RADIUS, - REQUIRED_PERMISSIONS, - REQUIRED_ITEMS, - REMOVE_ITEMS, - REQUIRED_EXPERIENCE, - REMOVE_EXPERIENCE, - REQUIRED_LEVEL, - REQUIRED_MONEY, - REMOVE_MONEY, - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * Variable holds challenge thats needs editing. - */ - private Challenge challenge; - - /** - * Variable holds current active menu. - */ - private MenuType currentMenuType; - - /** - * LineLength variable. - */ - private final int lineLength; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java new file mode 100644 index 0000000..db7e50b --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java @@ -0,0 +1,2121 @@ +package world.bentobox.challenges.panel.admin; + + +import java.time.Duration; +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.PanelListener; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.requirements.InventoryRequirements; +import world.bentobox.challenges.database.object.requirements.IslandRequirements; +import world.bentobox.challenges.database.object.requirements.OtherRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.*; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary methods that creates GUI and allow to edit challenges + * properties. + */ +public class EditChallengePanel extends CommonPanel +{ + // --------------------------------------------------------------------- + // Section: Constructors + // --------------------------------------------------------------------- + + + /** + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + * @param challenge - challenge that needs editing + */ + private EditChallengePanel(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + Challenge challenge) + { + super(addon, user, world, topLabel, permissionPrefix); + this.challenge = challenge; + this.currentMenuType = MenuType.PROPERTIES; + } + + + /** + * @param panel Parent panel + * @param challenge challenge that needs editing. + */ + private EditChallengePanel(CommonPanel panel, Challenge challenge) + { + super(panel); + this.challenge = challenge; + // Default panel should be Properties. + this.currentMenuType = MenuType.PROPERTIES; + } + + + /** + * Open the Challenges Edit GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + * @param challenge - challenge that needs editing + */ + public static void open(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + Challenge challenge) + { + new EditChallengePanel(addon, user, world, topLabel, permissionPrefix, challenge).build(); + } + + + /** + * Open the Challenges Edit GUI. + * + * @param panel - Parent Panel + * @param challenge - challenge that needs editing + */ + public static void open(CommonPanel panel, Challenge challenge) + { + new EditChallengePanel(panel, challenge).build(); + } + + + // --------------------------------------------------------------------- + // Section: Panel Creation related methods + // --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "edit-challenge", + "[challenge]", this.challenge.getFriendlyName())); + + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(2, this.createMenuButton(MenuType.PROPERTIES)); + panelBuilder.item(4, this.createMenuButton(MenuType.REQUIREMENTS)); + panelBuilder.item(6, this.createMenuButton(MenuType.REWARDS)); + + if (this.currentMenuType.equals(MenuType.PROPERTIES)) + { + this.buildMainPropertiesPanel(panelBuilder); + } + else if (this.currentMenuType.equals(MenuType.REQUIREMENTS)) + { + switch (this.challenge.getChallengeType()) + { + case INVENTORY_TYPE -> this.buildInventoryRequirementsPanel(panelBuilder); + case ISLAND_TYPE -> this.buildIslandRequirementsPanel(panelBuilder); + case OTHER_TYPE -> this.buildOtherRequirementsPanel(panelBuilder); + case STATISTIC_TYPE -> this.buildStatisticRequirementsPanel(panelBuilder); + } + } + else if (this.currentMenuType.equals(MenuType.REWARDS)) + { + this.buildRewardsPanel(panelBuilder); + } + + panelBuilder.item(44, this.returnButton); + + // Every time when this GUI is build, save challenge + // This will ensure that all main things will be always stored + this.addon.getChallengesManager().saveChallenge(this.challenge); + // If for some reason challenge is not loaded, do it. + this.addon.getChallengesManager().loadChallenge(this.challenge, false, null, true); + + panelBuilder.build(); + } + + + /** + * This class populate ChallengesEditGUI with main challenge settings. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildMainPropertiesPanel(PanelBuilder panelBuilder) + { + panelBuilder.listener(new IconChanger()); + + panelBuilder.item(10, this.createButton(Button.NAME)); + panelBuilder.item(16, this.createButton(Button.DEPLOYED)); + + panelBuilder.item(19, this.createButton(Button.ICON)); + panelBuilder.item(22, this.createButton(Button.DESCRIPTION)); + panelBuilder.item(25, this.createButton(Button.ORDER)); + + panelBuilder.item(28, this.createButton(Button.ENVIRONMENT)); + panelBuilder.item(31, this.createButton(Button.REMOVE_ON_COMPLETE)); + } + + + /** + * This class populates ChallengesEditGUI with island challenges requirement elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildIslandRequirementsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(19, this.createRequirementButton(RequirementButton.REQUIRED_ENTITIES)); + panelBuilder.item(28, this.createRequirementButton(RequirementButton.REMOVE_ENTITIES)); + + panelBuilder.item(21, this.createRequirementButton(RequirementButton.REQUIRED_BLOCKS)); + panelBuilder.item(30, this.createRequirementButton(RequirementButton.REMOVE_BLOCKS)); + + panelBuilder.item(23, this.createRequirementButton(RequirementButton.SEARCH_RADIUS)); + panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); + } + + + /** + * This class populates ChallengesEditGUI with inventory challenges requirement elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildInventoryRequirementsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createRequirementButton(RequirementButton.REQUIRED_ITEMS)); + panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_ITEMS)); + + if (!this.challenge.getRequirements().getRequiredItems().isEmpty()) + { + panelBuilder.item(12, this.createRequirementButton(RequirementButton.ADD_IGNORED_META)); + + if (!this.challenge.getRequirements().getIgnoreMetaData().isEmpty()) + { + panelBuilder.item(21, this.createRequirementButton(RequirementButton.REMOVE_IGNORED_META)); + } + } + + panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); + } + + + /** + * This class populates ChallengesEditGUI with other challenges requirement elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildOtherRequirementsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createRequirementButton(RequirementButton.REQUIRED_EXPERIENCE)); + panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_EXPERIENCE)); + + panelBuilder.item(12, this.createRequirementButton(RequirementButton.REQUIRED_MONEY)); + panelBuilder.item(21, this.createRequirementButton(RequirementButton.REMOVE_MONEY)); + + panelBuilder.item(23, this.createRequirementButton(RequirementButton.REQUIRED_LEVEL)); + + panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); + } + + + /** + * This class populates ChallengesEditGUI with other challenges requirement elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildStatisticRequirementsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createRequirementButton(RequirementButton.STATISTIC)); + panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_STATISTIC)); + + panelBuilder.item(11, this.createRequirementButton(RequirementButton.STATISTIC_AMOUNT)); + + StatisticRequirements requirements = this.challenge.getRequirements(); + + if (requirements.getStatistic() != null) + { + switch (requirements.getStatistic().getType()) + { + case ITEM -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_ITEMS)); + case BLOCK -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_BLOCKS)); + case ENTITY -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_ENTITIES)); + default -> {} + } + } + + panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); + } + + + /** + * This class populates ChallengesEditGUI with challenges reward elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildRewardsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createRewardButton(RewardButton.REWARD_TEXT)); + panelBuilder.item(19, this.createRewardButton(RewardButton.REWARD_COMMANDS)); + + panelBuilder.item(11, this.createRewardButton(RewardButton.REWARD_ITEMS)); + panelBuilder.item(20, this.createRewardButton(RewardButton.REWARD_EXPERIENCE)); + panelBuilder.item(29, this.createRewardButton(RewardButton.REWARD_MONEY)); + + panelBuilder.item(22, this.createRewardButton(RewardButton.REPEATABLE)); + + if (!this.challenge.getRewardItems().isEmpty() || !this.challenge.getRepeatItemReward().isEmpty()) + { + panelBuilder.item(31, this.createRewardButton(RewardButton.ADD_IGNORED_META)); + } + + if (!this.challenge.getIgnoreRewardMetaData().isEmpty()) + { + panelBuilder.item(32, this.createRewardButton(RewardButton.REMOVE_IGNORED_META)); + } + + if (this.challenge.isRepeatable()) + { + panelBuilder.item(13, this.createRewardButton(RewardButton.COOL_DOWN)); + panelBuilder.item(23, this.createRewardButton(RewardButton.REPEAT_COUNT)); + + panelBuilder.item(15, this.createRewardButton(RewardButton.REPEAT_REWARD_TEXT)); + panelBuilder.item(24, this.createRewardButton(RewardButton.REPEAT_REWARD_COMMANDS)); + + panelBuilder.item(16, this.createRewardButton(RewardButton.REPEAT_REWARD_ITEMS)); + panelBuilder.item(25, this.createRewardButton(RewardButton.REPEAT_REWARD_EXPERIENCE)); + panelBuilder.item(34, this.createRewardButton(RewardButton.REPEAT_REWARD_MONEY)); + } + } + + + // --------------------------------------------------------------------- + // Section: Other methods + // --------------------------------------------------------------------- + + + /** + * This method creates top menu buttons, that allows to switch "tabs". + * @param menuType Menu Type which button must be constructed. + * @return PanelItem that represents given menu type. + */ + private PanelItem createMenuButton(MenuType menuType) + { + final String reference = Constants.BUTTON + menuType.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (menuType) + { + case PROPERTIES -> { + icon = new ItemStack(Material.CRAFTING_TABLE); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.PROPERTIES; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.PROPERTIES); + } + case REQUIREMENTS -> { + icon = new ItemStack(Material.HOPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REQUIREMENTS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REQUIREMENTS); + } + case REWARDS -> { + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REWARDS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REWARDS); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for default main menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case NAME -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NAME, this.challenge.getFriendlyName())); + + icon = new ItemStack(Material.NAME_TAG); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer consumer = value -> + { + if (value != null) + { + this.challenge.setFriendlyName(value); + } + + this.build(); + }; + + // start conversation + ConversationUtils.createStringInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-name"), + user.getTranslation(Constants.CONVERSATIONS + "name-changed")); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case DEPLOYED -> { + description.add(this.user.getTranslation(reference + + (this.challenge.isDeployed() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + if (this.challenge.isValid()) + { + this.challenge.setDeployed(!this.challenge.isDeployed()); + } + else + { + Utils.sendMessage(this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "invalid-challenge", + "[challenge]", this.challenge.getFriendlyName())); + this.challenge.setDeployed(false); + } + + this.build(); + return true; + }; + glow = this.challenge.isDeployed(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case ICON -> { + icon = this.challenge.getIcon(); + clickHandler = (panel, user, clickType, i) -> + { + this.selectedButton = button; + this.build(); + return true; + }; + glow = this.selectedButton == button; + + if (this.selectedButton != button) + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-on-item")); + } + } + case DESCRIPTION -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + this.challenge.getDescription().forEach(line -> description.add(Util.translateColorCodes(line))); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setDescription(value); + } + + this.build(); + }; + + if (!this.challenge.getDescription().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-description"), + user.getTranslation(Constants.CONVERSATIONS + "description-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getDescription().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case ORDER -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getOrder()))); + + icon = new ItemStack(Material.HOPPER, Math.max(1, this.challenge.getOrder())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setOrder(number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case ENVIRONMENT -> { + description.add(this.user.getTranslation(this.challenge.getEnvironment().contains(World.Environment.NORMAL) ? + reference + "enabled" : reference + "disabled") + + Utils.prettifyObject(World.Environment.NORMAL, this.user)); + description.add(this.user.getTranslation(this.challenge.getEnvironment().contains(World.Environment.NETHER) ? + reference + "enabled" : reference + "disabled") + + Utils.prettifyObject(World.Environment.NETHER, this.user)); + description.add(this.user.getTranslation(this.challenge.getEnvironment().contains(World.Environment.THE_END) ? + reference + "enabled" : reference + "disabled") + + Utils.prettifyObject(World.Environment.THE_END, this.user)); + + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + EnvironmentSelector.open(this.user, + this.challenge.getEnvironment(), + (status, value) -> { + if (status) + { + this.challenge.setEnvironment(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + } + case REMOVE_ON_COMPLETE -> { + description.add(this.user.getTranslation(reference + + (this.challenge.isRemoveWhenCompleted() ? "enabled" : "disabled"))); + + if (this.challenge.isRemoveWhenCompleted()) + { + icon = new ItemStack(Material.LAVA_BUCKET); + } + else + { + icon = new ItemStack(Material.BUCKET); + } + + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setRemoveWhenCompleted(!this.challenge.isRemoveWhenCompleted()); + this.build(); + + return true; + }; + glow = this.challenge.isRemoveWhenCompleted(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for requirements menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createRequirementButton(RequirementButton button) + { + switch (button) + { + case REQUIRED_PERMISSIONS -> { + String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + if (this.challenge.getRequirements().getRequiredPermissions().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + this.challenge.getRequirements().getRequiredPermissions().forEach(permission -> + description.add(this.user.getTranslation(reference + "permission", + "[permission]", permission))); + } + + ItemStack icon = new ItemStack(Material.REDSTONE_LAMP); + + PanelItem.ClickHandler clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.getRequirements().setRequiredPermissions(new HashSet<>(value)); + } + + this.build(); + }; + + if (!this.challenge.getRequirements().getRequiredPermissions().isEmpty() && + clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-permissions"), + user.getTranslation(Constants.CONVERSATIONS + "permissions-changed")); + } + + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRequirements().getRequiredPermissions().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + // Buttons for Island Requirements + case REQUIRED_ENTITIES, REMOVE_ENTITIES, REQUIRED_BLOCKS, REMOVE_BLOCKS, SEARCH_RADIUS -> { + return this.createIslandRequirementButton(button); + } + // Buttons for Inventory Requirements + case REQUIRED_ITEMS, REMOVE_ITEMS, ADD_IGNORED_META, REMOVE_IGNORED_META -> { + return this.createInventoryRequirementButton(button); + } + // Buttons for Other Requirements + case REQUIRED_EXPERIENCE, REMOVE_EXPERIENCE, REQUIRED_LEVEL, REQUIRED_MONEY, REMOVE_MONEY -> { + return this.createOtherRequirementButton(button); + } + // Buttons for Statistic Requirements + case STATISTIC, STATISTIC_BLOCKS, STATISTIC_ITEMS, STATISTIC_ENTITIES, STATISTIC_AMOUNT, REMOVE_STATISTIC -> { + return this.createStatisticRequirementButton(button); + } + // Default behaviour. + default -> { + return PanelItem.empty(); + } + } + } + + + /** + * This method creates buttons for island requirements menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createIslandRequirementButton(RequirementButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final IslandRequirements requirements = this.challenge.getRequirements(); + + switch (button) + { + case REQUIRED_ENTITIES -> { + if (requirements.getRequiredEntities().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + requirements.getRequiredEntities().forEach((entity, count) -> + description.add(this.user.getTranslation(reference + "list", + "[entity]", Utils.prettifyObject(entity, this.user), + "[number]", String.valueOf(count)))); + } + + icon = new ItemStack(Material.CREEPER_HEAD); + clickHandler = (panel, user, clickType, slot) -> { + ManageEntitiesPanel.open(this, requirements.getRequiredEntities()); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_ENTITIES -> { + description.add(this.user.getTranslation(reference + + (requirements.isRemoveEntities() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setRemoveEntities(!requirements.isRemoveEntities()); + this.build(); + return true; + }; + glow = requirements.isRemoveEntities(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case REQUIRED_BLOCKS -> { + if (requirements.getRequiredBlocks().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + requirements.getRequiredBlocks().forEach((block, count) -> + description.add(this.user.getTranslation(reference + "list", + "[block]", Utils.prettifyObject(block, this.user), + "[number]", String.valueOf(count)))); + } + + icon = new ItemStack(Material.STONE); + clickHandler = (panel, user, clickType, slot) -> { + ManageBlocksPanel.open(this, requirements.getRequiredBlocks()); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_BLOCKS -> { + description.add(this.user.getTranslation(reference + + (requirements.isRemoveBlocks() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setRemoveBlocks(!requirements.isRemoveBlocks()); + this.build(); + return true; + }; + glow = requirements.isRemoveBlocks(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case SEARCH_RADIUS -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getSearchRadius()))); + icon = new ItemStack(Material.COBBLESTONE_WALL); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setSearchRadius(number.intValue()); + } + + // reopen panel + this.build(); + }; + + int maxSearchDistance = + this.addon.getPlugin().getIWM().getAddon(this.world).map(gameModeAddon -> + gameModeAddon.getWorldSettings().getIslandDistance()).orElse(100); + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 1, + maxSearchDistance); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for inventory requirements menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createInventoryRequirementButton(RequirementButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final InventoryRequirements requirements = this.challenge.getRequirements(); + + switch (button) + { + case REQUIRED_ITEMS -> { + if (requirements.getRequiredItems().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + Utils.groupEqualItems(requirements.getRequiredItems(), requirements.getIgnoreMetaData()). + stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + ItemSelector.open(this.user, + requirements.getRequiredItems(), + (status, value) -> { + if (status) + { + requirements.setRequiredItems(value); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_ITEMS -> { + description.add(this.user.getTranslation(reference + + (requirements.isTakeItems() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setTakeItems(!requirements.isTakeItems()); + this.build(); + return true; + }; + glow = requirements.isTakeItems(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case ADD_IGNORED_META -> { + if (requirements.getIgnoreMetaData().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + requirements.getIgnoreMetaData().stream(). + sorted(Comparator.comparing(Material::name)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.GREEN_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (requirements.getRequiredItems().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + requirements.getRequiredItems().stream(). + map(ItemStack::getType). + forEach(collection::remove); + collection.addAll(requirements.getIgnoreMetaData()); + + if (Material.values().length == collection.size()) + { + // If there are no items anymore, then do not allow opening gui. + return true; + } + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + materials.addAll(requirements.getIgnoreMetaData()); + requirements.setIgnoreMetaData(new HashSet<>(materials)); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_IGNORED_META -> { + icon = new ItemStack(Material.RED_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (requirements.getIgnoreMetaData().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + collection.removeAll(requirements.getIgnoreMetaData()); + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + requirements.getIgnoreMetaData().removeAll(materials); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for other requirements menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createOtherRequirementButton(RequirementButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final OtherRequirements requirements = this.challenge.getRequirements(); + + switch (button) + { + case REQUIRED_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getRequiredExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setRequiredExperience(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + + (requirements.isTakeExperience() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setTakeExperience(!requirements.isTakeExperience()); + this.build(); + return true; + }; + glow = requirements.isTakeExperience(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case REQUIRED_LEVEL -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getRequiredIslandLevel()))); + icon = new ItemStack(this.addon.isLevelProvided() ? Material.BEACON : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setRequiredIslandLevel(number.longValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REQUIRED_MONEY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getRequiredMoney()))); + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setRequiredMoney(number.doubleValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Double.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_MONEY -> { + description.add(this.user.getTranslation(reference + + (requirements.isTakeMoney() ? "enabled" : "disabled"))); + + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.LEVER : Material.BARRIER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setTakeMoney(!requirements.isTakeMoney()); + this.build(); + return true; + }; + glow = requirements.isTakeMoney(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * Creates a button for statistic requirements. + * @param button Button that must be created. + * @return PanelItem button. + */ + private PanelItem createStatisticRequirementButton(RequirementButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final StatisticRequirements requirements = this.challenge.getRequirements(); + + switch (button) + { + case STATISTIC -> { + description.add(this.user.getTranslation(reference + "value", + "[statistic]", Utils.prettifyObject(requirements.getStatistic(), this.user))); + + icon = new ItemStack(requirements.getStatistic() == null ? Material.BARRIER : Material.PAPER); + clickHandler = (panel, user, clickType, slot) -> { + StatisticSelector.open(this.user, (status, statistic) -> { + if (status) + { + requirements.setStatistic(statistic); + requirements.setMaterial(null); + requirements.setEntity(null); + requirements.setAmount(0); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_AMOUNT -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(requirements.getAmount()))); + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + requirements.setAmount(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_STATISTIC -> { + description.add(this.user.getTranslation(reference + + (requirements.isReduceStatistic() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + requirements.setReduceStatistic(!requirements.isReduceStatistic()); + this.build(); + return true; + }; + glow = requirements.isReduceStatistic(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case STATISTIC_BLOCKS -> { + description.add(this.user.getTranslation(reference + "value", + "[block]", Utils.prettifyObject(requirements.getMaterial(), this.user))); + + icon = requirements.getMaterial() == null ? + new ItemStack(Material.BARRIER) : + new ItemStack(requirements.getMaterial()); + clickHandler = (panel, user, clickType, slot) -> { + SingleBlockSelector.open(this.user, + SingleBlockSelector.Mode.BLOCKS, + (status, block) -> { + if (status) + { + requirements.setMaterial(block); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_ITEMS -> { + description.add(this.user.getTranslation(reference + "value", + "[item]", Utils.prettifyObject(requirements.getMaterial(), this.user))); + + icon = requirements.getMaterial() == null ? + new ItemStack(Material.BARRIER) : + new ItemStack(requirements.getMaterial()); + clickHandler = (panel, user, clickType, slot) -> { + SingleBlockSelector.open(this.user, + SingleBlockSelector.Mode.ITEMS, + (status, block) -> { + if (status) + { + requirements.setMaterial(block); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_ENTITIES -> { + description.add(this.user.getTranslation(reference + "value", + "[entity]", Utils.prettifyObject(requirements.getEntity(), this.user))); + + icon = requirements.getEntity() == null ? + new ItemStack(Material.BARRIER) : + new ItemStack(PanelUtils.getEntityEgg(requirements.getEntity())); + clickHandler = (panel, user, clickType, slot) -> { + SingleEntitySelector.open(this.user, + true, + (status, entity) -> { + if (status) + { + requirements.setEntity(entity); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates buttons for rewards menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createRewardButton(RewardButton button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case REWARD_TEXT -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + description.add(Util.translateColorCodes(this.challenge.getRewardText())); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setRewardText(String.join("\n", value)); + } + + this.build(); + }; + + if (!this.challenge.getRewardText().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-reward-text"), + user.getTranslation(Constants.CONVERSATIONS + "reward-text-changed")); + } + + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRewardText().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case REWARD_ITEMS -> { + + if (this.challenge.getRewardItems().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + Utils.groupEqualItems(this.challenge.getRewardItems(), this.challenge.getIgnoreRewardMetaData()). + stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + ItemSelector.open(this.user, + this.challenge.getRewardItems(), + (status, value) -> { + if (status) + { + this.challenge.setRewardItems(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getRewardExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setRewardExperience(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_MONEY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getRewardMoney()))); + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setRewardMoney(number.doubleValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Double.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_COMMANDS -> { + icon = new ItemStack(Material.COMMAND_BLOCK); + + description.add(this.user.getTranslation(reference + "value")); + description.addAll(this.challenge.getRewardCommands()); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setRewardCommands(value); + } + + this.build(); + }; + + if (!this.challenge.getRewardCommands().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-reward-commands"), + user.getTranslation(Constants.CONVERSATIONS + "reward-commands-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRewardCommands().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case REPEATABLE -> { + description.add(this.user.getTranslation(reference + + (this.challenge.isRepeatable() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setRepeatable(!this.challenge.isRepeatable()); + this.build(); + return true; + }; + glow = this.challenge.isRepeatable(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case REPEAT_COUNT -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getMaxTimes()))); + icon = new ItemStack(Material.COBBLESTONE_WALL); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setMaxTimes(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case COOL_DOWN -> { + description.add(this.user.getTranslation(reference + "value", + "[time]", + Utils.parseDuration(Duration.ofMillis(this.challenge.getTimeout()), this.user))); + icon = new ItemStack(Material.CLOCK); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setTimeout(number.longValue() * 1000); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-seconds"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REPEAT_REWARD_TEXT -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + description.add(Util.translateColorCodes(this.challenge.getRepeatRewardText())); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setRepeatRewardText(String.join("\n", value)); + } + + this.build(); + }; + + if (!this.challenge.getRepeatRewardText().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-repeat-reward-text"), + user.getTranslation(Constants.CONVERSATIONS + "repeat-reward-text-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRepeatRewardText().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case REPEAT_REWARD_ITEMS -> { + + if (this.challenge.getRepeatItemReward().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + Utils.groupEqualItems(this.challenge.getRepeatItemReward(), this.challenge.getIgnoreRewardMetaData()). + stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + ItemSelector.open(this.user, + this.challenge.getRewardItems(), + (status, value) -> { + if (status) + { + this.challenge.setRepeatItemReward(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REPEAT_REWARD_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getRepeatExperienceReward()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setRepeatExperienceReward(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REPEAT_REWARD_MONEY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challenge.getRepeatMoneyReward()))); + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_NUGGET : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challenge.setRepeatMoneyReward(number.doubleValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Double.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REPEAT_REWARD_COMMANDS -> { + icon = new ItemStack(Material.COMMAND_BLOCK); + + description.add(this.user.getTranslation(reference + "value")); + description.addAll(this.challenge.getRepeatRewardCommands()); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challenge.setRepeatRewardCommands(value); + } + + this.build(); + }; + + if (!this.challenge.getRepeatRewardCommands().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-repeat-reward-commands"), + user.getTranslation(Constants.CONVERSATIONS + "repeat-reward-commands-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challenge.getRepeatRewardCommands().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case ADD_IGNORED_META -> { + if (this.challenge.getIgnoreRewardMetaData().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + this.challenge.getIgnoreRewardMetaData().stream(). + sorted(Comparator.comparing(Material::name)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.GREEN_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (this.challenge.getRewardItems().isEmpty() && + this.challenge.getRepeatItemReward().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + this.challenge.getRewardItems().stream(). + map(ItemStack::getType). + forEach(collection::remove); + this.challenge.getRepeatItemReward().stream(). + map(ItemStack::getType). + forEach(collection::remove); + collection.addAll(this.challenge.getIgnoreRewardMetaData()); + + if (Material.values().length == collection.size()) + { + // If there are no items anymore, then do not allow opening gui. + return true; + } + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + materials.addAll(this.challenge.getIgnoreRewardMetaData()); + this.challenge.setIgnoreRewardMetaData(new HashSet<>(materials)); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_IGNORED_META -> { + icon = new ItemStack(Material.RED_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (this.challenge.getIgnoreRewardMetaData().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + collection.removeAll(this.challenge.getIgnoreRewardMetaData()); + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + this.challenge.getIgnoreRewardMetaData().removeAll(materials); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + // --------------------------------------------------------------------- + // Section: Classes + // --------------------------------------------------------------------- + + + /** + * This class allows changing icon for Generator Tier + */ + private class IconChanger implements PanelListener + { + /** + * Process inventory click. If generator icon is selected and user clicks on item in his inventory, then change + * icon to the item from inventory. + * + * @param user the user + * @param event the event + */ + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // Handle icon changing + if (EditChallengePanel.this.selectedButton != null && + event.getCurrentItem() != null && + !event.getCurrentItem().getType().equals(Material.AIR) && + event.getRawSlot() > 44) + { + // set material and amount only. Other data should be removed. + + if (EditChallengePanel.this.selectedButton == Button.ICON) + { + EditChallengePanel.this.challenge.setIcon(event.getCurrentItem().clone()); + // Deselect icon + EditChallengePanel.this.selectedButton = null; + // Rebuild icon + EditChallengePanel.this.build(); + } + } + } + + + /** + * On inventory close. + * + * @param event the event + */ + @Override + public void onInventoryClose(InventoryCloseEvent event) + { + // Do nothing + } + + + /** + * Setup current listener. + */ + @Override + public void setup() + { + // Do nothing + } + } + + + // --------------------------------------------------------------------- + // Section: Enums + // --------------------------------------------------------------------- + + + /** + * Represents different types of menus + */ + private enum MenuType + { + PROPERTIES, + REQUIREMENTS, + REWARDS + } + + + /** + * Represents different buttons that could be in menus. + */ + private enum Button + { + NAME, + DEPLOYED, + ICON, + DESCRIPTION, + ORDER, + ENVIRONMENT, + REMOVE_ON_COMPLETE, + } + + + /** + * Represents different rewards buttons that are used in menus. + */ + private enum RewardButton + { + REWARD_TEXT, + REWARD_ITEMS, + REWARD_EXPERIENCE, + REWARD_MONEY, + REWARD_COMMANDS, + + REPEATABLE, + REPEAT_COUNT, + COOL_DOWN, + + REPEAT_REWARD_TEXT, + REPEAT_REWARD_ITEMS, + REPEAT_REWARD_EXPERIENCE, + REPEAT_REWARD_MONEY, + REPEAT_REWARD_COMMANDS, + + ADD_IGNORED_META, + REMOVE_IGNORED_META, + } + + + /** + * Represents different requirement buttons that are used in menus. + */ + private enum RequirementButton + { + REQUIRED_ENTITIES, + REMOVE_ENTITIES, + REQUIRED_BLOCKS, + REMOVE_BLOCKS, + SEARCH_RADIUS, + REQUIRED_PERMISSIONS, + REQUIRED_ITEMS, + REMOVE_ITEMS, + ADD_IGNORED_META, + REMOVE_IGNORED_META, + REQUIRED_EXPERIENCE, + REMOVE_EXPERIENCE, + REQUIRED_LEVEL, + REQUIRED_MONEY, + REMOVE_MONEY, + STATISTIC, + STATISTIC_BLOCKS, + STATISTIC_ITEMS, + STATISTIC_ENTITIES, + STATISTIC_AMOUNT, + REMOVE_STATISTIC, + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * Variable holds challenge thats needs editing. + */ + private final Challenge challenge; + + + private Button selectedButton; + + /** + * Variable holds current active menu. + */ + private MenuType currentMenuType; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java deleted file mode 100644 index bd6b841..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java +++ /dev/null @@ -1,764 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.ChallengeLevel; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.ItemSwitchGUI; -import world.bentobox.challenges.panel.util.NumberGUI; -import world.bentobox.challenges.panel.util.SelectBlocksGUI; -import world.bentobox.challenges.panel.util.SelectChallengeGUI; -import world.bentobox.challenges.panel.util.StringListGUI; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.utils.Utils; - - -/** - * This class contains all necessary elements to create Levels Edit GUI. - */ -public class EditLevelGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructors - // --------------------------------------------------------------------- - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param challengeLevel ChallengeLevel that must be edited. - */ - public EditLevelGUI(ChallengesAddon addon, - World world, - User user, - ChallengeLevel challengeLevel, - String topLabel, - String permissionPrefix) - { - this(addon, world, user, challengeLevel, topLabel, permissionPrefix, null); - } - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param challengeLevel ChallengeLevel that must be edited. - */ - public EditLevelGUI(ChallengesAddon addon, - World world, - User user, - ChallengeLevel challengeLevel, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - super(addon, world, user, topLabel, permissionPrefix, parentGUI); - this.challengeLevel = challengeLevel; - this.currentMenuType = MenuType.PROPERTIES; - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * This method builds all necessary elements in GUI panel. - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.edit-level-title")); - - GuiUtils.fillBorder(panelBuilder); - - panelBuilder.item(2, this.createMenuButton(MenuType.PROPERTIES)); - panelBuilder.item(4, this.createMenuButton(MenuType.REWARDS)); - panelBuilder.item(6, this.createMenuButton(MenuType.CHALLENGES)); - - if (this.currentMenuType.equals(MenuType.PROPERTIES)) - { - this.buildMainPropertiesPanel(panelBuilder); - } - else if (this.currentMenuType.equals(MenuType.CHALLENGES)) - { - this.buildChallengesPanel(panelBuilder); - } - else if (this.currentMenuType.equals(MenuType.REWARDS)) - { - this.buildRewardsPanel(panelBuilder); - } - - panelBuilder.item(44, this.returnButton); - - // Save challenge level every time this gui is build. - // It will ensure that changes are stored in database. - this.addon.getChallengesManager().saveLevel(this.challengeLevel); - - panelBuilder.build(); - } - - - /** - * This class populate LevelsEditGUI with main level settings. - * @param panelBuilder PanelBuilder where icons must be added. - */ - private void buildMainPropertiesPanel(PanelBuilder panelBuilder) - { - panelBuilder.item(10, this.createButton(Button.NAME)); - - panelBuilder.item(19, this.createButton(Button.ICON)); - panelBuilder.item(28, this.createButton(Button.CLOSED_ICON)); - panelBuilder.item(22, this.createButton(Button.UNLOCK_MESSAGE)); - panelBuilder.item(25, this.createButton(Button.ORDER)); - - panelBuilder.item(31, this.createButton(Button.WAIVER_AMOUNT)); - } - - - /** - * This class populate LevelsEditGUI with level rewards. - * @param panelBuilder PanelBuilder where icons must be added. - */ - private void buildRewardsPanel(PanelBuilder panelBuilder) - { - panelBuilder.item(12, this.createButton(Button.REWARD_DESCRIPTION)); - panelBuilder.item(21, this.createButton(Button.REWARD_COMMANDS)); - - panelBuilder.item(13, this.createButton(Button.REWARD_ITEM)); - panelBuilder.item(22, this.createButton(Button.REWARD_EXPERIENCE)); - panelBuilder.item(31, this.createButton(Button.REWARD_MONEY)); - } - - - /** - * This class populate LevelsEditGUI with level challenges. - * @param panelBuilder PanelBuilder where icons must be added. - */ - private void buildChallengesPanel(PanelBuilder panelBuilder) - { - List challengeList = this.addon.getChallengesManager().getLevelChallenges(this.challengeLevel); - - final int MAX_ELEMENTS = 21; - - if (this.pageIndex < 0) - { - this.pageIndex = challengeList.size() / MAX_ELEMENTS; - } - else if (this.pageIndex > (challengeList.size() / MAX_ELEMENTS)) - { - this.pageIndex = 0; - } - - int challengeIndex = MAX_ELEMENTS * this.pageIndex; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (challengeIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && - challengeIndex < challengeList.size() && - index < 36) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createChallengeIcon(challengeList.get(challengeIndex++))); - } - - index++; - } - - // Navigation buttons only if necessary - if (challengeList.size() > MAX_ELEMENTS) - { - panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); - panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); - } - - panelBuilder.item(39, this.createButton(Button.ADD_CHALLENGE)); - panelBuilder.item(41, this.createButton(Button.REMOVE_CHALLENGE)); - } - - - // --------------------------------------------------------------------- - // Section: Other methods - // --------------------------------------------------------------------- - - - /** - * This method creates top menu buttons, that allows to switch "tabs". - * @param menuType Menu Type which button must be constructed. - * @return PanelItem that represents given menu type. - */ - private PanelItem createMenuButton(MenuType menuType) - { - ItemStack icon; - String name; - String description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (menuType) - { - case PROPERTIES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.properties"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.properties"); - icon = new ItemStack(Material.CRAFTING_TABLE); - clickHandler = (panel, user, clickType, slot) -> { - this.currentMenuType = MenuType.PROPERTIES; - this.build(); - - return true; - }; - glow = this.currentMenuType.equals(MenuType.PROPERTIES); - break; - } - case CHALLENGES: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.challenges"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.challenges"); - icon = new ItemStack(Material.RAIL); - clickHandler = (panel, user, clickType, slot) -> { - this.currentMenuType = MenuType.CHALLENGES; - this.build(); - - return true; - }; - glow = this.currentMenuType.equals(MenuType.CHALLENGES); - break; - } - case REWARDS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.rewards"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.rewards"); - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - this.currentMenuType = MenuType.REWARDS; - this.build(); - - return true; - }; - glow = this.currentMenuType.equals(MenuType.REWARDS); - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates given challenge icon. On click it should open Edit Challenge GUI. - * @param challenge Challenge which icon must be created. - * @return PanelItem that represents given challenge. - */ - private PanelItem createChallengeIcon(Challenge challenge) - { - return new PanelItemBuilder(). - name(ChatColor.translateAlternateColorCodes('&', challenge.getFriendlyName())). - description(GuiUtils.stringSplit( - challenge.getDescription(), - this.addon.getChallengesSettings().getLoreLineLength())). - icon(challenge.getIcon()). - clickHandler((panel, user1, clickType, slot) -> { - // Open challenges edit screen. - new EditChallengeGUI(this.addon, - this.world, - this.user, - challenge, - this.topLabel, - this.permissionPrefix, - this).build(); - return true; - }). - glow(!challenge.isDeployed()). - build(); - - } - - - /** - * This method creates buttons for default main menu. - * @param button Button which panel item must be created. - * @return PanelItem that represents given button. - */ - private PanelItem createButton(Button button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - final int lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - - switch (button) - { - case NAME: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.name"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.name-level")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", this.challengeLevel.getFriendlyName())); - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - - this.getFriendlyName(reply -> - { - if (reply != null) - { - this.challengeLevel.setFriendlyName(reply); - } - this.build(); - }, - this.user.getTranslation("challenges.gui.questions.admin.level-name"), - this.challengeLevel.getFriendlyName() - ); - - return true; - }; - glow = false; - break; - } - case ICON: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.icon"); - description = Collections.singletonList(this.user.getTranslation( - "challenges.gui.descriptions.admin.icon-level")); - icon = this.challengeLevel.getIcon(); - clickHandler = (panel, user, clickType, slot) -> { - - new SelectBlocksGUI(this.user, true, (status, materials) -> { - if (status) - { - materials.forEach(material -> - this.challengeLevel.setIcon(new ItemStack(material))); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case CLOSED_ICON: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.locked-icon"); - description = Collections.singletonList(this.user.getTranslation( - "challenges.gui.descriptions.admin.locked-icon")); - - boolean isNull = this.challengeLevel.getLockedIcon() == null; - - if (isNull) - { - icon = new ItemStack(Material.BARRIER); - } - else - { - icon = this.challengeLevel.getLockedIcon().clone(); - } - - clickHandler = (panel, user, clickType, slot) -> { - new SelectBlocksGUI(this.user, true, (status, materials) -> { - if (status) - { - materials.forEach(material -> - this.challengeLevel.setLockedIcon(new ItemStack(material))); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case UNLOCK_MESSAGE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.description"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.description")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", "|" + this.challengeLevel.getUnlockMessage())); - icon = new ItemStack(Material.WRITABLE_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, this.challengeLevel.getUnlockMessage(), lineLength, (status, value) -> { - if (status) - { - String singleLineMessage = value.stream(). - map(s -> s + "|"). - collect(Collectors.joining()); - - if (singleLineMessage.endsWith("|")) - { - singleLineMessage = singleLineMessage.substring(0, singleLineMessage.length() - 1); - } - - this.challengeLevel.setUnlockMessage(singleLineMessage); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case ORDER: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.order"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.order")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challengeLevel.getOrder()))); - icon = new ItemStack(Material.DROPPER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, this.challengeLevel.getOrder(), -1, 54, lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setOrder(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case WAIVER_AMOUNT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.waiver-amount"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.waiver-amount")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challengeLevel.getWaiverAmount()))); - - icon = new ItemStack(Material.REDSTONE_TORCH); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, this.challengeLevel.getWaiverAmount(), 0, lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setWaiverAmount(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case REWARD_DESCRIPTION: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-text"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-text-level")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", "|" + this.challengeLevel.getRewardText())); - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, this.challengeLevel.getRewardText(), lineLength, (status, value) -> { - if (status) - { - String singleLineMessage = value.stream(). - map(s -> s + "|"). - collect(Collectors.joining()); - - if (singleLineMessage.endsWith("|")) - { - singleLineMessage = singleLineMessage.substring(0, singleLineMessage.length() - 1); - } - - this.challengeLevel.setRewardText(singleLineMessage); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_ITEM: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-items"); - - description = new ArrayList<>(this.challengeLevel.getRewardItems().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-items")); - - Utils.groupEqualItems(this.challengeLevel.getRewardItems()).forEach(itemStack -> - description.addAll(this.generateItemStackDescription(itemStack))); - - icon = new ItemStack(Material.CHEST); - clickHandler = (panel, user, clickType, slot) -> { - new ItemSwitchGUI(this.user, this.challengeLevel.getRewardItems(), lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setRewardItems(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_EXPERIENCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-experience"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-experience")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challengeLevel.getRewardExperience()))); - icon = new ItemStack(Material.EXPERIENCE_BOTTLE); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, this.challengeLevel.getRewardExperience(), 0, lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setRewardExperience(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REWARD_MONEY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-money"); - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-money")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.challengeLevel.getRewardMoney()))); - - icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> { - new NumberGUI(this.user, this.challengeLevel.getRewardMoney(), 0, lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setRewardMoney(value); - } - - this.build(); - }); - - return true; - }; - - glow = false; - break; - } - case REWARD_COMMANDS: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reward-commands"); - description = new ArrayList<>(this.challengeLevel.getRewardCommands().size() + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reward-commands")); - - for (String command : this.challengeLevel.getRewardCommands()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.command", - "[command]", command)); - } - - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - new StringListGUI(this.user, this.challengeLevel.getRewardCommands(), lineLength, (status, value) -> { - if (status) - { - this.challengeLevel.setRewardCommands(value); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - - case ADD_CHALLENGE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.add-challenge"); - description = Collections.singletonList(this.user.getTranslation("challenges.gui.descriptions.admin.add-challenge")); - icon = new ItemStack(Material.WATER_BUCKET); - clickHandler = (panel, user, clickType, slot) -> { - ChallengesManager manager = this.addon.getChallengesManager(); - - // Get all challenge that is not in current level. - List challengeList = manager.getAllChallenges(this.world); - challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel)); - - // Generate descriptions for these challenges - Map> challengeDescriptionMap = challengeList.stream(). - collect(Collectors.toMap(challenge -> challenge, - challenge -> this.generateChallengeDescription(challenge, this.user.getPlayer()), - (a, b) -> b, - () -> new LinkedHashMap<>(challengeList.size()))); - - // Open select gui - new SelectChallengeGUI(this.user, challengeDescriptionMap, lineLength, (status, valueSet) -> { - if (status) - { - valueSet.forEach(challenge -> manager.addChallengeToLevel(challenge, this.challengeLevel)); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case REMOVE_CHALLENGE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-challenge"); - description = Collections.singletonList(this.user.getTranslation("challenges.gui.descriptions.admin.remove-challenge")); - icon = new ItemStack(Material.LAVA_BUCKET); - clickHandler = (panel, user, clickType, slot) -> { - ChallengesManager manager = this.addon.getChallengesManager(); - - // Get all challenge that is in current level. - List challengeList = manager.getLevelChallenges(this.challengeLevel); - - // Generate descriptions for these challenges - Map> challengeDescriptionMap = challengeList.stream(). - collect(Collectors.toMap(challenge -> challenge, - challenge -> this.generateChallengeDescription(challenge, this.user.getPlayer()), - (a, b) -> b, - () -> new LinkedHashMap<>(challengeList.size()))); - - // Open select gui - new SelectChallengeGUI(this.user, challengeDescriptionMap, lineLength, (status, valueSet) -> { - if (status) - { - valueSet.forEach(challenge -> manager.removeChallengeFromLevel(challenge, this.challengeLevel)); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - default: - return null; - } - - - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * Represents different buttons that could be in menus. - */ - private enum Button - { - NAME, - ICON, - CLOSED_ICON, - UNLOCK_MESSAGE, - ORDER, - WAIVER_AMOUNT, - - REWARD_DESCRIPTION, - REWARD_ITEM, - REWARD_EXPERIENCE, - REWARD_MONEY, - REWARD_COMMANDS, - - ADD_CHALLENGE, - REMOVE_CHALLENGE - } - - - /** - * Represents different types of menus - */ - private enum MenuType - { - PROPERTIES, - CHALLENGES, - REWARDS - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * This variable holds current challenge level that is in editing GUI. - */ - private ChallengeLevel challengeLevel; - - /** - * Variable holds current active menu. - */ - private MenuType currentMenuType; -} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java new file mode 100644 index 0000000..826e4c8 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java @@ -0,0 +1,998 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.PanelListener; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.managers.ChallengesManager; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.ItemSelector; +import world.bentobox.challenges.panel.util.ChallengeSelector; +import world.bentobox.challenges.panel.util.MultiBlockSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary elements to create Levels Edit GUI. + */ +public class EditLevelPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructors + // --------------------------------------------------------------------- + + + /** + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + * @param challengeLevel ChallengeLevel that must be edited. + */ + private EditLevelPanel(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + ChallengeLevel challengeLevel) + { + super(addon, user, world, topLabel, permissionPrefix); + this.challengeLevel = challengeLevel; + this.currentMenuType = MenuType.PROPERTIES; + } + + + /** + * @param challengeLevel ChallengeLevel that must be edited. + */ + private EditLevelPanel(CommonPanel parentGUI, ChallengeLevel challengeLevel) + { + super(parentGUI); + this.challengeLevel = challengeLevel; + this.currentMenuType = MenuType.PROPERTIES; + } + + + /** + * Open the Challenges Level Edit GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + * @param level - level that needs editing + */ + public static void open(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + ChallengeLevel level) + { + new EditLevelPanel(addon, user, world, topLabel, permissionPrefix, level).build(); + } + + + /** + * Open the Challenges Level Edit GUI. + * + * @param panel - Parent Panel + * @param level - level that needs editing + */ + public static void open(CommonPanel panel, ChallengeLevel level) + { + new EditLevelPanel(panel, level).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + // Do nothing here. + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "edit-level", + "[level]", this.challengeLevel.getFriendlyName())); + + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(2, this.createMenuButton(MenuType.PROPERTIES)); + panelBuilder.item(4, this.createMenuButton(MenuType.REWARDS)); + panelBuilder.item(6, this.createMenuButton(MenuType.CHALLENGES)); + + if (this.currentMenuType.equals(MenuType.PROPERTIES)) + { + this.buildMainPropertiesPanel(panelBuilder); + } + else if (this.currentMenuType.equals(MenuType.CHALLENGES)) + { + this.buildChallengesPanel(panelBuilder); + } + else if (this.currentMenuType.equals(MenuType.REWARDS)) + { + this.buildRewardsPanel(panelBuilder); + } + + panelBuilder.item(44, this.returnButton); + + // Save challenge level every time this gui is build. + // It will ensure that changes are stored in database. + this.addon.getChallengesManager().saveLevel(this.challengeLevel); + + panelBuilder.build(); + } + + + /** + * This class populate LevelsEditGUI with main level settings. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildMainPropertiesPanel(PanelBuilder panelBuilder) + { + panelBuilder.listener(new IconChanger()); + + panelBuilder.item(10, this.createButton(Button.NAME)); + + panelBuilder.item(19, this.createButton(Button.ICON)); + panelBuilder.item(28, this.createButton(Button.LOCKED_ICON)); + panelBuilder.item(22, this.createButton(Button.DESCRIPTION)); + panelBuilder.item(25, this.createButton(Button.ORDER)); + + panelBuilder.item(31, this.createButton(Button.WAIVER_AMOUNT)); + } + + + /** + * This class populate LevelsEditGUI with level rewards. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildRewardsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(12, this.createButton(Button.REWARD_TEXT)); + panelBuilder.item(21, this.createButton(Button.REWARD_COMMANDS)); + + panelBuilder.item(13, this.createButton(Button.REWARD_ITEMS)); + panelBuilder.item(22, this.createButton(Button.REWARD_EXPERIENCE)); + panelBuilder.item(31, this.createButton(Button.REWARD_MONEY)); + + if (!this.challengeLevel.getRewardItems().isEmpty()) + { + panelBuilder.item(33, this.createButton(Button.ADD_IGNORED_META)); + } + + if (!this.challengeLevel.getIgnoreRewardMetaData().isEmpty()) + { + panelBuilder.item(34, this.createButton(Button.REMOVE_IGNORED_META)); + } + } + + + /** + * This class populate LevelsEditGUI with level challenges. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildChallengesPanel(PanelBuilder panelBuilder) + { + List challengeList = this.addon.getChallengesManager(). + getLevelChallenges(this.challengeLevel).stream(). + filter(challenge -> this.searchString.isBlank() || + challenge.getFriendlyName().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getUniqueId().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getChallengeType().name().toLowerCase().contains(this.searchString)). + collect(Collectors.toList()); + + this.populateElements(panelBuilder, challengeList); + + panelBuilder.item(39, this.createButton(Button.ADD_CHALLENGES)); + panelBuilder.item(41, this.createButton(Button.REMOVE_CHALLENGES)); + } + + + // --------------------------------------------------------------------- + // Section: Other methods + // --------------------------------------------------------------------- + + + /** + * This method creates top menu buttons, that allows to switch "tabs". + * @param menuType Menu Type which button must be constructed. + * @return PanelItem that represents given menu type. + */ + private PanelItem createMenuButton(MenuType menuType) + { + final String reference = Constants.BUTTON + menuType.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (menuType) + { + case PROPERTIES -> { + icon = new ItemStack(Material.CRAFTING_TABLE); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.PROPERTIES; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.PROPERTIES); + } + case CHALLENGES -> { + icon = new ItemStack(Material.RAIL); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.CHALLENGES; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.CHALLENGES); + } + case REWARDS -> { + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REWARDS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REWARDS); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates given challenge icon. On click it should open Edit Challenge GUI. + * @param challenge Challenge which icon must be created. + * @return PanelItem that represents given challenge. + */ + @Override + protected PanelItem createElementButton(Challenge challenge) + { + return new PanelItemBuilder(). + name(Util.translateColorCodes(challenge.getFriendlyName())). + description(this.generateChallengeDescription(challenge, null)). + description(""). + description(this.user.getTranslation(Constants.TIPS + "click-to-edit")). + icon(challenge.getIcon()). + clickHandler((panel, user, clickType, slot) -> { + // Open challenges edit screen. + EditChallengePanel.open(this, challenge); + return true; + }). + glow(!challenge.isDeployed()). + build(); + } + + + /** + * This method creates buttons for default main menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case REWARD_TEXT -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + description.add(Util.translateColorCodes(this.challengeLevel.getRewardText())); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challengeLevel.setRewardText(String.join("\n", value)); + } + + this.build(); + }; + + if (!this.challengeLevel.getRewardText().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-reward-text"), + user.getTranslation(Constants.CONVERSATIONS + "reward-text-changed")); + } + + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challengeLevel.getRewardText().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case REWARD_ITEMS -> { + + if (this.challengeLevel.getRewardItems().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + Utils.groupEqualItems(this.challengeLevel.getRewardItems(), this.challengeLevel.getIgnoreRewardMetaData()). + stream(). + sorted(Comparator.comparing(ItemStack::getType)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[number]", String.valueOf(itemStack.getAmount()), + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + ItemSelector.open(this.user, + this.challengeLevel.getRewardItems(), + (status, value) -> { + if (status) + { + this.challengeLevel.setRewardItems(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_EXPERIENCE -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challengeLevel.getRewardExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challengeLevel.setRewardExperience(number.intValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_MONEY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challengeLevel.getRewardMoney()))); + icon = new ItemStack(this.addon.isEconomyProvided() ? Material.GOLD_INGOT : Material.BARRIER); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challengeLevel.setRewardMoney(number.doubleValue()); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + Double.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REWARD_COMMANDS -> { + icon = new ItemStack(Material.COMMAND_BLOCK); + + description.add(this.user.getTranslation(reference + "value")); + description.addAll(this.challengeLevel.getRewardCommands()); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challengeLevel.setRewardCommands(value); + } + + this.build(); + }; + + if (!this.challengeLevel.getRewardCommands().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-reward-commands"), + user.getTranslation(Constants.CONVERSATIONS + "reward-commands-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challengeLevel.getRewardCommands().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case ADD_IGNORED_META -> { + if (this.challengeLevel.getIgnoreRewardMetaData().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + this.challengeLevel.getIgnoreRewardMetaData().stream(). + sorted(Comparator.comparing(Material::name)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.GREEN_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (this.challengeLevel.getRewardItems().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + this.challengeLevel.getRewardItems().stream(). + map(ItemStack::getType). + forEach(collection::remove); + collection.addAll(this.challengeLevel.getIgnoreRewardMetaData()); + + if (Material.values().length == collection.size()) + { + // If all materials are blocked, then do not allow to open gui. + return true; + } + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + materials.addAll(this.challengeLevel.getIgnoreRewardMetaData()); + this.challengeLevel.setIgnoreRewardMetaData(new HashSet<>(materials)); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_IGNORED_META -> { + icon = new ItemStack(Material.RED_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (this.challengeLevel.getIgnoreRewardMetaData().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + collection.removeAll(this.challengeLevel.getIgnoreRewardMetaData()); + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + this.challengeLevel.getIgnoreRewardMetaData().removeAll(materials); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + case NAME -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NAME, this.challengeLevel.getFriendlyName())); + + icon = new ItemStack(Material.NAME_TAG); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer consumer = value -> + { + if (value != null) + { + this.challengeLevel.setFriendlyName(value); + } + + this.build(); + }; + + // start conversation + ConversationUtils.createStringInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-name"), + user.getTranslation(Constants.CONVERSATIONS + "name-changed")); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case DESCRIPTION -> { + icon = new ItemStack(Material.WRITTEN_BOOK); + + description.add(this.user.getTranslation(reference + "value")); + description.add(Util.translateColorCodes(this.challengeLevel.getUnlockMessage())); + + clickHandler = (panel, user, clickType, i) -> + { + // Create consumer that process description change + Consumer> consumer = value -> + { + if (value != null) + { + this.challengeLevel.setUnlockMessage(String.join("\n", value)); + } + + this.build(); + }; + + if (!this.challengeLevel.getUnlockMessage().isEmpty() && clickType.isShiftClick()) + { + // Reset to the empty value + consumer.accept(Collections.emptyList()); + } + else + { + // start conversation + ConversationUtils.createStringListInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-description"), + user.getTranslation(Constants.CONVERSATIONS + "description-changed")); + } + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + + if (!this.challengeLevel.getUnlockMessage().isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "shift-click-to-reset")); + } + } + case ICON, LOCKED_ICON -> { + icon = button == Button.LOCKED_ICON ? + this.challengeLevel.getLockedIcon() : + this.challengeLevel.getIcon(); + + clickHandler = (panel, user, clickType, i) -> + { + this.selectedButton = button; + this.build(); + return true; + }; + + if (this.selectedButton != button) + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-on-item")); + } + + glow = this.selectedButton == button; + } + case ORDER -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challengeLevel.getOrder()))); + + icon = new ItemStack(Material.HOPPER, Math.max(1, this.challengeLevel.getOrder())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challengeLevel.setOrder(number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case WAIVER_AMOUNT -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.challengeLevel.getWaiverAmount()))); + + icon = new ItemStack(Material.HOPPER, Math.max(1, this.challengeLevel.getWaiverAmount())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.challengeLevel.setWaiverAmount(number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case ADD_CHALLENGES -> { + icon = new ItemStack(Material.WATER_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ChallengesManager manager = this.addon.getChallengesManager(); + + // Get all challenge that is not in current level. + List challengeList = manager.getAllChallenges(this.world); + challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel)); + + // Generate descriptions for these challenges + Map> challengeDescriptionMap = challengeList.stream(). + collect(Collectors.toMap(challenge -> challenge, + challenge -> this.generateChallengeDescription(challenge, null), + (a, b) -> b, + () -> new LinkedHashMap<>(challengeList.size()))); + + // Open select gui + ChallengeSelector.open(this.user, + Material.BLUE_STAINED_GLASS_PANE, + challengeDescriptionMap, + (status, valueSet) -> { + if (status) + { + valueSet.forEach(challenge -> + manager.addChallengeToLevel(challenge, this.challengeLevel)); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_CHALLENGES -> { + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ChallengesManager manager = this.addon.getChallengesManager(); + + // Get all challenge that is in current level. + List challengeList = manager.getLevelChallenges(this.challengeLevel); + + // Generate descriptions for these challenges + Map> challengeDescriptionMap = challengeList.stream(). + collect(Collectors.toMap(challenge -> challenge, + challenge -> this.generateChallengeDescription(challenge, null), + (a, b) -> b, + () -> new LinkedHashMap<>(challengeList.size()))); + + // Open select gui + ChallengeSelector.open(this.user, + Material.RED_STAINED_GLASS_PANE, + challengeDescriptionMap, + (status, valueSet) -> { + if (status) + { + valueSet.forEach(challenge -> + manager.removeChallengeFromLevel(challenge, this.challengeLevel)); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + // --------------------------------------------------------------------- + // Section: Classes + // --------------------------------------------------------------------- + + + /** + * This class allows changing icon for Generator Tier + */ + private class IconChanger implements PanelListener + { + /** + * Process inventory click. If generator icon is selected and user clicks on item in his inventory, then change + * icon to the item from inventory. + * + * @param user the user + * @param event the event + */ + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // Handle icon changing + if (EditLevelPanel.this.selectedButton != null && + event.getCurrentItem() != null && + !event.getCurrentItem().getType().equals(Material.AIR) && + event.getRawSlot() > 44) + { + // set material and amount only. Other data should be removed. + + if (EditLevelPanel.this.selectedButton == Button.ICON) + { + EditLevelPanel.this.challengeLevel.setIcon(event.getCurrentItem().clone()); + // Deselect icon + EditLevelPanel.this.selectedButton = null; + // Rebuild icon + EditLevelPanel.this.build(); + } + else if (EditLevelPanel.this.selectedButton == Button.LOCKED_ICON) + { + EditLevelPanel.this.challengeLevel.setLockedIcon(event.getCurrentItem().clone()); + // Deselect icon + EditLevelPanel.this.selectedButton = null; + // Rebuild icon + EditLevelPanel.this.build(); + } + } + } + + + /** + * On inventory close. + * + * @param event the event + */ + @Override + public void onInventoryClose(InventoryCloseEvent event) + { + // Do nothing + } + + + /** + * Setup current listener. + */ + @Override + public void setup() + { + // Do nothing + } + } + + + // --------------------------------------------------------------------- + // Section: Enums + // --------------------------------------------------------------------- + + + /** + * Represents different buttons that could be in menus. + */ + private enum Button + { + NAME, + ICON, + LOCKED_ICON, + DESCRIPTION, + ORDER, + WAIVER_AMOUNT, + + REWARD_TEXT, + REWARD_ITEMS, + REWARD_EXPERIENCE, + REWARD_MONEY, + REWARD_COMMANDS, + + ADD_IGNORED_META, + REMOVE_IGNORED_META, + + ADD_CHALLENGES, + REMOVE_CHALLENGES + } + + + /** + * Represents different types of menus + */ + private enum MenuType + { + PROPERTIES, + CHALLENGES, + REWARDS + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * This variable holds current challenge level that is in editing GUI. + */ + private final ChallengeLevel challengeLevel; + + /** + * Variable holds current active menu. + */ + private MenuType currentMenuType; + + private Button selectedButton; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java deleted file mode 100644 index 860de1a..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java +++ /dev/null @@ -1,638 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import org.bukkit.Material; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.Nullable; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.PanelListener; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.config.SettingsUtils.ChallengeLore; -import world.bentobox.challenges.config.SettingsUtils.LevelLore; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class allows to change Input ItemStacks to different ItemStacks. - */ -public class EditLoreGUI extends CommonGUI -{ - public EditLoreGUI(CommonGUI parent, LoreType loreType) - { - super(parent); - - this.lore = loreType; - this.activeValues = new ArrayList<>(); - - switch (this.lore) - { - case CHALLENGES: - - for (ChallengeLore lore : this.addon.getChallengesSettings().getChallengeLoreMessage()) - { - this.activeValues.add(lore.name()); - } - - break; - case LEVELS: - - for (LevelLore lore : this.addon.getChallengesSettings().getLevelLoreMessage()) - { - this.activeValues.add(lore.name()); - } - - break; - } - } - - - /** - * This is static call method for easier GUI opening. - * @param parent Parent GUI. - * @param loreType loreType that will be edited. - */ - public static void open(CommonGUI parent, LoreType loreType) - { - new EditLoreGUI(parent, loreType).build(); - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * This method builds panel that allows to change given number value. - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(this.user.getTranslation("challenges.gui.title.admin.lore-edit")). - user(this.user). - listener(new CustomPanelListener()); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - // Define all active buttons - panelBuilder.item(1, this.getButton(Button.SAVE)); - - panelBuilder.item(3, this.getButton(Button.ADD)); - panelBuilder.item(4, this.getButton(Button.REMOVE)); - - // TODO: Need 2 View Buttons - // One for closes / One for opened. - // panelBuilder.item(6, this.getButton(Button.VIEW)); - - panelBuilder.item(44, this.returnButton); - - // necessary as I have a border around this GUI - int currentIndex = 10; - - // Only 21 elements will be displayed. On porpoise! - for (int i = 0; i < this.activeValues.size() || i > 21; i++) - { - panelBuilder.item(currentIndex++, this.getLoreButton(this.activeValues.get(i))); - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - } - - panelBuilder.build(); - } - - - /** - * This method create button that does some functionality in current gui. - * @param button Button functionality. - * @return PanelItem. - */ - private PanelItem getButton(Button button) - { - ItemStack icon; - String name; - List description; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case SAVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.save"); - description = Collections.emptyList(); - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - - switch (this.lore) - { - case CHALLENGES: - { - List lore = this.activeValues.stream(). - map(ChallengeLore::valueOf). - collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size()))); - - this.addon.getChallengesSettings().setChallengeLoreMessage(lore); - - break; - } - case LEVELS: - { - List lore = this.activeValues.stream(). - map(LevelLore::valueOf). - collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size()))); - - this.addon.getChallengesSettings().setLevelLoreMessage(lore); - - break; - } - } - - // Save and return to parent gui. - this.parentGUI.build(); - - return true; - }; - break; - } - case ADD: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.add"); - description = Collections.emptyList(); - icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - new AddLoreElementGUI(element -> { - this.activeValues.add(element); - this.build(); - }); - - return true; - }; - - break; - } - case REMOVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-selected"); - description = Collections.emptyList(); - icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - new RemoveLoreElementGUI((element, index) -> { - if (this.activeValues.get(index).equals(element)) - { - this.activeValues.remove(element); - } - - this.build(); - }); - - return true; - }; - - break; - } - case VIEW: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.view"); - description = Collections.emptyList(); - icon = new ItemStack(Material.YELLOW_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - return true; - }; - - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - glow(false). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates button for lore element. - * @param loreElement String that represents current lore element. - * @return PanelItem. - */ - @Nullable - private PanelItem getLoreButton(String loreElement) - { - switch (this.lore) - { - case CHALLENGES: - return this.getChallengeLoreButton(loreElement); - case LEVELS: - return this.getLevelLoreButton(loreElement); - default: - // this should never happen! - return null; - } - } - - - /** - * This method creates button for challenge lore element. - * @param loreElement String that represents current challenge lore element. - * @return PanelItem. - */ - private PanelItem getChallengeLoreButton(String loreElement) - { - Material icon; - String name = loreElement; - List description = new ArrayList<>(); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "lore." + loreElement.toLowerCase())); - - PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> true; - - switch (ChallengeLore.valueOf(loreElement)) - { - case LEVEL: - { - icon = Material.DIRT; - break; - } - case STATUS: - { - icon = Material.LEVER; - break; - } - case COUNT: - { - icon = Material.REPEATER; - break; - } - case DESCRIPTION: - { - icon = Material.WRITTEN_BOOK; - break; - } - case WARNINGS: - { - icon = Material.LAVA_BUCKET; - break; - } - case ENVIRONMENT: - { - icon = Material.GLASS; - break; - } - case REQUIREMENTS: - { - icon = Material.HOPPER; - break; - } - case REWARD_TEXT: - { - icon = Material.PAPER; - break; - } - case REWARD_OTHER: - { - icon = Material.CHEST; - break; - } - case REWARD_ITEMS: - { - icon = Material.TRAPPED_CHEST; - break; - } - case REWARD_COMMANDS: - { - icon = Material.COMMAND_BLOCK; - break; - } - default: - { - icon = Material.BARRIER; - break; - } - } - - return new PanelItemBuilder(). - name(name). - icon(icon). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - clickHandler(clickHandler). - glow(false). - build(); - } - - - /** - * This method creates button for challenge level lore element. - * @param loreElement String that represents current challenge level lore element. - * @return PanelItem. - */ - private PanelItem getLevelLoreButton(String loreElement) - { - Material icon; - String name = loreElement; - List description = new ArrayList<>(); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "lore." + loreElement.toLowerCase())); - - PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> true; - - switch (LevelLore.valueOf(loreElement)) - { - case LEVEL_STATUS: - { - icon = Material.DIRT; - break; - } - case CHALLENGE_COUNT: - { - icon = Material.REPEATER; - break; - } - case UNLOCK_MESSAGE: - { - icon = Material.WRITTEN_BOOK; - break; - } - case WAIVER_AMOUNT: - { - icon = Material.COMPARATOR; - break; - } - case LEVEL_REWARD_TEXT: - { - icon = Material.PAPER; - break; - } - case LEVEL_REWARD_OTHER: - { - icon = Material.CHEST; - break; - } - case LEVEL_REWARD_ITEMS: - { - icon = Material.TRAPPED_CHEST; - break; - } - case LEVEL_REWARD_COMMANDS: - { - icon = Material.COMMAND_BLOCK; - break; - } - default: - { - icon = Material.BARRIER; - break; - } - } - - return new PanelItemBuilder(). - name(name). - icon(icon). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - clickHandler(clickHandler). - glow(false). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Select GUI - // --------------------------------------------------------------------- - - - /** - * This class opens new GUI that add an element from all available lore values. - */ - private class AddLoreElementGUI - { - private AddLoreElementGUI(Consumer selectedElement) - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(EditLoreGUI.this.user.getTranslation("challenges.gui.title.admin.lore-add")). - user(EditLoreGUI.this.user); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - int currentIndex = 10; - - List values = new ArrayList<>(); - - // Populate list with all elements. - switch (EditLoreGUI.this.lore) - { - case CHALLENGES: - for (ChallengeLore value : ChallengeLore.values()) - { - values.add(value.name()); - } - break; - case LEVELS: - for (LevelLore value : LevelLore.values()) - { - values.add(value.name()); - } - break; - } - - for (String value : values) - { - PanelItem item = EditLoreGUI.this.getLoreButton(value); - if (item != null) { - item.setClickHandler((panel, user1, clickType, slot) -> { - selectedElement.accept(value); - return true; - }); - - panelBuilder.item(currentIndex++, item); - - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - - // Just in case. Should never occur. - if (currentIndex > 35) - { - break; - } - } - } - - panelBuilder.build(); - } - } - - - /** - * This class opens new GUI that remove an element from all available lore values. - */ - private class RemoveLoreElementGUI - { - private RemoveLoreElementGUI(BiConsumer selectedElement) - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(EditLoreGUI.this.user.getTranslation("challenges.gui.title.admin.lore-remove")). - user(EditLoreGUI.this.user); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - int currentIndex = 10; - - List values = EditLoreGUI.this.activeValues; - - for (int i = 0; i < values.size(); i++) - { - final int counter = i; - - String value = values.get(counter); - PanelItem item = EditLoreGUI.this.getLoreButton(value); - if (item != null) { - item.setClickHandler((panel, user1, clickType, slot) -> { - selectedElement.accept(value, counter); - return true; - }); - - panelBuilder.item(currentIndex++, item); - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - - // Just in case. Should never occur. - if (currentIndex > 35) - { - break; - } - } - } - - panelBuilder.build(); - } - } - - - // --------------------------------------------------------------------- - // Section: Private classes - // --------------------------------------------------------------------- - - - /** - * This CustomPanelListener allows to move items in current panel. - */ - private class CustomPanelListener implements PanelListener - { - @Override - public void setup() - { - } - - - @Override - public void onInventoryClose(InventoryCloseEvent inventoryCloseEvent) - { - } - - - @Override - public void onInventoryClick(User user, InventoryClickEvent event) - { - // First row of elements should be ignored, as it contains buttons and blocked slots. - event.setCancelled(event.getRawSlot() < 9 || - event.getRawSlot() < 35 || - event.getRawSlot() % 9 == 0 || - event.getRawSlot() % 9 == 8); - } - } - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * This enum holds all button values in current gui. - */ - private enum Button - { - SAVE, - ADD, - REMOVE, - VIEW, - RETURN - } - - - /** - * This enum holds which Lore is edited with current GUI. - */ - public enum LoreType - { - CHALLENGES, - LEVELS, - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * Lore that will be edited with current GUI. - */ - private final LoreType lore; - - /** - * List of lore elements that are currently enabled. - */ - private List activeValues; - - - // --------------------------------------------------------------------- - // Section: Constants - // --------------------------------------------------------------------- - - - private final static String REFERENCE_DESCRIPTION = "challenges.gui.descriptions.admin."; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java deleted file mode 100644 index c808dac..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java +++ /dev/null @@ -1,606 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.config.Settings; -import world.bentobox.challenges.config.SettingsUtils.GuiMode; -import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.NumberGUI; -import world.bentobox.challenges.panel.util.SelectBlocksGUI; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.utils.Utils; - - -/** - * This Class creates GUI that allows to change Challenges Addon Settings via in-game - * menu. - */ -public class EditSettingsGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructors - // --------------------------------------------------------------------- - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - */ - public EditSettingsGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix) - { - this(addon, world, user, topLabel, permissionPrefix, null); - } - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - */ - public EditSettingsGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - super(addon, world, user, topLabel, permissionPrefix, parentGUI); - this.settings = this.addon.getChallengesSettings(); - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.settings-title")); - - GuiUtils.fillBorder(panelBuilder); - - panelBuilder.item(10, this.getSettingsButton(Button.ENABLE_TITLE)); - - if (this.settings.isShowCompletionTitle()) - { - panelBuilder.item(19, this.getSettingsButton(Button.TITLE_SHOWTIME)); - } - - panelBuilder.item(28, this.getSettingsButton(Button.BROADCAST)); - - panelBuilder.item(11, this.getSettingsButton(Button.GLOW_COMPLETED)); - panelBuilder.item(20, this.getSettingsButton(Button.REMOVE_COMPLETED)); - panelBuilder.item(29, this.getSettingsButton(Button.VISIBILITY_MODE)); - - panelBuilder.item(21, this.getSettingsButton(Button.LOCKED_LEVEL_ICON)); - panelBuilder.item(30, this.getSettingsButton(Button.FREE_AT_TOP)); - - panelBuilder.item(22, this.getSettingsButton(Button.GAMEMODE_GUI)); - - if (this.settings.isUseCommonGUI()) - { - // This should be active only when single gui is enabled. - panelBuilder.item(31, this.getSettingsButton(Button.GAMEMODE_GUI_VIEW_MODE)); - } - - panelBuilder.item(14, this.getSettingsButton(Button.LORE_LENGTH)); - panelBuilder.item(23, this.getSettingsButton(Button.CHALLENGE_LORE)); - panelBuilder.item(32, this.getSettingsButton(Button.LEVEL_LORE)); - - panelBuilder.item(24, this.getSettingsButton(Button.HISTORY)); - - if (this.settings.isStoreHistory()) - { - panelBuilder.item(33, this.getSettingsButton(Button.PURGE_HISTORY)); - } - - panelBuilder.item(25, this.getSettingsButton(Button.RESET_CHALLENGES)); - panelBuilder.item(34, this.getSettingsButton(Button.STORE_MODE)); - - // Return Button - panelBuilder.item(44, this.returnButton); - - // Save Settings every time this GUI is created. It will avoid issues with - // Overwritten setting after server stop. - this.addon.saveSettings(); - - panelBuilder.build(); - } - - - private PanelItem getSettingsButton(Button button) - { - ItemStack icon; - String name; - List description; - boolean glow; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case RESET_CHALLENGES: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.reset-on-new")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isResetChallenges() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.reset-on-new"); - icon = new ItemStack(Material.LAVA_BUCKET); - - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setResetChallenges( - !this.settings.isResetChallenges()); - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - - glow = this.settings.isResetChallenges(); - break; - } - case BROADCAST: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.broadcast")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isBroadcastMessages() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - description = new ArrayList<>(2); - - name = this.user.getTranslation("challenges.gui.buttons.admin.broadcast"); - icon = new ItemStack(Material.JUKEBOX); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setBroadcastMessages( - !this.settings.isBroadcastMessages()); - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isBroadcastMessages(); - - break; - } - case REMOVE_COMPLETED: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-completed")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isRemoveCompleteOneTimeChallenges() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-completed"); - icon = new ItemStack(Material.MAGMA_BLOCK); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setRemoveCompleteOneTimeChallenges( - !this.settings.isRemoveCompleteOneTimeChallenges()); - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isRemoveCompleteOneTimeChallenges(); - - break; - } - case LORE_LENGTH: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.line-length")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.settings.getLoreLineLength()))); - name = this.user.getTranslation("challenges.gui.buttons.admin.line-length"); - icon = new ItemStack(Material.ANVIL); - clickHandler = (panel, user1, clickType, i) -> { - new NumberGUI(this.user, - this.settings.getLoreLineLength(), - 0, - this.settings.getLoreLineLength(), - (status, value) -> { - if (status) - { - this.settings.setLoreLineLength(value); - } - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - }); - - return true; - }; - glow = false; - break; - } - case LEVEL_LORE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.level-lore")); - name = this.user.getTranslation("challenges.gui.buttons.admin.level-lore"); - icon = new ItemStack(Material.MAP); - clickHandler = (panel, user1, clickType, i) -> { - - EditLoreGUI.open(this, EditLoreGUI.LoreType.LEVELS); - - return true; - }; - glow = false; - break; - } - case CHALLENGE_LORE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.challenge-lore")); - name = this.user.getTranslation("challenges.gui.buttons.admin.challenge-lore"); - icon = new ItemStack(Material.PAPER); - clickHandler = (panel, user1, clickType, i) -> { - - EditLoreGUI.open(this, EditLoreGUI.LoreType.CHALLENGES); - - return true; - }; - glow = false; - break; - } - case FREE_AT_TOP: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.free-at-top")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isAddCompletedGlow() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.free-at-top"); - icon = new ItemStack(Material.FILLED_MAP); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setFreeChallengesFirst(!this.settings.isFreeChallengesFirst()); - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isFreeChallengesFirst(); - break; - } - case GLOW_COMPLETED: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.glow")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isAddCompletedGlow() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - - name = this.user.getTranslation("challenges.gui.buttons.admin.glow"); - icon = new ItemStack(Material.GLOWSTONE); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setAddCompletedGlow(!this.settings.isAddCompletedGlow()); - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isAddCompletedGlow(); - break; - } - case GAMEMODE_GUI_VIEW_MODE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.gui-view-mode")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST) ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.gui-view-mode"); - icon = new ItemStack(Material.STONE_BUTTON); - clickHandler = (panel, user1, clickType, i) -> { - - if (this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST)) - { - this.settings.setUserGuiMode(GuiMode.CURRENT_WORLD); - } - else - { - this.settings.setUserGuiMode(GuiMode.GAMEMODE_LIST); - } - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST); - break; - } - case GAMEMODE_GUI: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.gui-mode")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isUseCommonGUI() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.gui-mode"); - icon = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setUseCommonGUI(!this.settings.isUseCommonGUI()); - // We cannot use single item changing as this option enabling/disabling will change other - // option visibility. - this.build(); - return true; - }; - glow = this.settings.isUseCommonGUI(); - break; - } - case HISTORY: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.history-store")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isStoreHistory() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.history-store"); - icon = new ItemStack(Material.WRITTEN_BOOK); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setStoreHistory(!this.settings.isStoreHistory()); - - // Need to rebuild all as new buttons will show up. - this.build(); - return true; - }; - glow = this.settings.isStoreHistory(); - break; - } - case PURGE_HISTORY: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.history-lifespan")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.settings.getLifeSpan()))); - name = this.user.getTranslation("challenges.gui.buttons.admin.history-lifespan"); - icon = new ItemStack(Material.FLINT_AND_STEEL); - clickHandler = (panel, user1, clickType, i) -> { - new NumberGUI(this.user, - this.settings.getLifeSpan(), - 0, - this.settings.getLoreLineLength(), - (status, value) -> { - if (status) - { - this.settings.setLifeSpan(value); - } - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - }); - - return true; - }; - glow = false; - break; - } - case STORE_MODE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.island-store")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isStoreAsIslandData() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.island-store"); - icon = new ItemStack(Material.GRASS_BLOCK); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setStoreAsIslandData(!this.settings.isStoreAsIslandData()); - // TODO: Data Migration must be added here. - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - return true; - }; - glow = this.settings.isStoreAsIslandData(); - break; - } - case LOCKED_LEVEL_ICON: - { - description = new ArrayList<>(1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.default-locked-icon")); - name = this.user.getTranslation("challenges.gui.buttons.admin.default-locked-icon"); - icon = this.settings.getLockedLevelIcon(); - clickHandler = (panel, user, clickType, slot) -> { - - new SelectBlocksGUI(this.user, true, (status, materials) -> { - if (status) - { - materials.forEach(material -> - this.settings.setLockedLevelIcon(new ItemStack(material))); - } - - this.build(); - }); - - return true; - }; - glow = false; - break; - } - case ENABLE_TITLE: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.title-enable")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.settings.isShowCompletionTitle() ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - name = this.user.getTranslation("challenges.gui.buttons.admin.title-enable"); - icon = new ItemStack(Material.OAK_SIGN); - clickHandler = (panel, user1, clickType, i) -> { - this.settings.setShowCompletionTitle(!this.settings.isShowCompletionTitle()); - - // Need to rebuild all as new buttons will show up. - this.build(); - return true; - }; - glow = this.settings.isShowCompletionTitle(); - break; - } - case TITLE_SHOWTIME: - { - description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.title-showtime")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", Integer.toString(this.settings.getTitleShowtime()))); - name = this.user.getTranslation("challenges.gui.buttons.admin.title-showtime"); - icon = new ItemStack(Material.CLOCK); - clickHandler = (panel, user1, clickType, i) -> { - new NumberGUI(this.user, - this.settings.getTitleShowtime(), - 0, - this.settings.getLoreLineLength(), - (status, value) -> { - if (status) - { - this.settings.setTitleShowtime(value); - } - - panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); - }); - - return true; - }; - glow = false; - break; - } - case VISIBILITY_MODE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.visibility-mode"); - - List values = new ArrayList<>(5); - values.add(this.user.getTranslation("challenges.gui.descriptions.admin.visibility-mode")); - - values.add((this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.visibility.visible")); - values.add((this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.visibility.hidden")); - values.add((this.settings.getVisibilityMode().equals(VisibilityMode.TOGGLEABLE) ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.visibility.toggleable")); - - values.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]",this.settings.getVisibilityMode().name())); - - description = values; - - if (this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE)) - { - icon = new ItemStack(Material.OAK_PLANKS); - } - else if (this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN)) - { - icon = new ItemStack(Material.OAK_SLAB); - } - else - { - icon = new ItemStack(Material.OAK_BUTTON); - } - - clickHandler = (panel, user, clickType, slot) -> { - if (clickType.isRightClick()) - { - this.settings.setVisibilityMode( - Utils.getPreviousValue(VisibilityMode.values(), - this.settings.getVisibilityMode())); - } - else - { - this.settings.setVisibilityMode( - Utils.getNextValue(VisibilityMode.values(), - this.settings.getVisibilityMode())); - } - - // Rebuild just this icon - panel.getInventory().setItem(slot, - this.getSettingsButton(button).getItem()); - - return true; - }; - glow = false; - break; - } - default: - return new PanelItemBuilder().build(); - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.settings.getLoreLineLength())). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - - /** - * This enum holds all settings buttons that must have been displayed in this panel. - */ - private enum Button - { - RESET_CHALLENGES, - BROADCAST, - REMOVE_COMPLETED, - LORE_LENGTH, - LEVEL_LORE, - CHALLENGE_LORE, - FREE_AT_TOP, - GAMEMODE_GUI_VIEW_MODE, - GAMEMODE_GUI, - HISTORY, - PURGE_HISTORY, - STORE_MODE, - GLOW_COMPLETED, - LOCKED_LEVEL_ICON, - ENABLE_TITLE, - TITLE_SHOWTIME, - - /** - * This allows to switch between different challenges visibility modes. - */ - VISIBILITY_MODE - } - - - /** - * This allows faster access to challenges settings object. - */ - private Settings settings; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java new file mode 100644 index 0000000..3d0eed9 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java @@ -0,0 +1,576 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.PanelListener; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.config.SettingsUtils.GuiMode; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This Class creates GUI that allows to change Challenges Addon Settings via in-game + * menu. + */ +public class EditSettingsPanel extends CommonPanel +{ + // --------------------------------------------------------------------- + // Section: Constructors + // --------------------------------------------------------------------- + + + /** + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + */ + private EditSettingsPanel(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + this.settings = this.addon.getChallengesSettings(); + } + + + /** + * @param parentGUI Parent GUI. + */ + private EditSettingsPanel(CommonPanel parentGUI) + { + super(parentGUI); + this.settings = this.addon.getChallengesSettings(); + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + new EditSettingsPanel(addon, user, world, topLabel, permissionPrefix).build(); + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI) + { + new EditSettingsPanel(parentGUI).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "settings")); + + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(10, this.getSettingsButton(Button.SHOW_TITLE)); + + if (this.settings.isShowCompletionTitle()) + { + panelBuilder.item(19, this.getSettingsButton(Button.TITLE_SHOWTIME)); + } + + panelBuilder.item(28, this.getSettingsButton(Button.BROADCAST)); + + panelBuilder.item(11, this.getSettingsButton(Button.GLOW_COMPLETED)); + panelBuilder.item(20, this.getSettingsButton(Button.REMOVE_COMPLETED)); + panelBuilder.item(29, this.getSettingsButton(Button.VISIBILITY_MODE)); + + panelBuilder.item(21, this.getSettingsButton(Button.LOCKED_LEVEL_ICON)); + + panelBuilder.item(22, this.getSettingsButton(Button.GAMEMODE_GUI)); + + if (this.settings.isUseCommonGUI()) + { + // This should be active only when single gui is enabled. + panelBuilder.item(31, this.getSettingsButton(Button.ACTIVE_WORLD_LIST)); + } + + panelBuilder.item(24, this.getSettingsButton(Button.STORE_HISTORY)); + + if (this.settings.isStoreHistory()) + { + panelBuilder.item(33, this.getSettingsButton(Button.PURGE_HISTORY)); + } + + panelBuilder.item(25, this.getSettingsButton(Button.RESET_ON_NEW)); + panelBuilder.item(34, this.getSettingsButton(Button.DATA_PER_ISLAND)); + + // Return Button + panelBuilder.item(44, this.returnButton); + panelBuilder.listener(new IconChanger()); + panelBuilder.build(); + } + + + private PanelItem getSettingsButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case RESET_ON_NEW -> { + description.add(this.user.getTranslation(reference + + (this.settings.isResetChallenges() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setResetChallenges(!this.settings.isResetChallenges()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isResetChallenges(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case BROADCAST -> { + description.add(this.user.getTranslation(reference + + (this.settings.isBroadcastMessages() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.JUKEBOX); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setBroadcastMessages(!this.settings.isBroadcastMessages()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isBroadcastMessages(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case REMOVE_COMPLETED -> { + description.add(this.user.getTranslation(reference + + (this.settings.isRemoveCompleteOneTimeChallenges() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.MAGMA_BLOCK); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setRemoveCompleteOneTimeChallenges(!this.settings.isRemoveCompleteOneTimeChallenges()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isRemoveCompleteOneTimeChallenges(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case ACTIVE_WORLD_LIST -> { + description.add(this.user.getTranslation(reference + + (this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST) ? + "disabled" : "enabled"))); + + icon = new ItemStack(Material.STONE_BUTTON); + clickHandler = (panel, user1, clickType, i) -> { + if (this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST)) + { + this.settings.setUserGuiMode(GuiMode.CURRENT_WORLD); + } + else + { + this.settings.setUserGuiMode(GuiMode.GAMEMODE_LIST); + } + + this.addon.saveSettings(); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + return true; + }; + glow = this.settings.getUserGuiMode().equals(GuiMode.GAMEMODE_LIST); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case GAMEMODE_GUI -> { + description.add(this.user.getTranslation(reference + + (this.settings.isUseCommonGUI() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setUseCommonGUI(!this.settings.isUseCommonGUI()); + // Need to rebuild more icons + this.build(); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isUseCommonGUI(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case STORE_HISTORY -> { + description.add(this.user.getTranslation(reference + + (this.settings.isStoreHistory() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setStoreHistory(!this.settings.isStoreHistory()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isStoreHistory(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case PURGE_HISTORY -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.settings.getLifeSpan()))); + + icon = new ItemStack(Material.FLINT_AND_STEEL, Math.max(1, this.settings.getLifeSpan())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.settings.setLifeSpan(number.intValue()); + this.addon.saveSettings(); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case DATA_PER_ISLAND -> { + description.add(this.user.getTranslation(reference + + (this.settings.isStoreAsIslandData() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.GRASS_BLOCK); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setStoreAsIslandData(!this.settings.isStoreAsIslandData()); + // TODO: Migration + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isStoreAsIslandData(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case GLOW_COMPLETED -> { + description.add(this.user.getTranslation(reference + + (this.settings.isAddCompletedGlow() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.GLOWSTONE); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setAddCompletedGlow(!this.settings.isAddCompletedGlow()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isAddCompletedGlow(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case LOCKED_LEVEL_ICON -> { + icon = this.settings.getLockedLevelIcon(); + + clickHandler = (panel, user, clickType, i) -> + { + if (this.selectedButton != null) + { + this.selectedButton = null; + } + else + { + this.selectedButton = button; + } + + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + return true; + }; + + if (this.selectedButton != button) + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-on-item")); + } + + glow = this.selectedButton == button; + } + case SHOW_TITLE -> { + description.add(this.user.getTranslation(reference + + (this.settings.isShowCompletionTitle() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.OAK_SIGN); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setShowCompletionTitle(!this.settings.isShowCompletionTitle()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isShowCompletionTitle(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case TITLE_SHOWTIME -> { + description.add(this.user.getTranslation(reference + "value", + Constants.PARAMETER_NUMBER, String.valueOf(this.settings.getTitleShowtime()))); + + icon = new ItemStack(Material.CLOCK, Math.max(1, this.settings.getTitleShowtime())); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) + { + this.settings.setTitleShowtime(number.intValue()); + this.addon.saveSettings(); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 0, + 2000); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case VISIBILITY_MODE -> { + description.add(this.user.getTranslation(reference + + (this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE) ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "visible")); + description.add(this.user.getTranslation(reference + + (this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN) ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "hidden")); + description.add(this.user.getTranslation(reference + + (this.settings.getVisibilityMode().equals(VisibilityMode.TOGGLEABLE) ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "toggleable")); + + if (this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE)) + { + icon = new ItemStack(Material.OAK_PLANKS); + } + else if (this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN)) + { + icon = new ItemStack(Material.OAK_SLAB); + } + else + { + icon = new ItemStack(Material.OAK_BUTTON); + } + + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.settings.setVisibilityMode(Utils.getPreviousValue(VisibilityMode.values(), + this.settings.getVisibilityMode())); + } + else + { + this.settings.setVisibilityMode(Utils.getNextValue(VisibilityMode.values(), + this.settings.getVisibilityMode())); + } + + // Rebuild just this icon + panel.getInventory().setItem(slot, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-cycle")); + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-cycle")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + glow(glow). + clickHandler(clickHandler). + build(); + } + + + // --------------------------------------------------------------------- + // Section: Classes + // --------------------------------------------------------------------- + + + /** + * This class allows changing icon for Generator Tier + */ + private class IconChanger implements PanelListener + { + /** + * Process inventory click. If generator icon is selected and user clicks on item in his inventory, then change + * icon to the item from inventory. + * + * @param user the user + * @param event the event + */ + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // Handle icon changing + if (EditSettingsPanel.this.selectedButton != null && + event.getCurrentItem() != null && + !event.getCurrentItem().getType().equals(Material.AIR) && + event.getRawSlot() > 44) + { + // set material and amount only. Other data should be removed. + + if (EditSettingsPanel.this.selectedButton == Button.LOCKED_LEVEL_ICON) + { + EditSettingsPanel.this.settings.setLockedLevelIcon(event.getCurrentItem().clone()); + EditSettingsPanel.this.addon.saveSettings(); + + // Deselect icon + EditSettingsPanel.this.selectedButton = null; + EditSettingsPanel.this.build(); + } + } + } + + + /** + * On inventory close. + * + * @param event the event + */ + @Override + public void onInventoryClose(InventoryCloseEvent event) + { + // Do nothing + } + + + /** + * Setup current listener. + */ + @Override + public void setup() + { + // Do nothing + } + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * This enum holds all settings buttons that must have been displayed in this panel. + */ + private enum Button + { + RESET_ON_NEW, + BROADCAST, + REMOVE_COMPLETED, + ACTIVE_WORLD_LIST, + GAMEMODE_GUI, + STORE_HISTORY, + PURGE_HISTORY, + DATA_PER_ISLAND, + GLOW_COMPLETED, + LOCKED_LEVEL_ICON, + SHOW_TITLE, + TITLE_SHOWTIME, + /** + * This allows to switch between different challenges visibility modes. + */ + VISIBILITY_MODE + } + + + /** + * This allows faster access to challenges settings object. + */ + private final Settings settings; + + /** + * Allows changing locked level icon. + */ + private Button selectedButton; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java new file mode 100644 index 0000000..cfa10d3 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java @@ -0,0 +1,471 @@ +package world.bentobox.challenges.panel.admin; + + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.scheduler.BukkitTask; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.PanelListener; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; +import world.bentobox.challenges.web.object.LibraryEntry; + + +/** + * This class contains all necessary elements to create GUI that lists all challenges. + * It allows to edit them or remove, depending on given input mode. + */ +public class LibraryPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- + + /** + * @param parentGUI ParentGUI object. + */ + private LibraryPanel(CommonPanel parentGUI, Library mode) + { + super(parentGUI); + + this.mode = mode; + + this.libraryEntries = switch (mode) + { + case WEB -> this.addon.getWebManager().getLibraryEntries(); + case DATABASE -> this.generateDatabaseEntries(); + case TEMPLATE -> this.generateTemplateEntries(); + }; + + this.filterElements = this.libraryEntries; + } + + + /** + * This static method allows to easier open Library GUI. + * @param parentGui ParentGUI object. + * @param mode Library view mode. + */ + public static void open(CommonPanel parentGui, Library mode) + { + new LibraryPanel(parentGui, mode).build(); + } + + +// --------------------------------------------------------------------- +// Section: Data Collectors +// --------------------------------------------------------------------- + + + /** + * This method generates list of database file entries. + * + * @return List of entries for database files. + */ + private List generateDatabaseEntries() + { + File localeDir = this.addon.getDataFolder(); + File[] files = localeDir.listFiles(pathname -> + pathname.getName().endsWith(".json") && pathname.isFile()); + + if (files == null || files.length == 0) + { + // No + return Collections.emptyList(); + } + + return Arrays.stream(files). + map(file -> LibraryEntry.fromTemplate( + file.getName().substring(0, file.getName().length() - 5), + Material.PAPER)). + collect(Collectors.toList()); + } + + + /** + * This method generates list of template file entries. + * + * @return List of entries for template files. + */ + private List generateTemplateEntries() + { + File localeDir = this.addon.getDataFolder(); + File[] files = localeDir.listFiles(pathname -> + pathname.getName().endsWith(".yml") && + pathname.isFile() && + !pathname.getName().equals("config.yml")); + + if (files == null || files.length == 0) + { + // No + return Collections.emptyList(); + } + + return Arrays.stream(files). + map(file -> LibraryEntry.fromTemplate( + file.getName().substring(0, file.getName().length() - 4), + Material.PAPER)). + collect(Collectors.toList()); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.libraryEntries; + } + else + { + this.filterElements = this.libraryEntries.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()) || + element.author().toLowerCase().contains(this.searchString.toLowerCase()) || + element.gameMode().toLowerCase().contains(this.searchString.toLowerCase()) || + element.language().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + if (this.libraryEntries.isEmpty()) + { + Utils.sendMessage(this.user, this.user.getTranslation( + Constants.ERRORS + "no-library-entries")); + return; + } + + // No point to display. Single element. + if (this.libraryEntries.size() == 1 && !this.mode.equals(Library.WEB)) + { + this.generateConfirmationInput(this.libraryEntries.get(0)); + return; + } + + + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "library")); + + PanelUtils.fillBorder(panelBuilder); + + this.populateElements(panelBuilder, this.filterElements); + + if (this.mode == Library.WEB) + { + panelBuilder.item(4, this.createDownloadNow()); + } + + panelBuilder.item(44, this.returnButton); + + panelBuilder.listener(new DownloadCanceller()); + + panelBuilder.build(); + } + + + /** + * This creates download now button, that can skip waiting for automatic request. + * @return PanelItem button that allows to manually download libraries. + */ + private PanelItem createDownloadNow() + { + final String reference = Constants.BUTTON + "download."; + + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + description.add(this.user.getTranslation(reference + + (this.clearCache ? "enabled" : "disabled"))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-download")); + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-toggle")); + + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name")). + description(description). + icon(Material.COBWEB). + glow(this.clearCache); + + itemBuilder.clickHandler((panel, user1, clickType, slot) -> + { + if (clickType.isRightClick()) + { + this.clearCache = !this.clearCache; + panel.getInventory().setItem(slot, this.createDownloadNow().getItem()); + } + else + { + this.addon.getWebManager().requestCatalogGitHubData(this.clearCache); + + // Fix multiclick issue. + if (this.updateTask != null) + { + this.updateTask.cancel(); + } + + // add some delay to rebuilding gui. + this.updateTask = this.addon.getPlugin().getServer().getScheduler().runTaskLater( + this.addon.getPlugin(), + this::build, + 100L); + } + + return true; + }); + + return itemBuilder.build(); + } + + + /** + * This method creates button for given library entry. + * @param libraryEntry LibraryEntry which button must be created. + * @return Entry button. + */ + @Override + protected PanelItem createElementButton(LibraryEntry libraryEntry) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(ChatColor.translateAlternateColorCodes('&', libraryEntry.name())). + description(this.generateEntryDescription(libraryEntry)). + description(""). + description(this.user.getTranslation(Constants.TIPS + "click-to-install")). + icon(libraryEntry.icon()). + glow(false); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + this.generateConfirmationInput(libraryEntry); + return true; + }); + + return itemBuilder.build(); + } + + + /** + * This method generates consumer and calls ConversationAPI for confirmation that processes file downloading, + * importing and gui opening or closing. + * + * @param libraryEntry Entry that must be processed. + */ + private void generateConfirmationInput(LibraryEntry libraryEntry) + { + Consumer consumer = value -> + { + if (value) + { + switch (this.mode) + { + case TEMPLATE -> { + this.addon.getImportManager().importFile(this.user, + this.world, + libraryEntry.name()); + + CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this); + } + case DATABASE -> { + this.addon.getImportManager().importDatabaseFile(this.user, + this.world, + libraryEntry.name()); + + CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this); + } + case WEB -> { + if (!this.blockedForDownland) + { + this.blockedForDownland = true; + + Utils.sendMessage(this.user, this.user.getTranslation( + Constants.MESSAGES + "start-downloading")); + + // Run download task after 5 ticks. + this.updateTask = this.addon.getPlugin().getServer().getScheduler(). + runTaskLaterAsynchronously( + this.addon.getPlugin(), + () -> this.addon.getWebManager().requestEntryGitHubData(this.user, + this.world, + libraryEntry), + 5L); + } + + CommonPanel.reopen(this.parentPanel != null ? this.parentPanel : this); + } + } + } + + if (this.mode.equals(Library.WEB) || this.libraryEntries.size() > 1) + { + this.build(); + } + }; + + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-data-replacement", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world)), + this.user.getTranslation(Constants.CONVERSATIONS + "new-challenges-imported", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world))); + } + + + /** + * This method generated description for LibraryEntry object. + * @param entry LibraryEntry object which description must be generated. + * @return List of strings that will be placed in ItemStack lore message. + */ + private List generateEntryDescription(LibraryEntry entry) + { + final String reference = Constants.DESCRIPTIONS + "library."; + + List description = new ArrayList<>(); + + description.add(this.user.getTranslation(reference + "author", + "[author]", entry.author())); + description.add(entry.description()); + + description.add(this.user.getTranslation(reference + "gamemode", + "[gamemode]", entry.gameMode())); + description.add(this.user.getTranslation(reference + "lang", + "[lang]", entry.language())); + description.add(this.user.getTranslation(reference + "version", + "[version]", entry.version())); + + return description; + } + + + /** + * This class allows changing icon for Generator Tier + */ + private class DownloadCanceller implements PanelListener + { + /** + * On inventory click. + * + * @param user the user + * @param event the event + */ + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // do nothing + } + + + /** + * On inventory close. + * + * @param event the event + */ + @Override + public void onInventoryClose(InventoryCloseEvent event) + { + if (LibraryPanel.this.updateTask != null) + { + LibraryPanel.this.updateTask.cancel(); + } + } + + + /** + * Setup current listener. + */ + @Override + public void setup() + { + // Do nothing + } + } + + + /** + * Enum that holds different view modes for current panel. + */ + public enum Library + { + /** + * Mode for templates available in main folder. + */ + TEMPLATE, + /** + * Mode for database files available in main folder. + */ + DATABASE, + /** + * Mode for web library. + */ + WEB + } + + +// --------------------------------------------------------------------- +// Section: Instance Variables +// --------------------------------------------------------------------- + + /** + * Indicates if download now button should trigger cache clearing. + */ + private boolean clearCache; + + /** + * Stores update task that is triggered. + */ + private BukkitTask updateTask = null; + + /** + * This variable will protect against spam-click. + */ + private boolean blockedForDownland; + + /** + * Stores active library that must be searched. + */ + private final Library mode; + + /** + * List of library elements. + */ + private final List libraryEntries; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java deleted file mode 100644 index ccdb1cf..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java +++ /dev/null @@ -1,207 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.ConfirmationGUI; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class contains all necessary elements to create GUI that lists all challenges. - * It allows to edit them or remove, depending on given input mode. - */ -public class ListChallengesGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructor - // --------------------------------------------------------------------- - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param mode - mode that indicate what should do icon clicking. - */ - public ListChallengesGUI(ChallengesAddon addon, - World world, - User user, - Mode mode, - String topLabel, - String permissionPrefix) - { - this(addon, world, user, mode, topLabel, permissionPrefix, null); - } - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param mode - mode that indicate what should do icon clicking. - */ - public ListChallengesGUI(ChallengesAddon addon, - World world, - User user, - Mode mode, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - super(addon, world, user, topLabel, permissionPrefix, parentGUI); - this.currentMode = mode; - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * {@inheritDoc} - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.choose-challenge-title")); - - if (this.currentMode.equals(Mode.DELETE)) - { - GuiUtils.fillBorder(panelBuilder, Material.RED_STAINED_GLASS_PANE); - } - else - { - GuiUtils.fillBorder(panelBuilder); - } - - List challengeList = this.addon.getChallengesManager().getAllChallenges(this.world); - - final int MAX_ELEMENTS = 21; - - if (this.pageIndex < 0) - { - this.pageIndex = challengeList.size() / MAX_ELEMENTS; - } - else if (this.pageIndex > (challengeList.size() / MAX_ELEMENTS)) - { - this.pageIndex = 0; - } - - int challengeIndex = MAX_ELEMENTS * this.pageIndex; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (challengeIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && - challengeIndex < challengeList.size() && - index < 36) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createChallengeIcon(challengeList.get(challengeIndex++))); - } - - index++; - } - - // Navigation buttons only if necessary - if (challengeList.size() > MAX_ELEMENTS) - { - panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); - panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); - } - - panelBuilder.item(44, this.returnButton); - - panelBuilder.build(); - } - - - /** - * This method creates button for given challenge. - * @param challenge Challenge which button must be created. - * @return Challenge button. - */ - private PanelItem createChallengeIcon(Challenge challenge) - { - PanelItemBuilder itemBuilder = new PanelItemBuilder(). - name(ChatColor.translateAlternateColorCodes('&', challenge.getFriendlyName())). - description(GuiUtils.stringSplit(this.generateChallengeDescription(challenge, this.user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength())). - icon(challenge.getIcon()). - glow(challenge.isDeployed()); - - if (this.currentMode.equals(Mode.EDIT)) - { - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - new EditChallengeGUI(this.addon, - this.world, - this.user, - challenge, - this.topLabel, - this.permissionPrefix, - this).build(); - return true; - }); - } - else if (this.currentMode.equals(Mode.DELETE)) - { - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - new ConfirmationGUI(this.user, value -> { - if (value) - { - this.addon.getChallengesManager().deleteChallenge(challenge); - } - - this.build(); - }); - return true; - }); - } - - return itemBuilder.build(); - } - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * Mode in which gui icons should processed. - */ - public enum Mode - { - EDIT, - DELETE - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * Current mode in which icons will act. - */ - private Mode currentMode; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesPanel.java new file mode 100644 index 0000000..3e047a7 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesPanel.java @@ -0,0 +1,225 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.World; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary elements to create GUI that lists all challenges. + * It allows to edit them or remove, depending on given input mode. + */ +public class ListChallengesPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- + + + /** + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + * @param mode - mode that indicate what should do icon clicking. + */ + private ListChallengesPanel(ChallengesAddon addon, + World world, + User user, + Mode mode, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + this.currentMode = mode; + } + + + /** + * @param mode - mode that indicate what should do icon clicking. + */ + private ListChallengesPanel(CommonPanel parentGUI, Mode mode) + { + super(parentGUI); + this.currentMode = mode; + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix, + Mode mode) + { + new ListChallengesPanel(addon, world, user, mode, topLabel, permissionPrefix).build(); + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Mode mode) + { + new ListChallengesPanel(parentGUI, mode).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + // Do nothing here. + } + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "choose-challenge")); + + if (this.currentMode.equals(Mode.DELETE)) + { + PanelUtils.fillBorder(panelBuilder, Material.RED_STAINED_GLASS_PANE); + } + else + { + PanelUtils.fillBorder(panelBuilder); + } + + List challengeList = this.addon.getChallengesManager().getAllChallenges(this.world). + stream(). + filter(challenge -> this.searchString.isBlank() || + challenge.getFriendlyName().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getUniqueId().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getChallengeType().name().toLowerCase().contains(this.searchString)). + collect(Collectors.toList()); + + this.populateElements(panelBuilder, challengeList); + + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates button for given challenge. + * @param challenge Challenge which button must be created. + * @return Challenge button. + */ + @Override + protected PanelItem createElementButton(Challenge challenge) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(Util.translateColorCodes(challenge.getFriendlyName())). + description(this.generateChallengeDescription(challenge, null)). + icon(challenge.getIcon()). + glow(!challenge.isDeployed()); + + if (this.currentMode.equals(Mode.EDIT)) + { + itemBuilder.description(""); + itemBuilder.description(this.user.getTranslation(Constants.TIPS + "click-to-edit")); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + EditChallengePanel.open(this, challenge); + return true; + }); + } + else if (this.currentMode.equals(Mode.DELETE)) + { + itemBuilder.description(""); + itemBuilder.description(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().deleteChallenge(challenge); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-challenge-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world), + Constants.PARAMETER_CHALLENGE, challenge.getFriendlyName()), + this.user.getTranslation(Constants.CONVERSATIONS + "challenge-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world), + Constants.PARAMETER_CHALLENGE, challenge.getFriendlyName())); + return true; + }); + } + + return itemBuilder.build(); + } + + + // --------------------------------------------------------------------- + // Section: Enums + // --------------------------------------------------------------------- + + + /** + * Mode in which gui icons should processed. + */ + public enum Mode + { + EDIT, + DELETE + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * Current mode in which icons will act. + */ + private final Mode currentMode; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java deleted file mode 100644 index 4a9dad5..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java +++ /dev/null @@ -1,209 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.database.object.ChallengeLevel; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.ConfirmationGUI; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class creates GUI that lists all Levels. Clicking on Level icon will be processed - * by input mode. - */ -public class ListLevelsGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructor - // --------------------------------------------------------------------- - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param mode - mode that indicate what should do icon clicking. - */ - public ListLevelsGUI(ChallengesAddon addon, - World world, - User user, - Mode mode, - String topLabel, - String permissionPrefix) - { - this(addon, world, user, mode, topLabel, permissionPrefix, null); - } - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param mode - mode that indicate what should do icon clicking. - */ - public ListLevelsGUI(ChallengesAddon addon, - World world, - User user, - Mode mode, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - super(addon, world, user, topLabel, permissionPrefix, parentGUI); - this.currentMode = mode; - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * {@inheritDoc} - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.choose-level-title")); - - if (this.currentMode.equals(Mode.DELETE)) - { - GuiUtils.fillBorder(panelBuilder, Material.RED_STAINED_GLASS_PANE); - } - else - { - GuiUtils.fillBorder(panelBuilder); - } - - List levelList = this.addon.getChallengesManager().getLevels(this.world); - - final int MAX_ELEMENTS = 21; - - if (this.pageIndex < 0) - { - this.pageIndex = levelList.size() / MAX_ELEMENTS; - } - else if (this.pageIndex > (levelList.size() / MAX_ELEMENTS)) - { - this.pageIndex = 0; - } - - int levelIndex = MAX_ELEMENTS * this.pageIndex; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (levelIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && - levelIndex < levelList.size() && - index < 36) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createLevelIcon(levelList.get(levelIndex++))); - } - - index++; - } - - // Navigation buttons only if necessary - if (levelList.size() > MAX_ELEMENTS) - { - panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); - panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); - } - - panelBuilder.item(44, this.returnButton); - - panelBuilder.build(); - } - - - /** - * This method creates button for given level - * @param challengeLevel Level which button must be created. - * @return Level button. - */ - private PanelItem createLevelIcon(ChallengeLevel challengeLevel) - { - PanelItemBuilder itemBuilder = new PanelItemBuilder(). - name(ChatColor.translateAlternateColorCodes('&', challengeLevel.getFriendlyName())). - description(GuiUtils.stringSplit( - this.generateLevelDescription(challengeLevel, this.user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength())). - icon(challengeLevel.getIcon()). - glow(false); - - if (this.currentMode.equals(Mode.EDIT)) - { - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - new EditLevelGUI(this.addon, - this.world, - this.user, - challengeLevel, - this.topLabel, - this.permissionPrefix, - this).build(); - return true; - }); - } - else if (this.currentMode.equals(Mode.DELETE)) - { - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - new ConfirmationGUI(this.user, value -> { - if (value) - { - this.addon.getChallengesManager(). - deleteChallengeLevel(challengeLevel); - } - - this.build(); - }); - return true; - }); - } - - return itemBuilder.build(); - } - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * Mode in which gui icons should processed. - */ - public enum Mode - { - EDIT, - DELETE - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * Current mode in which icons will act. - */ - private Mode currentMode; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsPanel.java new file mode 100644 index 0000000..9b344f5 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsPanel.java @@ -0,0 +1,224 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Material; +import org.bukkit.World; + +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class creates GUI that lists all Levels. Clicking on Level icon will be processed + * by input mode. + */ +public class ListLevelsPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructor + // --------------------------------------------------------------------- + + + /** + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + * @param mode - mode that indicate what should do icon clicking. + */ + private ListLevelsPanel(ChallengesAddon addon, + World world, + User user, + Mode mode, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + this.currentMode = mode; + } + + + /** + * @param mode - mode that indicate what should do icon clicking. + */ + private ListLevelsPanel(CommonPanel parentGUI, Mode mode) + { + super(parentGUI); + this.currentMode = mode; + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix, + Mode mode) + { + new ListLevelsPanel(addon, world, user, mode, topLabel, permissionPrefix).build(); + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Mode mode) + { + new ListLevelsPanel(parentGUI, mode).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + // Do nothing here. + } + + + /** + * {@inheritDoc} + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "choose-level")); + + if (this.currentMode.equals(Mode.DELETE)) + { + PanelUtils.fillBorder(panelBuilder, Material.RED_STAINED_GLASS_PANE); + } + else + { + PanelUtils.fillBorder(panelBuilder); + } + + List levelList = this.addon.getChallengesManager().getLevels(this.world). + stream(). + filter(challenge -> this.searchString.isBlank() || + challenge.getFriendlyName().toLowerCase().contains(this.searchString.toLowerCase()) || + challenge.getUniqueId().toLowerCase().contains(this.searchString.toLowerCase())). + collect(Collectors.toList()); + + this.populateElements(panelBuilder, levelList); + + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates button for given level + * @param challengeLevel Level which button must be created. + * @return Level button. + */ + @Override + protected PanelItem createElementButton(ChallengeLevel challengeLevel) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(Util.translateColorCodes(challengeLevel.getFriendlyName())). + description(this.generateLevelDescription(challengeLevel)). + icon(challengeLevel.getIcon()); + + if (this.currentMode.equals(Mode.EDIT)) + { + itemBuilder.description(""); + itemBuilder.description(this.user.getTranslation(Constants.TIPS + "click-to-edit")); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + EditLevelPanel.open(this, challengeLevel); + return true; + }); + } + else if (this.currentMode.equals(Mode.DELETE)) + { + itemBuilder.description(""); + itemBuilder.description(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + Consumer consumer = value -> { + if (value) + { + this.addon.getChallengesManager().deleteChallengeLevel(challengeLevel); + } + + this.build(); + }; + + // Create conversation that gets user acceptance to delete generator data. + ConversationUtils.createConfirmation( + consumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "confirm-level-deletion", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world), + Constants.PARAMETER_LEVEL, challengeLevel.getFriendlyName()), + this.user.getTranslation(Constants.CONVERSATIONS + "level-removed", + Constants.PARAMETER_GAMEMODE, Utils.getGameMode(this.world), + Constants.PARAMETER_LEVEL, challengeLevel.getFriendlyName())); + return true; + }); + } + + return itemBuilder.build(); + } + + + // --------------------------------------------------------------------- + // Section: Enums + // --------------------------------------------------------------------- + + + /** + * Mode in which gui icons should processed. + */ + public enum Mode + { + EDIT, + DELETE + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * Current mode in which icons will act. + */ + private final Mode currentMode; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java deleted file mode 100644 index d153da3..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListLibraryGUI.java +++ /dev/null @@ -1,313 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.scheduler.BukkitTask; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.web.object.LibraryEntry; - - -/** - * This class contains all necessary elements to create GUI that lists all challenges. - * It allows to edit them or remove, depending on given input mode. - */ -public class ListLibraryGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Constructor - // --------------------------------------------------------------------- - - /** - * @param parentGUI ParentGUI object. - */ - public ListLibraryGUI(CommonGUI parentGUI) - { - super(parentGUI); - } - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - */ - public ListLibraryGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix) - { - super(addon, world, user, topLabel, permissionPrefix, null); - } - - - /** - * This static method allows to easier open Library GUI. - * @param parentGui ParentGUI object. - */ - public static void open(CommonGUI parentGui) - { - new ListLibraryGUI(parentGui).build(); - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - /** - * {@inheritDoc} - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.library-title")); - - GuiUtils.fillBorder(panelBuilder); - - List libraryEntries = this.addon.getWebManager().getLibraryEntries(); - - final int MAX_ELEMENTS = 21; - - if (this.pageIndex < 0) - { - this.pageIndex = libraryEntries.size() / MAX_ELEMENTS; - } - else if (this.pageIndex > (libraryEntries.size() / MAX_ELEMENTS)) - { - this.pageIndex = 0; - } - - int entryIndex = MAX_ELEMENTS * this.pageIndex; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (entryIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && - entryIndex < libraryEntries.size() && - index < 36) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createEntryIcon(libraryEntries.get(entryIndex++))); - } - - index++; - } - - // Navigation buttons only if necessary - if (libraryEntries.size() > MAX_ELEMENTS) - { - panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); - panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); - } - - panelBuilder.item(4, this.createDownloadNow()); - panelBuilder.item(44, this.createReturnButton()); - - panelBuilder.build(); - } - - - /** - * This creates download now button, that can skip waiting for automatic request. - * @return PanelItem button that allows to manually download libraries. - */ - private PanelItem createDownloadNow() - { - List description = new ArrayList<>(2); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.download")); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", - "[value]", - this.clearCache ? - this.user.getTranslation("challenges.gui.descriptions.enabled") : - this.user.getTranslation("challenges.gui.descriptions.disabled"))); - - PanelItemBuilder itemBuilder = new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.admin.download")). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - icon(Material.HOPPER). - glow(this.clearCache); - - itemBuilder.clickHandler((panel, user1, clickType, slot) -> - { - if (clickType.isRightClick()) - { - this.clearCache = !this.clearCache; - panel.getInventory().setItem(slot, this.createDownloadNow().getItem()); - } - else - { - this.addon.getWebManager().requestCatalogGitHubData(this.clearCache); - - // Fix multiclick issue. - if (this.updateTask != null) - { - this.updateTask.cancel(); - } - - // add some delay to rebuilding gui. - this.updateTask = this.addon.getPlugin().getServer().getScheduler().runTaskLater( - this.addon.getPlugin(), - this::build, - 100L); - } - - return true; - }); - - return itemBuilder.build(); - } - - - /** - * This creates return button, that allows to exist or return to parent gui, - * @return PanelItem for return button. - */ - private PanelItem createReturnButton() - { - return new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.return")). - icon(Material.OAK_DOOR). - clickHandler((panel, user1, clickType, i) -> { - if (this.updateTask != null) - { - this.updateTask.cancel(); - } - - if (this.parentGUI == null) - { - this.user.closeInventory(); - return true; - } - - this.parentGUI.build(); - return true; - }).build(); - } - - - /** - * This method creates button for given library entry. - * @param libraryEntry LibraryEntry which button must be created. - * @return Entry button. - */ - private PanelItem createEntryIcon(LibraryEntry libraryEntry) - { - PanelItemBuilder itemBuilder = new PanelItemBuilder(). - name(ChatColor.translateAlternateColorCodes('&', libraryEntry.getName())). - description(this.generateEntryDescription(libraryEntry)). - icon(libraryEntry.getIcon()). - glow(false); - - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - - if (!this.blockedForDownland) - { - this.blockedForDownland = true; - - this.user.sendMessage("challenges.messages.admin.start-downloading"); - - // Run download task after 5 ticks. - this.addon.getPlugin().getServer().getScheduler(). - runTaskLaterAsynchronously( - this.addon.getPlugin(), - () -> this.addon.getWebManager().requestEntryGitHubData(this.user, this.world, libraryEntry), - 5L); - - if (this.parentGUI != null) - { - if (this.updateTask != null) - { - this.updateTask.cancel(); - } - - this.parentGUI.build(); - } - else - { - if (this.updateTask != null) - { - this.updateTask.cancel(); - } - - this.user.closeInventory(); - } - } - - return true; - }); - - return itemBuilder.build(); - } - - - /** - * This method generated description for LibraryEntry object. - * @param entry LibraryEntry object which description must be generated. - * @return List of strings that will be placed in ItemStack lore message. - */ - private List generateEntryDescription(LibraryEntry entry) - { - List description = new ArrayList<>(); - - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "library-author", - "[author]", - entry.getAuthor())); - description.add(entry.getDescription()); - - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "library-gamemode", - "[gamemode]", - entry.getForGameMode())); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "library-lang", - "[lang]", - entry.getLanguage())); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "library-version", - "[version]", - entry.getVersion())); - - return GuiUtils.stringSplit(description, - this.addon.getChallengesSettings().getLoreLineLength()); - } - - -// --------------------------------------------------------------------- -// Section: Instance Variables -// --------------------------------------------------------------------- - - /** - * Indicates if download now button should trigger cache clearing. - */ - private boolean clearCache; - - /** - * Stores update task that is triggered. - */ - private BukkitTask updateTask = null; - - /** - * This variable will protect against spam-click. - */ - private boolean blockedForDownland; - - /** - * Reference string to description. - */ - private static final String REFERENCE_DESCRIPTION = "challenges.gui.descriptions.admin."; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java deleted file mode 100644 index e22eead..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java +++ /dev/null @@ -1,334 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.database.objects.Players; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.ConfirmationGUI; -import world.bentobox.challenges.panel.util.SelectChallengeGUI; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class contains methods that allows to select specific user. - */ -public class ListUsersGUI extends CommonGUI -{ - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * List with players that should be in GUI. - */ - private List onlineUsers; - - /** - * Current operation mode. - */ - private Mode operationMode; - - /** - * Current index of view mode - */ - private int modeIndex = 2; - - /** - * This allows to switch which users should be in the list. - */ - private enum ViewMode - { - ONLINE, - WITH_ISLAND, - IN_WORLD - } - - /** - * This allows to decide what User Icon should do. - */ - public enum Mode - { - COMPLETE, - RESET, - RESET_ALL - } - - - // --------------------------------------------------------------------- - // Section: Constructors - // --------------------------------------------------------------------- - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param operationMode Indicate what should happen on player icon click. - */ - public ListUsersGUI(ChallengesAddon addon, - World world, - User user, - Mode operationMode, - String topLabel, - String permissionPrefix) - { - this(addon, world, user, operationMode, topLabel, permissionPrefix, null); - } - - - /** - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - * @param operationMode Indicate what should happen on player icon click. - */ - public ListUsersGUI(ChallengesAddon addon, - World world, - User user, - Mode operationMode, - String topLabel, - String permissionPrefix, - CommonGUI parentPanel) - { - super(addon, world, user, topLabel, permissionPrefix, parentPanel); - this.onlineUsers = this.collectUsers(ViewMode.IN_WORLD); - this.operationMode = operationMode; - } - - - // --------------------------------------------------------------------- - // Section: Methods - // --------------------------------------------------------------------- - - - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( - this.user.getTranslation("challenges.gui.title.admin.choose-user-title")); - - GuiUtils.fillBorder(panelBuilder); - - final int MAX_ELEMENTS = 21; - - if (this.pageIndex < 0) - { - this.pageIndex = this.onlineUsers.size() / MAX_ELEMENTS; - } - else if (this.pageIndex > (this.onlineUsers.size() / MAX_ELEMENTS)) - { - this.pageIndex = 0; - } - - int playerIndex = MAX_ELEMENTS * this.pageIndex; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (playerIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && - playerIndex < this.onlineUsers.size() && - index < 36) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createPlayerIcon(this.onlineUsers.get(playerIndex++))); - } - - index++; - } - - // Add button that allows to toggle different player lists. - panelBuilder.item( 4, this.createToggleButton()); - - // Navigation buttons only if necessary - if (this.onlineUsers.size() > MAX_ELEMENTS) - { - panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); - panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); - } - - panelBuilder.item(44, this.returnButton); - - - panelBuilder.build(); - } - - - /** - * This method creates button for given user. If user has island it will add valid click handler. - * @param player Player which button must be created. - * @return Player button. - */ - private PanelItem createPlayerIcon(Player player) - { - int lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - - if (this.addon.getIslands().getIsland(this.world, player.getUniqueId()) != null) - { - return new PanelItemBuilder().name(player.getName()).icon(player.getName()).clickHandler( - (panel, user1, clickType, slot) -> { - ChallengesManager manager = this.addon.getChallengesManager(); - Map> challengeDescriptionMap; - - switch (this.operationMode) - { - case COMPLETE: - challengeDescriptionMap = new LinkedHashMap<>(); - - for (Challenge challenge : manager.getAllChallenges(this.world)) - { - if (!manager.isChallengeComplete(player.getUniqueId(), this.world, challenge)) - { - challengeDescriptionMap.put(challenge, this.generateChallengeDescription(challenge, player)); - } - } - - new SelectChallengeGUI(this.user, challengeDescriptionMap, lineLength, (status, valueSet) -> { - if (status) - { - valueSet.forEach(challenge -> manager.setChallengeComplete(player.getUniqueId(), this.world, challenge, this.user.getUniqueId())); - } - - this.build(); - }); - break; - case RESET: - challengeDescriptionMap = new LinkedHashMap<>(); - - for (Challenge challenge : manager.getAllChallenges(this.world)) - { - if (manager.isChallengeComplete(player.getUniqueId(), this.world, challenge)) - { - challengeDescriptionMap.put(challenge, this.generateChallengeDescription(challenge, player)); - } - } - - new SelectChallengeGUI(this.user, challengeDescriptionMap, lineLength, (status, valueSet) -> { - if (status) - { - valueSet.forEach(challenge -> manager.resetChallenge(player.getUniqueId(), this.world, challenge, this.user.getUniqueId())); - } - - this.build(); - }); - break; - case RESET_ALL: - new ConfirmationGUI(this.user, status -> { - if (status) - { - manager.resetAllChallenges(player.getUniqueId(), this.world, this.user.getUniqueId()); - } - - this.build(); - }); - break; - } - - return true; - }).build(); - } - else - { - return new PanelItemBuilder(). - name(player.getName()). - icon(Material.BARRIER). - description(GuiUtils.stringSplit(this.user.getTranslation("general.errors.player-has-no-island"), lineLength)). - clickHandler((panel, user1, clickType, slot) -> false). - build(); - } - } - - - /** - * This method collects users based on view mode. - * @param mode Given view mode. - * @return List with players in necessary view mode. - */ - private List collectUsers(ViewMode mode) - { - if (mode.equals(ViewMode.ONLINE)) - { - return new ArrayList<>(Bukkit.getOnlinePlayers()); - } - else if (mode.equals(ViewMode.WITH_ISLAND)) - { - return this.addon.getPlayers().getPlayers().stream(). - filter(player -> this.addon.getIslands().getIsland(this.world, player.getPlayerUUID()) != null). - map(Players::getPlayer). - collect(Collectors.toList()); - } - else - { - return new ArrayList<>(this.world.getPlayers()); - } - } - - - /** - * This method creates Player List view Mode toggle button. - * @return Button that toggles through player view mode. - */ - private PanelItem createToggleButton() - { - List description = new ArrayList<>(ViewMode.values().length + 1); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.toggle-user-list")); - description.add((ViewMode.ONLINE == ViewMode.values()[this.modeIndex] ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.admin.mode-online")); - description.add((ViewMode.WITH_ISLAND == ViewMode.values()[this.modeIndex] ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.admin.mode-in-world")); - description.add((ViewMode.IN_WORLD == ViewMode.values()[this.modeIndex] ? "&2" : "&c") + - this.user.getTranslation("challenges.gui.descriptions.admin.mode-with-island")); - - return new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.admin.toggle-user-list")). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - icon(Material.STONE_BUTTON). - clickHandler( - (panel, user1, clickType, slot) -> { - if (clickType.isRightClick()) - { - this.modeIndex--; - - if (this.modeIndex < 0) - { - this.modeIndex = ViewMode.values().length - 1; - } - } - else - { - this.modeIndex++; - - if (this.modeIndex >= ViewMode.values().length) - { - this.modeIndex = 0; - } - } - - this.onlineUsers = this.collectUsers(ViewMode.values()[this.modeIndex]); - this.pageIndex = 0; - this.build(); - return true; - }).build(); - } -} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListUsersPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersPanel.java new file mode 100644 index 0000000..1fb3596 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersPanel.java @@ -0,0 +1,408 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.database.objects.Players; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.util.ChallengeSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains methods that allows to select specific user. + */ +public class ListUsersPanel extends CommonPagedPanel +{ + // --------------------------------------------------------------------- + // Section: Constructors + // --------------------------------------------------------------------- + + + /** + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + * @param operationMode Indicate what should happen on player icon click. + */ + private ListUsersPanel(ChallengesAddon addon, + User user, + World world, + String topLabel, + String permissionPrefix, + Mode operationMode) + { + super(addon, user, world, topLabel, permissionPrefix); + this.onlineUsers = this.collectUsers(ViewMode.IN_WORLD); + this.operationMode = operationMode; + this.filterElements = this.onlineUsers; + } + + + /** + * @param operationMode Indicate what should happen on player icon click. + */ + private ListUsersPanel(CommonPanel panel, Mode operationMode) + { + super(panel); + this.onlineUsers = this.collectUsers(ViewMode.IN_WORLD); + this.operationMode = operationMode; + this.filterElements = this.onlineUsers; + } + + + /** + * Open the Challenges Admin GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix, + Mode mode) + { + new ListUsersPanel(addon, user, world, topLabel, permissionPrefix, mode).build(); + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Mode mode) + { + new ListUsersPanel(parentGUI, mode).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.onlineUsers; + } + else + { + this.filterElements = this.onlineUsers.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.getDisplayName().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation(Constants.TITLE + "choose-player")); + + PanelUtils.fillBorder(panelBuilder); + + this.populateElements(panelBuilder, this.filterElements); + + // Add button that allows to toggle different player lists. + panelBuilder.item( 4, this.createToggleButton()); + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates button for given user. If user has island it will add valid click handler. + * @param player Player which button must be created. + * @return Player button. + */ + @Override + protected PanelItem createElementButton(Player player) + { + final String reference = Constants.BUTTON + "player."; + + Island island = this.addon.getIslands().getIsland(this.world, player.getUniqueId()); + + if (island == null) + { + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", Constants.PARAMETER_NAME, player.getName())). + icon(Material.BARRIER). + description(this.user.getTranslation(reference + "no-island")). + build(); + } + + List description = new ArrayList<>(4); + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_OWNER, this.addon.getPlayers().getName(island.getOwner()))); + + // Is owner in his own island member set? I assume yes. Need testing. + if (island.getMemberSet().size() > 1) + { + description.add(this.user.getTranslation(reference + "members")); + island.getMemberSet().forEach(member -> { + if (member != island.getOwner()) + { + description.add(this.user.getTranslation(reference + "member", + Constants.PARAMETER_NAME, this.addon.getPlayers().getName(member))); + } + }); + } + + description.add(""); + + if (this.operationMode == Mode.RESET_ALL && this.selectedPlayer != null) + { + description.add(this.user.getTranslation(Constants.TIPS + "click-to-reset-all")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", Constants.PARAMETER_NAME, player.getName())). + icon(player.getName()). + description(description). + glow(this.operationMode == Mode.RESET_ALL && this.selectedPlayer == player). + clickHandler((panel, user1, clickType, i) -> { + switch (this.operationMode) + { + case COMPLETE -> { + // Get all challenge that is in current level. + List challengeList = this.manager.getAllChallenges(this.world); + + // Generate descriptions for these challenges + Map> challengeDescriptionMap = challengeList.stream(). + filter(challenge -> !this.manager.isChallengeComplete(player.getUniqueId(), this.world, challenge)). + collect(Collectors.toMap(challenge -> challenge, + challenge -> this.generateChallengeDescription(challenge, User.getInstance(player)), + (a, b) -> b, + () -> new LinkedHashMap<>(challengeList.size()))); + + // Open select gui + ChallengeSelector.open(this.user, + Material.LIME_STAINED_GLASS_PANE, + challengeDescriptionMap, + (status, valueSet) -> { + if (status) + { + valueSet.forEach(challenge -> + manager.setChallengeComplete(player.getUniqueId(), + this.world, + challenge, + this.user.getUniqueId())); + } + + this.build(); + }); + } + case RESET -> { + // Get all challenge that is in current level. + List challengeList = this.manager.getAllChallenges(this.world); + + // Generate descriptions for these challenges + Map> challengeDescriptionMap = challengeList.stream(). + filter(challenge -> this.manager.isChallengeComplete(player.getUniqueId(), this.world, challenge)). + collect(Collectors.toMap(challenge -> challenge, + challenge -> this.generateChallengeDescription(challenge, User.getInstance(player)), + (a, b) -> b, + () -> new LinkedHashMap<>(challengeList.size()))); + + // Open select gui + ChallengeSelector.open(this.user, + Material.ORANGE_STAINED_GLASS_PANE, + challengeDescriptionMap, + (status, valueSet) -> { + if (status) + { + valueSet.forEach(challenge -> + this.manager.resetChallenge(player.getUniqueId(), + this.world, + challenge, + this.user.getUniqueId())); + } + + this.build(); + }); + } + case RESET_ALL -> { + if (this.selectedPlayer == null) + { + this.selectedPlayer = player; + } + else + { + this.manager.resetAllChallenges(player.getUniqueId(), this.world, this.user.getUniqueId()); + this.selectedPlayer = null; + } + + this.build(); + } + } + + return true; + }). + build(); + } + + + /** + * This method collects users based on view mode. + * @param mode Given view mode. + * @return List with players in necessary view mode. + */ + private List collectUsers(ViewMode mode) + { + return switch (mode) { + case ONLINE -> new ArrayList<>(Bukkit.getOnlinePlayers()); + case WITH_ISLAND -> this.addon.getPlayers().getPlayers().stream(). + filter(player -> this.addon.getIslands().getIsland(this.world, player.getPlayerUUID()) != null). + map(Players::getPlayer). + collect(Collectors.toList()); + default -> new ArrayList<>(this.world.getPlayers()); + }; + } + + + /** + * This method creates Player List view Mode toggle button. + * @return Button that toggles through player view mode. + */ + private PanelItem createToggleButton() + { + final String reference = Constants.BUTTON + "player_list."; + + List description = new ArrayList<>(5); + + description.add(this.user.getTranslation(reference + "description")); + description.add(this.user.getTranslation(reference + + (ViewMode.ONLINE == this.mode ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "online")); + description.add(this.user.getTranslation(reference + + (ViewMode.WITH_ISLAND == this.mode ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "with_island")); + description.add(this.user.getTranslation(reference + + (ViewMode.IN_WORLD == this.mode ? "enabled" : "disabled")) + + this.user.getTranslation(reference + "in_world")); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-cycle")); + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-cycle")); + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name")). + icon(Material.STONE_BUTTON). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.mode = Utils.getPreviousValue(ViewMode.values(), this.mode); + } + else + { + this.mode = Utils.getNextValue(ViewMode.values(), this.mode); + } + this.onlineUsers = this.collectUsers(this.mode); + + // Reset search + this.searchString = ""; + this.updateFilters(); + + this.build(); + return true; + }).build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * This allows to switch which users should be in the list. + */ + private enum ViewMode + { + ONLINE, + WITH_ISLAND, + IN_WORLD + } + + /** + * This allows to decide what User Icon should do. + */ + public enum Mode + { + COMPLETE, + RESET, + RESET_ALL + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with players that should be in GUI. + */ + private List onlineUsers; + + /** + * List with players that should be in GUI. + */ + private List filterElements; + + /** + * Current operation mode. + */ + private final Mode operationMode; + + /** + * Current index of view mode + */ + private ViewMode mode = ViewMode.ONLINE; + + /** + * Stores clicked player. + */ + private Player selectedPlayer; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java deleted file mode 100644 index eab6371..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java +++ /dev/null @@ -1,241 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Material; -import org.bukkit.World; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.NumberGUI; -import world.bentobox.challenges.panel.util.SelectBlocksGUI; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class allows to edit material that are in required material map. - */ -public class ManageBlocksGUI extends CommonGUI -{ - public ManageBlocksGUI(ChallengesAddon addon, - World world, - User user, - Map materialMap, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - super(addon, world, user, topLabel, permissionPrefix, parentGUI); - this.materialMap = materialMap; - - this.materialList = new ArrayList<>(this.materialMap.keySet()); - - // Sort materials by their ordinal value. - this.materialList.sort(Comparator.comparing(Enum::ordinal)); - - this.selectedMaterials = new HashSet<>(); - } - - -// --------------------------------------------------------------------- -// Section: Methods -// --------------------------------------------------------------------- - - - /** - * This method builds all necessary elements in GUI panel. - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user). - name(this.user.getTranslation("challenges.gui.title.admin.manage-blocks")); - - // Create nice border. - GuiUtils.fillBorder(panelBuilder); - - panelBuilder.item(3, this.createButton(Button.ADD)); - panelBuilder.item(5, this.createButton(Button.REMOVE)); - - final int MAX_ELEMENTS = 21; - - if (this.pageIndex < 0) - { - this.pageIndex = this.materialList.size() / MAX_ELEMENTS; - } - else if (this.pageIndex > (this.materialList.size() / MAX_ELEMENTS)) - { - this.pageIndex = 0; - } - - int entitiesIndex = MAX_ELEMENTS * this.pageIndex; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (entitiesIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && - entitiesIndex < this.materialList.size() && - index < 36) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createElementButton(this.materialList.get(entitiesIndex++))); - } - - index++; - } - - // Navigation buttons only if necessary - if (this.materialList.size() > MAX_ELEMENTS) - { - panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); - panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); - } - - // Add return button. - panelBuilder.item(44, this.returnButton); - - panelBuilder.build(); - } - - - /** - * This method creates PanelItem button of requested type. - * @param button Button which must be created. - * @return new PanelItem with requested functionality. - */ - private PanelItem createButton(Button button) - { - int lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - PanelItemBuilder builder = new PanelItemBuilder(); - - switch (button) - { - case ADD: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.add")); - builder.icon(Material.BUCKET); - builder.clickHandler((panel, user1, clickType, slot) -> { - - new SelectBlocksGUI(this.user, false, new HashSet<>(this.materialList), (status, materials) -> { - if (status) - { - materials.forEach(material -> { - this.materialMap.put(material, 1); - this.materialList.add(material); - }); - } - - this.build(); - }); - return true; - }); - break; - case REMOVE: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.remove-selected")); - builder.description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.descriptions.admin.remove-selected"), lineLength)); - builder.icon(Material.LAVA_BUCKET); - builder.clickHandler((panel, user1, clickType, slot) -> { - this.materialMap.keySet().removeAll(this.selectedMaterials); - this.materialList.removeAll(this.selectedMaterials); - this.build(); - return true; - }); - break; - } - - return builder.build(); - } - - - /** - * This method creates button for given material. - * @param material material which button must be created. - * @return new Button for material. - */ - private PanelItem createElementButton(Material material) - { - return new PanelItemBuilder(). - name(WordUtils.capitalize(material.name().toLowerCase().replace("_", " "))). - icon(GuiUtils.getMaterialItem(material, this.materialMap.get(material))). - description(this.selectedMaterials.contains(material) ? - this.user.getTranslation("challenges.gui.descriptions.admin.selected") : ""). - clickHandler((panel, user1, clickType, slot) -> { - // On right click change which entities are selected for deletion. - if (clickType.isRightClick()) - { - if (!this.selectedMaterials.add(material)) - { - // Remove material if it is already selected - this.selectedMaterials.remove(material); - } - - this.build(); - } - else - { - new NumberGUI(this.user, - this.materialMap.get(material), - 1, - this.addon.getChallengesSettings().getLoreLineLength(), - (status, value) -> { - if (status) - { - // Update value only when something changes. - this.materialMap.put(material, value); - } - - this.build(); - }); - } - return true; - }). - glow(this.selectedMaterials.contains(material)). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Enums -// --------------------------------------------------------------------- - - - /** - * Functional buttons in current GUI. - */ - private enum Button - { - ADD, - REMOVE - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * Contains selected materials. - */ - private Set selectedMaterials; - - /** - * List of materials to avoid order issues. - */ - private List materialList; - - /** - * List of required materials. - */ - private Map materialMap; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksPanel.java new file mode 100644 index 0000000..cd43adf --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksPanel.java @@ -0,0 +1,312 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.MultiBlockSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class allows to edit material that are in required material map. + */ +public class ManageBlocksPanel extends CommonPagedPanel +{ + private ManageBlocksPanel(CommonPanel parentGUI, Map materialMap) + { + super(parentGUI); + this.materialMap = materialMap; + this.materialList = new ArrayList<>(this.materialMap.keySet()); + + // Sort materials by their ordinal value. + this.materialList.sort(Comparator.comparing(Enum::name)); + + this.selectedMaterials = new HashSet<>(); + + // Init without filters applied. + this.filterElements = this.materialList; + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Map materialMap) + { + new ManageBlocksPanel(parentGUI, materialMap).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.materialList; + } + else + { + this.filterElements = this.materialList.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation(Constants.TITLE + "manage-blocks")); + + // Create nice border. + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(3, this.createButton(Button.ADD_BLOCK)); + panelBuilder.item(5, this.createButton(Button.REMOVE_BLOCK)); + + this.populateElements(panelBuilder, this.filterElements); + + // Add return button. + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + boolean glow; + + switch (button) + { + case ADD_BLOCK -> { + icon = new ItemStack(Material.BUCKET); + clickHandler = (panel, user1, clickType, slot) -> + { + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.BLOCKS, + new HashSet<>(this.materialList), + (status, materials) -> + { + if (status) + { + materials.forEach(material -> + { + this.materialMap.put(material, 1); + this.materialList.add(material); + }); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_BLOCK -> { + + if (!this.selectedMaterials.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedMaterials.forEach(material -> + description.add(this.user.getTranslation(reference + "material", + "[material]", Utils.prettifyObject(material, this.user)))); + } + + icon = new ItemStack(Material.LAVA_BUCKET); + + clickHandler = (panel, user1, clickType, slot) -> + { + if (!this.selectedMaterials.isEmpty()) + { + this.materialMap.keySet().removeAll(this.selectedMaterials); + this.materialList.removeAll(this.selectedMaterials); + this.selectedMaterials.clear(); + this.build(); + } + + return true; + }; + + glow = !this.selectedMaterials.isEmpty(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + glow(glow). + build(); + } + + + /** + * This method creates button for given material. + * @param material material which button must be created. + * @return new Button for material. + */ + @Override + protected PanelItem createElementButton(Material material) + { + final String reference = Constants.BUTTON + "material."; + + List description = new ArrayList<>(); + + if (this.selectedMaterials.contains(material)) + { + description.add(this.user.getTranslation(reference + "selected")); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-choose")); + + if (this.selectedMaterials.contains(material)) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-deselect")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[material]", + Utils.prettifyObject(material, this.user))). + icon(PanelUtils.getMaterialItem(material, this.materialMap.get(material))). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (clickType.isRightClick()) + { + if (!this.selectedMaterials.add(material)) + { + // Remove material if it is already selected + this.selectedMaterials.remove(material); + } + + this.build(); + } + else + { + Consumer numberConsumer = number -> { + if (number != null) + { + this.materialMap.put(material, number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 1, + Integer.MAX_VALUE); + } + return true; + }). + glow(this.selectedMaterials.contains(material)). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ADD_BLOCK, + REMOVE_BLOCK + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Contains selected materials. + */ + private final Set selectedMaterials; + + /** + * List of materials to avoid order issues. + */ + private final List materialList; + + /** + * List of required materials. + */ + private final Map materialMap; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java deleted file mode 100644 index ed29747..0000000 --- a/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java +++ /dev/null @@ -1,258 +0,0 @@ -package world.bentobox.challenges.panel.admin; - - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.EntityType; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.panel.util.NumberGUI; -import world.bentobox.challenges.panel.util.SelectEntityGUI; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class allows to edit entities that are in required entities map. - */ -public class ManageEntitiesGUI extends CommonGUI -{ - public ManageEntitiesGUI(ChallengesAddon addon, - World world, - User user, - Map requiredEntities, - String topLabel, - String permissionPrefix, - CommonGUI parentGUI) - { - super(addon, world, user, topLabel, permissionPrefix, parentGUI); - this.requiredEntities = requiredEntities; - - this.entityList = new ArrayList<>(this.requiredEntities.keySet()); - this.entityList.sort(Comparator.comparing(Enum::name)); - - this.selectedEntities = new HashSet<>(EntityType.values().length); - } - - -// --------------------------------------------------------------------- -// Section: Methods -// --------------------------------------------------------------------- - - - /** - * This method builds all necessary elements in GUI panel. - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user). - name(this.user.getTranslation("challenges.gui.title.admin.manage-entities")); - - // create border - GuiUtils.fillBorder(panelBuilder); - - panelBuilder.item(3, this.createButton(Button.ADD)); - panelBuilder.item(5, this.createButton(Button.REMOVE)); - panelBuilder.item(8, this.createButton(Button.SWITCH)); - - final int MAX_ELEMENTS = 21; - - if (this.pageIndex < 0) - { - this.pageIndex = this.entityList.size() / MAX_ELEMENTS; - } - else if (this.pageIndex > (this.entityList.size() / MAX_ELEMENTS)) - { - this.pageIndex = 0; - } - - int entitiesIndex = MAX_ELEMENTS * this.pageIndex; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (entitiesIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && - entitiesIndex < this.entityList.size() && - index < 26) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createEntityButton(this.entityList.get(entitiesIndex++))); - } - - index++; - } - - // Navigation buttons only if necessary - if (this.entityList.size() > MAX_ELEMENTS) - { - panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); - panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); - } - - // Add return button. - panelBuilder.item(44, this.returnButton); - - panelBuilder.build(); - } - - - /** - * This method creates PanelItem button of requested type. - * @param button Button which must be created. - * @return new PanelItem with requested functionality. - */ - private PanelItem createButton(Button button) - { - int lineLength = this.addon.getChallengesSettings().getLoreLineLength(); - PanelItemBuilder builder = new PanelItemBuilder(); - - switch (button) - { - case ADD: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.add")); - builder.icon(Material.BUCKET); - builder.clickHandler((panel, user1, clickType, slot) -> { - new SelectEntityGUI(this.user, this.requiredEntities.keySet(), this.asEggs, (status, entities) -> { - if (status) - { - entities.forEach(entity -> { - this.requiredEntities.put(entity, 1); - this.entityList.add(entity); - }); - } - - this.build(); - }); - return true; - }); - break; - case REMOVE: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.remove-selected")); - builder.description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.descriptions.admin.remove-selected"), lineLength)); - builder.icon(Material.LAVA_BUCKET); - builder.clickHandler((panel, user1, clickType, slot) -> { - this.requiredEntities.keySet().removeAll(this.selectedEntities); - this.entityList.removeAll(this.selectedEntities); - this.build(); - return true; - }); - break; - case SWITCH: - builder.name(this.user.getTranslation("challenges.gui.buttons.admin.show-eggs")); - builder.description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.descriptions.admin.show-eggs"), lineLength)); - builder.icon(this.asEggs ? Material.EGG : Material.PLAYER_HEAD); - builder.clickHandler((panel, user1, clickType, slot) -> { - this.asEggs = !this.asEggs; - this.build(); - return true; - }); - break; - } - - return builder.build(); - } - - - /** - * This method creates button for given entity. - * @param entity Entity which button must be created. - * @return new Button for entity. - */ - private PanelItem createEntityButton(EntityType entity) - { - return new PanelItemBuilder(). - name(WordUtils.capitalize(entity.name().toLowerCase().replace("_", " "))). - description(this.selectedEntities.contains(entity) ? - this.user.getTranslation("challenges.gui.descriptions.admin.selected") : ""). - icon(this.asEggs ? - GuiUtils.getEntityEgg(entity, this.requiredEntities.get(entity)) : - GuiUtils.getEntityHead(entity, this.requiredEntities.get(entity))). - clickHandler((panel, user1, clickType, slot) -> { - // On right click change which entities are selected for deletion. - if (clickType.isRightClick()) - { - if (!this.selectedEntities.add(entity)) - { - // Remove entity if it is already selected - this.selectedEntities.remove(entity); - } - - this.build(); - } - else - { - new NumberGUI(this.user, - this.requiredEntities.get(entity), - 1, - this.addon.getChallengesSettings().getLoreLineLength(), - (status, value) -> { - if (status) - { - // Update value only when something changes. - this.requiredEntities.put(entity, value); - } - - this.build(); - }); - } - return true; - }). - glow(this.selectedEntities.contains(entity)). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Enums -// --------------------------------------------------------------------- - - - /** - * Functional buttons in current GUI. - */ - private enum Button - { - ADD, - REMOVE, - SWITCH - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * List with entities to avoid list irregularities. - */ - private List entityList; - - /** - * Set with entities that are selected. - */ - private Set selectedEntities; - - /** - * Map that contains all entities and their cound. - */ - private Map requiredEntities; - - /** - * Boolean indicate if entities should be displayed as eggs or mob heads. - */ - private boolean asEggs; -} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesPanel.java new file mode 100644 index 0000000..951467b --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesPanel.java @@ -0,0 +1,330 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.util.MultiEntitySelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class allows to edit entities that are in required entities map. + */ +public class ManageEntitiesPanel extends CommonPagedPanel +{ + private ManageEntitiesPanel(CommonPanel parentGUI, Map requiredEntities) + { + super(parentGUI); + this.requiredEntities = requiredEntities; + + this.entityList = new ArrayList<>(this.requiredEntities.keySet()); + this.entityList.sort(Comparator.comparing(Enum::name)); + + this.selectedEntities = new HashSet<>(EntityType.values().length); + this.filterElements = this.entityList; + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, Map requiredEntities) + { + new ManageEntitiesPanel(parentGUI, requiredEntities).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.entityList; + } + else + { + this.filterElements = this.entityList.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation(Constants.TITLE + "manage-entities")); + + // create border + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(3, this.createButton(Button.ADD_ENTITY)); + panelBuilder.item(5, this.createButton(Button.REMOVE_ENTITY)); + panelBuilder.item(8, this.createButton(Button.SWITCH_ENTITY)); + + this.populateElements(panelBuilder, this.filterElements); + + // Add return button. + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + boolean glow; + + switch (button) + { + case ADD_ENTITY -> { + icon = new ItemStack(Material.BUCKET); + clickHandler = (panel, user1, clickType, slot) -> { + MultiEntitySelector.open(this.user, + this.asEggs, + MultiEntitySelector.Mode.ALIVE, + this.requiredEntities.keySet(), + (status, entities) -> { + if (status) + { + entities.forEach(entity -> { + this.requiredEntities.put(entity, 1); + this.entityList.add(entity); + }); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_ENTITY -> { + + if (!this.selectedEntities.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedEntities.forEach(entity -> + description.add(this.user.getTranslation(reference + "entity", + "[entity]", Utils.prettifyObject(entity, this.user)))); + } + + icon = new ItemStack(Material.LAVA_BUCKET); + + clickHandler = (panel, user1, clickType, slot) -> + { + if (!this.selectedEntities.isEmpty()) + { + this.requiredEntities.keySet().removeAll(this.selectedEntities); + this.entityList.removeAll(this.selectedEntities); + this.selectedEntities.clear(); + this.build(); + } + + return true; + }; + + glow = !this.entityList.isEmpty(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + case SWITCH_ENTITY -> { + icon = new ItemStack(this.asEggs ? Material.EGG : Material.PLAYER_HEAD); + + clickHandler = (panel, user1, clickType, slot) -> { + this.asEggs = !this.asEggs; + this.build(); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + glow(glow). + build(); + } + + + /** + * This method creates button for given entity. + * @param entity Entity which button must be created. + * @return new Button for entity. + */ + @Override + protected PanelItem createElementButton(EntityType entity) + { + final String reference = Constants.BUTTON + "entity."; + + List description = new ArrayList<>(); + + if (this.selectedEntities.contains(entity)) + { + description.add(this.user.getTranslation(reference + "selected")); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-choose")); + + if (this.selectedEntities.contains(entity)) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-deselect")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[entity]", + Utils.prettifyObject(entity, this.user))). + icon(this.asEggs ? + PanelUtils.getEntityEgg(entity, this.requiredEntities.get(entity)) : + PanelUtils.getEntityHead(entity, this.requiredEntities.get(entity))). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (clickType.isRightClick()) + { + if (!this.selectedEntities.add(entity)) + { + // Remove entity if it is already selected + this.selectedEntities.remove(entity); + } + + this.build(); + } + else + { + Consumer numberConsumer = number -> { + if (number != null) + { + this.requiredEntities.put(entity, number.intValue()); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 1, + Integer.MAX_VALUE); + } + return true; + }). + glow(this.selectedEntities.contains(entity)). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ADD_ENTITY, + REMOVE_ENTITY, + SWITCH_ENTITY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with entities to avoid list irregularities. + */ + private final List entityList; + + /** + * Set with entities that are selected. + */ + private final Set selectedEntities; + + /** + * Map that contains all entities and their cound. + */ + private final Map requiredEntities; + + /** + * Boolean indicate if entities should be displayed as eggs or mob heads. + */ + private boolean asEggs; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java deleted file mode 100644 index 222e7bb..0000000 --- a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java +++ /dev/null @@ -1,528 +0,0 @@ -package world.bentobox.challenges.panel.user; - - -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.panel.CommonGUI; -import world.bentobox.challenges.tasks.TryToComplete; -import world.bentobox.challenges.utils.GuiUtils; -import world.bentobox.challenges.utils.LevelStatus; - - -/** - * This is UserGUI class. It contains everything necessary for user to use it. - */ -public class ChallengesGUI extends CommonGUI -{ -// --------------------------------------------------------------------- -// Section: Constructors -// --------------------------------------------------------------------- - - /** - * Default constructor that inits panels with minimal requirements, without parent panel. - * - * @param addon Addon where panel operates. - * @param world World from which panel was created. - * @param user User who created panel. - * @param topLabel Command top label which creates panel (f.e. island or ai) - * @param permissionPrefix Command permission prefix (f.e. bskyblock.) - */ - public ChallengesGUI(ChallengesAddon addon, - World world, - User user, - String topLabel, - String permissionPrefix) - { - super(addon, world, user, topLabel, permissionPrefix); - this.challengesManager = this.addon.getChallengesManager(); - - this.levelStatusList = this.challengesManager.getAllChallengeLevelStatus(this.user, this.world); - - for (LevelStatus levelStatus : this.levelStatusList) - { - if (levelStatus.isUnlocked()) - { - this.lastSelectedLevel = levelStatus; - } - else - { - break; - } - } - - this.containsChallenges = this.challengesManager.hasAnyChallengeData(this.world); - } - -// --------------------------------------------------------------------- -// Section: Methods -// --------------------------------------------------------------------- - - - /** - * This method builds all necessary elements in GUI panel. - */ - @Override - public void build() - { - // Do not open gui if there is no challenges. - if (!this.containsChallenges) - { - this.addon.logError("There are no challenges set up!"); - this.user.sendMessage("challenges.errors.no-challenges"); - return; - } - - PanelBuilder panelBuilder = new PanelBuilder().user(this.user). - name(this.user.getTranslation("challenges.gui.title.challenges")); - - // TODO: get last completed level. - - int nextItemIndex = 0; - - if (this.addon.getChallengesSettings().isFreeChallengesFirst()) - { - this.addFreeChallenges(panelBuilder, nextItemIndex); - - // Start new row for challenges. - if (panelBuilder.nextSlot() % 9 != 0) - { - nextItemIndex = panelBuilder.nextSlot() - panelBuilder.nextSlot() % 9 + 9; - } - else - { - nextItemIndex = panelBuilder.nextSlot(); - } - } - - this.addChallenges(panelBuilder, nextItemIndex); - - // Start new row for levels. - // Start new row for challenges. - if (panelBuilder.nextSlot() % 9 != 0) - { - nextItemIndex = panelBuilder.nextSlot() - panelBuilder.nextSlot() % 9 + 9; - } - else - { - nextItemIndex = panelBuilder.nextSlot(); - } - - this.addChallengeLevels(panelBuilder, nextItemIndex); - - if (!this.addon.getChallengesSettings().isFreeChallengesFirst()) - { - // Start new row for free challenges. - if (panelBuilder.nextSlot() % 9 != 0) - { - nextItemIndex = panelBuilder.nextSlot() - panelBuilder.nextSlot() % 9 + 9; - } - else - { - nextItemIndex = panelBuilder.nextSlot(); - } - - this.addFreeChallenges(panelBuilder, nextItemIndex); - } - - panelBuilder.build(); - } - - - /** - * This method adds free challenges to panelBuilder. - * @param panelBuilder where free challenges must be added. - * @param firstItemIndex index of first element. - */ - private void addFreeChallenges(PanelBuilder panelBuilder, int firstItemIndex) - { - List freeChallenges = this.challengesManager.getFreeChallenges(this.world); - - if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) - { - freeChallenges.removeIf(challenge -> !challenge.isRepeatable() && - this.challengesManager.isChallengeComplete(this.user, this.world, challenge)); - } - - // Remove all undeployed challenges if VisibilityMode is set to Hidden. - if (this.addon.getChallengesSettings().getVisibilityMode().equals(VisibilityMode.HIDDEN)) - { - freeChallenges.removeIf(challenge -> !challenge.isDeployed()); - } - - final int freeChallengesCount = freeChallenges.size(); - - if (freeChallengesCount > 18) - { - int index = firstItemIndex; - - if (this.freeChallengeIndex > 0) - { - panelBuilder.item(index++, new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.previous")). - clickHandler((panel, user1, clickType, slot) -> { - this.freeChallengeIndex--; - this.build(); - return true; - }).build()); - } - - int currentIndex = this.freeChallengeIndex; - - while (panelBuilder.nextSlot() != firstItemIndex + 18 && currentIndex < freeChallengesCount) - { - panelBuilder.item(index++, this.getChallengeButton(freeChallenges.get(currentIndex++))); - } - - // Check if one challenge is left - if (currentIndex + 1 == freeChallengesCount) - { - panelBuilder.item(index, this.getChallengeButton(freeChallenges.get(currentIndex))); - } - else if (currentIndex < freeChallengesCount) - { - panelBuilder.item(index, new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.next")). - clickHandler((panel, user1, clickType, slot) -> { - this.freeChallengeIndex++; - this.build(); - return true; - }).build()); - } - } - else - { - for (Challenge challenge : freeChallenges) - { - // there are no limitations. Just bunch insert. - panelBuilder.item(firstItemIndex++, this.getChallengeButton(challenge)); - } - } - } - - - /** - * This method adds last selected level challenges to panelBuilder. - * @param panelBuilder where last selected level challenges must be added. - * @param firstItemIndex index of first element. - */ - private void addChallenges(PanelBuilder panelBuilder, int firstItemIndex) - { - if (this.lastSelectedLevel != null) - { - List challenges = this.challengesManager.getLevelChallenges(this.lastSelectedLevel.getLevel()); - - if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) - { - challenges.removeIf(challenge -> !challenge.isRepeatable() && - this.challengesManager.isChallengeComplete(this.user, this.world, challenge)); - } - - // Remove all undeployed challenges if VisibilityMode is set to Hidden. - if (this.addon.getChallengesSettings().getVisibilityMode().equals(VisibilityMode.HIDDEN)) - { - challenges.removeIf(challenge -> !challenge.isDeployed()); - } - - final int challengesCount = challenges.size(); - - if (challengesCount > 18) - { - int index = firstItemIndex; - - if (this.pageIndex > 0) - { - panelBuilder.item(index++, new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.previous")). - clickHandler((panel, user1, clickType, slot) -> { - this.pageIndex--; - this.build(); - return true; - }).build()); - } - - int currentIndex = this.pageIndex; - - while (panelBuilder.nextSlot() != firstItemIndex + 18 && currentIndex < challengesCount) - { - panelBuilder.item(index++, this.getChallengeButton(challenges.get(currentIndex++))); - } - - // Check if one challenge is left - if (currentIndex + 1 == challengesCount) - { - panelBuilder.item(index, this.getChallengeButton(challenges.get(currentIndex))); - } - else if (currentIndex < challengesCount) - { - panelBuilder.item(index, new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.next")). - clickHandler((panel, user1, clickType, slot) -> { - this.pageIndex++; - this.build(); - return true; - }).build()); - } - } - else - { - for (Challenge challenge : challenges) - { - // there are no limitations. Just bunch insert. - panelBuilder.item(firstItemIndex++, this.getChallengeButton(challenge)); - } - } - } - } - - - /** - * This method adds challenge levels to panelBuilder. - * @param panelBuilder where challenge levels must be added. - * @param firstItemIndex index of first element. - */ - private void addChallengeLevels(PanelBuilder panelBuilder, int firstItemIndex) - { - final int levelCounts = this.levelStatusList.size(); - - if (levelCounts > 9) - { - int index = firstItemIndex; - - if (this.levelIndex > 0) - { - panelBuilder.item(index++, new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.previous")). - clickHandler((panel, user1, clickType, slot) -> { - this.levelIndex--; - this.build(); - return true; - }).build()); - } - - int currentIndex = this.levelIndex; - - while (panelBuilder.nextSlot() != firstItemIndex + 9 && currentIndex < levelCounts) - { - panelBuilder.item(index++, this.getLevelButton(this.levelStatusList.get(currentIndex++))); - } - - // Check if one challenge is left - if (currentIndex + 1 == levelCounts) - { - panelBuilder.item(index, this.getLevelButton(this.levelStatusList.get(currentIndex))); - } - else if (currentIndex < levelCounts) - { - panelBuilder.item(index, new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.next")). - clickHandler((panel, user1, clickType, slot) -> { - this.levelIndex++; - this.build(); - return true; - }).build()); - } - } - else - { - for (LevelStatus level : this.levelStatusList) - { - // there are no limitations. Just bunch insert. - panelBuilder.item(firstItemIndex++, this.getLevelButton(level)); - } - } - } - - -// --------------------------------------------------------------------- -// Section: Icon building -// --------------------------------------------------------------------- - - - /** - * This method creates given challenges icon that on press tries to complete it. - * @param challenge which icon must be constructed. - * @return PanelItem icon for challenge. - */ - private PanelItem getChallengeButton(Challenge challenge) - { - return new PanelItemBuilder(). - icon(challenge.getIcon()). - name(challenge.getFriendlyName().isEmpty() ? - challenge.getUniqueId() : - ChatColor.translateAlternateColorCodes('&', challenge.getFriendlyName())). - description(GuiUtils.stringSplit(this.generateChallengeDescription(challenge, this.user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength())). - clickHandler((panel, user1, clickType, slot) -> { - - // Add ability to input how many repeats player should do. - // Do not open if challenge is not repeatable. - - if (clickType.isRightClick() && - challenge.isRepeatable() && - this.user.hasPermission(this.permissionPrefix + "complete.multiple")) - { - new MultipleGUI(this.user, - this.addon.getChallengesSettings().getLoreLineLength(), - value -> { - TryToComplete.complete(this.addon, - this.user, - challenge, - this.world, - this.topLabel, - this.permissionPrefix, - value); - - this.build(); - }); - } - else - { - if (TryToComplete.complete(this.addon, - this.user, - challenge, - this.world, - this.topLabel, - this.permissionPrefix)) - { - panel.getInventory().setItem(slot, this.getChallengeButton(challenge).getItem()); - } - } - - return true; - }). - glow(this.addon.getChallengesSettings().isAddCompletedGlow() && - this.challengesManager.isChallengeComplete(this.user, this.world, challenge)). - build(); - } - - - /** - * This method creates button for given level. - * @param level which button must be created. - * @return Button for given level. - */ - private PanelItem getLevelButton(LevelStatus level) - { - // Create a nice name for the level - String name = level.getLevel().getFriendlyName().isEmpty() ? - level.getLevel().getUniqueId() : - level.getLevel().getFriendlyName(); - - ItemStack icon; - List description; - PanelItem.ClickHandler clickHandler; - boolean glow; - - if (level == this.lastSelectedLevel) - { - icon = level.getLevel().getIcon(); - description = GuiUtils.stringSplit( - this.generateLevelDescription(level.getLevel(), user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength()); - clickHandler = null; - glow = true; - } - else if (level.isUnlocked()) - { - icon = level.getLevel().getIcon(); - description = GuiUtils.stringSplit( - this.generateLevelDescription(level.getLevel(), user.getPlayer()), - this.addon.getChallengesSettings().getLoreLineLength()); - clickHandler = (panel, user1, clickType, slot) -> { - this.lastSelectedLevel = level; - - // Reset page index for challenges. - this.pageIndex = 0; - - this.build(); - return true; - }; - glow = this.addon.getChallengesSettings().isAddCompletedGlow() && - this.challengesManager.isLevelCompleted(this.user, this.world, level.getLevel()); - } - else - { - if (level.getLevel().getLockedIcon() != null) - { - // Clone will prevent issues with description storing. - // It can be done only here as it can be null. - icon = level.getLevel().getLockedIcon().clone(); - } - else - { - icon = this.addon.getChallengesSettings().getLockedLevelIcon(); - } - - description = GuiUtils.stringSplit( - this.user.getTranslation("challenges.gui.descriptions.level-locked", - "[count]", Integer.toString(level.getNumberOfChallengesStillToDo()), - "[level]", level.getPreviousLevel().getFriendlyName()), - this.addon.getChallengesSettings().getLoreLineLength()); - - clickHandler = null; - glow = false; - } - - return new PanelItemBuilder(). - icon(icon). - name(ChatColor.translateAlternateColorCodes('&', name)). - description(description). - glow(glow). - clickHandler(clickHandler). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * This will be used if free challenges are more then 18. - */ - private int freeChallengeIndex = 0; - - /** - * This will be used if levels are more then 9. - */ - private int levelIndex; - - /** - * This list contains all information about level completion in current world. - */ - private List levelStatusList; - - /** - * This indicate last selected level. - */ - private LevelStatus lastSelectedLevel; - - /** - * Challenge Manager object. - */ - private ChallengesManager challengesManager; - - /** - * This boolean indicates if in the world there exist challenges for displaying in GUI. - */ - private final boolean containsChallenges; -} diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java new file mode 100644 index 0000000..b99c44d --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java @@ -0,0 +1,818 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel.user; + + +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.TemplatedPanel; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder; +import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.config.SettingsUtils; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.tasks.TryToComplete; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.LevelStatus; +import world.bentobox.challenges.utils.Utils; + + +/** + * Main challenges panel builder. + */ +public class ChallengesPanel extends CommonPanel +{ + private ChallengesPanel(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + super(addon, user, world, topLabel, permissionPrefix); + this.updateLevelList(); + this.containsChallenges = this.manager.hasAnyChallengeData(this.world); + } + + + /** + * Open the Challenges GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param topLabel the top label + * @param permissionPrefix the permission prefix + */ + public static void open(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + new ChallengesPanel(addon, world, user, topLabel, permissionPrefix).build(); + } + + + protected void build() + { + // Do not open gui if there is no challenges. + if (!this.containsChallenges) + { + this.addon.logError("There are no challenges set up!"); + Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-challenges")); + return; + } + + // Create lists for builder. + this.updateFreeChallengeList(); + this.updateChallengeList(); + // this.updateLevelList(); + + // Start building panel. + TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); + + // Set main template. + panelBuilder.template("main_panel", new File(this.addon.getDataFolder(), "panels")); + panelBuilder.user(this.user); + panelBuilder.world(this.user.getWorld()); + + // Register button builders + panelBuilder.registerTypeBuilder("CHALLENGE", this::createChallengeButton); + panelBuilder.registerTypeBuilder("LEVEL", this::createLevelButton); + + panelBuilder.registerTypeBuilder("UNASSIGNED_CHALLENGES", this::createFreeChallengesButton); + + panelBuilder.registerTypeBuilder("NEXT", this::createNextButton); + panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton); + + // Register unknown type builder. + panelBuilder.build(); + } + + + private void updateFreeChallengeList() + { + this.freeChallengeList = this.manager.getFreeChallenges(this.world); + + if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) + { + this.freeChallengeList.removeIf(challenge -> !challenge.isRepeatable() && + this.manager.isChallengeComplete(this.user, this.world, challenge)); + } + + // Remove all undeployed challenges if VisibilityMode is set to Hidden. + if (this.addon.getChallengesSettings().getVisibilityMode().equals(SettingsUtils.VisibilityMode.HIDDEN)) + { + this.freeChallengeList.removeIf(challenge -> !challenge.isDeployed()); + } + } + + + private void updateChallengeList() + { + if (this.lastSelectedLevel != null) + { + this.challengeList = this.manager.getLevelChallenges(this.lastSelectedLevel.getLevel()); + + if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) + { + this.challengeList.removeIf(challenge -> !challenge.isRepeatable() && + this.manager.isChallengeComplete(this.user, this.world, challenge)); + } + + // Remove all undeployed challenges if VisibilityMode is set to Hidden. + if (this.addon.getChallengesSettings().getVisibilityMode().equals(SettingsUtils.VisibilityMode.HIDDEN)) + { + this.challengeList.removeIf(challenge -> !challenge.isDeployed()); + } + } + else + { + this.challengeList = this.freeChallengeList; + } + } + + + /** + * Updates level status list and selects last unlocked level. + */ + private void updateLevelList() + { + this.levelList = this.manager.getAllChallengeLevelStatus(this.user, this.world); + + for (LevelStatus levelStatus : this.levelList) + { + if (levelStatus.isUnlocked()) + { + this.lastSelectedLevel = levelStatus; + } + else + { + break; + } + } + } + + + /** + * Updates level status list and returns if any new level has been unlocked. + * @return {code true} if a new level was unlocked, {@code false} otherwise. + */ + private boolean updateLevelListSilent() + { + Optional firstLockedLevel = + this.levelList.stream().filter(levelStatus -> !levelStatus.isUnlocked()).findFirst(); + + if (firstLockedLevel.isPresent()) + { + // If there still exist any locked level, update level status list. + this.levelList = this.manager.getAllChallengeLevelStatus(this.user, this.world); + + // Find a new first locked level. + Optional newLockedLevel = + this.levelList.stream().filter(levelStatus -> !levelStatus.isUnlocked()).findFirst(); + + return newLockedLevel.isEmpty() || + firstLockedLevel.get().getLevel() != newLockedLevel.get().getLevel(); + } + else + { + // If locked level is not present, return false. + return false; + } + } + + + @Nullable + private PanelItem createChallengeButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + if (this.challengeList.isEmpty()) + { + // Does not contain any free challenges. + return null; + } + + Challenge levelChallenge; + + // Check if that is a specific free challenge + if (template.dataMap().containsKey("id")) + { + String id = (String) template.dataMap().get("id"); + + // Find a challenge with given Id; + levelChallenge = this.challengeList.stream(). + filter(challenge -> challenge.getUniqueId().equals(id)). + findFirst(). + orElse(null); + + if (levelChallenge == null) + { + // There is no challenge in the list with specific id. + return null; + } + } + else + { + int index = this.challengeIndex * slot.amountMap().getOrDefault("CHALLENGE", 1) + slot.slot(); + + if (index >= this.challengeList.size()) + { + // Out of index. + return null; + } + + levelChallenge = this.challengeList.get(index); + } + + return this.createChallengeButton(template, levelChallenge); + } + + + @NonNull + private PanelItem createChallengeButton(ItemTemplateRecord template, @NonNull Challenge challenge) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + // Template specification are always more important than dynamic content. + builder.icon(template.icon() != null ? template.icon().clone() : challenge.getIcon()); + + // Template specific title is always more important than challenge name. + if (template.title() != null && !template.title().isBlank()) + { + builder.name(this.user.getTranslation(this.world, template.title(), + Constants.PARAMETER_CHALLENGE, challenge.getFriendlyName())); + } + else + { + builder.name(Util.translateColorCodes(challenge.getFriendlyName())); + } + + if (template.description() != null && !template.description().isBlank()) + { + // TODO: adding parameters could be useful. + builder.description(this.user.getTranslation(this.world, template.description())); + } + else + { + builder.description(this.generateChallengeDescription(challenge, this.user)); + } + + // Add Click handler + builder.clickHandler((panel, user, clickType, i) -> { + for (ItemTemplateRecord.ActionRecords action : template.actions()) + { + if (clickType == action.clickType()) + { + switch (action.actionType().toUpperCase()) + { + case "COMPLETE": + if (TryToComplete.complete(this.addon, + this.user, + challenge, + this.world, + this.topLabel, + this.permissionPrefix)) + { + if (this.updateLevelListSilent()) + { + // Need to rebuild all because completing a challenge + // may unlock a new level. #187 + this.build(); + } + else + { + // There was no unlocked levels. + panel.getInventory().setItem(i, + this.createChallengeButton(template, challenge).getItem()); + } + } + else if (challenge.isRepeatable() && challenge.getTimeout() > 0) + { + // Update timeout after clicking. + panel.getInventory().setItem(i, + this.createChallengeButton(template, challenge).getItem()); + } + break; + case "COMPLETE_MAX": + if (challenge.isRepeatable()) + { + if (TryToComplete.complete(this.addon, + this.user, + challenge, + this.world, + this.topLabel, + this.permissionPrefix, + Integer.MAX_VALUE)) + { + if (this.updateLevelListSilent()) + { + // Need to rebuild all because completing a challenge + // may unlock a new level. #187 + this.build(); + } + else + { + // There was no unlocked levels. + panel.getInventory().setItem(i, + this.createChallengeButton(template, challenge).getItem()); + } + } + else if (challenge.getTimeout() > 0) + { + // Update timeout after clicking. + panel.getInventory().setItem(i, + this.createChallengeButton(template, challenge).getItem()); + } + } + break; + case "MULTIPLE_PANEL": + if (challenge.isRepeatable()) + { + MultiplePanel.open(this.addon, this.user, value -> + { + TryToComplete.complete(this.addon, + this.user, + challenge, + this.world, + this.topLabel, + this.permissionPrefix, + value); + + this.updateLevelListSilent(); + this.build(); + }); + } + break; + } + } + } + + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + filter(action -> challenge.isRepeatable() || "COMPLETE".equalsIgnoreCase(action.actionType())). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + // Glow the icon. + builder.glow(this.addon.getChallengesSettings().isAddCompletedGlow() && + this.manager.isChallengeComplete(this.user, this.world, challenge)); + + // Click Handlers are managed by custom addon buttons. + return builder.build(); + } + + + @Nullable + private PanelItem createLevelButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + if (this.levelList.isEmpty()) + { + // Does not contain any levels. + return null; + } + + LevelStatus level; + + // Check if that is a specific level + if (template.dataMap().containsKey("id")) + { + String id = (String) template.dataMap().get("id"); + + // Find a challenge with given Id; + level = this.levelList.stream(). + filter(levelStatus -> levelStatus.getLevel().getUniqueId().equals(id)). + findFirst(). + orElse(null); + + if (level == null) + { + // There is no challenge in the list with specific id. + return null; + } + } + else + { + int index = this.levelIndex * slot.amountMap().getOrDefault("LEVEL", 1) + slot.slot(); + + if (index >= this.levelList.size()) + { + // Out of index. + return null; + } + + level = this.levelList.get(index); + } + + return this.createLevelButton(template, level); + } + + + @NonNull + private PanelItem createLevelButton(ItemTemplateRecord template, @NonNull LevelStatus level) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + // Template specification are always more important than dynamic content. + if (template.icon() != null) + { + builder.icon(template.icon().clone()); + } + else + { + if (level.isUnlocked()) + { + builder.icon(level.getLevel().getIcon()); + } + else if (level.getLevel().getLockedIcon() != null) + { + // Clone will prevent issues with description storing. + // It can be done only here as it can be null. + builder.icon(level.getLevel().getLockedIcon().clone()); + } + else + { + builder.icon(this.addon.getChallengesSettings().getLockedLevelIcon()); + } + } + + if (template.title() != null && !template.title().isBlank()) + { + builder.name(this.user.getTranslation(this.world, template.title(), + Constants.PARAMETER_LEVEL, level.getLevel().getFriendlyName())); + } + else + { + builder.name(Util.translateColorCodes(level.getLevel().getFriendlyName())); + } + + if (template.description() != null && !template.description().isBlank()) + { + // TODO: adding parameters could be useful. + builder.description(this.user.getTranslation(this.world, template.description())); + } + else + { + // TODO: Complete description generate. + builder.description(this.generateLevelDescription(level, this.user)); + } + + // Add click handler + builder.clickHandler((panel, user, clickType, i) -> { + if (level != this.lastSelectedLevel && level.isUnlocked()) + { + this.lastSelectedLevel = level; + this.challengeIndex = 0; + + this.build(); + } + + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + filter(action -> level != this.lastSelectedLevel && level.isUnlocked()). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + // Glow the icon. + builder.glow(level == this.lastSelectedLevel || + level.isUnlocked() && + this.addon.getChallengesSettings().isAddCompletedGlow() && + this.manager.isLevelCompleted(this.user, this.world, level.getLevel())); + + // Click Handlers are managed by custom addon buttons. + return builder.build(); + } + + + @Nullable + private PanelItem createFreeChallengesButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + if (this.freeChallengeList.isEmpty()) + { + // There are no free challenges for selection. + return null; + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + builder.icon(template.icon().clone()); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description())); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + if (this.lastSelectedLevel != null) + { + this.lastSelectedLevel = null; + this.build(); + } + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + filter(action -> this.lastSelectedLevel == null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @Nullable + private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + String target = template.dataMap().getOrDefault("target", "").toString().toUpperCase(); + + int nextPageIndex; + + switch (target) + { + case "CHALLENGE" -> { + int size = this.challengeList.size(); + + if (size <= slot.amountMap().getOrDefault("CHALLENGE", 1) || + 1.0 * size / slot.amountMap().getOrDefault("CHALLENGE", 1) <= this.challengeIndex + 1) + { + // There are no next elements + return null; + } + + nextPageIndex = this.challengeIndex + 2; + } + case "LEVEL" -> { + int size = this.levelList.size(); + + if (size <= slot.amountMap().getOrDefault("LEVEL", 1) || + 1.0 * size / slot.amountMap().getOrDefault("LEVEL", 1) <= this.levelIndex + 1) + { + // There are no next elements + return null; + } + + nextPageIndex = this.levelIndex + 2; + } + default -> { + // If not assigned to any type, return null. + return null; + } + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + + if ((Boolean) template.dataMap().getOrDefault("indexing", false)) + { + clone.setAmount(nextPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(nextPageIndex))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + // Next button ignores click type currently. + switch (target) + { + case "CHALLENGE" -> this.challengeIndex++; + case "LEVEL" -> this.levelIndex++; + } + + this.build(); + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @Nullable + private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + String target = template.dataMap().getOrDefault("target", "").toString().toUpperCase(); + + int previousPageIndex; + + if ("CHALLENGE".equals(target)) + { + if (this.challengeIndex == 0) + { + // There are no next elements + return null; + } + + previousPageIndex = this.challengeIndex; + } + else if ("LEVEL".equals(target)) + { + if (this.levelIndex == 0) + { + // There are no next elements + return null; + } + + previousPageIndex = this.levelIndex; + } + else + { + // If not assigned to any type, return null. + return null; + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + + if ((Boolean) template.dataMap().getOrDefault("indexing", false)) + { + clone.setAmount(previousPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(previousPageIndex))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + // Next button ignores click type currently. + switch (target) + { + case "CHALLENGE" -> this.challengeIndex--; + case "LEVEL" -> this.levelIndex--; + } + + this.build(); + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This boolean indicates if in the world there exist challenges for displaying in GUI. + */ + private final boolean containsChallenges; + + /** + * This list contains free challenges in current Panel. + */ + private List freeChallengeList; + + /** + * This will be used if levels are more than 18. + */ + private int levelIndex; + + /** + * This list contains all information about level completion in current world. + */ + private List levelList; + + /** + * This will be used if free challenges are more than 18. + */ + private int challengeIndex; + + /** + * This list contains challenges in current Panel. + */ + private List challengeList; + + /** + * This indicates last selected level. + */ + private LevelStatus lastSelectedLevel; +} diff --git a/src/main/java/world/bentobox/challenges/panel/user/GameModePanel.java b/src/main/java/world/bentobox/challenges/panel/user/GameModePanel.java new file mode 100644 index 0000000..c8ed477 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/user/GameModePanel.java @@ -0,0 +1,385 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel.user; + + +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.TemplatedPanel; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder; +import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.utils.Constants; + + +/** + * Main challenges panel builder. + */ +public class GameModePanel extends CommonPanel +{ + private GameModePanel(ChallengesAddon addon, + World world, + User user, + List addonList, + boolean adminMode) + { + super(addon, user, world, null, null); + this.addonList = addonList; + this.adminMode = adminMode; + } + + + /** + * Open the Challenges GUI. + * + * @param addon the addon + * @param world the world + * @param user the user + * @param addonList List of gamemode addons + * @param adminMode Indicate if admin mode. + */ + public static void open(ChallengesAddon addon, + World world, + User user, + List addonList, + boolean adminMode) + { + new GameModePanel(addon, world, user, addonList, adminMode).build(); + } + + + protected void build() + { + // Start building panel. + TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); + + // Set main template. + panelBuilder.template("gamemode_panel", new File(this.addon.getDataFolder(), "panels")); + panelBuilder.user(this.user); + panelBuilder.world(this.user.getWorld()); + + // Register button builders + panelBuilder.registerTypeBuilder("GAMEMODE", this::createGameModeButton); + + panelBuilder.registerTypeBuilder("NEXT", this::createNextButton); + panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton); + + // Register unknown type builder. + panelBuilder.build(); + } + + + @Nullable + private PanelItem createGameModeButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + if (this.addonList.isEmpty()) + { + // Does not contain any free challenges. + return null; + } + + GameModeAddon gameModeAddon; + + // Check if that is a specific free challenge + if (template.dataMap().containsKey("id")) + { + String id = (String) template.dataMap().get("id"); + + // Find a challenge with given Id; + gameModeAddon = this.addonList.stream(). + filter(gamemode -> gamemode.getDescription().getName().equals(id)). + findFirst(). + orElse(null); + + if (gameModeAddon == null) + { + // There is no gamemode in the list with specific id. + return null; + } + } + else + { + int index = this.addonIndex * slot.amountMap().getOrDefault("GAMEMODE", 1) + slot.slot(); + + if (index >= this.addonList.size()) + { + // Out of index. + return null; + } + + gameModeAddon = this.addonList.get(index); + } + + return this.createGameModeButton(template, gameModeAddon); + } + + + @NonNull + private PanelItem createGameModeButton(ItemTemplateRecord template, @NonNull GameModeAddon gameModeAddon) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + // Template specification are always more important than dynamic content. + builder.icon(template.icon() != null ? + template.icon().clone() : + new ItemStack(gameModeAddon.getDescription().getIcon())); + + // Template specific title is always more important than challenge name. + if (template.title() != null && !template.title().isBlank()) + { + builder.name(this.user.getTranslation(this.world, template.title(), + Constants.PARAMETER_GAMEMODE, gameModeAddon.getDescription().getName())); + } + else + { + builder.name(Util.translateColorCodes(gameModeAddon.getDescription().getName())); + } + + if (template.description() != null && !template.description().isBlank()) + { + // TODO: adding parameters could be useful. + builder.description(this.user.getTranslation(this.world, template.description())); + } + else + { + builder.description(gameModeAddon.getDescription().getDescription()); + } + + // Add Click handler + builder.clickHandler((panel, user, clickType, i) -> { + for (ItemTemplateRecord.ActionRecords action : template.actions()) + { + if (clickType == action.clickType()) + { + if (this.adminMode) + { + gameModeAddon.getAdminCommand().ifPresent(compositeCommand -> + user.performCommand(compositeCommand.getTopLabel() + " " + + this.addon.getChallengesSettings().getAdminMainCommand().split(" ")[0])); + } + else + { + gameModeAddon.getPlayerCommand().ifPresent(compositeCommand -> + user.performCommand(compositeCommand.getTopLabel() + " " + + this.addon.getChallengesSettings().getPlayerMainCommand().split(" ")[0])); + } + } + } + + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + // Glow the icon. + builder.glow(gameModeAddon.inWorld(this.user.getWorld())); + + // Click Handlers are managed by custom addon buttons. + return builder.build(); + } + + + @Nullable + private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + String target = template.dataMap().getOrDefault("target", "").toString().toUpperCase(); + + int nextPageIndex; + + if ("GAMEMODE".equals(target)) + { + int size = this.addonList.size(); + + if (size <= slot.amountMap().getOrDefault("GAMEMODE", 1) || + 1.0 * size / slot.amountMap().getOrDefault("GAMEMODE", 1) <= this.addonIndex + 1) + { + // There are no next elements + return null; + } + + nextPageIndex = this.addonIndex + 2; + } + else + {// If not assigned to any type, return null. + return null; + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + + if ((Boolean) template.dataMap().getOrDefault("indexing", false)) + { + clone.setAmount(nextPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description()), + Constants.PARAMETER_NUMBER, String.valueOf(nextPageIndex)); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + this.addonIndex++; + this.build(); + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @Nullable + private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + String target = template.dataMap().getOrDefault("target", "").toString().toUpperCase(); + + int previousPageIndex; + + if ("GAMEMODE".equals(target)) + { + if (this.addonIndex == 0) + { + // There are no next elements + return null; + } + + previousPageIndex = this.addonIndex; + } + else + { + // If not assigned to any type, return null. + return null; + } + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + + if ((Boolean) template.dataMap().getOrDefault("indexing", false)) + { + clone.setAmount(previousPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(this.world, template.description()), + Constants.PARAMETER_NUMBER, String.valueOf(previousPageIndex)); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + // Next button ignores click type currently. + this.addonIndex--; + this.build(); + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This will be used if free challenges are more than 18. + */ + private int addonIndex; + + /** + * This list contains challenges in current Panel. + */ + private final List addonList; + + /** + * Indicate if gui is for players or admins. + */ + private final boolean adminMode; +} diff --git a/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java b/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java deleted file mode 100644 index 26a9690..0000000 --- a/src/main/java/world/bentobox/challenges/panel/user/MultipleGUI.java +++ /dev/null @@ -1,215 +0,0 @@ - -package world.bentobox.challenges.panel.user; - -import java.util.function.Consumer; - -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.Panel; -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This GUI will pop out when user uses right click on challenge. It is meant to choose - * how many times player will complete challenges. - */ -public class MultipleGUI -{ - /** - * Default constructor. - * @param user User who opens gui. - * @param lineLength Length of lore message. - * @param action Action that will be performed on value clicking. - */ - public MultipleGUI(User user, int lineLength, Consumer action) - { - this.user = user; - this.lineLength = lineLength; - this.action = action; - - this.build(); - } - - /** - * This method builds panel that allows to change given number value. - */ - private void build() - { - PanelBuilder panelBuilder = new PanelBuilder(). - user(this.user). - type(Panel.Type.HOPPER). - name(this.user.getTranslation("challenges.gui.title.multiple-complete")); - - panelBuilder.item(2, this.getButton(Button.VALUE)); - - // Reduce - panelBuilder.item(0, this.getButton(Button.REDUCE_LOT)); - panelBuilder.item(1, this.getButton(Button.REDUCE)); - - // Increase - panelBuilder.item(3, this.getButton(Button.INCREASE)); - panelBuilder.item(4, this.getButton(Button.INCREASE_LOT)); - - panelBuilder.build(); - } - - - /** - * This method creates PanelItem with required functionality. - * @param button Functionality requirement. - * @return PanelItem with functionality. - */ - private PanelItem getButton(Button button) - { - ItemStack icon; - String name; - String description; - PanelItem.ClickHandler clickHandler; - boolean glow; - - switch (button) - { - case VALUE: - { - name = this.user.getTranslation("challenges.gui.buttons.value"); - description = this.user.getTranslation("challenges.gui.descriptions.current-value", "[value]", Integer.toString(this.value)); - icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.action.accept(this.value); - return true; - }; - glow = false; - break; - } - case INCREASE: - { - name = this.user.getTranslation("challenges.gui.buttons.increase"); - description = this.user.getTranslation("challenges.gui.descriptions.increase-by", "[value]", "1"); - icon = new ItemStack(Material.BLUE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value++; - // Necessary just to update second item - panel.getInventory().setItem(2, this.getButton(Button.VALUE).getItem()); - return true; - }; - glow = false; - break; - } - case INCREASE_LOT: - { - name = this.user.getTranslation("challenges.gui.buttons.increase"); - description = this.user.getTranslation("challenges.gui.descriptions.increase-by", "[value]", "5"); - icon = new ItemStack(Material.MAGENTA_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value += 5; - // Necessary just to update second item - panel.getInventory().setItem(2, this.getButton(Button.VALUE).getItem()); - return true; - }; - glow = false; - break; - } - case REDUCE: - { - name = this.user.getTranslation("challenges.gui.buttons.reduce"); - description = this.user.getTranslation("challenges.gui.descriptions.reduce-by", "[value]", "1"); - icon = new ItemStack(Material.ORANGE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value--; - - if (this.value < 1) - { - this.value = 1; - } - - // Necessary just to update second item - panel.getInventory().setItem(2, this.getButton(Button.VALUE).getItem()); - - return true; - }; - glow = false; - break; - } - case REDUCE_LOT: - { - name = this.user.getTranslation("challenges.gui.buttons.reduce"); - description = this.user.getTranslation("challenges.gui.descriptions.reduce-by", "[value]", "5"); - icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value -= 5; - - if (this.value < 1) - { - this.value = 1; - } - - // Necessary just to update second item - panel.getInventory().setItem(2, this.getButton(Button.VALUE).getItem()); - - return true; - }; - glow = false; - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Enums -// --------------------------------------------------------------------- - - - /** - * This enum allows to easier define available buttons. - */ - enum Button - { - VALUE, - REDUCE, - REDUCE_LOT, - INCREASE, - INCREASE_LOT - } - - -// --------------------------------------------------------------------- -// Section: Instance variables -// --------------------------------------------------------------------- - - - /** - * This variable allows to access to user object. - */ - private User user; - - /** - * This variable holds action that will be performed on accept. - */ - private Consumer action; - - /** - * This variable holds a number of characters in single line for lore message. - */ - private int lineLength; - - /** - * This integer holds current value of completion count. - */ - private int value = 1; -} diff --git a/src/main/java/world/bentobox/challenges/panel/user/MultiplePanel.java b/src/main/java/world/bentobox/challenges/panel/user/MultiplePanel.java new file mode 100644 index 0000000..011f66a --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/user/MultiplePanel.java @@ -0,0 +1,282 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel.user; + + +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.TemplatedPanel; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder; +import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; + + +public class MultiplePanel +{ + private MultiplePanel(ChallengesAddon addon, User user, Consumer action) + { + this.addon = addon; + this.user = user; + this.action = action; + } + + + public static void open(ChallengesAddon addon, User user, Consumer action) + { + new MultiplePanel(addon, user, action).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + private void build() + { + // Start building panel. + TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); + + // Set main template. + panelBuilder.template("multiple_panel", new File(this.addon.getDataFolder(), "panels")); + panelBuilder.user(this.user); + panelBuilder.world(this.user.getWorld()); + + // Register button builders + panelBuilder.registerTypeBuilder("INCREASE", this::createIncreaseButton); + panelBuilder.registerTypeBuilder("REDUCE", this::createReduceButton); + panelBuilder.registerTypeBuilder("ACCEPT", this::createValueButton); + + // Register unknown type builder. + panelBuilder.build(); + } + + + @NonNull + private PanelItem createIncreaseButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot itemSlot) + { + int increaseValue = (int) template.dataMap().getOrDefault("value", 1); + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + clone.setAmount(increaseValue); + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(increaseValue))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + this.completionValue += increaseValue; + this.build(); + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @NonNull + private PanelItem createReduceButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot itemSlot) + { + int decreaseValue = (int) template.dataMap().getOrDefault("value", 1); + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + clone.setAmount(decreaseValue); + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(decreaseValue))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + this.completionValue = Math.max(this.completionValue - decreaseValue, 1); + this.build(); + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + + @NonNull + private PanelItem createValueButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot itemSlot) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + ItemStack clone = template.icon().clone(); + clone.setAmount(this.completionValue); + builder.icon(clone); + } + + if (template.title() != null) + { + builder.name(this.user.getTranslation(template.title())); + } + + if (template.description() != null) + { + builder.description(this.user.getTranslation(template.description(), + Constants.PARAMETER_NUMBER, String.valueOf(completionValue))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> + { + for (ItemTemplateRecord.ActionRecords actionRecords : template.actions()) + { + if (clickType == actionRecords.clickType()) + { + if (actionRecords.actionType().equalsIgnoreCase("input")) + { + // Input consumer. + Consumer numberConsumer = number -> + { + if (number != null) + { + this.completionValue = number.intValue(); + } + + // reopen panel + this.build(); + }; + + ConversationUtils.createNumericInput(numberConsumer, + this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), + 1, + 2000); + } + else if (actionRecords.actionType().equalsIgnoreCase("accept")) + { + this.action.accept(this.completionValue); + } + } + } + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Variable stores user who created this panel. + */ + private final User user; + + /** + * This variable holds action that will be performed on accept. + */ + private final Consumer action; + + /** + * Variable stores Challenges addon. + */ + protected final ChallengesAddon addon; + + /** + * Local storing of selected value. + */ + private int completionValue = 1; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ChallengeSelector.java b/src/main/java/world/bentobox/challenges/panel/util/ChallengeSelector.java new file mode 100644 index 0000000..c24b355 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/ChallengeSelector.java @@ -0,0 +1,253 @@ +package world.bentobox.challenges.panel.util; + + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.utils.Constants; + + +/** + * This class creates new GUI that allows to select single challenge, which is returned via consumer. + */ +public class ChallengeSelector extends PagedSelector +{ + private ChallengeSelector(User user, Material border, Map> challengesDescriptionMap, BiConsumer> consumer) + { + super(user); + this.consumer = consumer; + this.challengesDescriptionMap = challengesDescriptionMap; + this.border = border; + + this.elements = challengesDescriptionMap.keySet().stream().toList(); + this.selectedElements = new HashSet<>(this.elements.size()); + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Material border, Map> challengesDescriptionMap, BiConsumer> consumer) + { + new ChallengeSelector(user, border, challengesDescriptionMap, consumer).build(); + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "challenge-selector")); + + PanelUtils.fillBorder(panelBuilder, this.border); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(3, this.createButton(Button.ACCEPT_SELECTED)); + panelBuilder.item(5, this.createButton(Button.CANCEL)); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.getUniqueId().toLowerCase(). + contains(this.searchString.toLowerCase()) || + element.getFriendlyName().toLowerCase(). + contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case ACCEPT_SELECTED -> { + if (!this.selectedElements.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedElements.forEach(challenge -> + description.add(this.user.getTranslation(reference + "element", + "[element]", challenge.getFriendlyName()))); + } + + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(true, this.selectedElements); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); + } + case CANCEL -> { + + icon = new ItemStack(Material.IRON_DOOR); + + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for given challenge. + * @param challenge challenge which button must be created. + * @return new Button for challenge. + */ + @Override + protected PanelItem createElementButton(Challenge challenge) + { + final String reference = Constants.BUTTON + "entity."; + + List description = new ArrayList<>(this.challengesDescriptionMap.get(challenge)); + description.add(""); + + if (this.selectedElements.contains(challenge)) + { + description.add(this.user.getTranslation(reference + "selected")); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-deselect")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + } + + return new PanelItemBuilder(). + name(Util.translateColorCodes(challenge.getFriendlyName())). + icon(challenge.getIcon()). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (!this.selectedElements.add(challenge)) + { + // Remove challenge if it is already selected + this.selectedElements.remove(challenge); + } + + this.build(); + return true; + }). + glow(this.selectedElements.contains(challenge)). + build(); + } + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ACCEPT_SELECTED, + CANCEL + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * This variable stores consumer. + */ + private final BiConsumer> consumer; + + /** + * Current value. + */ + private final List elements; + + /** + * Selected challenges that will be returned to consumer. + */ + private final Set selectedElements; + + /** + * Map that contains all challenge descriptions + */ + private final Map> challengesDescriptionMap; + + /** + * Border Material. + */ + private final Material border; + + /** + * Current value. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java deleted file mode 100644 index 68bf018..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeGUI.java +++ /dev/null @@ -1,146 +0,0 @@ -// -// Created by BONNe -// Copyright - 2019 -// - - -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.List; -import java.util.function.BiConsumer; - -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.Panel; -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.requirements.InventoryRequirements; -import world.bentobox.challenges.database.object.requirements.IslandRequirements; -import world.bentobox.challenges.database.object.requirements.OtherRequirements; -import world.bentobox.challenges.database.object.requirements.Requirements; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class creates GUI that allows to select challenge type. - */ -public class ChallengeTypeGUI -{ - /** - * Default constructor that builds gui. - * @param user User who opens GUI. - * @param lineLength Lore line length - * @param consumer Consumer that allows to get clicked type. - */ - private ChallengeTypeGUI(User user, int lineLength, BiConsumer consumer) - { - this.user = user; - this.lineLength = lineLength; - this.consumer = consumer; - } - - - /** - * This method opens GUI that allows to select challenge type. - * @param user User who opens GUI. - * @param lineLength Lore line length - * @param consumer Consumer that allows to get clicked type. - */ - public static void open(User user, int lineLength, BiConsumer consumer) - { - new ChallengeTypeGUI(user, lineLength, consumer).build(); - } - - - /** - * This method builds GUI that allows to select challenge type. - */ - private void build() - { - PanelBuilder panelBuilder = new PanelBuilder(). - user(this.user). - type(Panel.Type.HOPPER). - name(this.user.getTranslation("challenges.gui.title.admin.type-select")); - - panelBuilder.item(0, this.getButton(Challenge.ChallengeType.INVENTORY)); - panelBuilder.item(1, this.getButton(Challenge.ChallengeType.ISLAND)); - panelBuilder.item(2, this.getButton(Challenge.ChallengeType.OTHER)); - - panelBuilder.build(); - } - - - /** - * Creates ChallengeType button. - * @param type Challenge type which button must be created. - * @return PanelItem button. - */ - private PanelItem getButton(Challenge.ChallengeType type) - { - ItemStack icon; - String name = this.user.getTranslation("challenges.gui.buttons.admin.type." + type.name().toLowerCase()); - List description = new ArrayList<>(); - description.add(this.user.getTranslation("challenges.gui.descriptions.type." + type.name().toLowerCase())); - PanelItem.ClickHandler clickHandler; - - switch (type) - { - case INVENTORY: - icon = new ItemStack(Material.CHEST); - clickHandler = ((panel, user1, clickType, slot) -> { - this.consumer.accept(type, new InventoryRequirements()); - return true; - }); - break; - case ISLAND: - icon = new ItemStack(Material.GRASS_BLOCK); - clickHandler = ((panel, user1, clickType, slot) -> { - this.consumer.accept(type, new IslandRequirements()); - return true; - }); - break; - case OTHER: - icon = new ItemStack(Material.EXPERIENCE_BOTTLE); - clickHandler = ((panel, user1, clickType, slot) -> { - this.consumer.accept(type, new OtherRequirements()); - return true; - }); - break; - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - clickHandler(clickHandler). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * User who runs GUI. - */ - private final User user; - - /** - * Lore line max length. - */ - private final int lineLength; - - /** - * Consumer that returns Challenge Type. - */ - private final BiConsumer consumer; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeSelector.java b/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeSelector.java new file mode 100644 index 0000000..024f441 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/ChallengeTypeSelector.java @@ -0,0 +1,133 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.challenges.panel.util; + + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import world.bentobox.bentobox.api.panels.Panel; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.requirements.*; +import world.bentobox.challenges.utils.Constants; + + +/** + * This class creates GUI that allows to select challenge type. + */ +public record ChallengeTypeSelector(User user, BiConsumer consumer) +{ + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, BiConsumer consumer) + { + new ChallengeTypeSelector(user, consumer).build(); + } + + + /** + * This method builds GUI that allows to select challenge type. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder(). + user(this.user). + type(Panel.Type.HOPPER). + name(this.user.getTranslation(Constants.TITLE + "type-selector")); + + panelBuilder.item(0, this.getButton(Challenge.ChallengeType.INVENTORY_TYPE)); + panelBuilder.item(1, this.getButton(Challenge.ChallengeType.ISLAND_TYPE)); + panelBuilder.item(2, this.getButton(Challenge.ChallengeType.OTHER_TYPE)); + panelBuilder.item(3, this.getButton(Challenge.ChallengeType.STATISTIC_TYPE)); + + panelBuilder.build(); + } + + + /** + * Creates ChallengeType button. + * + * @param type Challenge type which button must be created. + * @return PanelItem button. + */ + private PanelItem getButton(Challenge.ChallengeType type) + { + final String reference = Constants.BUTTON + type.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (type) + { + case INVENTORY_TYPE -> { + icon = new ItemStack(Material.CHEST); + clickHandler = ( + (panel, user1, clickType, slot) -> + { + this.consumer.accept(type, new InventoryRequirements()); + return true; + }); + } + case ISLAND_TYPE -> { + icon = new ItemStack(Material.GRASS_BLOCK); + clickHandler = ( + (panel, user1, clickType, slot) -> + { + this.consumer.accept(type, new IslandRequirements()); + return true; + }); + } + case OTHER_TYPE -> { + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = ( + (panel, user1, clickType, slot) -> + { + this.consumer.accept(type, new OtherRequirements()); + return true; + }); + } + case STATISTIC_TYPE -> { + icon = new ItemStack(Material.BOOK); + clickHandler = ( + (panel, user1, clickType, slot) -> + { + this.consumer.accept(type, new StatisticRequirements()); + return true; + }); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java deleted file mode 100644 index 5a979cc..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java +++ /dev/null @@ -1,114 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.function.Consumer; - -import org.bukkit.Material; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This GUI is used to confirm that user wants to run command, that should be created from - * command string list. - */ -public class ConfirmationGUI -{ - /** - * This constructor inits and opens ConfirmationGUI. - * - * @param user Gui Caller. - */ - public ConfirmationGUI(User user, Consumer consumer) - { - this.user = user; - this.consumer = consumer; - - this.build(); - } - - - /** - * This method builds confirmation panel with 2 buttons. - */ - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.title.admin.confirm-title")); - - GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); - - // Accept buttons - panelBuilder.item(10, this.getButton(true)); - panelBuilder.item(11, this.getButton(true)); - panelBuilder.item(12, this.getButton(true)); - - panelBuilder.item(19, this.getButton(true)); - panelBuilder.item(20, this.getButton(true)); - panelBuilder.item(21, this.getButton(true)); - - panelBuilder.item(28, this.getButton(true)); - panelBuilder.item(29, this.getButton(true)); - panelBuilder.item(30, this.getButton(true)); - - // Cancel Buttons - panelBuilder.item(14, this.getButton(false)); - panelBuilder.item(15, this.getButton(false)); - panelBuilder.item(16, this.getButton(false)); - - panelBuilder.item(23, this.getButton(false)); - panelBuilder.item(24, this.getButton(false)); - panelBuilder.item(25, this.getButton(false)); - - panelBuilder.item(32, this.getButton(false)); - panelBuilder.item(33, this.getButton(false)); - panelBuilder.item(34, this.getButton(false)); - - panelBuilder.item(44, - new PanelItemBuilder(). - icon(Material.OAK_DOOR). - name(this.user.getTranslation("challenges.gui.buttons.return")). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(false); - return true; - }).build()); - - panelBuilder.build(); - } - - - /** - * This method creates button with requested value. - * @param returnValue requested value - * @return PanelItem button. - */ - private PanelItem getButton(boolean returnValue) - { - return new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.admin." + (returnValue ? "accept" : "cancel"))). - icon(returnValue ? Material.GREEN_STAINED_GLASS_PANE : Material.RED_STAINED_GLASS_PANE). - clickHandler((panel, user1, clickType, i) -> { - this.consumer.accept(returnValue); - return true; - }). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * User who wants to run command. - */ - private User user; - - /** - * Stores current Consumer - */ - private Consumer consumer; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/EnvironmentSelector.java b/src/main/java/world/bentobox/challenges/panel/util/EnvironmentSelector.java new file mode 100644 index 0000000..23c8adf --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/EnvironmentSelector.java @@ -0,0 +1,195 @@ +package world.bentobox.challenges.panel.util; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; + +import world.bentobox.bentobox.api.panels.Panel; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class creates panel that allows to select and deselect World Environments. On save it runs + * input consumer with true and selected values. + */ +public record EnvironmentSelector(User user, Set values, BiConsumer> consumer) +{ + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Set values, BiConsumer> consumer) + { + new EnvironmentSelector(user, values, consumer).build(); + } + + + /** + * This method builds GUI that allows to select challenge type. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder(). + user(this.user). + type(Panel.Type.HOPPER). + name(this.user.getTranslation(Constants.TITLE + "environment-selector")); + + panelBuilder.item(0, this.getButton(World.Environment.NORMAL)); + panelBuilder.item(1, this.getButton(World.Environment.NETHER)); + panelBuilder.item(2, this.getButton(World.Environment.THE_END)); + panelBuilder.item(3, this.getButton(Button.ACCEPT_SELECTED)); + panelBuilder.item(4, this.getButton(Button.CANCEL)); + + panelBuilder.build(); + } + + + /** + * This method create button that does some functionality in current gui. + * + * @param environment Environment + * @return PanelItem. + */ + private PanelItem getButton(World.Environment environment) + { + final String reference = Constants.BUTTON + "environment_element."; + + String name = this.user.getTranslation(reference + "name", + "[environment]", Utils.prettifyObject(environment, this.user)); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslationOrNothing(reference + "description", + "[description]", Utils.prettifyDescription(environment, this.user))); + + if (this.values.contains(environment)) + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-deselect")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + } + + PanelItem.ClickHandler clickHandler = (panel, user, clickType, slot) -> + { + if (this.values.contains(environment)) + { + this.values.remove(environment); + } + else + { + this.values.add(environment); + } + + this.build(); + return true; + }; + + ItemStack icon; + + switch (environment) + { + case NORMAL -> icon = new ItemStack(Material.DIRT); + case NETHER -> icon = new ItemStack(Material.NETHERRACK); + case THE_END -> icon = new ItemStack(Material.END_STONE); + default -> icon = new ItemStack(Material.PAPER); + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + glow(this.values.contains(environment)). + build(); + } + + + /** + * This method create button that does some functionality in current gui. + * + * @param button Button functionality. + * @return PanelItem. + */ + private PanelItem getButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case ACCEPT_SELECTED -> { + if (!this.values.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.values.forEach(element -> + description.add(this.user.getTranslation(reference + "element", + "[element]", Utils.prettifyObject(element, this.user)))); + } + + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user, clickType, slot) -> + { + this.consumer.accept(true, this.values); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); + } + case CANCEL -> { + icon = new ItemStack(Material.IRON_DOOR); + clickHandler = (panel, user, clickType, slot) -> + { + this.consumer.accept(false, Collections.emptySet()); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This enum holds all button values in current gui. + */ + private enum Button + { + CANCEL, + ACCEPT_SELECTED + } +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ItemSelector.java similarity index 66% rename from src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java rename to src/main/java/world/bentobox/challenges/panel/util/ItemSelector.java index e7caf56..605a2fe 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/util/ItemSelector.java @@ -16,21 +16,23 @@ import world.bentobox.bentobox.api.panels.PanelListener; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.utils.GuiUtils; +import world.bentobox.challenges.utils.Constants; /** * This class allows to change Input ItemStacks to different ItemStacks. */ -public class ItemSwitchGUI +public record ItemSelector(User user, List itemStacks, BiConsumer> consumer) { - public ItemSwitchGUI(User user, List itemStacks, int lineLength, BiConsumer> consumer) + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, List itemStacks, BiConsumer> consumer) { - this.consumer = consumer; - this.user = user; - this.itemStacks = itemStacks; - this.lineLength = lineLength; - this.build(); + new ItemSelector(user, itemStacks, consumer).build(); } @@ -39,7 +41,9 @@ public class ItemSwitchGUI */ private void build() { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.title.admin.manage-items")); + PanelBuilder panelBuilder = new PanelBuilder(). + user(this.user). + name(this.user.getTranslation(Constants.TITLE + "item-selector")); // Size of inventory that user can set via GUI. panelBuilder.size(45); @@ -66,24 +70,27 @@ public class ItemSwitchGUI /** * This method create button that does some functionality in current gui. + * * @param button Button functionality. * @return PanelItem. */ private PanelItem getButton(Button button) { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + ItemStack icon; - String name; - List description; PanelItem.ClickHandler clickHandler; switch (button) { - case SAVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.save"); - description = Collections.emptyList(); + case SAVE -> { icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { + clickHandler = (panel, user, clickType, slot) -> + { // Magic number 9 - second row. First row is for custom buttons. // Magic number 45 - This GUI is initialed with 45 elements. List returnItems = new ArrayList<>(36); @@ -102,36 +109,37 @@ public class ItemSwitchGUI return true; }; - break; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); } - case CANCEL: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.cancel"); - description = Collections.emptyList(); + case CANCEL -> { icon = new ItemStack(Material.IRON_DOOR); - clickHandler = (panel, user, clickType, slot) -> { + clickHandler = (panel, user, clickType, slot) -> + { this.consumer.accept(false, Collections.emptyList()); return true; }; - break; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); } - case EMPTY: - { - name = ""; - description = Collections.emptyList(); + case EMPTY -> { + description.clear(); + name = "&r"; icon = new ItemStack(Material.BARRIER); - clickHandler = (panel, user, clickType, slot) -> true; - break; + clickHandler = null; + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; } - default: - return null; } return new PanelItemBuilder(). icon(icon). name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(false). + description(description). clickHandler(clickHandler). build(); } @@ -143,11 +151,10 @@ public class ItemSwitchGUI /** - * This CustomPanelItem does no lose Item original MetaData. After PanelItem has been - * created it restores original meta data. It also does not allow to change anything that - * could destroy meta data. + * This CustomPanelItem does no lose Item original MetaData. After PanelItem has been created it + * restores original meta data. It also does not allow to change anything that could destroy meta data. */ - private class CustomPanelItem extends PanelItem + private static class CustomPanelItem extends PanelItem { CustomPanelItem(ItemStack item) { @@ -190,7 +197,7 @@ public class ItemSwitchGUI /** * This CustomPanelListener allows to move items in current panel. */ - private class CustomPanelListener implements PanelListener + private static class CustomPanelListener implements PanelListener { @Override public void setup() @@ -227,30 +234,4 @@ public class ItemSwitchGUI SAVE, EMPTY } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - - /** - * User who opens current gui. - */ - private User user; - - /** - * List with original items. - */ - private List itemStacks; - - /** - * Consumer that returns item stacks on save action. - */ - private BiConsumer> consumer; - - /** - * This variable stores how large line can be, before warp it. - */ - private int lineLength; } diff --git a/src/main/java/world/bentobox/challenges/panel/util/MultiBlockSelector.java b/src/main/java/world/bentobox/challenges/panel/util/MultiBlockSelector.java new file mode 100644 index 0000000..e847c97 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/MultiBlockSelector.java @@ -0,0 +1,294 @@ +package world.bentobox.challenges.panel.util; + + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary things that allows to select single block from all ingame blocks. Selected + * block will be returned via BiConsumer. + */ +public class MultiBlockSelector extends PagedSelector +{ + private MultiBlockSelector(User user, Mode mode, Set excluded, BiConsumer> consumer) + { + super(user); + this.consumer = consumer; + + // Current GUI cannot display air blocks. It crashes with null-pointer + excluded.add(Material.AIR); + excluded.add(Material.CAVE_AIR); + excluded.add(Material.VOID_AIR); + + // Piston head and moving piston is not necessary. useless. + excluded.add(Material.PISTON_HEAD); + excluded.add(Material.MOVING_PISTON); + + // Barrier cannot be accessible to user. + excluded.add(Material.BARRIER); + + this.selectedElements = new HashSet<>(); + + this.elements = Arrays.stream(Material.values()). + filter(material -> !excluded.contains(material)). + filter(material -> { + switch (mode) + { + case BLOCKS -> { + return material.isBlock(); + } + case ITEMS -> { + return material.isItem(); + } + default -> { + return true; + } + } + }). + // Sort by name + sorted(Comparator.comparing(Material::name)). + collect(Collectors.toList()); + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Mode mode, Set excluded, BiConsumer> consumer) + { + new MultiBlockSelector(user, mode, excluded, consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, BiConsumer> consumer) + { + new MultiBlockSelector(user, Mode.ANY, new HashSet<>(), consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "block-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(3, this.createButton(Button.ACCEPT_SELECTED)); + panelBuilder.item(5, this.createButton(Button.CANCEL)); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case ACCEPT_SELECTED -> { + if (!this.selectedElements.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedElements.forEach(material -> + description.add(this.user.getTranslation(reference + "element", + "[element]", Utils.prettifyObject(material, this.user)))); + } + + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(true, this.selectedElements); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); + } + case CANCEL -> { + + icon = new ItemStack(Material.IRON_DOOR); + + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for given material. + * @param material material which button must be created. + * @return new Button for material. + */ + @Override + protected PanelItem createElementButton(Material material) + { + final String reference = Constants.BUTTON + "material."; + + List description = new ArrayList<>(); + description.add(this.user.getTranslation(reference + "description", + "[id]", material.name())); + + if (this.selectedElements.contains(material)) + { + description.add(this.user.getTranslation(reference + "selected")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-deselect")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[material]", + Utils.prettifyObject(material, this.user))). + icon(PanelUtils.getMaterialItem(material)). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (!this.selectedElements.add(material)) + { + // Remove material if it is already selected + this.selectedElements.remove(material); + } + + this.build(); + return true; + }). + glow(this.selectedElements.contains(material)). + build(); + } + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ACCEPT_SELECTED, + CANCEL + } + + + public enum Mode + { + BLOCKS, + ITEMS, + ANY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * Set that contains selected materials. + */ + private final Set selectedElements; + + /** + * This variable stores consumer. + */ + private final BiConsumer> consumer; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/MultiEntitySelector.java b/src/main/java/world/bentobox/challenges/panel/util/MultiEntitySelector.java new file mode 100644 index 0000000..fefe992 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/MultiEntitySelector.java @@ -0,0 +1,282 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary things that allows to select single block from all ingame blocks. Selected + * block will be returned via BiConsumer. + */ +public class MultiEntitySelector extends PagedSelector +{ + private MultiEntitySelector(User user, boolean asEgg, Mode mode, Set excluded, BiConsumer> consumer) + { + super(user); + + this.consumer = consumer; + this.asEgg = asEgg; + this.selectedElements = new HashSet<>(); + + this.elements = Arrays.stream(EntityType.values()). + filter(entity -> !excluded.contains(entity)). + filter(entity -> { + if (mode == Mode.ALIVE) + { + return entity.isAlive(); + } + else + { + return true; + } + }). + // Sort by name + sorted(Comparator.comparing(EntityType::name)). + collect(Collectors.toList()); + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEgg, Mode mode, Set excluded, BiConsumer> consumer) + { + new MultiEntitySelector(user, asEgg, mode, excluded, consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEgg, BiConsumer> consumer) + { + new MultiEntitySelector(user, asEgg, Mode.ANY, new HashSet<>(), consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "entity-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(3, this.createButton(Button.ACCEPT_SELECTED)); + panelBuilder.item(5, this.createButton(Button.CANCEL)); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case ACCEPT_SELECTED -> { + if (!this.selectedElements.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedElements.forEach(material -> + description.add(this.user.getTranslation(reference + "element", + "[element]", Utils.prettifyObject(material, this.user)))); + } + + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(true, this.selectedElements); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-save")); + } + case CANCEL -> { + + icon = new ItemStack(Material.IRON_DOOR); + + clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for given entity. + * @param entity entity which button must be created. + * @return new Button for entity. + */ + @Override + protected PanelItem createElementButton(EntityType entity) + { + final String reference = Constants.BUTTON + "entity."; + + List description = new ArrayList<>(); + description.add(this.user.getTranslation(reference + "description", + "[id]", entity.name())); + + if (this.selectedElements.contains(entity)) + { + description.add(this.user.getTranslation(reference + "selected")); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-deselect")); + } + else + { + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[entity]", + Utils.prettifyObject(entity, this.user))). + icon(this.asEgg ? PanelUtils.getEntityEgg(entity) : PanelUtils.getEntityHead(entity)). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (!this.selectedElements.add(entity)) + { + // Remove entity if it is already selected + this.selectedElements.remove(entity); + } + + this.build(); + return true; + }). + glow(this.selectedElements.contains(entity)). + build(); + } + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ACCEPT_SELECTED, + CANCEL + } + + + public enum Mode + { + ALIVE, + ANY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * Set that contains selected materials. + */ + private final Set selectedElements; + + /** + * This variable stores consumer. + */ + private final BiConsumer> consumer; + + /** + * Indicates that entity must be displayed as egg. + */ + private final boolean asEgg; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java b/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java deleted file mode 100644 index e58a153..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java +++ /dev/null @@ -1,559 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import org.bukkit.Material; -import org.bukkit.conversations.Conversation; -import org.bukkit.conversations.ConversationContext; -import org.bukkit.conversations.ConversationFactory; -import org.bukkit.conversations.NumericPrompt; -import org.bukkit.conversations.Prompt; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; - -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This gui allows to change current number and returns it to previous GUI - */ -public class NumberGUI -{ - public NumberGUI(User user, int value, int lineLength, BiConsumer consumer) - { - this(user, value, Integer.MIN_VALUE, Integer.MAX_VALUE, lineLength, consumer); - } - - - public NumberGUI(User user, int value, int minValue, int lineLength, BiConsumer consumer) - { - this(user, value, minValue, Integer.MAX_VALUE, lineLength, consumer); - } - - - public NumberGUI(User user, int value, int minValue, int maxValue, int lineLength, BiConsumer consumer) - { - this.user = user; - this.value = value; - this.consumer = consumer; - - this.minValue = minValue; - this.maxValue = maxValue; - - this.currentOperation = Button.SET; - - this.lineLength = lineLength; - - this.build(); - } - - - /** - * This method builds panel that allows to change given number value. - */ - private void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.title.admin.manage-numbers")); - - GuiUtils.fillBorder(panelBuilder); - - // Others - panelBuilder.item(1, this.getButton(Button.SAVE)); - - panelBuilder.item(19, this.getButton(Button.VALUE)); - panelBuilder.item(44, this.getButton(Button.CANCEL)); - - panelBuilder.item(2, this.getButton(Button.INPUT)); - - // operations - panelBuilder.item(3, this.getButton(Button.SET)); - panelBuilder.item(4, this.getButton(Button.INCREASE)); - panelBuilder.item(5, this.getButton(Button.REDUCE)); - panelBuilder.item(6, this.getButton(Button.MULTIPLY)); - - // Numbers - panelBuilder.item(11, this.createNumberButton(1)); - panelBuilder.item(12, this.createNumberButton(10)); - panelBuilder.item(13, this.createNumberButton(100)); - panelBuilder.item(14, this.createNumberButton(1000)); - panelBuilder.item(15, this.createNumberButton(10000)); - - panelBuilder.item(20, this.createNumberButton(2)); - panelBuilder.item(21, this.createNumberButton(20)); - panelBuilder.item(22, this.createNumberButton(200)); - panelBuilder.item(23, this.createNumberButton(2000)); - panelBuilder.item(24, this.createNumberButton(20000)); - - panelBuilder.item(29, this.createNumberButton(5)); - panelBuilder.item(30, this.createNumberButton(50)); - panelBuilder.item(31, this.createNumberButton(500)); - panelBuilder.item(32, this.createNumberButton(5000)); - panelBuilder.item(33, this.createNumberButton(50000)); - - panelBuilder.build(); - } - - - /** - * This method creates PanelItem with required functionality. - * @param button Functionality requirement. - * @return PanelItem with functionality. - */ - private PanelItem getButton(Button button) - { - ItemStack icon; - String name; - String description; - PanelItem.ClickHandler clickHandler; - boolean glow; - - switch (button) - { - case SAVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.save"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.save"); - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - this.consumer.accept(true, this.value); - return true; - }; - glow = false; - break; - } - case CANCEL: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.cancel"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.cancel"); - icon = new ItemStack(Material.OAK_DOOR); - clickHandler = (panel, user, clickType, slot) -> { - this.consumer.accept(false, this.value); - return true; - }; - glow = false; - break; - } - case INPUT: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.input"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.input"); - icon = new ItemStack(Material.ANVIL); - clickHandler = (panel, user, clickType, slot) -> { - - this.getNumberInput(number -> { - if (number != null) - { - // Null value is passed if user write cancel. - this.value = number.intValue(); - } - - this.build(); - }, - this.user.getTranslation("challenges.gui.questions.admin.number")); - - return true; - }; - glow = false; - break; - } - case VALUE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.value"); - description = this.user.getTranslation("challenges.gui.descriptions.current-value", "[value]", Integer.toString(this.value)); - icon = new ItemStack(Material.PAPER); - clickHandler = (panel, user, clickType, slot) -> true; - glow = false; - break; - } - case SET: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.set"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.set"); - icon = new ItemStack(Material.WHITE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.currentOperation = Button.SET; - this.build(); - return true; - }; - glow = this.currentOperation.equals(Button.SET); - break; - } - case INCREASE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.increase"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.increase"); - icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.currentOperation = Button.INCREASE; - this.build(); - return true; - }; - glow = this.currentOperation.equals(Button.INCREASE); - break; - } - case REDUCE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.reduce"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.reduce"); - icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.currentOperation = Button.REDUCE; - this.build(); - return true; - }; - glow = this.currentOperation.equals(Button.REDUCE); - break; - } - case MULTIPLY: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.multiply"); - description = this.user.getTranslation("challenges.gui.descriptions.admin.multiply"); - icon = new ItemStack(Material.BLUE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.currentOperation = Button.MULTIPLY; - this.build(); - return true; - }; - glow = this.currentOperation.equals(Button.MULTIPLY); - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(glow). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates Number Button based on input number. - * @param number Number which button must be created. - * @return PanelItem that represents number button. - */ - private PanelItem createNumberButton(int number) - { - PanelItemBuilder itemBuilder = new PanelItemBuilder(); - - switch (this.currentOperation) - { - case SET: - { - itemBuilder.name(this.user.getTranslation("challenges.gui.buttons.admin.number","[number]", Integer.toString(number))); - itemBuilder.icon(Material.WHITE_STAINED_GLASS_PANE); - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - this.value = number; - - if (this.value > this.maxValue) - { - this.user.sendMessage("challenges.errors.not-valid-integer", - "[value]", Integer.toString(this.value), - "[min]", Integer.toString(this.minValue), - "[max]", Integer.toString(this.maxValue)); - - this.value = this.maxValue; - } - - if (this.value < this.minValue) - { - this.user.sendMessage("challenges.errors.not-valid-integer", - "[value]", Integer.toString(this.value), - "[min]", Integer.toString(this.minValue), - "[max]", Integer.toString(this.maxValue)); - - this.value = this.minValue; - } - - this.build(); - return true; - }); - - break; - } - case INCREASE: - { - itemBuilder.name(this.user.getTranslation("challenges.gui.buttons.admin.number","[number]", Integer.toString(number))); - itemBuilder.icon(Material.GREEN_STAINED_GLASS_PANE); - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - this.value += number; - - if (this.value > this.maxValue) - { - this.user.sendMessage("challenges.errors.not-valid-integer", - "[value]", Integer.toString(this.value), - "[min]", Integer.toString(this.minValue), - "[max]", Integer.toString(this.maxValue)); - - this.value = this.maxValue; - } - - this.build(); - return true; - }); - - break; - } - case REDUCE: - { - itemBuilder.name(this.user.getTranslation("challenges.gui.buttons.admin.number","[number]", Integer.toString(number))); - itemBuilder.icon(Material.RED_STAINED_GLASS_PANE); - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - this.value -= number; - - if (this.value < this.minValue) - { - this.user.sendMessage("challenges.errors.not-valid-integer", - "[value]", Integer.toString(this.value), - "[min]", Integer.toString(this.minValue), - "[max]", Integer.toString(this.maxValue)); - - this.value = this.minValue; - } - - this.build(); - return true; - }); - - break; - } - case MULTIPLY: - { - itemBuilder.name(this.user.getTranslation("challenges.gui.buttons.admin.number","[number]", Integer.toString(number))); - itemBuilder.icon(Material.BLUE_STAINED_GLASS_PANE); - itemBuilder.clickHandler((panel, user1, clickType, i) -> { - this.value *= number; - - if (this.value > this.maxValue) - { - this.user.sendMessage("challenges.errors.not-valid-integer", - "[value]", Integer.toString(this.value), - "[min]", Integer.toString(this.minValue), - "[max]", Integer.toString(this.maxValue)); - - this.value = this.maxValue; - } - - this.build(); - return true; - }); - - break; - } - default: - break; - } - - return itemBuilder.build(); - } - - - // --------------------------------------------------------------------- - // Section: Conversation - // --------------------------------------------------------------------- - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on - * inputText in chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - */ - private void getNumberInput(Consumer consumer, @NonNull String question) - { - final User user = this.user; - - Conversation conversation = - new ConversationFactory(BentoBox.getInstance()).withFirstPrompt( - new NumericPrompt() - { - /** - * Override this method to perform some action with - * the user's integer response. - * - * @param context Context information about the - * conversation. - * @param input The user's response as a {@link - * Number}. - * @return The next {@link Prompt} in the prompt - * graph. - */ - @Override - protected Prompt acceptValidatedInput(ConversationContext context, Number input) - { - // Add answer to consumer. - consumer.accept(input); - // Reopen GUI - NumberGUI.this.build(); - // End conversation - return Prompt.END_OF_CONVERSATION; - } - - - /** - * Override this method to do further validation on - * the numeric player input after the input has been - * determined to actually be a number. - * - * @param context Context information about the - * conversation. - * @param input The number the player provided. - * @return The validity of the player's input. - */ - @Override - protected boolean isNumberValid(ConversationContext context, Number input) - { - return input.intValue() >= NumberGUI.this.minValue && - input.intValue() <= NumberGUI.this.maxValue; - } - - - /** - * Optionally override this method to display an - * additional message if the user enters an invalid - * number. - * - * @param context Context information about the - * conversation. - * @param invalidInput The invalid input provided by - * the user. - * @return A message explaining how to correct the - * input. - */ - @Override - protected String getInputNotNumericText(ConversationContext context, - String invalidInput) - { - return NumberGUI.this.user - .getTranslation("challenges.errors.not-a-integer", "[value]", invalidInput); - } - - - /** - * Optionally override this method to display an - * additional message if the user enters an invalid - * numeric input. - * - * @param context Context information about the - * conversation. - * @param invalidInput The invalid input provided by - * the user. - * @return A message explaining how to correct the - * input. - */ - @Override - protected String getFailedValidationText(ConversationContext context, - Number invalidInput) - { - return NumberGUI.this.user.getTranslation("challenges.errors.not-valid-integer", - "[value]", invalidInput.toString(), - "[min]", Integer.toString(NumberGUI.this.minValue), - "[max]", Integer.toString(NumberGUI.this.maxValue)); - } - - - /** - * @see Prompt#getPromptText(ConversationContext) - */ - @Override - public String getPromptText(ConversationContext conversationContext) - { - // Close input GUI. - user.closeInventory(); - - // There are no editable message. Just return question. - return question; - } - }). - withLocalEcho(false). - // On cancel conversation will be closed. - withEscapeSequence("cancel"). - // Use null value in consumer to detect if user has abandoned conversation. - addConversationAbandonedListener(abandonedEvent -> - { - if (!abandonedEvent.gracefulExit()) - { - consumer.accept(null); - } - }). - withPrefix(context -> - NumberGUI.this.user.getTranslation("challenges.gui.questions.prefix")). - buildConversation(user.getPlayer()); - - conversation.begin(); - } - - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * This enum contains all button types. - */ - private enum Button - { - SAVE, - CANCEL, - INPUT, - - VALUE, - - SET, - INCREASE, - REDUCE, - MULTIPLY - } - - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * This variable stores current GUI consumer. - */ - private BiConsumer consumer; - - /** - * User who runs GUI. - */ - private User user; - - /** - * Current value. - */ - private int value; - - /** - * Minimal value that is allowed to set. - */ - private int minValue; - - /** - * Maximal value that is allowed to set. - */ - private int maxValue; - - /** - * This variable holds which operation now is processed. - */ - private Button currentOperation; - - /** - * This variable stores how large line can be, before warp it. - */ - private int lineLength; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/PagedSelector.java b/src/main/java/world/bentobox/challenges/panel/util/PagedSelector.java new file mode 100644 index 0000000..290fdf2 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/PagedSelector.java @@ -0,0 +1,262 @@ +// +// Created by BONNe +// Copyright - 2021 +// + + +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.utils.Constants; + + +/** + * This single abstract class will manage paged selectors similar to CommonPagedPanel. + */ +public abstract class PagedSelector +{ + /** + * Instantiates a new Paged selector. + * + * @param user the user + */ + protected PagedSelector(User user) + { + this.user = user; + this.searchString = ""; + } + + + /** + * Build. + */ + protected abstract void build(); + + + /** + * Create element button panel item. + * + * @param object the object + * @return the panel item + */ + protected abstract PanelItem createElementButton(T object); + + + /** + * This method is called when filter value is updated. + */ + protected abstract void updateFilters(); + + + /** + * Populate elements. + * + * @param panelBuilder the panel builder + * @param objectList the object list + */ + protected void populateElements(PanelBuilder panelBuilder, List objectList) + { + final int MAX_ELEMENTS = 21; + final int size = objectList.size(); + + if (this.pageIndex < 0) + { + this.pageIndex = size / MAX_ELEMENTS; + } + else if (this.pageIndex > (size / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int objectIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (objectIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + objectIndex < size && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createElementButton(objectList.get(objectIndex++))); + } + + index++; + } + + // Add next page button if there are more than MAX_ELEMENTS objects and pageIndex + 1 is + // larger or equal to the max page count. + if (size > MAX_ELEMENTS && !(1.0 * size / MAX_ELEMENTS <= this.pageIndex + 1)) + { + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + + } + + // Add previous page button if pageIndex is not 0. + if (this.pageIndex > 0) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + } + + // Add search button only if there is more than MAX_ELEMENTS objects or searchString + // is not blank. + if (!this.searchString.isBlank() || objectList.size() > MAX_ELEMENTS) + { + panelBuilder.item(40, this.getButton(CommonButtons.SEARCH)); + } + } + + + /** + * This method returns PanelItem that represents given Button. + * @param button Button that must be returned. + * @return PanelItem with requested functionality. + */ + protected PanelItem getButton(CommonButtons button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + + if (button == CommonButtons.NEXT) + { + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_NUMBER, String.valueOf(this.pageIndex + 2))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-next")); + + icon = new ItemStack(Material.OAK_SIGN, this.pageIndex + 2); + clickHandler = (panel, user, clickType, slot) -> + { + this.pageIndex++; + this.build(); + return true; + }; + } + else if (button == CommonButtons.PREVIOUS) + { + description.add(this.user.getTranslation(reference + "description", + Constants.PARAMETER_NUMBER, String.valueOf(this.pageIndex))); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-previous")); + + icon = new ItemStack(Material.OAK_SIGN, Math.max(1, this.pageIndex)); + clickHandler = (panel, user, clickType, slot) -> + { + this.pageIndex--; + this.build(); + return true; + }; + } + else if (button == CommonButtons.SEARCH) + { + description.add(this.user.getTranslation(reference + "description")); + + if (this.searchString != null && !this.searchString.isEmpty()) + { + description.add(this.user.getTranslation(reference + "search", + Constants.PARAMETER_VALUE, this.searchString)); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-edit")); + + if (this.searchString != null && !this.searchString.isEmpty()) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-clear")); + } + + icon = new ItemStack(Material.ANVIL); + + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + // Clear string. + this.searchString = ""; + this.updateFilters(); + // Rebuild gui. + this.build(); + } + else + { + // Create consumer that process description change + Consumer consumer = value -> + { + if (value != null) + { + this.searchString = value; + this.updateFilters(); + } + + this.build(); + }; + + // start conversation + ConversationUtils.createStringInput(consumer, + user, + user.getTranslation(Constants.CONVERSATIONS + "write-search"), + user.getTranslation(Constants.CONVERSATIONS + "search-updated")); + } + + return true; + }; + } + else + { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * Next and Previous Buttons. + */ + private enum CommonButtons + { + NEXT, + PREVIOUS, + SEARCH + } + + + /** + * Current page index. + */ + private int pageIndex; + + /** + * User who opens gui. + */ + protected final User user; + + /** + * Text that contains filter string. + */ + protected String searchString; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java deleted file mode 100644 index d3ffdcb..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java +++ /dev/null @@ -1,276 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class contains all necessary things that allows to select single block from all ingame blocks. Selected - * block will be returned via BiConsumer. - */ -public class SelectBlocksGUI -{ - public SelectBlocksGUI(User user, BiConsumer> consumer) - { - this(user, false, new HashSet<>(), consumer); - } - - public SelectBlocksGUI(User user, boolean singleSelect, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.singleSelect = singleSelect; - - // Current GUI cannot display air blocks. It crashes with null-pointer - Set excludedMaterial = new HashSet<>(); - - excludedMaterial.add(Material.AIR); - excludedMaterial.add(Material.CAVE_AIR); - excludedMaterial.add(Material.VOID_AIR); - - // Piston head and moving piston is not necessary. useless. - excludedMaterial.add(Material.PISTON_HEAD); - excludedMaterial.add(Material.MOVING_PISTON); - - // Barrier cannot be accessible to user. - excludedMaterial.add(Material.BARRIER); - - this.elements = new ArrayList<>(); - this.selectedMaterials = new HashSet<>(); - - for (Material material : Material.values()) - { - if (!material.isLegacy() && !excludedMaterial.contains(material)) - { - this.elements.add(material); - } - } - - this.build(0); - } - - - public SelectBlocksGUI(User user, boolean singleSelect, Set excludedMaterial, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.singleSelect = singleSelect; - - // Current GUI cannot display air blocks. It crashes with null-pointer - excludedMaterial.add(Material.AIR); - excludedMaterial.add(Material.CAVE_AIR); - excludedMaterial.add(Material.VOID_AIR); - - // Piston head and moving piston is not necessary. useless. - excludedMaterial.add(Material.PISTON_HEAD); - excludedMaterial.add(Material.MOVING_PISTON); - - // Barrier cannot be accessible to user. - excludedMaterial.add(Material.BARRIER); - - this.elements = new ArrayList<>(); - this.selectedMaterials = new HashSet<>(); - - for (Material material : Material.values()) - { - if (material.isBlock() && !material.isLegacy() && !excludedMaterial.contains(material)) - { - this.elements.add(material); - } - } - - this.build(0); - } - - -// --------------------------------------------------------------------- -// Section: Methods -// --------------------------------------------------------------------- - - - /** - * This method builds all necessary elements in GUI panel. - */ - public void build(int pageIndex) - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user). - name(this.user.getTranslation("challenges.gui.title.admin.select-block")); - - GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); - - final int MAX_ELEMENTS = 21; - final int correctPage; - - if (pageIndex < 0) - { - correctPage = this.elements.size() / MAX_ELEMENTS; - } - else if (pageIndex > (this.elements.size() / MAX_ELEMENTS)) - { - correctPage = 0; - } - else - { - correctPage = pageIndex; - } - - int entitiesIndex = MAX_ELEMENTS * correctPage; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (entitiesIndex < ((correctPage + 1) * MAX_ELEMENTS) && - entitiesIndex < this.elements.size()) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, this.createMaterialButton(this.elements.get(entitiesIndex++))); - } - - index++; - } - - panelBuilder.item(3, - new PanelItemBuilder(). - icon(Material.RED_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.admin.cancel")). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(false, null); - return true; - }).build()); - - - List description = new ArrayList<>(); - if (!this.selectedMaterials.isEmpty()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.selected") + ":"); - this.selectedMaterials.forEach(material -> description.add(" - " + material.name())); - } - - panelBuilder.item(5, - new PanelItemBuilder(). - icon(Material.GREEN_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.admin.accept")). - description(description). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(true, this.selectedMaterials); - return true; - }).build()); - - if (this.elements.size() > MAX_ELEMENTS) - { - // Navigation buttons if necessary - - panelBuilder.item(18, - new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.previous")). - clickHandler((panel, user1, clickType, slot) -> { - this.build(correctPage - 1); - return true; - }).build()); - - panelBuilder.item(26, - new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.next")). - clickHandler((panel, user1, clickType, slot) -> { - this.build(correctPage + 1); - return true; - }).build()); - } - - panelBuilder.item(44, - new PanelItemBuilder(). - icon(Material.OAK_DOOR). - name(this.user.getTranslation("challenges.gui.buttons.return")). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(false, null); - return true; - }).build()); - - panelBuilder.build(); - } - - - /** - * This method creates PanelItem that represents given material. - * Some materials is not displayable in Inventory GUI, so they are replaced with "placeholder" items. - * @param material Material which icon must be created. - * @return PanelItem that represents given material. - */ - private PanelItem createMaterialButton(Material material) - { - ItemStack itemStack = GuiUtils.getMaterialItem(material); - - return new PanelItemBuilder(). - name(WordUtils.capitalize(material.name().toLowerCase().replace("_", " "))). - description(this.selectedMaterials.contains(material) ? - this.user.getTranslation("challenges.gui.descriptions.admin.selected") : ""). - icon(itemStack). - clickHandler((panel, user1, clickType, slot) -> { - if (!this.singleSelect && clickType.isRightClick()) - { - if (!this.selectedMaterials.add(material)) - { - this.selectedMaterials.remove(material); - } - - panel.getInventory().setItem(slot, this.createMaterialButton(material).getItem()); - } - else - { - this.selectedMaterials.add(material); - this.consumer.accept(true, this.selectedMaterials); - } - - return true; - }). - glow(!itemStack.getType().equals(material)). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * List with elements that will be displayed in current GUI. - */ - private List elements; - - /** - * Set that contains selected materials. - */ - private Set selectedMaterials; - - /** - * This variable stores consumer. - */ - private BiConsumer> consumer; - - /** - * User who runs GUI. - */ - private User user; - - /** - * This indicate that return set must contain only single item. - */ - private boolean singleSelect; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java deleted file mode 100644 index 16cc3b7..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java +++ /dev/null @@ -1,215 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.event.inventory.ClickType; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class creates new GUI that allows to select single challenge, which is returned via consumer. - */ -public class SelectChallengeGUI -{ - public SelectChallengeGUI(User user, Map> challengesDescriptionMap, int lineLength, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.challengesList = new ArrayList<>(challengesDescriptionMap.keySet()); - this.challengesDescriptionMap = challengesDescriptionMap; - this.lineLength = lineLength; - this.selectedChallenges = new HashSet<>(this.challengesList.size()); - - this.build(0); - } - - - /** - * This method builds panel that allows to select single challenge from input challenges. - */ - private void build(int pageIndex) - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.title.admin.select-challenge")); - - GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); - - // Maximal elements in page. - final int MAX_ELEMENTS = 21; - - final int correctPage; - - if (pageIndex < 0) - { - correctPage = this.challengesList.size() / MAX_ELEMENTS; - } - else if (pageIndex > (this.challengesList.size() / MAX_ELEMENTS)) - { - correctPage = 0; - } - else - { - correctPage = pageIndex; - } - - panelBuilder.item(4, - new PanelItemBuilder(). - icon(Material.RED_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.return")). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(false, null); - return true; - }).build()); - - if (this.challengesList.size() > MAX_ELEMENTS) - { - // Navigation buttons if necessary - - panelBuilder.item(18, - new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.previous")). - clickHandler((panel, user1, clickType, slot) -> { - this.build(correctPage - 1); - return true; - }).build()); - - panelBuilder.item(26, - new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.next")). - clickHandler((panel, user1, clickType, slot) -> { - this.build(correctPage + 1); - return true; - }).build()); - } - - int challengesIndex = MAX_ELEMENTS * correctPage; - - // I want first row to be only for navigation and return button. - int index = 10; - - while (challengesIndex < ((correctPage + 1) * MAX_ELEMENTS) && - challengesIndex < this.challengesList.size() && - index < 36) - { - if (!panelBuilder.slotOccupied(index)) - { - panelBuilder.item(index, - this.createChallengeButton(this.challengesList.get(challengesIndex++))); - } - - index++; - } - - panelBuilder.item(44, - new PanelItemBuilder(). - icon(Material.OAK_DOOR). - name(this.user.getTranslation("challenges.gui.buttons.return")). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(false, null); - return true; - }).build()); - - panelBuilder.build(); - } - - - /** - * This method builds PanelItem for given challenge. - * @param challenge Challenge which PanelItem must be created. - * @return new PanelItem for given Challenge. - */ - private PanelItem createChallengeButton(Challenge challenge) - { - List description; - - if (this.selectedChallenges.contains(challenge)) - { - description = new ArrayList<>(); - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.selected")); - description.addAll(this.challengesDescriptionMap.get(challenge)); - } - else - { - description = this.challengesDescriptionMap.get(challenge); - } - - - return new PanelItemBuilder(). - name(ChatColor.translateAlternateColorCodes('&', challenge.getFriendlyName())). - description(GuiUtils.stringSplit(description, this.lineLength)). - icon(challenge.getIcon()). - clickHandler((panel, user1, clickType, slot) -> { - if (clickType == ClickType.RIGHT) - { - // If challenge is not selected, then select :) - if (!this.selectedChallenges.remove(challenge)) - { - this.selectedChallenges.add(challenge); - } - - // Reset button. - panel.getInventory().setItem(slot, this.createChallengeButton(challenge).getItem()); - } - else - { - this.selectedChallenges.add(challenge); - this.consumer.accept(true, this.selectedChallenges); - } - - return true; - }). - glow(this.selectedChallenges.contains(challenge)). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - - /** - * This variable stores consumer. - */ - private BiConsumer> consumer; - - /** - * User who runs GUI. - */ - private User user; - - /** - * Current value. - */ - private List challengesList; - - /** - * Selected challenges that will be returned to consumer. - */ - private Set selectedChallenges; - - /** - * Map that contains all challenge descriptions - */ - private Map> challengesDescriptionMap; - - /** - * This variable stores how large line can be, before warp it. - */ - private int lineLength; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java deleted file mode 100644 index 4104f8a..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java +++ /dev/null @@ -1,237 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Material; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This GUI allows to select single entity and return it via Consumer. - */ -public class SelectEntityGUI -{ - public SelectEntityGUI(User user, BiConsumer> consumer) - { - this(user, Collections.emptySet(), true, consumer); - } - - - public SelectEntityGUI(User user, Set excludedEntities, boolean asEggs, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.asEggs = asEggs; - - this.entities = new ArrayList<>(EntityType.values().length); - this.selectedEntities = new HashSet<>(EntityType.values().length); - - for (EntityType entityType : EntityType.values()) - { - if (entityType.isAlive() && !excludedEntities.contains(entityType)) - { - this.entities.add(entityType); - } - } - - // Sort mobs by their name for easier search. - this.entities.sort(Comparator.comparing(Enum::name)); - - this.build(0); - } - - -// --------------------------------------------------------------------- -// Section: Methods -// --------------------------------------------------------------------- - - - /** - * This method builds - */ - private void build(int pageIndex) - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.title.admin.select-entity")); - - GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); - - // Maximal elements in page. - final int MAX_ELEMENTS = 21; - - final int correctPage; - - if (pageIndex < 0) - { - correctPage = this.entities.size() / MAX_ELEMENTS; - } - else if (pageIndex > (this.entities.size() / MAX_ELEMENTS)) - { - correctPage = 0; - } - else - { - correctPage = pageIndex; - } - - panelBuilder.item(3, - new PanelItemBuilder(). - icon(Material.RED_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.admin.cancel")). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(false, null); - return true; - }).build()); - - List description = new ArrayList<>(); - if (!this.selectedEntities.isEmpty()) - { - description.add(this.user.getTranslation("challenges.gui.descriptions.admin.selected") + ":"); - this.selectedEntities.forEach(entity -> description.add(" - " + entity.name())); - } - - panelBuilder.item(5, - new PanelItemBuilder(). - icon(Material.GREEN_STAINED_GLASS_PANE). - name(this.user.getTranslation("challenges.gui.buttons.admin.accept")). - description(description). - clickHandler( (panel, user1, clickType, slot) -> { - this.consumer.accept(true, this.selectedEntities); - return true; - }).build()); - - if (this.entities.size() > MAX_ELEMENTS) - { - // Navigation buttons if necessary - - panelBuilder.item(18, - new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.previous")). - clickHandler((panel, user1, clickType, slot) -> { - this.build(correctPage - 1); - return true; - }).build()); - - panelBuilder.item(26, - new PanelItemBuilder(). - icon(Material.OAK_SIGN). - name(this.user.getTranslation("challenges.gui.buttons.next")). - clickHandler((panel, user1, clickType, slot) -> { - this.build(correctPage + 1); - return true; - }).build()); - } - - int entitiesIndex = MAX_ELEMENTS * correctPage; - - // I want first row to be only for navigation and return button. - int slot = 10; - - while (entitiesIndex < ((correctPage + 1) * MAX_ELEMENTS) && - entitiesIndex < this.entities.size() && - slot < 36) - { - if (!panelBuilder.slotOccupied(slot)) - { - panelBuilder.item(slot, - this.createEntityButton(this.entities.get(entitiesIndex++))); - } - - slot++; - } - - panelBuilder.item(44, - new PanelItemBuilder(). - icon(Material.OAK_DOOR). - name(this.user.getTranslation("challenges.gui.buttons.return")). - clickHandler( (panel, user1, clickType, i) -> { - this.consumer.accept(false, null); - return true; - }).build()); - - panelBuilder.build(); - } - - - /** - * This method builds PanelItem for given entity. - * @param entity Entity which PanelItem must be created. - * @return new PanelItem for given Entity. - */ - private PanelItem createEntityButton(EntityType entity) - { - ItemStack itemStack = this.asEggs ? GuiUtils.getEntityEgg(entity) : GuiUtils.getEntityHead(entity); - - return new PanelItemBuilder(). - name(WordUtils.capitalize(entity.name().toLowerCase().replace("_", " "))). - icon(itemStack). - description(this.selectedEntities.contains(entity) ? - this.user.getTranslation("challenges.gui.descriptions.admin.selected") : ""). - clickHandler((panel, user1, clickType, slot) -> { - if (clickType.isRightClick()) - { - if (!this.selectedEntities.add(entity)) - { - this.selectedEntities.remove(entity); - } - - panel.getInventory().setItem(slot, this.createEntityButton(entity).getItem()); - } - else - { - this.selectedEntities.add(entity); - this.consumer.accept(true, this.selectedEntities); - } - - return true; - }). - glow(this.selectedEntities.contains(entity)). - build(); - } - - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * This variable stores consumer. - */ - private BiConsumer> consumer; - - /** - * Set that contains selected entities. - */ - private Set selectedEntities; - - /** - * User who runs GUI. - */ - private User user; - - /** - * This variable stores if mobs must be displayed as Eggs "true" or Heads "false". - */ - private boolean asEggs; - - /** - * Entities that must be in list. - */ - private List entities; -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java deleted file mode 100644 index 928998a..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java +++ /dev/null @@ -1,148 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.Collections; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.bukkit.Material; -import org.bukkit.World; - -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This class creates panel that allows to select and deselect World Environments. On save it runs - * input consumer with true and selected values. - */ -public class SelectEnvironmentGUI -{ - public SelectEnvironmentGUI(User user, Set values, BiConsumer> consumer) - { - this.user = user; - this.values = values; - this.consumer = consumer; - - this.build(); - } - - - /** - * This method builds environment select panel. - */ - private void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.title.admin.toggle-environment")); - - GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); - - panelBuilder.item(3, new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.admin.save")). - icon(Material.GREEN_STAINED_GLASS_PANE). - clickHandler((panel, user1, clickType, index) -> { - this.consumer.accept(true, this.values); - return true; - }). - build()); - - panelBuilder.item(5, new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.admin.cancel")). - icon(Material.RED_STAINED_GLASS_PANE). - clickHandler((panel, user1, clickType, i) -> { - this.consumer.accept(false, Collections.emptySet()); - return true; - }). - build()); - - panelBuilder.item(20, new PanelItemBuilder(). - name(World.Environment.NETHER.name()). - icon(Material.NETHERRACK). - clickHandler((panel, user1, clickType, i) -> { - if (this.values.contains(World.Environment.NETHER)) - { - this.values.remove(World.Environment.NETHER); - } - else - { - this.values.add(World.Environment.NETHER); - } - - this.build(); - return true; - }). - glow(this.values.contains(World.Environment.NETHER)). - build()); - panelBuilder.item(22, new PanelItemBuilder(). - name(World.Environment.NORMAL.name()). - icon(Material.DIRT). - clickHandler((panel, user1, clickType, i) -> { - if (this.values.contains(World.Environment.NORMAL)) - { - this.values.remove(World.Environment.NORMAL); - } - else - { - this.values.add(World.Environment.NORMAL); - } - - this.build(); - return true; - }). - glow(this.values.contains(World.Environment.NORMAL)). - build()); - panelBuilder.item(24, new PanelItemBuilder(). - name(World.Environment.THE_END.name()). - icon(Material.END_STONE). - clickHandler((panel, user1, clickType, i) -> { - if (this.values.contains(World.Environment.THE_END)) - { - this.values.remove(World.Environment.THE_END); - } - else - { - this.values.add(World.Environment.THE_END); - } - - this.build(); - return true; - }). - glow(this.values.contains(World.Environment.THE_END)). - build()); - - - panelBuilder.item(44, new PanelItemBuilder(). - name(this.user.getTranslation("challenges.gui.buttons.return")). - icon(Material.OAK_DOOR). - clickHandler((panel, user1, clickType, i) -> { - this.consumer.accept(false, Collections.emptySet()); - return true; - }). - build()); - - panelBuilder.build(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * User who wants to run command. - */ - private User user; - - /** - * List with selected environments. - */ - private Set values; - - /** - * Stores current Consumer - */ - private BiConsumer> consumer; - -} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SingleBlockSelector.java b/src/main/java/world/bentobox/challenges/panel/util/SingleBlockSelector.java new file mode 100644 index 0000000..d9fae37 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/SingleBlockSelector.java @@ -0,0 +1,241 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary things that allows to select single block from all ingame blocks. Selected + * block will be returned via BiConsumer. + */ +public class SingleBlockSelector extends PagedSelector +{ + private SingleBlockSelector(User user, Mode mode, Set excluded, BiConsumer consumer) + { + super(user); + this.consumer = consumer; + + // Current GUI cannot display air blocks. It crashes with null-pointer + excluded.add(Material.AIR); + excluded.add(Material.CAVE_AIR); + excluded.add(Material.VOID_AIR); + + // Piston head and moving piston is not necessary. useless. + excluded.add(Material.PISTON_HEAD); + excluded.add(Material.MOVING_PISTON); + + // Barrier cannot be accessible to user. + excluded.add(Material.BARRIER); + excluded.add(Material.STRUCTURE_VOID); + + this.elements = Arrays.stream(Material.values()). + filter(material -> !excluded.contains(material)). + filter(material -> { + switch (mode) + { + case BLOCKS -> { + return material.isBlock(); + } + case ITEMS -> { + return material.isItem(); + } + default -> { + return true; + } + } + }). + // Sort by name + sorted(Comparator.comparing(Material::name)). + collect(Collectors.toList()); + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Mode mode, Set excluded, BiConsumer consumer) + { + new SingleBlockSelector(user, mode, excluded, consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, BiConsumer consumer) + { + new SingleBlockSelector(user, Mode.ANY, new HashSet<>(), consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, Mode mode, BiConsumer consumer) + { + new SingleBlockSelector(user, mode, new HashSet<>(), consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "block-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(4, this.createButton()); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem button of requested type. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton() + { + final String reference = Constants.BUTTON + "cancel."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon = new ItemStack(Material.IRON_DOOR); + PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for given material. + * @param material material which button must be created. + * @return new Button for material. + */ + @Override + protected PanelItem createElementButton(Material material) + { + final String reference = Constants.BUTTON + "material."; + + List description = new ArrayList<>(); + description.add(this.user.getTranslation(reference + "description", + "[id]", material.name())); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose")); + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[material]", + Utils.prettifyObject(material, this.user))). + icon(PanelUtils.getMaterialItem(material)). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, material); + return true; + }). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Mode +// --------------------------------------------------------------------- + + + public enum Mode + { + BLOCKS, + ITEMS, + ANY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * This variable stores consumer. + */ + private final BiConsumer consumer; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SingleEntitySelector.java b/src/main/java/world/bentobox/challenges/panel/util/SingleEntitySelector.java new file mode 100644 index 0000000..b0219aa --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/SingleEntitySelector.java @@ -0,0 +1,238 @@ +package world.bentobox.challenges.panel.util; + + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This GUI allows to select single entity and return it via Consumer. + */ +public class SingleEntitySelector extends PagedSelector +{ + /** + * Instantiates a new Single entity selector. + * + * @param user the user + * @param asEggs the boolean + * @param mode the mode + * @param excluded the excluded + * @param consumer the consumer + */ + private SingleEntitySelector(User user, boolean asEggs, Mode mode, Set excluded, BiConsumer consumer) + { + super(user); + this.consumer = consumer; + this.asEggs = asEggs; + this.elements = Arrays.stream(EntityType.values()). + filter(entity -> !excluded.contains(entity)). + filter(entity -> { + if (mode == Mode.ALIVE) + { + return entity.isAlive(); + } + else + { + return true; + } + }). + // Sort by names + sorted(Comparator.comparing(EntityType::name)). + collect(Collectors.toList()); + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEggs, Mode mode, Set excluded, BiConsumer consumer) + { + new SingleEntitySelector(user, asEggs, mode, excluded, consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEggs, BiConsumer consumer) + { + new SingleEntitySelector(user, asEggs, Mode.ANY, new HashSet<>(), consumer).build(); + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, boolean asEggs, Mode mode, BiConsumer consumer) + { + new SingleEntitySelector(user, asEggs, mode, new HashSet<>(), consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "entity-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(4, this.createButton()); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method builds PanelItem for given entity. + * @param entity Entity which PanelItem must be created. + * @return new PanelItem for given Entity. + */ + @Override + protected PanelItem createElementButton(EntityType entity) + { + final String reference = Constants.BUTTON + "entity."; + + List description = new ArrayList<>(); + description.add(this.user.getTranslation(reference + "description", + "[id]", entity.name())); + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose")); + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[entity]", + Utils.prettifyObject(entity, this.user))). + icon(this.asEggs ? PanelUtils.getEntityEgg(entity) : PanelUtils.getEntityHead(entity)). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, entity); + return true; + }). + build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton() + { + final String reference = Constants.BUTTON + "cancel."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon = new ItemStack(Material.IRON_DOOR); + PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Mode +// --------------------------------------------------------------------- + + + public enum Mode + { + ALIVE, + ANY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * Indicates if entities are displayed as eggs or heads. + */ + private final boolean asEggs; + + /** + * This variable stores consumer. + */ + private final BiConsumer consumer; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java b/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java new file mode 100644 index 0000000..f6d2aed --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java @@ -0,0 +1,190 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.inventory.ItemStack; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class contains all necessary things that allows to select single statistic. Selected + * stats will be returned via BiConsumer. + */ +public class StatisticSelector extends PagedSelector +{ + /** + * Instantiates a new Statistic selector. + * + * @param user the user + * @param consumer the consumer + */ + private StatisticSelector(User user, BiConsumer consumer) + { + super(user); + + this.consumer = consumer; + this.elements = new ArrayList<>(Arrays.asList(Statistic.values())); + this.elements.sort(Comparator.comparing(Statistic::name)); + + // Init without filters applied. + this.filterElements = this.elements; + } + + + /** + * This method opens GUI that allows to select challenge type. + * + * @param user User who opens GUI. + * @param consumer Consumer that allows to get clicked type. + */ + public static void open(User user, BiConsumer consumer) + { + new StatisticSelector(user, consumer).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "statistic-selector")); + + PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + this.populateElements(panelBuilder, this.filterElements); + + panelBuilder.item(4, this.createButton()); + + panelBuilder.build(); + } + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.elements; + } + else + { + this.filterElements = this.elements.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.name().toLowerCase().contains(this.searchString.toLowerCase()); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method creates PanelItem that represents given statistic. + * Some materials is not displayable in Inventory GUI, so they are replaced with "placeholder" items. + * @param statistic Material which icon must be created. + * @return PanelItem that represents given statistic. + */ + @Override + protected PanelItem createElementButton(Statistic statistic) + { + final String reference = Constants.BUTTON + "statistic_element."; + + List description = new ArrayList<>(); + + String descriptionText = this.user.getTranslationOrNothing(reference + description, + "[description]", Utils.prettifyDescription(statistic, user)); + + if (!descriptionText.isEmpty()) + { + description.add(descriptionText); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose")); + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[statistic]", + Utils.prettifyObject(statistic, this.user))). + icon(Material.PAPER). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, statistic); + return true; + }). + build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton() + { + final String reference = Constants.BUTTON + "cancel."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon = new ItemStack(Material.IRON_DOOR); + PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> + { + this.consumer.accept(false, null); + return true; + }; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel")); + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private final List elements; + + /** + * This variable stores consumer. + */ + private final BiConsumer consumer; + + /** + * Stores filtered items. + */ + private List filterElements; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java b/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java deleted file mode 100644 index 07f30a3..0000000 --- a/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java +++ /dev/null @@ -1,366 +0,0 @@ -package world.bentobox.challenges.panel.util; - - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import org.bukkit.Material; -import org.bukkit.conversations.Conversation; -import org.bukkit.conversations.ConversationContext; -import org.bukkit.conversations.ConversationFactory; -import org.bukkit.conversations.Prompt; -import org.bukkit.conversations.StringPrompt; -import org.bukkit.inventory.ItemStack; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; - -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.TextComponent; -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.challenges.utils.GuiUtils; - - -/** - * This GUI allows to edit List of strings. AnvilGUI has limited text space, so splitting - * text in multiple rows allows to edit each row separately. - */ -public class StringListGUI -{ - public StringListGUI(User user, String value, int lineLength, BiConsumer> consumer) - { - this(user, Collections.singleton(value), lineLength, consumer); - } - - - public StringListGUI(User user, Collection value, int lineLength, BiConsumer> consumer) - { - this(user, new ArrayList<>(value), lineLength, consumer); - } - - - public StringListGUI(User user, List value, int lineLength, BiConsumer> consumer) - { - this.consumer = consumer; - this.user = user; - this.value = value; - this.lineLength = lineLength; - - if (this.value.size() > 21) - { - // TODO: throw error that so large list cannot be edited. - this.consumer.accept(false, this.value); - } - else - { - this.build(); - } - } - - - /** - * This method builds panel that allows to change given string value. - */ - private void build() - { - PanelBuilder panelBuilder = new PanelBuilder().user(this.user). - name(this.user.getTranslation("challenges.gui.title.admin.edit-text-fields")); - - GuiUtils.fillBorder(panelBuilder, Material.BLACK_STAINED_GLASS_PANE); - - panelBuilder.item(1, this.getButton(Button.SAVE)); - panelBuilder.item(2, this.getButton(Button.VALUE)); - - panelBuilder.item(4, this.getButton(Button.ADD)); - panelBuilder.item(5, this.getButton(Button.REMOVE)); - panelBuilder.item(6, this.getButton(Button.CLEAR)); - - panelBuilder.item(44, this.getButton(Button.CANCEL)); - - int slot = 10; - - for (int stringIndex = 0; stringIndex < this.value.size() && slot < 36; stringIndex++) - { - if (!panelBuilder.slotOccupied(slot)) - { - panelBuilder.item(slot, - this.createStringElement(this.value.get(stringIndex), stringIndex)); - } - - slot++; - } - - panelBuilder.build(); - } - - - /** - * This method create button that does some functionality in current gui. - * @param button Button functionality. - * @return PanelItem. - */ - private PanelItem getButton(Button button) - { - ItemStack icon; - String name; - List description; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case SAVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.save"); - description = Collections.singletonList(this.user.getTranslation("challenges.gui.descriptions.admin.save")); - icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.consumer.accept(true, this.value); - - return true; - }; - break; - } - case CANCEL: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.cancel"); - description = Collections.singletonList(this.user.getTranslation("challenges.gui.descriptions.admin.cancel")); - icon = new ItemStack(Material.OAK_DOOR); - clickHandler = (panel, user, clickType, slot) -> { - this.consumer.accept(false, this.value); - - return true; - }; - break; - } - case VALUE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.value"); - description = new ArrayList<>(); - description.add(this.user.getTranslation("challenges.gui.descriptions.current-value", "[value]", "")); - description.addAll(this.value); - icon = new ItemStack(Material.PAPER); - clickHandler = (panel, user, clickType, slot) -> true; - break; - } - case ADD: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.add"); - description = Collections.emptyList(); - icon = new ItemStack(Material.WHITE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - - this.getStringInput(value -> { - if (value != null) - { - this.value.add(value); - } - - // Reopen GUI. - this.build(); - }, - this.user.getTranslation("challenges.gui.descriptions.admin.add-text-line")); - - return true; - }; - break; - } - case CLEAR: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.clear"); - description = Collections.emptyList(); - icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value.clear(); - this.build(); - return true; - }; - break; - } - case REMOVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-empty"); - description = Collections.emptyList(); - icon = new ItemStack(Material.BLUE_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - this.value.removeIf(String::isEmpty); - - this.build(); - return true; - }; - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.lineLength)). - glow(false). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates paper icon that represents single line from list. - * @param element Paper Icon name - * @return PanelItem. - */ - private PanelItem createStringElement(String element, int stringIndex) - { - return new PanelItemBuilder(). - name(element). - icon(Material.PAPER). - clickHandler((panel, user1, clickType, i) -> { - - this.getStringInput( - value -> { - if (value != null) - { - this.value.set(stringIndex, value); - } - - // Reopen GUI - this.build(); - }, - this.user.getTranslation("challenges.gui.descriptions.admin.edit-text-line"), - element); - - return true; - }).build(); - } - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on inputText in - * chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - */ - private void getStringInput(Consumer consumer, @NonNull String question) - { - this.getStringInput(consumer, question, null); - } - - - /** - * This method will close opened gui and writes inputText in chat. After players answers on inputText in - * chat, message will trigger consumer and gui will reopen. - * @param consumer Consumer that accepts player output text. - * @param question Message that will be displayed in chat when player triggers conversion. - * @param message Message that will be set in player text field when clicked on question. - */ - private void getStringInput(Consumer consumer, @NonNull String question, @Nullable String message) - { - final User user = this.user; - - Conversation conversation = - new ConversationFactory(BentoBox.getInstance()).withFirstPrompt( - new StringPrompt() - { - /** - * @see Prompt#getPromptText(ConversationContext) - */ - @Override - public String getPromptText(ConversationContext conversationContext) - { - // Close input GUI. - user.closeInventory(); - - if (message != null) - { - // Create Edit Text message. - TextComponent component = new TextComponent(user.getTranslation("challenges.gui.descriptions.admin.click-to-edit")); - component.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, message)); - // Send question and message to player. - user.getPlayer().spigot().sendMessage(component); - } - - // There are no editable message. Just return question. - return question; - } - - - /** - * @see Prompt#acceptInput(ConversationContext, String) - */ - @Override - public Prompt acceptInput(ConversationContext conversationContext, String answer) - { - // Add answer to consumer. - consumer.accept(answer); - // End conversation - return Prompt.END_OF_CONVERSATION; - } - }). - // On cancel conversation will be closed. - withEscapeSequence("cancel"). - // Use null value in consumer to detect if user has abandoned conversation. - addConversationAbandonedListener(abandonedEvent -> - { - if (!abandonedEvent.gracefulExit()) - { - consumer.accept(null); - } - }). - withLocalEcho(false). - withPrefix(context -> user.getTranslation("challenges.gui.questions.prefix")). - buildConversation(user.getPlayer()); - - conversation.begin(); - } - - -// --------------------------------------------------------------------- -// Section: Enums -// --------------------------------------------------------------------- - - - /** - * This enum holds all button values in current gui. - */ - private enum Button - { - VALUE, - ADD, - REMOVE, - CANCEL, - CLEAR, - SAVE - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - - /** - * This variable stores consumer. - */ - private BiConsumer> consumer; - - /** - * User who runs GUI. - */ - private User user; - - /** - * Current value. - */ - private List value; - - /** - * This variable stores how large line can be, before warp it. - */ - private int lineLength; -} diff --git a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java index 1b29ace..6b8c161 100644 --- a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java +++ b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java @@ -2,6 +2,8 @@ package world.bentobox.challenges.tasks; +import com.google.common.collect.UnmodifiableIterator; +import java.time.*; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; @@ -11,6 +13,7 @@ import java.util.Map; import java.util.Objects; import java.util.PriorityQueue; import java.util.Queue; +import java.util.UUID; import java.util.stream.Collectors; import org.bukkit.Bukkit; @@ -31,13 +34,15 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.database.object.Challenge; import world.bentobox.challenges.database.object.Challenge.ChallengeType; import world.bentobox.challenges.database.object.ChallengeLevel; import world.bentobox.challenges.database.object.requirements.InventoryRequirements; import world.bentobox.challenges.database.object.requirements.IslandRequirements; import world.bentobox.challenges.database.object.requirements.OtherRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; @@ -55,7 +60,7 @@ public class TryToComplete /** * Challenges addon variable. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; /** * Challenges manager for addon. @@ -77,11 +82,6 @@ public class TryToComplete */ private String permissionPrefix; - /** - * Top command first label. - */ - private String topLabel; - /** * Challenge that should be completed. */ @@ -96,13 +96,6 @@ public class TryToComplete // Section: Builder // --------------------------------------------------------------------- - @Deprecated - public TryToComplete label(String label) - { - this.topLabel = label; - return this; - } - @Deprecated public TryToComplete user(User user) @@ -112,38 +105,6 @@ public class TryToComplete } - @Deprecated - public TryToComplete manager(ChallengesManager manager) - { - this.manager = manager; - return this; - } - - - @Deprecated - public TryToComplete challenge(Challenge challenge) - { - this.challenge = challenge; - return this; - } - - - @Deprecated - public TryToComplete world(World world) - { - this.world = world; - return this; - } - - - @Deprecated - public TryToComplete permPrefix(String prefix) - { - this.permissionPrefix = prefix; - return this; - } - - @Deprecated public TryToComplete(ChallengesAddon addon) { @@ -179,7 +140,6 @@ public class TryToComplete // To avoid any modifications that may occur to challenges in current completion // just clone it. this.challenge = challenge.clone(); - this.topLabel = topLabel; } @@ -224,7 +184,8 @@ public class TryToComplete int maxTimes) { return new TryToComplete(addon, user, challenge, world, topLabel, permissionPrefix). - build(maxTimes).meetsRequirements; + build(maxTimes). + meetsRequirements; } @@ -247,6 +208,13 @@ public class TryToComplete return result; } + if (this.user.getLocation() == null || this.user.getInventory() == null) + { + // This is just a cleaning check. There is no situations where location or inventory + // could be null at this point of code. + return result; + } + this.fullFillRequirements(result); // Validation to avoid rewarding if something goes wrong in removing requirements. @@ -297,32 +265,29 @@ public class TryToComplete // Send message about first completion only if it is completed only once. if (result.getFactor() == 1) { - this.user.sendMessage("challenges.messages.you-completed-challenge", "[value]", this.challenge.getFriendlyName()); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-completed-challenge", + "[value]", this.challenge.getFriendlyName())); } if (this.addon.getChallengesSettings().isBroadcastMessages()) { - for (Player player : Bukkit.getOnlinePlayers()) - { - // Only other players should see message. - if (!player.getUniqueId().equals(this.user.getUniqueId())) - { - User.getInstance(player).sendMessage("challenges.messages.name-has-completed-challenge", - "[name]", this.user.getName(), - "[value]", this.challenge.getFriendlyName()); - } - } + Bukkit.getOnlinePlayers().stream(). + map(User::getInstance). + forEach(user -> Utils.sendMessage(user, user.getTranslation( + "challenges.messages.name-has-completed-challenge", + Constants.PARAMETER_NAME, this.user.getName(), + "[value]", this.challenge.getFriendlyName()))); } // sends title to player on challenge completion if (this.addon.getChallengesSettings().isShowCompletionTitle()) { this.user.getPlayer().sendTitle( - this.parseChallenge(this.user.getTranslation("challenges.titles.challenge-title"), this.challenge), - this.parseChallenge(this.user.getTranslation("challenges.titles.challenge-subtitle"), this.challenge), - 10, - this.addon.getChallengesSettings().getTitleShowtime(), - 20); + this.parseChallenge(this.user.getTranslation("challenges.titles.challenge-title"), this.challenge), + this.parseChallenge(this.user.getTranslation("challenges.titles.challenge-subtitle"), this.challenge), + 10, + this.addon.getChallengesSettings().getTitleShowtime(), + 20); } } @@ -347,7 +312,7 @@ public class TryToComplete if (this.addon.isEconomyProvided()) { this.addon.getEconomyProvider().deposit(this.user, - (double)this.challenge.getRepeatMoneyReward() * rewardFactor); + this.challenge.getRepeatMoneyReward() * rewardFactor); } // Experience Repeat Reward @@ -362,13 +327,14 @@ public class TryToComplete if (result.getFactor() > 1) { - this.user.sendMessage("challenges.messages.you-repeated-challenge-multiple", - "[value]", this.challenge.getFriendlyName(), - "[count]", Integer.toString(result.getFactor())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-repeated-challenge-multiple", + "[value]", this.challenge.getFriendlyName(), + "[count]", Integer.toString(result.getFactor()))); } else { - this.user.sendMessage("challenges.messages.you-repeated-challenge", "[value]", this.challenge.getFriendlyName()); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-repeated-challenge", + "[value]", this.challenge.getFriendlyName())); } } @@ -406,19 +372,17 @@ public class TryToComplete // Run commands this.runCommands(level.getRewardCommands()); - this.user.sendMessage("challenges.messages.you-completed-level", "[value]", level.getFriendlyName()); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-completed-level", + "[value]", level.getFriendlyName())); if (this.addon.getChallengesSettings().isBroadcastMessages()) { - for (Player player : this.addon.getServer().getOnlinePlayers()) - { - // Only other players should see message. - if (!player.getUniqueId().equals(this.user.getUniqueId())) - { - User.getInstance(player).sendMessage("challenges.messages.name-has-completed-level", - "[name]", this.user.getName(), "[value]", level.getFriendlyName()); - } - } + Bukkit.getOnlinePlayers().stream(). + map(User::getInstance). + forEach(user -> Utils.sendMessage(user, user.getTranslation( + "challenges.messages.name-has-completed-level", + Constants.PARAMETER_NAME, this.user.getName(), + "[value]", level.getFriendlyName()))); } this.manager.setLevelComplete(this.user, this.world, level); @@ -427,11 +391,11 @@ public class TryToComplete if (this.addon.getChallengesSettings().isShowCompletionTitle()) { this.user.getPlayer().sendTitle( - this.parseLevel(this.user.getTranslation("challenges.titles.level-title"), level), - this.parseLevel(this.user.getTranslation("challenges.titles.level-subtitle"), level), - 10, - this.addon.getChallengesSettings().getTitleShowtime(), - 20); + this.parseLevel(this.user.getTranslation("challenges.titles.level-title"), level), + this.parseLevel(this.user.getTranslation("challenges.titles.level-subtitle"), level), + 10, + this.addon.getChallengesSettings().getTitleShowtime(), + 20); } } } @@ -442,68 +406,222 @@ public class TryToComplete /** - * This method full fills all challenge type requirements, that is not full filled yet. + * This method fulfills all challenge type requirements, that is not fulfilled yet. * @param result Challenge Results */ private void fullFillRequirements(ChallengeResult result) { - if (this.challenge.getChallengeType().equals(ChallengeType.ISLAND)) + switch (this.challenge.getChallengeType()) { - IslandRequirements requirements = this.challenge.getRequirements(); + case ISLAND_TYPE -> { + IslandRequirements requirements = this.challenge.getRequirements(); - if (result.meetsRequirements && + if (result.meetsRequirements && requirements.isRemoveEntities() && !requirements.getRequiredEntities().isEmpty()) - { - this.removeEntities(result.entities, result.getFactor()); - } + { + this.removeEntities(result.entities, result.getFactor()); + } - if (result.meetsRequirements && + if (result.meetsRequirements && requirements.isRemoveBlocks() && !requirements.getRequiredBlocks().isEmpty()) - { - this.removeBlocks(result.blocks, result.getFactor()); + { + this.removeBlocks(result.blocks, result.getFactor()); + } } - } - else if (this.challenge.getChallengeType().equals(ChallengeType.INVENTORY)) - { - // If remove items, then remove them - if (this.getInventoryRequirements().isTakeItems()) - { - int sumEverything = result.requiredItems.stream(). + case INVENTORY_TYPE -> { + // If remove items, then remove them + if (this.getInventoryRequirements().isTakeItems()) + { + int sumEverything = result.requiredItems.stream(). mapToInt(itemStack -> itemStack.getAmount() * result.getFactor()). sum(); - Map removedItems = + Map removedItems = this.removeItems(result.requiredItems, result.getFactor()); - int removedAmount = removedItems.values().stream().mapToInt(num -> num).sum(); + int removedAmount = removedItems.values().stream().mapToInt(num -> num).sum(); - // Something is not removed. - if (sumEverything != removedAmount) - { - this.user.sendMessage("challenges.errors.cannot-remove-items"); + // Something is not removed. + if (sumEverything != removedAmount) + { + Utils.sendMessage(this.user, + this.user.getTranslation("challenges.errors.cannot-remove-items")); - result.removedItems = removedItems; - result.meetsRequirements = false; + result.removedItems = removedItems; + result.meetsRequirements = false; + } } } - } - else if (this.challenge.getChallengeType().equals(ChallengeType.OTHER)) - { - OtherRequirements requirements = this.challenge.getRequirements(); + case OTHER_TYPE -> { + OtherRequirements requirements = this.challenge.getRequirements(); - if (this.addon.isEconomyProvided() && requirements.isTakeMoney()) - { - this.addon.getEconomyProvider().withdraw(this.user, requirements.getRequiredMoney()); - } + if (this.addon.isEconomyProvided() && requirements.isTakeMoney()) + { + this.addon.getEconomyProvider().withdraw(this.user, requirements.getRequiredMoney()); + } - if (requirements.isTakeExperience() && + if (requirements.isTakeExperience() && this.user.getPlayer().getGameMode() != GameMode.CREATIVE) - { - // Cannot take anything from creative game mode. - this.user.getPlayer().setTotalExperience( + { + // Cannot take anything from creative game mode. + this.user.getPlayer().setTotalExperience( this.user.getPlayer().getTotalExperience() - requirements.getRequiredExperience()); + } + } + case STATISTIC_TYPE -> { + StatisticRequirements requirements = this.challenge.getRequirements(); + + if (requirements.isReduceStatistic() && requirements.getStatistic() != null) + { + int removeAmount = result.getFactor() * requirements.getAmount(); + + // Start to remove from player who called the completion. + switch (requirements.getStatistic().getType()) + { + case UNTYPED -> { + int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); + + if (removeAmount >= statistic) + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), 0); + removeAmount -= statistic; + } + else + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), statistic - removeAmount); + removeAmount = 0; + } + } + case ITEM, BLOCK -> { + int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); + + if (requirements.getMaterial() == null) + { + // Just a sanity check. Material cannot be null at this point of code. + removeAmount = 0; + } + else if (removeAmount >= statistic) + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0); + removeAmount -= statistic; + } + else + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), + requirements.getMaterial(), + statistic - removeAmount); + removeAmount = 0; + } + } + case ENTITY -> { + int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); + + if (requirements.getEntity() == null) + { + // Just a sanity check. Entity cannot be null at this point of code. + removeAmount = 0; + } + else if (removeAmount >= statistic) + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getEntity(), 0); + removeAmount -= statistic; + } + else + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), + requirements.getEntity(), + statistic - removeAmount); + removeAmount = 0; + } + } + } + + // If challenges are in sync with all island members, then punish others too. + if (this.addon.getChallengesSettings().isStoreAsIslandData()) + { + Island island = this.addon.getIslands().getIsland(this.world, this.user); + + if (island == null) + { + // hmm + return; + } + + for (UnmodifiableIterator iterator = island.getMemberSet().iterator(); + iterator.hasNext() && removeAmount > 0; ) + { + Player player = Bukkit.getPlayer(iterator.next()); + + if (player == null || player == this.user.getPlayer()) + { + // cannot punish null or player who already was punished. + continue; + } + + switch (Objects.requireNonNull(requirements.getStatistic()).getType()) + { + case UNTYPED -> { + int statistic = player.getStatistic(requirements.getStatistic()); + + if (removeAmount >= statistic) + { + removeAmount -= statistic; + player.setStatistic(requirements.getStatistic(), 0); + } + else + { + player.setStatistic(requirements.getStatistic(), statistic - removeAmount); + removeAmount = 0; + } + } + case ITEM, BLOCK -> { + int statistic = player.getStatistic(requirements.getStatistic()); + + if (requirements.getMaterial() == null) + { + // Just a sanity check. Entity cannot be null at this point of code. + removeAmount = 0; + } + else if (removeAmount >= statistic) + { + removeAmount -= statistic; + player.setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0); + } + else + { + player.setStatistic(requirements.getStatistic(), + requirements.getMaterial(), + statistic - removeAmount); + removeAmount = 0; + } + } + case ENTITY -> { + int statistic = player.getStatistic(requirements.getStatistic()); + + if (requirements.getEntity() == null) + { + // Just a sanity check. Entity cannot be null at this point of code. + removeAmount = 0; + } + else if (removeAmount >= statistic) + { + removeAmount -= statistic; + player.setStatistic(requirements.getStatistic(), requirements.getEntity(), 0); + } + else + { + player.setStatistic(requirements.getStatistic(), + requirements.getEntity(), + statistic - removeAmount); + removeAmount = 0; + } + } + } + } + } + } } } } @@ -517,85 +635,101 @@ public class TryToComplete private ChallengeResult checkIfCanCompleteChallenge(int maxTimes) { ChallengeResult result; - + ChallengeType type = this.challenge.getChallengeType(); // Check the world if (!this.challenge.isDeployed()) { - this.user.sendMessage("challenges.errors.not-deployed"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-deployed")); result = EMPTY_RESULT; } else if (maxTimes < 1) { - this.user.sendMessage("challenges.errors.not-valid-integer"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-valid-integer")); result = EMPTY_RESULT; } else if (Util.getWorld(this.world) != Util.getWorld(this.user.getWorld()) || - !this.challenge.matchGameMode(Utils.getGameMode(this.world))) + !this.challenge.matchGameMode(Utils.getGameMode(this.world))) { - this.user.sendMessage("general.errors.wrong-world"); + Utils.sendMessage(this.user, this.user.getTranslation("general.errors.wrong-world")); result = EMPTY_RESULT; } // Player is not on island - else if (ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.world) && + else if (this.user.getLocation() == null || + ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.world) && !this.addon.getIslands().locationIsOnIsland(this.user.getPlayer(), this.user.getLocation())) { - this.user.sendMessage("challenges.errors.not-on-island"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-on-island")); result = EMPTY_RESULT; } // Check player permission else if (!this.addon.getIslands().getIslandAt(this.user.getLocation()). - map(i -> i.isAllowed(this.user, ChallengesAddon.CHALLENGES_ISLAND_PROTECTION)). - orElse(false)) + map(i -> i.isAllowed(this.user, ChallengesAddon.CHALLENGES_ISLAND_PROTECTION)). + orElse(false)) { - this.user.sendMessage("challenges.errors.no-rank"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.no-rank")); result = EMPTY_RESULT; } // Check if user has unlocked challenges level. else if (!this.challenge.getLevel().equals(ChallengesManager.FREE) && - !this.manager.isLevelUnlocked(this.user, this.world, this.manager.getLevel(this.challenge.getLevel()))) + !this.manager.isLevelUnlocked(this.user, this.world, this.manager.getLevel(this.challenge.getLevel()))) { - this.user.sendMessage("challenges.errors.challenge-level-not-available"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.challenge-level-not-available")); result = EMPTY_RESULT; } // Check max times else if (this.challenge.isRepeatable() && this.challenge.getMaxTimes() > 0 && - this.manager.getChallengeTimes(this.user, this.world, this.challenge) >= this.challenge.getMaxTimes()) + this.manager.getChallengeTimes(this.user, this.world, this.challenge) >= this.challenge.getMaxTimes()) { - this.user.sendMessage("challenges.errors.not-repeatable"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-repeatable")); result = EMPTY_RESULT; } // Check repeatability else if (!this.challenge.isRepeatable() && this.manager.isChallengeComplete(this.user, this.world, this.challenge)) { - this.user.sendMessage("challenges.errors.not-repeatable"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-repeatable")); + result = EMPTY_RESULT; + } + // Check if timeout is not broken + else if (this.manager.isBreachingTimeOut(this.user, this.world, this.challenge)) + { + long missing = this.manager.getLastCompletionDate(this.user, this.world, challenge) + + this.challenge.getTimeout() - System.currentTimeMillis(); + + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.timeout", + "[timeout]", Utils.parseDuration(Duration.ofMillis(this.challenge.getTimeout()), this.user), + "[wait-time]", Utils.parseDuration(Duration.ofMillis(missing), this.user))); result = EMPTY_RESULT; } // Check environment else if (!this.challenge.getEnvironment().isEmpty() && - !this.challenge.getEnvironment().contains(this.user.getWorld().getEnvironment())) + !this.challenge.getEnvironment().contains(this.user.getWorld().getEnvironment())) { - this.user.sendMessage("challenges.errors.wrong-environment"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.wrong-environment")); result = EMPTY_RESULT; } // Check permission else if (!this.checkPermissions()) { - this.user.sendMessage("general.errors.no-permission"); + Utils.sendMessage(this.user, this.user.getTranslation("general.errors.no-permission")); result = EMPTY_RESULT; } - else if (type.equals(ChallengeType.INVENTORY)) + else if (type.equals(ChallengeType.INVENTORY_TYPE)) { result = this.checkInventory(this.getAvailableCompletionTimes(maxTimes)); } - else if (type.equals(ChallengeType.ISLAND)) + else if (type.equals(ChallengeType.ISLAND_TYPE)) { result = this.checkSurrounding(this.getAvailableCompletionTimes(maxTimes)); } - else if (type.equals(ChallengeType.OTHER)) + else if (type.equals(ChallengeType.OTHER_TYPE)) { result = this.checkOthers(this.getAvailableCompletionTimes(maxTimes)); } + else if (type.equals(ChallengeType.STATISTIC_TYPE)) + { + result = this.checkStatistic(this.getAvailableCompletionTimes(maxTimes)); + } else { result = EMPTY_RESULT; @@ -618,7 +752,7 @@ public class TryToComplete private boolean checkPermissions() { return this.challenge.getRequirements().getRequiredPermissions().isEmpty() || - this.challenge.getRequirements().getRequiredPermissions().stream().allMatch(s -> this.user.hasPermission(s)); + this.challenge.getRequirements().getRequiredPermissions().stream().allMatch(s -> this.user.hasPermission(s)); } @@ -630,12 +764,12 @@ public class TryToComplete */ private int getAvailableCompletionTimes(int vantedTimes) { - if (!this.challenge.isRepeatable()) + if (!this.challenge.isRepeatable() || this.challenge.getTimeout() > 0) { // Challenge is not repeatable vantedTimes = 1; } - else if (this.challenge.getMaxTimes() != 0) + else if (this.challenge.getMaxTimes() > 0) { // Challenge has limitations long availableTimes = this.challenge.getMaxTimes() - this.manager.getChallengeTimes(this.user, this.world, this.challenge); @@ -667,7 +801,7 @@ public class TryToComplete { String alert = "Running command '" + cmd + "' as " + this.user.getName(); this.addon.getLogger().info(alert); - cmd = cmd.substring(6, cmd.length()).replace("[player]", this.user.getName()).trim(); + cmd = cmd.substring(6).replace(Constants.PARAMETER_PLAYER, this.user.getName()).trim(); try { if (!user.performCommand(cmd)) @@ -686,7 +820,7 @@ public class TryToComplete try { if (!this.addon.getServer().dispatchCommand(this.addon.getServer().getConsoleSender(), - cmd.replace("[player]", this.user.getName()))) + cmd.replace(Constants.PARAMETER_PLAYER, this.user.getName()))) { this.showError(cmd); } @@ -722,43 +856,47 @@ public class TryToComplete */ private ChallengeResult checkInventory(int maxTimes) { + if (maxTimes <= 0) + { + return EMPTY_RESULT; + } + // Run through inventory List requiredItems; // Players in creative game mode has got all items. No point to search for them. if (this.user.getPlayer().getGameMode() != GameMode.CREATIVE) { - requiredItems = Utils.groupEqualItems(this.getInventoryRequirements().getRequiredItems()); + requiredItems = Utils.groupEqualItems(this.getInventoryRequirements().getRequiredItems(), + this.getInventoryRequirements().getIgnoreMetaData()); // Check if all required items are in players inventory. for (ItemStack required : requiredItems) { int numInInventory; - if (Utils.canIgnoreMeta(required.getType())) + if (this.getInventoryRequirements().getIgnoreMetaData().contains(required.getType())) { - numInInventory = - Arrays.stream(this.user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.getType().equals(required.getType())). - mapToInt(ItemStack::getAmount). - sum(); + numInInventory = Arrays.stream(this.user.getInventory().getContents()). + filter(Objects::nonNull). + filter(i -> i.getType().equals(required.getType())). + mapToInt(ItemStack::getAmount). + sum(); } else { - numInInventory = - Arrays.stream(this.user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.isSimilar(required)). - mapToInt(ItemStack::getAmount). - sum(); + numInInventory = Arrays.stream(this.user.getInventory().getContents()). + filter(Objects::nonNull). + filter(i -> i.isSimilar(required)). + mapToInt(ItemStack::getAmount). + sum(); } if (numInInventory < required.getAmount()) { - this.user.sendMessage("challenges.errors.not-enough-items", - "[items]", - Util.prettifyText(required.getType().toString())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-items", + "[items]", + Utils.prettifyObject(required, this.user))); return EMPTY_RESULT; } @@ -792,22 +930,28 @@ public class TryToComplete int amountToBeRemoved = required.getAmount() * factor; List itemsInInventory; - if (Utils.canIgnoreMeta(required.getType())) + if (this.user.getInventory() == null) + { + // Sanity check. User always has inventory at this point of code. + itemsInInventory = Collections.emptyList(); + } + else if (this.getInventoryRequirements().getIgnoreMetaData().contains(required.getType())) { // Use collecting method that ignores item meta. itemsInInventory = Arrays.stream(user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.getType().equals(required.getType())). - collect(Collectors.toList()); + filter(Objects::nonNull). + filter(i -> i.getType().equals(required.getType())). + collect(Collectors.toList()); } else { // Use collecting method that compares item meta. itemsInInventory = Arrays.stream(user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.isSimilar(required)). - collect(Collectors.toList()); + filter(Objects::nonNull). + filter(i -> i.isSimilar(required)). + collect(Collectors.toList()); } + for (ItemStack itemStack : itemsInInventory) { if (amountToBeRemoved > 0) @@ -854,6 +998,11 @@ public class TryToComplete */ private ChallengeResult checkSurrounding(int factor) { + if (factor <= 0) + { + return EMPTY_RESULT; + } + // Init location in player position. BoundingBox boundingBox = this.user.getPlayer().getBoundingBox().clone(); @@ -905,14 +1054,14 @@ public class TryToComplete // Protection code. Do not allow to select too large region for completing challenge. if (boundingBox.getWidthX() > distance * 2 + 3 || - boundingBox.getWidthZ() > distance * 2 + 3 || - boundingBox.getHeight() > distance * 2 + 3) + boundingBox.getWidthZ() > distance * 2 + 3 || + boundingBox.getHeight() > distance * 2 + 3) { this.addon.logError("BoundingBox is larger than SearchRadius. " + - " | BoundingBox: " + boundingBox.toString() + + " | BoundingBox: " + boundingBox + " | Search Distance: " + requirements.getSearchRadius() + - " | Location: " + this.user.getLocation().toString() + - " | Center: " + island.getCenter().toString() + + " | Location: " + this.user.getLocation() + + " | Center: " + island.getCenter() + " | Range: " + range); return EMPTY_RESULT; @@ -950,10 +1099,10 @@ public class TryToComplete // This queue will contain only blocks whit required type ordered by distance till player. Queue blockFromWorld = new PriorityQueue<>((o1, o2) -> { - if (o1.getType().equals(o2.getType())) + if (o1.getType().equals(o2.getType()) && this.user.getLocation() != null) { return Double.compare(o1.getLocation().distance(this.user.getLocation()), - o2.getLocation().distance(this.user.getLocation())); + o2.getLocation().distance(this.user.getLocation())); } else { @@ -1010,13 +1159,13 @@ public class TryToComplete return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor).setBlockQueue(blockFromWorld); } - this.user.sendMessage("challenges.errors.not-close-enough", - "[number]", - String.valueOf(this.getIslandRequirements().getSearchRadius())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-close-enough", + "[number]", String.valueOf(this.getIslandRequirements().getSearchRadius()))); - blocks.forEach((k, v) -> user.sendMessage("challenges.errors.you-still-need", + blocks.forEach((k, v) -> Utils.sendMessage(this.user, + this.user.getTranslation("challenges.errors.you-still-need", "[amount]", String.valueOf(v), - "[item]", Util.prettifyText(k.toString()))); + "[item]", Utils.prettifyObject(k, this.user)))); // kick garbage collector @@ -1050,10 +1199,10 @@ public class TryToComplete // Create queue that contains all required entities ordered by distance till player. Queue entityQueue = new PriorityQueue<>((o1, o2) -> { - if (o1.getType().equals(o2.getType())) + if (o1.getType().equals(o2.getType()) && this.user.getLocation() != null) { return Double.compare(o1.getLocation().distance(this.user.getLocation()), - o2.getLocation().distance(this.user.getLocation())); + o2.getLocation().distance(this.user.getLocation())); } else { @@ -1095,9 +1244,10 @@ public class TryToComplete return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor).setEntityQueue(entityQueue); } - minimalRequirements.forEach((reqEnt, amount) -> this.user.sendMessage("challenges.errors.you-still-need", + minimalRequirements.forEach((reqEnt, amount) -> + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.you-still-need", "[amount]", String.valueOf(amount), - "[item]", Util.prettifyText(reqEnt.toString()))); + "[item]", Utils.prettifyObject(reqEnt, this.user)))); // Kick garbage collector entitiesFound.clear(); @@ -1169,48 +1319,51 @@ public class TryToComplete */ private ChallengeResult checkOthers(int factor) { + if (factor <= 0) + { + return EMPTY_RESULT; + } + OtherRequirements requirements = this.getOtherRequirements(); - if (!this.addon.isLevelProvided() && - requirements.getRequiredIslandLevel() != 0) + if (!this.addon.isLevelProvided() && requirements.getRequiredIslandLevel() != 0) { - this.user.sendMessage("challenges.errors.missing-addon"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.missing-addon")); } else if (!this.addon.isEconomyProvided() && requirements.getRequiredMoney() != 0) { - this.user.sendMessage("challenges.errors.missing-addon"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.missing-addon")); } else if (this.addon.isEconomyProvided() && requirements.getRequiredMoney() < 0) { - this.user.sendMessage("challenges.errors.incorrect"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.incorrect")); } else if (this.addon.isEconomyProvided() && - !this.addon.getEconomyProvider().has(this.user, requirements.getRequiredMoney())) + !this.addon.getEconomyProvider().has(this.user, requirements.getRequiredMoney())) { - this.user.sendMessage("challenges.errors.not-enough-money", - "[value]", - Double.toString(requirements.getRequiredMoney())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-money", + "[value]", + Double.toString(requirements.getRequiredMoney()))); } else if (requirements.getRequiredExperience() < 0) { - this.user.sendMessage("challenges.errors.incorrect"); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.incorrect")); } else if (this.user.getPlayer().getTotalExperience() < requirements.getRequiredExperience() && - this.user.getPlayer().getGameMode() != GameMode.CREATIVE) + this.user.getPlayer().getGameMode() != GameMode.CREATIVE) { // Players in creative gamemode has infinite amount of EXP. - - this.user.sendMessage("challenges.errors.not-enough-experience", - "[value]", - Integer.toString(requirements.getRequiredExperience())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-experience", + "[value]", + Integer.toString(requirements.getRequiredExperience()))); } else if (this.addon.isLevelProvided() && this.addon.getLevelAddon().getIslandLevel(this.world, this.user.getUniqueId()) < requirements.getRequiredIslandLevel()) { - this.user.sendMessage("challenges.errors.island-level", - TextVariables.NUMBER, - String.valueOf(requirements.getRequiredIslandLevel())); + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.island-level", + TextVariables.NUMBER, + String.valueOf(requirements.getRequiredIslandLevel()))); } else { @@ -1233,6 +1386,61 @@ public class TryToComplete } + // --------------------------------------------------------------------- + // Section: Statistic Challenge + // --------------------------------------------------------------------- + + + /** + * Checks if a statistic challenge can be completed or not + * It returns ChallengeResult. + * @param factor - times that user wanted to complete + */ + private ChallengeResult checkStatistic(int factor) + { + if (factor <= 0) + { + return EMPTY_RESULT; + } + + StatisticRequirements requirements = this.challenge.getRequirements(); + + int currentValue; + + if (requirements.getStatistic() == null) + { + // Sanity check. + return EMPTY_RESULT; + } + + switch (Objects.requireNonNull(requirements.getStatistic()).getType()) + { + case UNTYPED -> currentValue = + this.manager.getStatisticData(this.user, this.world, requirements.getStatistic()); + case ITEM, BLOCK -> currentValue = + this.manager.getStatisticData(this.user, this.world, requirements.getStatistic(), requirements.getMaterial()); + case ENTITY -> currentValue = + this.manager.getStatisticData(this.user, this.world, requirements.getStatistic(), requirements.getEntity()); + default -> currentValue = 0; + } + + if (currentValue < requirements.getAmount()) + { + Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.requirement-not-met", + TextVariables.NUMBER, String.valueOf(requirements.getAmount()), + "[value]", String.valueOf(currentValue))); + } + else + { + factor = requirements.getAmount() == 0 ? factor : Math.min(factor, currentValue / requirements.getAmount()); + + return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor); + } + + return EMPTY_RESULT; + } + + // --------------------------------------------------------------------- // Section: Title parsings // --------------------------------------------------------------------- @@ -1251,7 +1459,10 @@ public class TryToComplete if (inputMessage.contains("[") && inputMessage.contains("]")) { outputMessage = outputMessage.replace("[friendlyName]", challenge.getFriendlyName()); - outputMessage = outputMessage.replace("[level]", challenge.getLevel().isEmpty() ? "" : this.manager.getLevel(challenge.getLevel()).getFriendlyName()); + + ChallengeLevel level = challenge.getLevel().isEmpty() ? null : this.manager.getLevel(challenge.getLevel()); + outputMessage = outputMessage.replace("[level]", level == null ? "" : level.getFriendlyName()); + outputMessage = outputMessage.replace("[rewardText]", challenge.getRewardText()); } @@ -1324,7 +1535,7 @@ public class TryToComplete * * @author tastybento */ - class ChallengeResult + static class ChallengeResult { /** * This method sets that challenge meets all requirements at least once. diff --git a/src/main/java/world/bentobox/challenges/utils/Constants.java b/src/main/java/world/bentobox/challenges/utils/Constants.java new file mode 100644 index 0000000..bfe490a --- /dev/null +++ b/src/main/java/world/bentobox/challenges/utils/Constants.java @@ -0,0 +1,226 @@ +// +// Created by BONNe +// Copyright - 2020 +// + + +package world.bentobox.challenges.utils; + + +/** + * This class contains String constants used in messages and guis. + */ +public class Constants +{ + /** + * Reference string to ADDON_NAME in translations. + */ + public static final String ADDON_NAME = "challenges."; + +// --------------------------------------------------------------------- +// Section: Commands +// --------------------------------------------------------------------- + + + /** + * Reference string to Commands in translations. + */ + public static final String COMMANDS = ADDON_NAME + "commands."; + + /** + * Reference string to Admin in translations. + */ + public static final String ADMIN_COMMANDS = COMMANDS + "admin."; + + /** + * Reference string to Player in translations. + */ + public static final String PLAYER_COMMANDS = COMMANDS + "player."; + +// --------------------------------------------------------------------- +// Section: GUI +// --------------------------------------------------------------------- + + /** + * Reference string to GUI in translations. + */ + public static final String GUI = ADDON_NAME + "gui."; + + /** + * Reference string to TITLE in translations. + */ + public static final String TITLE = GUI + "titles."; + + /** + * Reference string to BUTTON in translations. + */ + public static final String BUTTON = GUI + "buttons."; + + /** + * Reference string to TIPS in translations. + */ + public static final String TIPS = GUI + "tips."; + + /** + * Reference string to DESCRIPTION in translations. + */ + public static final String DESCRIPTIONS = GUI + "descriptions."; + + /** + * Reference string to Messages in translations. + */ + public static final String MESSAGES = ADDON_NAME + "messages."; + + /** + * Reference string to Errors in translations. + */ + public static final String ERRORS = ADDON_NAME + "errors."; + + /** + * Reference string to Questions in translations. + */ + public static final String CONVERSATIONS = ADDON_NAME + "conversations."; + +// --------------------------------------------------------------------- +// Section: Other +// --------------------------------------------------------------------- + + /** + * Reference string to materials in translations. + */ + public static final String MATERIALS = ADDON_NAME + "materials."; + + /** + * Reference string to entities in translations. + */ + public static final String ENTITIES = ADDON_NAME + "entities."; + + /** + * Reference string to environments in translations. + */ + public static final String ENVIRONMENTS = ADDON_NAME + "environments."; + + /** + * Reference string to statistics in translations. + */ + public static final String STATISTICS = ADDON_NAME + "statistics."; + + /** + * Reference string to item stacks in translations. + */ + public static final String ITEM_STACKS = ADDON_NAME + "item-stacks."; + +// --------------------------------------------------------------------- +// Section: Parameters +// --------------------------------------------------------------------- + + /** + * Reference string to gamemode parameter in translations. + */ + public static final String PARAMETER_GAMEMODE = "[gamemode]"; + + /** + * Reference string to world parameter in translations. + */ + public static final String PARAMETER_WORLD = "[world]"; + + /** + * Reference string to value parameter in translations. + */ + public static final String PARAMETER_VALUE = "[value]"; + + /** + * Reference string to block parameter in translations. + */ + public static final String PARAMETER_MATERIAL = "[material]"; + + /** + * Reference string to entity parameter in translations. + */ + public static final String PARAMETER_ENTITY = "[entity]"; + + /** + * Reference string to environment parameter in translations. + */ + public static final String PARAMETER_ENVIRONMENT = "[environment]"; + + /** + * Reference string to file parameter in translations. + */ + public static final String PARAMETER_FILE = "[file]"; + + /** + * Reference string to id parameter in translations. + */ + public static final String PARAMETER_ID = "[id]"; + + /** + * Reference string to min parameter in translations. + */ + public static final String PARAMETER_MIN = "[min]"; + + /** + * Reference string to max parameter in translations. + */ + public static final String PARAMETER_MAX = "[max]"; + + /** + * Reference to an author parameter in translation. + */ + public static final String PARAMETER_AUTHOR = "[author]"; + + /** + * Reference to an lang parameter in translation. + */ + public static final String PARAMETER_LANG = "[lang]"; + + /** + * Reference to an version parameter in translation. + */ + public static final String PARAMETER_VERSION = "[version]"; + + /** + * Reference to island in translations. + */ + public static final String PARAMETER_ISLAND = "[island]"; + + /** + * Reference string to number parameter in translations. + */ + public static final String PARAMETER_NUMBER = "[number]"; + + /** + * Reference string to permission parameter in translations. + */ + public static final String PARAMETER_PERMISSION = "[permission]"; + + /** + * Reference string to player parameter in translations. + */ + public static final String PARAMETER_PLAYER = "[player]"; + + /** + * Reference string to owner parameter in translations. + */ + public static final String PARAMETER_OWNER = "[owner]"; + + /** + * Reference string to name parameter in translations. + */ + public static final String PARAMETER_NAME = "[name]"; + + /** + * Reference string to level parameter in translations. + */ + public static final String PARAMETER_LEVEL = "[level]"; + + /** + * Reference string to description parameter in translations. + */ + public static final String PARAMETER_DESCRIPTION = "[description]"; + + /** + * Reference string to challenge parameter in translations. + */ + public static final String PARAMETER_CHALLENGE = "[challenge]"; +} diff --git a/src/main/java/world/bentobox/challenges/utils/GuiUtils.java b/src/main/java/world/bentobox/challenges/utils/GuiUtils.java deleted file mode 100644 index 55edbb9..0000000 --- a/src/main/java/world/bentobox/challenges/utils/GuiUtils.java +++ /dev/null @@ -1,439 +0,0 @@ -package world.bentobox.challenges.utils; - - -import java.util.*; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.bentobox.api.panels.PanelItem; -import world.bentobox.bentobox.api.panels.builders.PanelBuilder; -import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; - - -/** - * This class contains static methods that is used through multiple GUIs. - */ -public class GuiUtils -{ -// --------------------------------------------------------------------- -// Section: Border around GUIs -// --------------------------------------------------------------------- - - - /** - * This method creates border of black panes around given panel with 5 rows. - * @param panelBuilder PanelBuilder which must be filled with border blocks. - */ - public static void fillBorder(PanelBuilder panelBuilder) - { - GuiUtils.fillBorder(panelBuilder, 5, Material.BLACK_STAINED_GLASS_PANE); - } - - - /** - * This method sets black stained glass pane around Panel with given row count. - * @param panelBuilder object that builds Panel. - * @param rowCount in Panel. - */ - public static void fillBorder(PanelBuilder panelBuilder, int rowCount) - { - GuiUtils.fillBorder(panelBuilder, rowCount, Material.BLACK_STAINED_GLASS_PANE); - } - - - /** - * This method sets blocks with given Material around Panel with 5 rows. - * @param panelBuilder object that builds Panel. - * @param material that will be around Panel. - */ - public static void fillBorder(PanelBuilder panelBuilder, Material material) - { - GuiUtils.fillBorder(panelBuilder, 5, material); - } - - - /** - * This method sets blocks with given Material around Panel with given row count. - * @param panelBuilder object that builds Panel. - * @param rowCount in Panel. - * @param material that will be around Panel. - */ - public static void fillBorder(PanelBuilder panelBuilder, int rowCount, Material material) - { - // Only for useful filling. - if (rowCount < 3) - { - return; - } - - for (int i = 0; i < 9 * rowCount; i++) - { - // First (i < 9) and last (i > 35) rows must be filled - // First column (i % 9 == 0) and last column (i % 9 == 8) also must be filled. - - if (i < 9 || i > 9 * (rowCount - 1) || i % 9 == 0 || i % 9 == 8) - { - panelBuilder.item(i, BorderBlock.getPanelBorder(material)); - } - } - } - - -// --------------------------------------------------------------------- -// Section: ItemStack transformations -// --------------------------------------------------------------------- - - /** - * This method transforms entity into egg or block that corresponds given entity. - * If entity egg is not found, then it is replaced by block that represents entity or - * barrier block. - * @param entity Entity which egg must be returned. - * @return ItemStack that may be egg for given entity. - */ - public static ItemStack getEntityEgg(EntityType entity) - { - return GuiUtils.getEntityEgg(entity, 1); - } - - - /** - * This method transforms entity into egg or block that corresponds given entity. - * If entity egg is not found, then it is replaced by block that represents entity or - * barrier block. - * @param entity Entity which egg must be returned. - * @param amount Amount of ItemStack elements. - * @return ItemStack that may be egg for given entity. - */ - public static ItemStack getEntityEgg(EntityType entity, int amount) - { - ItemStack itemStack; - - switch (entity) - { - case ENDER_DRAGON: - itemStack = new ItemStack(Material.DRAGON_EGG); - break; - case WITHER: - itemStack = new ItemStack(Material.SOUL_SAND); - break; - case PLAYER: - itemStack = new ItemStack(Material.PLAYER_HEAD); - break; - case MUSHROOM_COW: - itemStack = new ItemStack(Material.MOOSHROOM_SPAWN_EGG); - break; - case SNOWMAN: - itemStack = new ItemStack(Material.CARVED_PUMPKIN); - break; - case IRON_GOLEM: - itemStack = new ItemStack(Material.IRON_BLOCK); - break; - case ARMOR_STAND: - itemStack = new ItemStack(Material.ARMOR_STAND); - break; - default: - Material material = Material.getMaterial(entity.name() + "_SPAWN_EGG"); - - if (material == null) - { - itemStack = new ItemStack(Material.BARRIER); - } - else - { - itemStack = new ItemStack(material); - } - - break; - } - - if (entity.name().equals("PIG_ZOMBIE")) - { - // If pig zombie exist, then pigman spawn egg exists too. - itemStack = new ItemStack(Material.getMaterial("ZOMBIE_PIGMAN_SPAWN_EGG")); - } - - itemStack.setAmount(amount); - - return itemStack; - } - - - /** - * This method transforms entity into player head with skin that corresponds given - * entity. If entity head is not found, then it is replaced by barrier block. - * @param entity Entity which head must be returned. - * @return ItemStack that may be head for given entity. - */ - public static ItemStack getEntityHead(EntityType entity) - { - return GuiUtils.getEntityHead(entity, 1); - } - - - /** - * This method transforms entity into player head with skin that corresponds given - * entity. If entity head is not found, then it is replaced by barrier block. - * @param entity Entity which head must be returned. - * @param amount Amount of ItemStack elements. - * @return ItemStack that may be head for given entity. - */ - public static ItemStack getEntityHead(EntityType entity, int amount) - { - ItemStack itemStack; - - switch (entity) - { - case PLAYER: - itemStack = new ItemStack(Material.PLAYER_HEAD); - break; - case WITHER_SKELETON: - itemStack = new ItemStack(Material.WITHER_SKELETON_SKULL); - break; - case ARMOR_STAND: - itemStack = new ItemStack(Material.ARMOR_STAND); - break; - case SKELETON: - itemStack = new ItemStack(Material.SKELETON_SKULL); - break; - case GIANT: - case ZOMBIE: - itemStack = new ItemStack(Material.ZOMBIE_HEAD); - break; - case CREEPER: - itemStack = new ItemStack(Material.CREEPER_HEAD); - break; - case ENDER_DRAGON: - itemStack = new ItemStack(Material.DRAGON_HEAD); - break; - default: - HeadLib head = HeadLib.getHead(entity.name()); - - if (head == null) - { - itemStack = new ItemStack(Material.BARRIER); - } - else - { - itemStack = head.toItemStack(); - } - break; - } - - itemStack.setAmount(amount); - - return itemStack; - } - - - /** - * This method transforms material into item stack that can be displayed in users - * inventory. - * @param material Material which item stack must be returned. - * @return ItemStack that represents given material. - */ - public static ItemStack getMaterialItem(Material material) - { - return GuiUtils.getMaterialItem(material, 1); - } - - - /** - * This method transforms material into item stack that can be displayed in users - * inventory. - * @param material Material which item stack must be returned. - * @param amount Amount of ItemStack elements. - * @return ItemStack that represents given material. - */ - public static ItemStack getMaterialItem(Material material, int amount) - { - ItemStack itemStack; - - // Process items that cannot be item-stacks. - if (material.name().contains("WALL_")) - { - // Materials that is attached to wall cannot be showed in GUI. But they should be in list. - itemStack = new ItemStack(Material.getMaterial(material.name().replace("WALL_", ""))); - } - else if (material.name().startsWith("POTTED_")) - { - // Materials Potted elements cannot be in inventory. - itemStack = new ItemStack(Material.getMaterial(material.name().replace("POTTED_", ""))); - } - else if (material.equals(Material.MELON_STEM) || material.equals(Material.ATTACHED_MELON_STEM)) - { - itemStack = new ItemStack(Material.MELON_SEEDS); - } - else if (material.equals(Material.PUMPKIN_STEM) || material.equals(Material.ATTACHED_PUMPKIN_STEM)) - { - itemStack = new ItemStack(Material.PUMPKIN_SEEDS); - } - else if (material.equals(Material.TALL_SEAGRASS)) - { - itemStack = new ItemStack(Material.SEAGRASS); - } - else if (material.equals(Material.CARROTS)) - { - itemStack = new ItemStack(Material.CARROT); - } - else if (material.equals(Material.BEETROOTS)) - { - itemStack = new ItemStack(Material.BEETROOT); - } - else if (material.equals(Material.POTATOES)) - { - itemStack = new ItemStack(Material.POTATO); - } - else if (material.equals(Material.COCOA)) - { - itemStack = new ItemStack(Material.COCOA_BEANS); - } - else if (material.equals(Material.KELP_PLANT)) - { - itemStack = new ItemStack(Material.KELP); - } - else if (material.equals(Material.REDSTONE_WIRE)) - { - itemStack = new ItemStack(Material.REDSTONE); - } - else if (material.equals(Material.TRIPWIRE)) - { - itemStack = new ItemStack(Material.STRING); - } - else if (material.equals(Material.FROSTED_ICE)) - { - itemStack = new ItemStack(Material.ICE); - } - else if (material.equals(Material.END_PORTAL) || material.equals(Material.END_GATEWAY) || material.equals(Material.NETHER_PORTAL)) - { - itemStack = new ItemStack(Material.PAPER); - } - else if (material.equals(Material.BUBBLE_COLUMN) || material.equals(Material.WATER)) - { - itemStack = new ItemStack(Material.WATER_BUCKET); - } - else if (material.equals(Material.LAVA)) - { - itemStack = new ItemStack(Material.LAVA_BUCKET); - } - else if (material.equals(Material.FIRE)) - { - itemStack = new ItemStack(Material.FIRE_CHARGE); - } - else if (material.equals(Material.AIR) || material.equals(Material.CAVE_AIR) || material.equals(Material.VOID_AIR)) - { - itemStack = new ItemStack(Material.GLASS_BOTTLE); - } - else if (material.equals(Material.PISTON_HEAD) || material.equals(Material.MOVING_PISTON)) - { - itemStack = new ItemStack(Material.PISTON); - } - else - { - itemStack = new ItemStack(material); - } - - itemStack.setAmount(amount); - - return itemStack; - } - - - /** - * This BorderBlock is simple PanelItem but without item meta data. - */ - private static class BorderBlock extends PanelItem - { - private BorderBlock(ItemStack icon) - { - super(new PanelItemBuilder(). - icon(icon.clone()). - name(" "). - description(Collections.emptyList()). - glow(false). - clickHandler(null)); - } - - - /** - * This method retunrs BorderBlock with requested item stack. - * @param material of which broder must be created. - * @return PanelItem that acts like border. - */ - private static BorderBlock getPanelBorder(Material material) - { - ItemStack itemStack = new ItemStack(material); - itemStack.getItemMeta().setDisplayName(" "); - - return new BorderBlock(itemStack); - } - } - - - /** - * Simple splitter - * - * @param string - string to be split - * @param warpLength - whn warp should be affected. - * @return list of split strings - */ - public static List stringSplit(String string, int warpLength) - { - // Remove all ending lines from string. - string = string.replaceAll("([\\r\\n])", "\\|"); - string = ChatColor.translateAlternateColorCodes('&', string); - // Check length of lines - List result = new ArrayList<>(); - - Arrays.stream(string.split("\\|")). - map(line -> Arrays.asList(WordUtils.wrap(line, warpLength).split(System.getProperty("line.separator")))). - forEach(result::addAll); - - // Fix colors, as splitting my lost that information. - - for (int i = 0, resultSize = result.size(); i < resultSize; i++) - { - if (i > 0) - { - String lastColor = ChatColor.getLastColors(result.get(i - 1)); - result.set(i, lastColor + result.get(i)); - } - } - - return result; - } - - - /** - * Simple splitter for all strings in list. - * @param stringList - list of string to be split - * @param warpLength - whn warp should be affected. - * @return list of split strings - */ - public static List stringSplit(List stringList, int warpLength) - { - if (stringList.isEmpty()) - { - return stringList; - } - - List newList = new ArrayList<>(stringList.size()); - stringList.stream().map(string -> GuiUtils.stringSplit(string, warpLength)).forEach(newList::addAll); - return newList; - } - - - /** - * Sanitizes the provided input. - * It replaces spaces and hyphens with underscores and lower cases the input. - * @param input input to sanitize - * @return sanitized input - */ - public static String sanitizeInput(String input) - { - return input.toLowerCase(Locale.ENGLISH).replace(" ", "_").replace("-", "_"); - } -} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/utils/HeadLib.java b/src/main/java/world/bentobox/challenges/utils/HeadLib.java deleted file mode 100644 index d08b06e..0000000 --- a/src/main/java/world/bentobox/challenges/utils/HeadLib.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Written in 2018 by Daniel Saukel - * - * To the extent possible under law, the author(s) have dedicated all - * copyright and related and neighboring rights to this software - * to the public domain worldwide. - * - * This software is distributed without any warranty. - * - * You should have received a copy of the CC0 Public Domain Dedication - * along with this software. If not, see . - * - * @url https://github.com/DRE2N/HeadLib - */ -package world.bentobox.challenges.utils; - - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; - -import world.bentobox.bentobox.BentoBox; - - -/** - * @author Daniel Saukel - * - * BONNe modified it for BentoBox by leaving only mob heads and removing unused code. - */ -public enum HeadLib -{ -// --------------------------------------------------------------------- -// Section: Library of All Mob heads -// --------------------------------------------------------------------- - - /** - * All enum values. - */ - SPIDER("8bdb71d0-4724-48b2-9344-e79480424798", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2Q1NDE1NDFkYWFmZjUwODk2Y2QyNThiZGJkZDRjZjgwYzNiYTgxNjczNTcyNjA3OGJmZTM5MzkyN2U1N2YxIn19fQ=="), - CAVE_SPIDER("39173a7a-c957-4ec1-ac1a-43e5a64983df", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDE2NDVkZmQ3N2QwOTkyMzEwN2IzNDk2ZTk0ZWViNWMzMDMyOWY5N2VmYzk2ZWQ3NmUyMjZlOTgyMjQifX19"), - ENDERMAN("0de98464-1274-4dd6-bba8-370efa5d41a8", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2E1OWJiMGE3YTMyOTY1YjNkOTBkOGVhZmE4OTlkMTgzNWY0MjQ1MDllYWRkNGU2YjcwOWFkYTUwYjljZiJ9fX0="), - SLIME("7f0b0873-df6a-4a19-9bcd-f6c90ef804c7", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODk1YWVlYzZiODQyYWRhODY2OWY4NDZkNjViYzQ5NzYyNTk3ODI0YWI5NDRmMjJmNDViZjNiYmI5NDFhYmU2YyJ9fX0="), - GUARDIAN("f3898fe0-04fb-4f9c-8f8b-146a1d894007", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzI1YWY5NjZhMzI2ZjlkOTg0NjZhN2JmODU4MmNhNGRhNjQ1M2RlMjcxYjNiYzllNTlmNTdhOTliNjM1MTFjNiJ9fX0="), - GHAST("807f287f-6499-4e93-a887-0a298ab3091f", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGI2YTcyMTM4ZDY5ZmJiZDJmZWEzZmEyNTFjYWJkODcxNTJlNGYxYzk3ZTVmOTg2YmY2ODU1NzFkYjNjYzAifX19"), - BLAZE("7ceb88b2-7f5f-4399-abb9-7068251baa9d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjc4ZWYyZTRjZjJjNDFhMmQxNGJmZGU5Y2FmZjEwMjE5ZjViMWJmNWIzNWE0OWViNTFjNjQ2Nzg4MmNiNWYwIn19fQ=="), - MAGMA_CUBE("96aced64-5b85-4b99-b825-53cd7a9f9726", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzg5NTdkNTAyM2M5MzdjNGM0MWFhMjQxMmQ0MzQxMGJkYTIzY2Y3OWE5ZjZhYjM2Yjc2ZmVmMmQ3YzQyOSJ9fX0="), - WITHER("119c371b-ea16-47c9-ad7f-23b3d894520a", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2RmNzRlMzIzZWQ0MTQzNjk2NWY1YzU3ZGRmMjgxNWQ1MzMyZmU5OTllNjhmYmI5ZDZjZjVjOGJkNDEzOWYifX19"), - ENDER_DRAGON("433562fa-9e23-443e-93b0-d67228435e77", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzRlY2MwNDA3ODVlNTQ2NjNlODU1ZWYwNDg2ZGE3MjE1NGQ2OWJiNGI3NDI0YjczODFjY2Y5NWIwOTVhIn19fQ=="), - SHULKER("d700b0b9-be74-4630-8cb5-62c979828ef6", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjFkMzUzNGQyMWZlODQ5OTI2MmRlODdhZmZiZWFjNGQyNWZmZGUzNWM4YmRjYTA2OWU2MWUxNzg3ZmYyZiJ9fX0="), - CREEPER("eed2d903-ca32-4cc7-b33b-ca3bdbe18da4", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjQyNTQ4MzhjMzNlYTIyN2ZmY2EyMjNkZGRhYWJmZTBiMDIxNWY3MGRhNjQ5ZTk0NDQ3N2Y0NDM3MGNhNjk1MiJ9fX0="), - ZOMBIE("9959dd98-efb3-4ee9-a8fb-2fda0218cda0", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTZmYzg1NGJiODRjZjRiNzY5NzI5Nzk3M2UwMmI3OWJjMTA2OTg0NjBiNTFhNjM5YzYwZTVlNDE3NzM0ZTExIn19fQ=="), - ZOMBIE_VILLAGER("bcaf2b85-d421-47cc-a40a-455e77bfb60b", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzdlODM4Y2NjMjY3NzZhMjE3YzY3ODM4NmY2YTY1NzkxZmU4Y2RhYjhjZTljYTRhYzZiMjgzOTdhNGQ4MWMyMiJ9fX0="), - ZOMBIE_PIGMAN("6540c046-d6ea-4aff-9766-32a54ebe6958", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzRlOWM2ZTk4NTgyZmZkOGZmOGZlYjMzMjJjZDE4NDljNDNmYjE2YjE1OGFiYjExY2E3YjQyZWRhNzc0M2ViIn19fQ=="), - DOG("9655594c-5b1c-48a5-8e12-ffd7e0c735f2", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDk1MTgzY2E0Y2RkMjk2MjhmZTZjNzIyZjc3OTA4N2I4M2MyMWJhOTdmNDIyNWU0YWQ5YjNlNjE4ZWNjZDMwIn19fQ=="), - HORSE("c6abc94e-a5ff-45fe-a0d7-4e479f290a6f", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDJlYjk2N2FiOTRmZGQ0MWE2MzI1ZjEyNzdkNmRjMDE5MjI2ZTVjZjM0OTc3ZWVlNjk1OTdmYWZjZjVlIn19fQ=="), - TURTLE("ef56c7a3-a5e7-4a7f-9786-a4b6273a591d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTJlNTQ4NDA4YWI3NWQ3ZGY4ZTZkNWQyNDQ2ZDkwYjZlYzYyYWE0ZjdmZWI3OTMwZDFlZTcxZWVmZGRmNjE4OSJ9fX0="), - OCELOT("664dd492-3fcd-443b-9e61-4c7ebd9e4e10", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTY1N2NkNWMyOTg5ZmY5NzU3MGZlYzRkZGNkYzY5MjZhNjhhMzM5MzI1MGMxYmUxZjBiMTE0YTFkYjEifX19"), - SHEEP("fa234925-9dbe-4b8f-a544-7c70fb6b6ac5", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjMxZjljY2M2YjNlMzJlY2YxM2I4YTExYWMyOWNkMzNkMThjOTVmYzczZGI4YTY2YzVkNjU3Y2NiOGJlNzAifX19"), - COW("97ddf3b3-9dbe-4a3b-8a0f-1b19ddeac0bd", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWQ2YzZlZGE5NDJmN2Y1ZjcxYzMxNjFjNzMwNmY0YWVkMzA3ZDgyODk1ZjlkMmIwN2FiNDUyNTcxOGVkYzUifX19"), - CHICKEN("7d3a8ace-e045-4eba-ab71-71dbf525daf1", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTYzODQ2OWE1OTljZWVmNzIwNzUzNzYwMzI0OGE5YWIxMWZmNTkxZmQzNzhiZWE0NzM1YjM0NmE3ZmFlODkzIn19fQ=="), - PIG("e1e1c2e4-1ed2-473d-bde2-3ec718535399", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjIxNjY4ZWY3Y2I3OWRkOWMyMmNlM2QxZjNmNGNiNmUyNTU5ODkzYjZkZjRhNDY5NTE0ZTY2N2MxNmFhNCJ9fX0="), - SQUID("f95d9504-ea2b-4b89-b2d0-d400654a7010", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMDE0MzNiZTI0MjM2NmFmMTI2ZGE0MzRiODczNWRmMWViNWIzY2IyY2VkZTM5MTQ1OTc0ZTljNDgzNjA3YmFjIn19fQ=="), - MUSHROOM_COW("e206ac29-ae69-475b-909a-fb523d894336", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDBiYzYxYjk3NTdhN2I4M2UwM2NkMjUwN2EyMTU3OTEzYzJjZjAxNmU3YzA5NmE0ZDZjZjFmZTFiOGRiIn19fQ=="), - ELDER_GUARDIAN("f2e933a7-614f-44e0-bf18-289b102104ab", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWM3OTc0ODJhMTRiZmNiODc3MjU3Y2IyY2ZmMWI2ZTZhOGI4NDEzMzM2ZmZiNGMyOWE2MTM5Mjc4YjQzNmIifX19"), - STRAY("644c9bad-958b-43ce-9d2f-199d85be607c", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzhkZGY3NmU1NTVkZDVjNGFhOGEwYTVmYzU4NDUyMGNkNjNkNDg5YzI1M2RlOTY5ZjdmMjJmODVhOWEyZDU2In19fQ=="), - HUSK("2e387bc6-774b-4fda-ba22-eb54a26dfd9e", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzc3MDY4MWQxYTI1NWZiNGY3NTQ3OTNhYTA1NWIyMjA0NDFjZGFiOWUxMTQxZGZhNTIzN2I0OTkzMWQ5YjkxYyJ9fX0="), - SKELETON_HORSE("bcbce5bf-86c4-4e62-9fc5-0cc90de94b6d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdlZmZjZTM1MTMyYzg2ZmY3MmJjYWU3N2RmYmIxZDIyNTg3ZTk0ZGYzY2JjMjU3MGVkMTdjZjg5NzNhIn19fQ=="), - ZOMBIE_HORSE("506ced1a-dac8-4d84-b341-645fbb297335", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2Q2YjllZjhkZDEwYmE2NDE0MjJiNDQ5ZWQxNWFkYzI5MmQ3M2Y1NzI5ODRkNDdlMjhhMjI2YWE2ZWRkODcifX19"), - DONKEY("3da7917b-cb95-40b3-a516-9befa4f4d71d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjEyNTJjMjI1MGM0NjhkOWZkZTUzODY3Nzg1NWJjOWYyODQzM2RmNjkyNDdkNzEzODY4NzgxYjgyZDE0YjU1In19fQ=="), - MULE("fac6815e-02d5-4776-a5d6-f6d6535b7831", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzY5Y2E0YzI5NTZhNTY3Yzk2ZWUwNGM1MzE0OWYxODY0NjIxODM5M2JjN2IyMWVkNDVmZGFhMTNiZWJjZGFkIn19fQ=="), - EVOKER("36ee7e5b-c092-48ad-9673-2a73b0a44b4f", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTAwZDNmZmYxNmMyZGNhNTliOWM1OGYwOTY1MjVjODY5NzExNjZkYmFlMTMzYjFiMDUwZTVlZTcxNjQ0MyJ9fX0="), - VEX("f83bcfc1-0213-4957-888e-d3e2fae71203", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWU3MzMwYzdkNWNkOGEwYTU1YWI5ZTk1MzIxNTM1YWM3YWUzMGZlODM3YzM3ZWE5ZTUzYmVhN2JhMmRlODZiIn19fQ=="), - VINDICATOR("5f958e1c-91ea-42d3-9d26-09e5925f2d9c", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2RhNTg1ZWJkZGNjNDhmMzA3YmU2YTgzOTE2Zjg3OGVkNGEwMTRlYzNkNGYyODZhMmNmZDk1MzI4MTk2OSJ9fX0="), - ILLUSIONER("ccb79aa9-1764-4e5b-8ff3-e7be661ac7e2", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjYxNWUxMjQ1ZDBkODJkODFkZmEzNzUzMDYzZDhhYWQwZmE2NjU3NTk5ODcxY2Y0YzY5YmFiNzNjNjk5MDU1In19fQ=="), - PIG_ZOMBIE("6540c046-d6ea-4aff-9766-32a54ebe6958", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzRlOWM2ZTk4NTgyZmZkOGZmOGZlYjMzMjJjZDE4NDljNDNmYjE2YjE1OGFiYjExY2E3YjQyZWRhNzc0M2ViIn19fQ=="), - SILVERFISH("30a4cd5c-5754-4db8-8960-18022a74627d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGE5MWRhYjgzOTFhZjVmZGE1NGFjZDJjMGIxOGZiZDgxOWI4NjVlMWE4ZjFkNjIzODEzZmE3NjFlOTI0NTQwIn19fQ=="), - BAT("cfdaf903-18cf-4a92-acf2-efa8626cf0b2", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWU5OWRlZWY5MTlkYjY2YWMyYmQyOGQ2MzAyNzU2Y2NkNTdjN2Y4YjEyYjlkY2E4ZjQxYzNlMGEwNGFjMWNjIn19fQ=="), - WITCH("7f92b3d6-5ee0-4ab6-afae-2206b9514a63", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjBlMTNkMTg0NzRmYzk0ZWQ1NWFlYjcwNjk1NjZlNDY4N2Q3NzNkYWMxNmY0YzNmODcyMmZjOTViZjlmMmRmYSJ9fX0="), - ENDERMITE("33c425bb-a294-4e01-9b5b-a8ad652bb5cf", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODRhYWZmYTRjMDllMmVhZmI4NWQzNTIyMTIyZGIwYWE0NTg3NGJlYTRlM2Y1ZTc1NjZiNGQxNjZjN2RmOCJ9fX0="), - WOLF("4aabc2be-340a-46ad-a42b-0c348344750a", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdhZGU0OWY1MDEzMTExNTExZGM1MWJhYjc2OWMxYWQ2OTUzMTlhNWQzNTViMzZhZTkyMzRlYTlkMWZmOGUifX19"), - SNOWMAN("d71e165b-b49d-4180-9ccf-8ad3084df1dc", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTdlOTgzMWRhZjY4MWY4YzRjNDc3NWNiNDY1M2MzNGJlMjg5OGY4N2VmZDNiNTk4ZDU1NTUxOGYyZmFjNiJ9fX0="), - IRON_GOLEM("7cb6e9a5-994f-40d5-9bfc-4ba5d796d21e", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODkwOTFkNzllYTBmNTllZjdlZjk0ZDdiYmE2ZTVmMTdmMmY3ZDQ1NzJjNDRmOTBmNzZjNDgxOWE3MTQifX19"), - RABBIT("2186bdc6-55b1-4b44-8a46-3c8a11d40f3d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2QxMTY5YjI2OTRhNmFiYTgyNjM2MDk5MjM2NWJjZGE1YTEwYzg5YTNhYTJiNDhjNDM4NTMxZGQ4Njg1YzNhNyJ9fX0="), - POLAR_BEAR("87324464-1700-468f-8333-e7779ec8c21e", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDQ2ZDIzZjA0ODQ2MzY5ZmEyYTM3MDJjMTBmNzU5MTAxYWY3YmZlODQxOTk2NjQyOTUzM2NkODFhMTFkMmIifX19"), - LLAMA("75fb08e5-2419-46fa-bf09-57362138f234", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzJiMWVjZmY3N2ZmZTNiNTAzYzMwYTU0OGViMjNhMWEwOGZhMjZmZDY3Y2RmZjM4OTg1NWQ3NDkyMTM2OCJ9fX0="), - PARROT("da0cac14-3763-45df-b884-c99a567882ac", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTZkZTFlYjllMzI1ZTYyZjI4ZjJjMTgzZDM5YTY4MzExMzY0NDYzNjU3MjY0Njc1YThiNDYxY2QyOGM5In19fQ=="), - VILLAGER("b3ed4a1b-dfff-464c-87c0-c8029e1de47b", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzZhYjYxYWNlMTM2MDE3YTg3YjFiODFiMTQ1ZWJjNjNlMmU2ZGE5ZDM2NGM4MTE5NGIzM2VlODY2ZmU0ZCJ9fX0="), - PHANTOM("9290add8-c291-4a5a-8f8a-594f165406a3", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2U5NTE1M2VjMjMyODRiMjgzZjAwZDE5ZDI5NzU2ZjI0NDMxM2EwNjFiNzBhYzAzYjk3ZDIzNmVlNTdiZDk4MiJ9fX0="), - COD("d6d4c744-06b4-4a8a-bc7a-bdb0770bb1cf", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MmQ3ZGQ2YWFkZjM1Zjg2ZGEyN2ZiNjNkYTRlZGRhMjExZGY5NmQyODI5ZjY5MTQ2MmE0ZmIxY2FiMCJ9fX0="), - SALMON("0354c430-3979-4b6e-8e65-a99eb3ea8818", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGFlYjIxYTI1ZTQ2ODA2Y2U4NTM3ZmJkNjY2ODI4MWNmMTc2Y2VhZmU5NWFmOTBlOTRhNWZkODQ5MjQ4NzgifX19"), - PUFFERFISH("258e3114-368c-48a1-85fd-be580912f0df", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTcxNTI4NzZiYzNhOTZkZDJhMjI5OTI0NWVkYjNiZWVmNjQ3YzhhNTZhYzg4NTNhNjg3YzNlN2I1ZDhiYiJ9fX0="), - TROPICAL_FISH("d93c1bf6-616f-401a-af6e-f9b9803a0024", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTc5ZTQ4ZDgxNGFhM2JjOTg0ZThhNmZkNGZiMTcwYmEwYmI0ODkzZjRiYmViZGU1ZmRmM2Y4Zjg3MWNiMjkyZiJ9fX0="), - DROWNED("2f169660-61be-46bd-acb5-1abef9fe5731", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzNmN2NjZjYxZGJjM2Y5ZmU5YTYzMzNjZGUwYzBlMTQzOTllYjJlZWE3MWQzNGNmMjIzYjNhY2UyMjA1MSJ9fX0="), - DOLPHIN("8b7ccd6d-36de-47e0-8d5a-6f6799c6feb8", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU5Njg4Yjk1MGQ4ODBiNTViN2FhMmNmY2Q3NmU1YTBmYTk0YWFjNmQxNmY3OGU4MzNmNzQ0M2VhMjlmZWQzIn19fQ=="), - // Since 1.14 - CAT("f0aaa05b-0283-4663-9b57-52dbf2ca2750", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTBkYjQxMzc2Y2E1N2RmMTBmY2IxNTM5ZTg2NjU0ZWVjZmQzNmQzZmU3NWU4MTc2ODg1ZTkzMTg1ZGYyODBhNSJ9fX0="), - FOX("237a2651-7da8-457a-aaea-3714bcc196a2", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDg5NTRhNDJlNjllMDg4MWFlNmQyNGQ0MjgxNDU5YzE0NGEwZDVhOTY4YWVkMzVkNmQzZDczYTNjNjVkMjZhIn19fQ=="), - PANDA("bf7435c9-b7eb-49e9-8887-60697f8081b9", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGNhMDk2ZWVhNTA2MzAxYmVhNmQ0YjE3ZWUxNjA1NjI1YTZmNTA4MmM3MWY3NGE2MzljYzk0MDQzOWY0NzE2NiJ9fX0="), - PILLAGER("1ac9d5aa-46ef-4d71-b077-4564382c0a43", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGFlZTZiYjM3Y2JmYzkyYjBkODZkYjVhZGE0NzkwYzY0ZmY0NDY4ZDY4Yjg0OTQyZmRlMDQ0MDVlOGVmNTMzMyJ9fX0="), - RAVAGER("def81bd7-85e5-4644-b1b2-e7521e53bba8", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWNiOWYxMzlmOTQ4OWQ4NmU0MTBhMDZkOGNiYzY3MGM4MDI4MTM3NTA4ZTNlNGJlZjYxMmZlMzJlZGQ2MDE5MyJ9fX0="), - TRADER_LLAMA("47dbdab5-105f-42bc-9580-c61cee9231f3", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzA4N2E1NTZkNGZmYTk1ZWNkMjg0NGYzNTBkYzQzZTI1NGU1ZDUzNWZhNTk2ZjU0MGQ3ZTc3ZmE2N2RmNDY5NiJ9fX0="), - WANDERING_TRADER("943947ea-3e1a-4fdc-85e5-f538379f05e9", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWYxMzc5YTgyMjkwZDdhYmUxZWZhYWJiYzcwNzEwZmYyZWMwMmRkMzRhZGUzODZiYzAwYzkzMGM0NjFjZjkzMiJ9fX0="), - // Since 1.15 - BEE("77342662-8870-445a-869f-f0aef1406b3d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTlhYzE2ZjI5NmI0NjFkMDVlYTA3ODVkNDc3MDMzZTUyNzM1OGI0ZjMwYzI2NmFhMDJmMDIwMTU3ZmZjYTczNiJ9fX0="), - // Since 1.16 - PIGLIN("7b3f9b15-325b-4d6e-a184-0455e233a1cc", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2NlZDlkODAxYWE2ZjgzZjhlNDlmOTBkOWE4Yjg1YjdmOGZkYTU4M2Q4NWY3MmNmZmI2OTg2NzI1Nzg5ZjYzNiJ9fX0="), - ZOMBIFIED_PIGLIN("4f013cfb-84f8-4d80-8529-25127f6c70ee", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2VhYmFlY2M1ZmFlNWE4YTQ5Yzg4NjNmZjQ4MzFhYWEyODQxOThmMWEyMzk4ODkwYzc2NWUwYThkZTE4ZGE4YyJ9fX0="), - STRIDER("d1c2fba9-6633-4625-9cda-8528fae6fe09", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMThhOWFkZjc4MGVjN2RkNDYyNWM5YzA3NzkwNTJlNmExNWE0NTE4NjY2MjM1MTFlNGM4MmU5NjU1NzE0YjNjMSJ9fX0="), - HOGLIN("8196c240-e96a-4434-b630-6b191ceeb480", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWJiOWJjMGYwMWRiZDc2MmEwOGQ5ZTc3YzA4MDY5ZWQ3Yzk1MzY0YWEzMGNhMTA3MjIwODU2MWI3MzBlOGQ3NSJ9fX0="), - ZOGLIN("d6f4e7ce-dc71-4c81-97dc-df0d15d39a68", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWZhMGFkYTM0MTFmYmE4Yjg4NTgzZDg2NGIyNTI2MDZlOTNkZmRmNjQ3NjkwZDNjZjRjMDE3YjYzYmFiMTJiMCJ9fX0="); - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * User UUID that has given skin. - */ - private String uuid; - - /** - * Base64 Encoded link to Minecraft texture. - */ - private String textureValue; - - /** - * Skull owner. - */ - private Object skullOwner; - - /** - * This map allows to access all enum values via their string. - */ - private final static Map BY_NAME = new HashMap<>(); - - -// --------------------------------------------------------------------- -// Section: Constructor -// --------------------------------------------------------------------- - - - /** - * This inits new enum value by given UUID and textureValue that is encoded in Base64. - * @param uuid User UUID String which skin must be loaded. - * @param textureValue Texture link encoded in Base64. - */ - HeadLib(String uuid, String textureValue) - { - this.uuid = uuid; - this.textureValue = textureValue; - } - - -// --------------------------------------------------------------------- -// Section: Methods that returns ItemStacks -// --------------------------------------------------------------------- - - /** - * Returns an ItemStack of the size 1 of the custom head. - * - * @return an ItemStack of the custom head. - */ - public ItemStack toItemStack() - { - return this.toItemStack(1); - } - - - /** - * Returns an ItemStack of the custom head. - * - * @param amount the amount of items in the stack - * @return an ItemStack of the custom head. - */ - public ItemStack toItemStack(int amount) - { - return this.toItemStack(amount, null); - } - - - /** - * Returns an ItemStack of the size 1 of the custom head. - * - * @param displayName the name to display. Supports "&" color codes - * @param loreLines optional lore lines. Supports "&" color codes - * @return an ItemStack of the custom head. - */ - public ItemStack toItemStack(String displayName, String... loreLines) - { - return this.toItemStack(1, displayName, loreLines); - } - - - /** - * Returns an ItemStack of the custom head. - * - * @param amount the amount of items in the stack - * @param displayName the name to display. Supports "&" color codes - * @param loreLines optional lore lines. Supports "&" color codes - * @return an ItemStack of the custom head. - */ - public ItemStack toItemStack(int amount, String displayName, String... loreLines) - { - ItemStack item = new ItemStack(Material.PLAYER_HEAD, amount); - ItemMeta meta = item.getItemMeta(); - - // Set Lora and DisplayName - if (meta != null && displayName != null) - { - meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', displayName)); - - if (loreLines.length != 0) - { - List loreCC = new ArrayList<>(); - Arrays.stream(loreLines).forEach(l -> loreCC.add(ChatColor.translateAlternateColorCodes('&', l))); - meta.setLore(loreCC); - } - - item.setItemMeta(meta); - } - - // Set correct Skull texture - if (meta != null && this.textureValue != null && !this.textureValue.isEmpty()) - { - GameProfile profile = new GameProfile(UUID.fromString(this.uuid), null); - profile.getProperties().put("textures", new Property("textures", this.textureValue)); - - try - { - Field profileField = meta.getClass().getDeclaredField("profile"); - profileField.setAccessible(true); - profileField.set(meta, profile); - item.setItemMeta(meta); - } - catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) - { - BentoBox.getInstance().log("Error while creating Skull Icon"); - } - } - - return item; - } - - -// --------------------------------------------------------------------- -// Section: Other methods -// --------------------------------------------------------------------- - - - /** - * This method returns HeadLib enum object with given name. If enum value with given name does not exist, - * then return null. - * @param name Name of object that must be returned. - * @return HeadLib with given name or null. - */ - public static HeadLib getHead(String name) - { - return BY_NAME.get(name.toUpperCase()); - } - - - // - // This static call populates all existing enum values into static map. - // - static - { - for (HeadLib head : values()) - { - BY_NAME.put(head.name(), head); - } - } -} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/utils/LevelStatus.java b/src/main/java/world/bentobox/challenges/utils/LevelStatus.java index 5e99cf2..12b9067 100644 --- a/src/main/java/world/bentobox/challenges/utils/LevelStatus.java +++ b/src/main/java/world/bentobox/challenges/utils/LevelStatus.java @@ -19,7 +19,7 @@ public class LevelStatus { * @param previousLevel - previous level * @param numberOfChallengesStillToDo - number of challenges still to do on this level * @param complete - whether complete or not - * @param isUnlocked + * @param isUnlocked - if level is unlocked or not. */ public LevelStatus(ChallengeLevel level, ChallengeLevel previousLevel, int numberOfChallengesStillToDo, boolean complete, boolean isUnlocked) { super(); diff --git a/src/main/java/world/bentobox/challenges/utils/Utils.java b/src/main/java/world/bentobox/challenges/utils/Utils.java index b67593a..dee956d 100644 --- a/src/main/java/world/bentobox/challenges/utils/Utils.java +++ b/src/main/java/world/bentobox/challenges/utils/Utils.java @@ -1,14 +1,25 @@ package world.bentobox.challenges.utils; +import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Locale; +import java.util.Set; -import org.bukkit.Material; -import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.*; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.hooks.LangUtilsHook; +import world.bentobox.bentobox.util.Util; /** @@ -16,13 +27,38 @@ import world.bentobox.bentobox.BentoBox; */ public class Utils { + /** + * This method checks if 2 given item stacks are similar without durability check. + * @param input First item. + * @param stack Second item. + * @return {@code true} if items are equal, {@code false} otherwise. + */ + public static boolean isSimilarNoDurability(@Nullable ItemStack input, @Nullable ItemStack stack) + { + if (stack == null || input == null) + { + return false; + } + else if (stack == input) + { + return true; + } + else + { + return input.getType() == stack.getType() && + input.hasItemMeta() == stack.hasItemMeta() && + (!input.hasItemMeta() || Bukkit.getItemFactory().equals(input.getItemMeta(), stack.getItemMeta())); + } + } + + /** * This method groups input items in single itemstack with correct amount and returns it. * Allows to remove duplicate items from list. * @param requiredItems Input item list * @return List that contains unique items that cannot be grouped. */ - public static List groupEqualItems(List requiredItems) + public static List groupEqualItems(List requiredItems, Set ignoreMetaData) { List returnItems = new ArrayList<>(requiredItems.size()); @@ -40,8 +76,8 @@ public class Utils ItemStack required = returnItems.get(i); // Merge items which meta can be ignored or is similar to item in required list. - if (Utils.canIgnoreMeta(item.getType()) && item.getType().equals(required.getType()) || - required.isSimilar(item)) + if (Utils.isSimilarNoDurability(required, item) || + ignoreMetaData.contains(item.getType()) && item.getType().equals(required.getType())) { required.setAmount(required.getAmount() + item.getAmount()); isUnique = false; @@ -50,7 +86,7 @@ public class Utils i++; } - if (isUnique && item != null) + if (isUnique) { // The same issue as in other places. Clone prevents from changing original item. returnItems.add(item.clone()); @@ -61,29 +97,6 @@ public class Utils } - /** - * This method returns if meta data of these items can be ignored. It means, that items will be searched - * and merged by they type instead of using ItemStack#isSimilar(ItemStack) method. - * - * This limits custom Challenges a lot. It comes from ASkyBlock times, and that is the reason why it is - * still here. It would be a great Challenge that could be completed by collecting 4 books, that cannot - * be crafted. Unfortunately, this prevents it. - * The same happens with firework rockets, enchanted books and filled maps. - * In future it should be able to specify, which items meta should be ignored when adding item in required - * item list. - * - * @param material Material that need to be checked. - * @return True if material meta can be ignored, otherwise false. - */ - public static boolean canIgnoreMeta(Material material) - { - return material.equals(Material.FIREWORK_ROCKET) || - material.equals(Material.ENCHANTED_BOOK) || - material.equals(Material.WRITTEN_BOOK) || - material.equals(Material.FILLED_MAP); - } - - /** * This method transforms given World into GameMode name. If world is not a GameMode * world then it returns null. @@ -105,7 +118,7 @@ public class Utils * @param Instance of given object. * @return Next value after currentValue in values array. */ - public static T getNextValue(T[] values, T currentValue) + public static T getNextValue(T[] values, T currentValue) { for (int i = 0; i < values.length; i++) { @@ -133,7 +146,7 @@ public class Utils * @param Instance of given object. * @return Previous value before currentValue in values array. */ - public static T getPreviousValue(T[] values, T currentValue) + public static T getPreviousValue(T[] values, T currentValue) { for (int i = 0; i < values.length; i++) { @@ -152,4 +165,750 @@ public class Utils return currentValue; } + + + /** + * Sanitizes the provided input. It replaces spaces and hyphens with underscores and lower cases the input. + * This code also removes all color codes from the input. + * @param input input to sanitize + * @return sanitized input + */ + public static String sanitizeInput(String input) + { + return ChatColor.stripColor( + Util.translateColorCodes(input.toLowerCase(Locale.ENGLISH). + replace(" ", "_"). + replace("-", "_"))); + } + + + /** + * Send given message to user and add prefix to the start of the message. + * + * @param user User who need to receive message. + * @param message String of message that must be send. + */ + public static void sendMessage(User user, String message) + { + user.sendMessage(user.getTranslation(Constants.CONVERSATIONS + "prefix") + message); + } + + + /** + * Prettify World.Environment object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for World.Environment. + */ + public static String prettifyObject(World.Environment object, User user) + { + // Find addon structure with: + // [addon]: + // environments: + // [environment]: + // name: [name] + String translation = user.getTranslationOrNothing(Constants.ENVIRONMENTS + object.name().toLowerCase() + ".name"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // environments: + // [environment]: [name] + + translation = user.getTranslationOrNothing(Constants.ENVIRONMENTS + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // environments: + // [environment]: [name] + + translation = user.getTranslationOrNothing("environments." + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Lang Utils do not have Environment :( + //LangUtilsHook.getEnvrionmentName(object, user); + + // Nothing was found. Use just a prettify text function. + return Util.prettifyText(object.name()); + } + + + /** + * Prettify World.Environment object description for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified description string for World.Environment. + */ + public static String prettifyDescription(World.Environment object, User user) + { + // Find addon structure with: + // [addon]: + // environments: + // [environment]: + // description: [text] + String translation = user.getTranslationOrNothing(Constants.ENVIRONMENTS + object.name().toLowerCase() + ".description"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // No text to return. + return ""; + } + + + /** + * Prettify Material object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for Material. + */ + public static String prettifyObject(@Nullable Material object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // materials: + // [material]: + // name: [name] + String translation = user.getTranslationOrNothing(Constants.MATERIALS + object.name().toLowerCase() + ".name"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // materials: + // [material]: [name] + + translation = user.getTranslationOrNothing(Constants.MATERIALS + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // materials: + // [material]: [name] + + translation = user.getTranslationOrNothing("materials." + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Use Lang Utils Hook to translate material + return LangUtilsHook.getMaterialName(object, user); + } + + + /** + * Prettify Material object description for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified description string for Material. + */ + public static String prettifyDescription(@Nullable Material object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // materials: + // [material]: + // description: [text] + String translation = user.getTranslationOrNothing(Constants.MATERIALS + object.name().toLowerCase() + ".description"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // No text to return. + return ""; + } + + + /** + * Prettify EntityType object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for EntityType. + */ + public static String prettifyObject(@Nullable EntityType object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // entities: + // [entity]: + // name: [name] + String translation = user.getTranslationOrNothing(Constants.ENTITIES + object.name().toLowerCase() + ".name"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // entities: + // [entity]: [name] + + translation = user.getTranslationOrNothing(Constants.ENTITIES + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // entities: + // [entity]: [name] + + translation = user.getTranslationOrNothing("entities." + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Use Lang Utils Hook to translate material + return LangUtilsHook.getEntityName(object, user); + } + + + /** + * Prettify EntityType object description for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified description string for EntityType. + */ + public static String prettifyDescription(@Nullable EntityType object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // entities: + // [entity]: + // description: [text] + String translation = user.getTranslationOrNothing(Constants.ENTITIES + object.name().toLowerCase() + ".description"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // No text to return. + return ""; + } + + + /** + * Prettify Statistic object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for Statistic. + */ + public static String prettifyObject(@Nullable Statistic object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // statistics: + // [statistic]: + // name: [name] + String translation = user.getTranslationOrNothing(Constants.STATISTICS + object.name().toLowerCase() + ".name"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // statistics: + // [statistic]: [name] + + translation = user.getTranslationOrNothing(Constants.STATISTICS + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // statistics: + // [statistic]: [name] + + translation = user.getTranslationOrNothing("statistics." + object.name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Use Lang Utils Hook to translate material + //return LangUtilsHook.getStatisticName(object, user); + return Util.prettifyText(object.name()); + } + + + /** + * Prettify Statistic object description for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified description string for Statistic. + */ + public static String prettifyDescription(@Nullable Statistic object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // statistics: + // [statistic]: + // description: [text] + String translation = user.getTranslationOrNothing(Constants.STATISTICS + object.name().toLowerCase() + ".description"); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // No text to return. + return ""; + } + + + /** + * Prettify ItemStack object for user. + * @param object Object that must be pretty. + * @param user User who will see the object. + * @return Prettified string for ItemStack. + */ + public static String prettifyObject(@Nullable ItemStack object, User user) + { + // Nothing to translate + if (object == null) + { + return ""; + } + + // Find addon structure with: + // [addon]: + // item-stacks: + // [material]: ... + // meta: + // potion-type: ... + // ... + // generic: [amount] [name] [meta] + String translation; + switch (object.getType()) + { + case POTION, SPLASH_POTION, LINGERING_POTION, TIPPED_ARROW -> + // Get Potion Meta + translation = prettifyObject(object, (PotionMeta) object.getItemMeta(), user); + case PLAYER_HEAD, PLAYER_WALL_HEAD -> + translation = prettifyObject(object, (SkullMeta) object.getItemMeta(), user); + case ENCHANTED_BOOK -> + translation = prettifyObject(object, (EnchantmentStorageMeta) object.getItemMeta(), user); + case WRITTEN_BOOK, WRITABLE_BOOK -> + translation = prettifyObject(object, (BookMeta) object.getItemMeta(), user); + case LEATHER_BOOTS,LEATHER_CHESTPLATE,LEATHER_HELMET,LEATHER_LEGGINGS,LEATHER_HORSE_ARMOR, + TRIDENT,CROSSBOW,CHAINMAIL_HELMET,CHAINMAIL_CHESTPLATE,CHAINMAIL_LEGGINGS,CHAINMAIL_BOOTS,IRON_HELMET, + IRON_CHESTPLATE,IRON_LEGGINGS,IRON_BOOTS,DIAMOND_HELMET,DIAMOND_CHESTPLATE,DIAMOND_LEGGINGS,DIAMOND_BOOTS, + GOLDEN_HELMET,GOLDEN_CHESTPLATE,GOLDEN_LEGGINGS,GOLDEN_BOOTS,NETHERITE_HELMET,NETHERITE_CHESTPLATE, + NETHERITE_LEGGINGS,NETHERITE_BOOTS,WOODEN_SWORD,WOODEN_SHOVEL,WOODEN_PICKAXE,WOODEN_AXE,WOODEN_HOE, + STONE_SWORD,STONE_SHOVEL,STONE_PICKAXE,STONE_AXE,STONE_HOE,GOLDEN_SWORD,GOLDEN_SHOVEL,GOLDEN_PICKAXE, + GOLDEN_AXE,GOLDEN_HOE,IRON_SWORD,IRON_SHOVEL,IRON_PICKAXE,IRON_AXE,IRON_HOE,DIAMOND_SWORD,DIAMOND_SHOVEL, + DIAMOND_PICKAXE,DIAMOND_AXE,DIAMOND_HOE,NETHERITE_SWORD,NETHERITE_SHOVEL,NETHERITE_PICKAXE,NETHERITE_AXE, + NETHERITE_HOE,TURTLE_HELMET,SHEARS,SHIELD,FLINT_AND_STEEL,BOW -> + translation = prettifyObject(object, object.getItemMeta(), user); + default -> + translation = ""; + } + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find addon structure with: + // [addon]: + // materials: + // [material]: [name] + + translation = user.getTranslationOrNothing(Constants.MATERIALS + object.getType().name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Find general structure with: + // materials: + // [material]: [name] + + translation = user.getTranslationOrNothing("materials." + object.getType().name().toLowerCase()); + + if (!translation.isEmpty()) + { + // We found our translation. + return translation; + } + + // Use Lang Utils + return LangUtilsHook.getItemDisplayName(object, user); + } + + + /** + * Prettify enchant string. + * + * @param enchantment the enchantment + * @param user the user + * @return the string + */ + public static String prettifyObject(Enchantment enchantment, User user) + { + if (enchantment == null) + { + return ""; + } + + String type = user.getTranslationOrNothing(Constants.ITEM_STACKS + "enchant." + enchantment.getKey().getKey()); + + if (type.isEmpty()) + { + type = LangUtilsHook.getEnchantName(enchantment, user); + } + + return type; + } + + + /** + * Prettify type string. + * + * @param type the potion type + * @param user the user + * @return the string + */ + public static String prettifyObject(PotionType type, User user) + { + if (type == null) + { + return ""; + } + + String text = user.getTranslationOrNothing(Constants.ITEM_STACKS + "potion-type." + type.name().toLowerCase()); + + if (text.isEmpty()) + { + text = LangUtilsHook.getPotionBaseEffectName(type, user); + } + + return text; + } + + + /** + * Prettify potion item string. + * + * @param item the item + * @param potionMeta the potion meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable PotionMeta potionMeta, User user) + { + if (potionMeta == null) + { + return ""; + } + + Material itemType = item.getType(); + + final String itemReference = Constants.ITEM_STACKS + itemType.name().toLowerCase() + "."; + final String metaReference = Constants.ITEM_STACKS + "meta."; + + PotionData potionData = potionMeta.getBasePotionData(); + + // Check custom translation for potions. + String type = user.getTranslationOrNothing(itemReference + potionData.getType().name().toLowerCase()); + + if (type.isEmpty()) + { + // Check potion types translation. + type = prettifyObject(potionData.getType(), user); + } + + String upgraded = user.getTranslationOrNothing(metaReference + "upgraded"); + String extended = user.getTranslationOrNothing(metaReference + "extended"); + + // Get item specific translation. + String specific = user.getTranslationOrNothing(itemReference + "name", + "[type]", type, + "[upgraded]", (potionData.isUpgraded() ? upgraded : ""), + "[extended]", (potionData.isExtended() ? extended : "")); + + if (specific.isEmpty()) + { + // Use generic translation. + String meta = user.getTranslationOrNothing(metaReference + "potion-meta", + "[type]", type, + "[upgraded]", (potionData.isUpgraded() ? upgraded : ""), + "[extended]", (potionData.isExtended() ? extended : "")); + + specific = user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", meta); + } + + return specific; + } + + + /** + * Prettify skull item string. + * + * @param item the item + * @param skullMeta the skull meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable SkullMeta skullMeta, User user) + { + if (skullMeta == null) + { + return ""; + } + + Material itemType = item.getType(); + final String metaReference = Constants.ITEM_STACKS + "meta."; + + String meta = user.getTranslationOrNothing(metaReference + "skull-meta", + "[player-name]", skullMeta.getDisplayName()); + + return user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", meta); + } + + + /** + * Prettify item string. + * + * @param item the item + * @param itemMeta the item meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable ItemMeta itemMeta, User user) + { + if (itemMeta == null) + { + return ""; + } + + StringBuilder builder = new StringBuilder(); + + itemMeta.getEnchants().forEach((enchantment, level) -> { + builder.append("\n"); + builder.append(user.getTranslationOrNothing(Constants.ITEM_STACKS + "meta.enchant-meta", + "[type]", prettifyObject(enchantment, user), + "[level]", String.valueOf(level))); + }); + + + Material itemType = item.getType(); + final String itemReference = Constants.ITEM_STACKS + itemType.name().toLowerCase() + "."; + + String translation = user.getTranslationOrNothing(itemReference + "name", + "[type]", prettifyObject(itemType, user), + "[enchant]", builder.toString()); + + if (translation.isEmpty()) + { + translation = user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", builder.toString()); + } + + return translation; + } + + + /** + * Prettify enchantment storage string. + * + * @param item the item + * @param enchantmentMeta the enchantment storage meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable EnchantmentStorageMeta enchantmentMeta, User user) + { + if (enchantmentMeta == null) + { + return ""; + } + + StringBuilder builder = new StringBuilder(); + + enchantmentMeta.getEnchants().forEach((enchantment, level) -> { + builder.append("\n"); + builder.append(user.getTranslationOrNothing(Constants.ITEM_STACKS + "meta.enchant-meta", + "[type]", prettifyObject(enchantment, user), + "[level]", String.valueOf(level))); + }); + + + Material itemType = item.getType(); + final String itemReference = Constants.ITEM_STACKS + itemType.name().toLowerCase() + "."; + + String translation = user.getTranslationOrNothing(itemReference + "name", + "[type]", prettifyObject(itemType, user), + "[enchant]", builder.toString()); + + if (translation.isEmpty()) + { + translation = user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", builder.toString()); + } + + return translation; + } + + + /** + * Prettify book item string. + * + * @param item the item + * @param bookMeta the book meta + * @param user the user + * @return the string + */ + public static String prettifyObject(ItemStack item, @Nullable BookMeta bookMeta, User user) + { + if (bookMeta == null) + { + return ""; + } + + Material itemType = item.getType(); + final String metaReference = Constants.ITEM_STACKS + "meta."; + + String meta = user.getTranslationOrNothing(metaReference + "book-meta", + "[title]", bookMeta.getTitle(), + "[author]", bookMeta.getAuthor()); + + return user.getTranslationOrNothing(Constants.ITEM_STACKS + "generic", + "[type]", prettifyObject(itemType, user), + "[meta]", meta); + } + + + /** + * This method parses duration to a readable format. + * @param duration that needs to be parsed. + * @return parsed duration string. + */ + public static String parseDuration(Duration duration, User user) + { + final String reference = Constants.DESCRIPTIONS + "challenge.cooldown."; + + String returnString = ""; + + if (duration.toDays() > 0) + { + returnString += user.getTranslationOrNothing(reference + "in-days", + Constants.PARAMETER_NUMBER, String.valueOf(duration.toDays())); + } + + if (duration.toHoursPart() > 0) + { + returnString += user.getTranslationOrNothing(reference + "in-hours", + Constants.PARAMETER_NUMBER, String.valueOf(duration.toHoursPart())); + } + + if (duration.toMinutesPart() > 0) + { + returnString += user.getTranslationOrNothing(reference + "in-minutes", + Constants.PARAMETER_NUMBER, String.valueOf(duration.toMinutesPart())); + } + + if (duration.toSecondsPart() > 0 || returnString.isBlank()) + { + returnString += user.getTranslationOrNothing(reference + "in-seconds", + Constants.PARAMETER_NUMBER, String.valueOf(duration.toSecondsPart())); + } + + return returnString; + } } diff --git a/src/main/java/world/bentobox/challenges/web/WebManager.java b/src/main/java/world/bentobox/challenges/web/WebManager.java index 702d7ef..23a4a01 100644 --- a/src/main/java/world/bentobox/challenges/web/WebManager.java +++ b/src/main/java/world/bentobox/challenges/web/WebManager.java @@ -115,7 +115,7 @@ public class WebManager JsonObject catalog = new JsonParser().parse(catalogContent).getAsJsonObject(); catalog.getAsJsonArray("challenges").forEach(gamemode -> - this.library.add(new LibraryEntry(gamemode.getAsJsonObject()))); + this.library.add(LibraryEntry.fromJson(gamemode.getAsJsonObject()))); } }); } @@ -142,7 +142,7 @@ public class WebManager try { challengeLibrary = gitHubWebAPI.getRepository("BentoBoxWorld", "weblink"). - getContent("challenges/library/" + entry.getRepository() + ".json"). + getContent("challenges/library/" + entry.repository() + ".json"). getContent(). replaceAll("\\n", ""); } @@ -192,7 +192,7 @@ public class WebManager public List getLibraryEntries() { List entries = new ArrayList<>(this.library); - entries.sort(Comparator.comparingInt(LibraryEntry::getSlot)); + entries.sort(Comparator.comparingInt(LibraryEntry::slot)); return entries; } @@ -216,15 +216,15 @@ public class WebManager /** * Challenges Addon variable. */ - private ChallengesAddon addon; + private final ChallengesAddon addon; /** * BentoBox plugin variable. */ - private BentoBox plugin; + private final BentoBox plugin; /** * This list contains all entries that were downloaded from GitHub. */ - private List library; + private final List library; } diff --git a/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java b/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java index 69e6528..c31ac54 100644 --- a/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java +++ b/src/main/java/world/bentobox/challenges/web/object/LibraryEntry.java @@ -3,185 +3,47 @@ package world.bentobox.challenges.web.object; import org.bukkit.Material; import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; import com.google.gson.JsonObject; /** - * This objects allows to load each Challenges Catalog library entry. + * The type Library entry. + * @param name Name of the library entry + * @param icon Icon of the library entry + * @param description Description of the library entry + * @param repository Link to the repository + * @param language Language of the entry + * @param slot order of the entry + * @param gameMode Made primary for gamemode + * @param author Author of the entry + * @param version version of the challenges. */ -public class LibraryEntry +public record LibraryEntry(String name, Material icon, String description, String repository, + String language, int slot, String gameMode, String author, String version) { /** * Default constructor. * @param object Json Object that must be translated to LibraryEntry. */ - public LibraryEntry(@NonNull JsonObject object) + public static LibraryEntry fromJson(@NonNull JsonObject object) { - this.name = object.get("name").getAsString(); - Material material = Material.matchMaterial(object.get("icon").getAsString()); - this.icon = (material != null) ? material : Material.PAPER; - this.description = object.get("description").getAsString(); - this.repository = object.get("repository").getAsString(); - this.language = object.get("language").getAsString(); - - this.slot = object.get("slot").getAsInt(); - - this.forGameMode = object.get("for").getAsString(); - this.author = object.get("author").getAsString(); - this.version = object.get("version").getAsString(); + return new LibraryEntry(object.get("name").getAsString(), + (material != null) ? material : Material.PAPER, + object.get("description").getAsString(), + object.get("repository").getAsString(), + object.get("language").getAsString(), + object.get("slot").getAsInt(), + object.get("for").getAsString(), + object.get("author").getAsString(), + object.get("version").getAsString()); } - /** - * This method returns the name value. - * @return the value of name. - */ - @NonNull - public String getName() + public static LibraryEntry fromTemplate(String name, Material icon) { - return name; + return new LibraryEntry(name, icon, "", "", "", 0, "", "", ""); } - - - /** - * This method returns the icon value. - * @return the value of icon. - */ - @NonNull - public Material getIcon() - { - return icon; - } - - - /** - * This method returns the description value. - * @return the value of description. - */ - @NonNull - public String getDescription() - { - return description; - } - - - /** - * This method returns the repository value. - * @return the value of repository. - */ - @NonNull - public String getRepository() - { - return repository; - } - - - /** - * This method returns the language value. - * @return the value of language. - */ - @NonNull - public String getLanguage() - { - return language; - } - - - /** - * This method returns the slot value. - * @return the value of slot. - */ - @NonNull - public int getSlot() - { - return slot; - } - - - /** - * This method returns the forGameMode value. - * @return the value of forGameMode. - */ - @NonNull - public String getForGameMode() - { - return forGameMode; - } - - - /** - * This method returns the author value. - * @return the value of author. - */ - @NonNull - public String getAuthor() - { - return author; - } - - - /** - * This method returns the version value. - * @return the value of version. - */ - @NonNull - public String getVersion() - { - return version; - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - - /** - * Name of entry object - */ - private @NonNull String name; - - /** - * Defaults to {@link Material#PAPER}. - */ - private @NonNull Material icon; - - /** - * Description of entry object. - */ - private @NonNull String description; - - /** - * File name in challenges library. - */ - private @NonNull String repository; - - /** - * Language of content. - */ - private @Nullable String language; - - /** - * Desired slot number. - */ - private int slot; - - /** - * Main GameMode for which challenges were created. - */ - private @Nullable String forGameMode; - - /** - * Author (-s) who created current configuration. - */ - private @Nullable String author; - - /** - * Version of Challenges Addon, for which challenges were created. - */ - private @Nullable String version; } \ No newline at end of file diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml index c43b240..13c59e0 100755 --- a/src/main/resources/addon.yml +++ b/src/main/resources/addon.yml @@ -1,7 +1,7 @@ name: Challenges main: world.bentobox.challenges.ChallengesAddon version: ${version}${build.number} -api-version: 1.14 +api-version: 1.17 repository: 'BentoBoxWorld/Challenges' metrics: true diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 63539f1..64f9eea 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,32 +1,57 @@ -# Challenges Configuration ${project.version} +# Challenges 1.0.0-SNAPSHOT-LOCAL Configuration # This config file is dynamic and saved when the server is shutdown. # You cannot edit it while the server is running because changes will # be lost! Use in-game settings GUI or edit when server is offline. # commands: - # - # Allows to define common challenges command that will open User GUI - # with all GameMode selection or Challenges from user world. - # This will not affect /{gamemode_user} challenges command. - user: challenges c - # - # Allows to define common challenges command that will open Admin GUI - # with all GameMode selection. - # This will not affect /{gamemode_admin} challenges command. - admin: challengesadmin chadmin # # This enables/disables common command that will be independent from # all GameModes. For admins it will open selection with all GameModes # (unless there is one), but for users it will open GUI that corresponds # to their world (unless it is specified other way in Admin GUI). - single-gui: false + # This means that writing `/[user_global]` will open Challenges GUI's + # and `/[admin_global]` will open Admin GUI's + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + global-command: false # - # This allows for admins to define which GUI will be opened for admins - # when users calls single-gui command. + # This allows to define which GUI will be opened when `single-gui` is enabled. + # This option is ignored if `single-gui` is disabled. # Acceptable values: # - CURRENT_WORLD - will open GUI that corresponds to user location. # - GAMEMODE_LIST - will open GUI with all installed game modes. - single-gamemode: CURRENT_WORLD + global-view-mode: GAMEMODE_LIST + player: + # + # Allows to define a global challenges user command. This command will work + # only if `global-commands` is enabled. This allows to execute `/challenges` + # without referring to the gamemode. + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + global: challenges c + # + # Allows to define user command for opening challenges GUI's. + # Unlike `global` command, this requires to have gamemode player command before it. + # This will look like: `/[player_cmd] challenges` + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + main: challenges + # + # Allows to define complete command. + # This will look like: `/[player_cmd] challenges complete` + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + complete: complete + admin: + # + # Allows to define a global challenges admin command. This command will work + # only if `global-commands` is enabled. This allows to execute `/chadmin` + # without referring to the gamemode. + # Note, this must not be the same as user global command. + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + global: challengesadmin chadmin + # + # Allows to define admin command for opening challenges GUI's. + # Unlike `global` command, this requires to have gamemode admin command before it. + # This will look like: `/[admin_cmd] challenges` + # /!\ In order to apply the changes made to this option, you must restart your server. Reloading BentoBox or the server won't work. + main: challenges history: # # This indicate if player challenges data history will be stored or not. @@ -56,72 +81,11 @@ gui-settings: # their locked level icon, then it will be used, instead of this one. locked-level-icon: ==: org.bukkit.inventory.ItemStack - v: 1631 - type: BOOK - # - # This indicate if free challenges must be at the start (true) or at the end (false) of list. - free-challenges-first: true - # - # This allows to change lore description line length. By default it is 25, but some server - # owners may like it to be larger. - lore-length: 25 - # - # This string allows to change element order in Challenge description. Each letter represents - # one object from challenge description. If letter is not used, then its represented part - # will not be in description. If use any letter that is not recognized, then it will be - # ignored. Some strings can be customized via lang file under 'challenges.gui.challenge-description'. - # List of values and their meaning: - # - LEVEL - Level String: '*.level' - # - STATUS - Status String: '*.completed' - # - COUNT - Times String: '*.completed-times', '*.completed-times-of' or '*.maxed-reached' - # - DESCRIPTION - Description String: defined in challenge object - challenge.description - # - WARNINGS - Warning String: '*.warning-items-take', '*.objects-close-by', '*.warning-entities-kill', '*.warning-blocks-remove' - # - ENVIRONMENT - Environment String: defined in challenge object - challenge.environment - # - REQUIREMENTS - Requirement String: '*.required-level', '*.required-money', '*.required-experience' and items, blocks or entities - # - REWARD_TEXT - Reward String: message that is defined in challenge.rewardTest and challenge.repeatRewardText - # - REWARD_OTHER - Reward extra String: '*.experience-reward', '*.money-reward', '*.not-repeatable' - # - REWARD_ITEMS - Reward Items: List of items that will be rewarded. - # - REWARD_COMMANDS - Reward Commands: List of commands that will be rewarded. - # Requirement and reward items, blocks and entities that are defined in challenge and can be customized under 'challenges.gui.item-description.*' - challenge-lore: - - LEVEL - - STATUS - - COUNT - - DESCRIPTION - - WARNINGS - - ENVIRONMENT - - REQUIREMENTS - - REWARD_TEXT - - REWARD_OTHER - - REWARD_ITEMS - - REWARD_COMMANDS - # - # This string allows to change element order in Level description. Each letter represents - # one object from level description. If letter is not used, then its represented part - # will not be in description. If use any letter that is not recognized, then it will be - # ignored. Some strings can be customized via lang file under 'challenges.gui.level-description'. - # List of values and their meaning: - # - LEVEL_STATUS - Status String: '*.completed' - # - CHALLENGE_COUNT - Count of completed challenges String: '*.completed-challenges-of' - # - UNLOCK_MESSAGE - Description String: defined in level object - challengeLevel.unlockMessage - # - WAIVER_AMOUNT - WaiverAmount String: '*.waver-amount' - # - LEVEL_REWARD_TEXT - Reward String: message that is defined in challengeLevel.rewardText. - # - LEVEL_REWARD_OTHER - Reward extra String: '*.experience-reward', '*.money-reward' - # - LEVEL_REWARD_ITEMS - Reward Items: List of items that will be rewarded. - # - LEVEL_REWARD_COMMANDS - Reward Commands: List of commands that will be rewarded. - # Reward items that are defined in challenge level and can be customized under 'challenges.gui.item-description.*' - level-lore: - - LEVEL_STATUS - - CHALLENGE_COUNT - - UNLOCK_MESSAGE - - WAIVER_AMOUNT - - LEVEL_REWARD_TEXT - - LEVEL_REWARD_OTHER - - LEVEL_REWARD_ITEMS - - LEVEL_REWARD_COMMANDS + v: 2730 + type: BARRIER # # This indicate if challenges data will be stored per island (true) or per player (false). -store-island-data: false +store-island-data: true # # Reset Challenges - if this is true, player's challenges will reset when users # reset an island or if users are kicked or leave a team. Prevents exploiting the @@ -149,7 +113,4 @@ auto-saver: 30 # disabled-gamemodes: # - BSkyBlock disabled-gamemodes: [] -# -uniqueId: config -# configVersion: v3 diff --git a/src/main/resources/default.json b/src/main/resources/default.json index e98a54f..4e9e6a3 100644 --- a/src/main/resources/default.json +++ b/src/main/resources/default.json @@ -2,10 +2,11 @@ "challengeList": [ { "uniqueId": "cobblecollector", - "friendlyName": "Cobble Stone Exchange", + "friendlyName": "&f&l Cobble Stone Exchange", "deployed": true, "description": [ - "Exchange cobble stone to some useful materials" + "&7 Exchange cobble stone", + "&7 to some useful materials" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: COBBLESTONE\n", "order": 1, @@ -23,7 +24,7 @@ "requiredPermissions": [] } }, - "rewardText": "Small amount of gravel will always be handy", + "rewardText": "&7 Small amount of gravel \n&7 will always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GRAVEL\n amount: 4\n" ], @@ -31,7 +32,7 @@ "rewardMoney": 10, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Small amount of gravel will always be handy", + "repeatRewardText": "&7 Small amount of gravel \n&7 will always be handy", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -42,10 +43,11 @@ }, { "uniqueId": "seedcollector", - "friendlyName": "Seeds Exchange", + "friendlyName": "&f&l Seeds Exchange", "deployed": true, "description": [ - "Exchange seeds to some useful materials" + "&7 Exchange seeds to ", + "&7 some useful materials" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: WHEAT_SEEDS\n", "order": 2, @@ -63,7 +65,7 @@ "requiredPermissions": [] } }, - "rewardText": "Small amount of dirt will always be handy", + "rewardText": "&7 Small amount of dirt \n&7 will always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 4\n" ], @@ -71,7 +73,7 @@ "rewardMoney": 10, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Small amount of dirt will always be handy", + "repeatRewardText": "&7 Small amount of dirt \n&7 will always be handy", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -82,10 +84,11 @@ }, { "uniqueId": "cactuscollector", - "friendlyName": "Cactus Exchange", + "friendlyName": "&f&l Cactus Exchange", "deployed": true, "description": [ - "Exchange cactus to some useful materials" + "&7 Exchange cactus to ", + "&7 some useful materials" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CACTUS\n", "order": 3, @@ -103,7 +106,7 @@ "requiredPermissions": [] } }, - "rewardText": "Small amount of sand will always be handy", + "rewardText": "&7 Small amount of sand \n&7 will always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SAND\n amount: 4\n" ], @@ -111,7 +114,7 @@ "rewardMoney": 10, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Small amount of sand will always be handy", + "repeatRewardText": "&7 Small amount of sand will always be handy", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -122,10 +125,11 @@ }, { "uniqueId": "treecollector", - "friendlyName": "Logs Exchange", + "friendlyName": "&f&l Logs Exchange", "deployed": true, "description": [ - "Fell trees and collect some logs" + "&7 Fell trees and collect ", + "&7 some logs" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OAK_LOG\n", "order": 4, @@ -143,7 +147,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your logs for some clay", + "rewardText": "&7 Exchange your logs \n&7 for some clay", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CLAY\n amount: 4\n" ], @@ -151,7 +155,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your logs for some clay balls", + "repeatRewardText": "&7 Exchange your logs for some clay balls", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -162,10 +166,11 @@ }, { "uniqueId": "cobblegenerator", - "friendlyName": "Cobble Stone Generator", + "friendlyName": "&f&l Cobble Stone Generator", "deployed": true, "description": [ - "Everything starts with a simple cobblestone generator." + "&7 Everything starts with", + "&7 a simple cobblestone generator." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: COBBLESTONE\n", "order": 1, @@ -188,7 +193,7 @@ "requiredPermissions": [] } }, - "rewardText": "These tools will help you with some grind.", + "rewardText": "&7 These tools will help \n&7 you with some grind.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: STONE_PICKAXE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DIG_SPEED: 5\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: STONE_AXE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DIG_SPEED: 5\n", @@ -208,10 +213,11 @@ }, { "uniqueId": "cactusfarm", - "friendlyName": "Cactus farm", + "friendlyName": "&f&l Cactus farm", "deployed": true, "description": [ - "Create a simple cactus farm" + "&7 Create a simple", + "&7 cactus farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CACTUS\n", "order": 2, @@ -232,7 +238,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra sand will always be handy", + "rewardText": "&7 Extra sand will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SAND\n amount: 10\n" ], @@ -249,10 +255,11 @@ }, { "uniqueId": "melonfarm", - "friendlyName": "Melon farm", + "friendlyName": "&f&l Melon farm", "deployed": true, "description": [ - "Create a simple Melon farm" + "&7 Create a simple", + "&7 melon farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MELON\n", "order": 2, @@ -273,7 +280,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -290,10 +297,11 @@ }, { "uniqueId": "mushroomfarm", - "friendlyName": "Mushroom farm", + "friendlyName": "&f&l Mushroom farm", "deployed": true, "description": [ - "Create a simple mushroom farm" + "&7 Create a simple", + "&7 mushroom farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BROWN_MUSHROOM\n", "order": 2, @@ -315,7 +323,7 @@ "requiredPermissions": [] } }, - "rewardText": "This mycelium will help you with collecting mushrooms faster.", + "rewardText": "&7 This mycelium will \n&7 help you with collecting \n&7 mushrooms faster.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MYCELIUM\n" ], @@ -332,10 +340,11 @@ }, { "uniqueId": "pumpkinfarm", - "friendlyName": "Pumpkin farm", + "friendlyName": "&f&l Pumpkin farm", "deployed": true, "description": [ - "Create a simple Pumpkin farm" + "&7 Create a simple", + "&7 pumpkin farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PUMPKIN\n", "order": 2, @@ -356,7 +365,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -373,10 +382,11 @@ }, { "uniqueId": "sugarcanefarm", - "friendlyName": "Sugarcane farm", + "friendlyName": "&f&l Sugarcane farm", "deployed": true, "description": [ - "Create a simple sugarcane farm" + "&7 Create a simple", + "&7 sugarcane farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SUGAR_CANE\n", "order": 2, @@ -397,7 +407,7 @@ "requiredPermissions": [] } }, - "rewardText": "This sand will help you to increase your farm size.", + "rewardText": "&7 This sand will \n&7 help you to increase \n&7 your farm size.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SAND\n amount: 16\n" ], @@ -414,10 +424,11 @@ }, { "uniqueId": "treefarm", - "friendlyName": "Tree farm", + "friendlyName": "&f&l Tree farm", "deployed": true, "description": [ - "Create a simple tree farm" + "&7 Create a simple", + "&7 tree farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OAK_SAPLING\n", "order": 2, @@ -438,7 +449,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 10\n" ], @@ -455,10 +466,11 @@ }, { "uniqueId": "applecollector", - "friendlyName": "Apple Picker", + "friendlyName": "&f&l Apple Picker", "deployed": true, "description": [ - "Find and collect some apples from trees" + "&7 Find and collect some", + "&7 apples from trees" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: APPLE\n", "order": 3, @@ -476,7 +488,7 @@ "requiredPermissions": [] } }, - "rewardText": "These saplings will help you to get more trees", + "rewardText": "&7 These saplings will \n&7 help you to get more \n&7 trees", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SPRUCE_SAPLING\n amount: 4\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ACACIA_SAPLING\n amount: 4\n", @@ -488,7 +500,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "These saplings will help you to get more trees", + "repeatRewardText": "&7 These saplings will help \n&7 you to get more trees", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -503,10 +515,11 @@ }, { "uniqueId": "melonmaker", - "friendlyName": "Melon collector", + "friendlyName": "&f&l Melon collector", "deployed": true, "description": [ - "Grow, collect and craft some melons" + "&7 Grow, collect and craft", + "&7 some melons" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MELON\n", "order": 3, @@ -524,7 +537,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed collected melons into some clay blocks", + "rewardText": "&7 Magic transformed \n&7 collected melons into \n&7 some clay blocks", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CLAY\n amount: 4\n" ], @@ -532,7 +545,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed collected melons into some clay balls", + "repeatRewardText": "&7 Magic transformed collected \n&7 melons into some clay balls", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -543,10 +556,11 @@ }, { "uniqueId": "papermaker", - "friendlyName": "Paper Maker", + "friendlyName": "&f&l Paper Maker", "deployed": true, "description": [ - "Collect sugar canes and craft some paper" + "&7 Collect sugar canes and", + "&7 craft some paper" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PAPER\n", "order": 3, @@ -564,7 +578,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic will transform crafted paper into red sand", + "rewardText": "&7 Magic will transform \n&7 crafted paper into red sand", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: RED_SAND\n amount: 4\n" ], @@ -572,7 +586,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic will transform crafted paper into red sand", + "repeatRewardText": "&7 Magic will transform \n&7 crafted paper into red sand", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -583,10 +597,11 @@ }, { "uniqueId": "pumpkinmaker", - "friendlyName": "Pumpkin collector", + "friendlyName": "&f&l Pumpkin collector", "deployed": true, "description": [ - "Grow and collect some pumpkins" + "&7 Grow and collect", + "&7 some pumpkins" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PUMPKIN\n", "order": 3, @@ -604,7 +619,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed collected pumpkins into some clay blocks", + "rewardText": "&7 Magic transformed collected \n&7 pumpkins into some \n&7 clay blocks", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CLAY\n amount: 4\n" ], @@ -612,7 +627,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed collected pumpkins into some clay balls", + "repeatRewardText": "&7 Magic transformed collected \n&7 pumpkins into some clay balls", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -623,10 +638,12 @@ }, { "uniqueId": "chiseledmaker", - "friendlyName": "Chiseled stone bricks crafter", + "friendlyName": "&f&l Chiseled stone bricks crafter", "deployed": true, "description": [ - "Smelt some stone and craft some chiseled stone bricks" + "&7 Smelt some stone and", + "&7 craft some chiseled", + "&7 stone bricks" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CHISELED_STONE_BRICKS\n", "order": 4, @@ -644,7 +661,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed these bricks into andesite", + "rewardText": "&7 Magic transformed these \n&7 bricks into andesite", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ANDESITE\n amount: 4\n" ], @@ -652,7 +669,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed these bricks into andesite", + "repeatRewardText": "&7 Magic transformed these \n&7 bricks into andesite", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -663,10 +680,11 @@ }, { "uniqueId": "crackedmaker", - "friendlyName": "Cracked stone bricks crafter", + "friendlyName": "&f&l Cracked stone bricks crafter", "deployed": true, "description": [ - "Smelt some bricks to get cracked stone bricks." + "&7 Smelt some bricks to", + "&7 get cracked stone bricks." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CRACKED_STONE_BRICKS\n", "order": 4, @@ -684,7 +702,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed these bricks into diorite.", + "rewardText": "&7 Magic transformed these \n&7 bricks into bird-poop.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIORITE\n amount: 4\n" ], @@ -692,7 +710,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed these bricks into diorite.", + "repeatRewardText": "&7 Magic transformed these \n&7 bricks into diorite.", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -703,10 +721,11 @@ }, { "uniqueId": "stonesmelter", - "friendlyName": "Stone brick maker", + "friendlyName": "&f&l Stone brick maker", "deployed": true, "description": [ - "Smelt some stone and craft some stone bricks" + "&7 Smelt some stone and", + "&7 craft some stone bricks" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: STONE_BRICKS\n", "order": 4, @@ -724,7 +743,7 @@ "requiredPermissions": [] } }, - "rewardText": "Magic transformed these bricks into granite", + "rewardText": "&7 Magic transformed these \n&7 bricks into granite", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GRANITE\n amount: 4\n" ], @@ -732,7 +751,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Magic transformed these bricks into granite", + "repeatRewardText": "&7 Magic transformed these \n&7 bricks into granite", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -743,10 +762,11 @@ }, { "uniqueId": "beetrootfarm", - "friendlyName": "Beetroot farm", + "friendlyName": "&f&l Beetroot farm", "deployed": true, "description": [ - "Create a simple beetroot farm" + "&7 Create a simple", + "&7 beetroot farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BEETROOT\n", "order": 1, @@ -767,7 +787,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -784,10 +804,11 @@ }, { "uniqueId": "carrotfarm", - "friendlyName": "Carrot farm", + "friendlyName": "&f&l Carrot farm", "deployed": true, "description": [ - "Create a simple carrot farm" + "&7 Create a simple", + "&7 carrot farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CARROT\n", "order": 1, @@ -808,7 +829,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -825,10 +846,11 @@ }, { "uniqueId": "potatofarm", - "friendlyName": "Potato farm", + "friendlyName": "&f&l Potato farm", "deployed": true, "description": [ - "Create a simple potato farm" + "&7 Create a simple", + "&7 potato farm" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: POTATO\n", "order": 1, @@ -849,7 +871,7 @@ "requiredPermissions": [] } }, - "rewardText": "Extra dirt will always be handy", + "rewardText": "&7 Extra dirt will \n&7 always be handy", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -866,10 +888,11 @@ }, { "uniqueId": "animalfarm", - "friendlyName": "Animal farm", + "friendlyName": "&f&l Animal farm", "deployed": true, "description": [ - "Create a grass field and wait for animals to spawn." + "&7 Create a grass field and", + "&7 wait for animals to spawn." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GRASS_BLOCK\n", "order": 2, @@ -895,7 +918,7 @@ "requiredPermissions": [] } }, - "rewardText": "Iron to craft some tools and a couple of cats.", + "rewardText": "&7 Iron to craft some \n&7 tools and a couple of \n&7 cats.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OCELOT_SPAWN_EGG\n amount: 2\n" @@ -913,10 +936,11 @@ }, { "uniqueId": "mobfarm", - "friendlyName": "Mob farm", + "friendlyName": "&f&l Mob farm", "deployed": true, "description": [ - "Create a dark place and spawn some hostile mobs." + "&7 Create a dark place and", + "&7 spawn some hostile mobs." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: STONE\n", "order": 2, @@ -940,7 +964,7 @@ "requiredPermissions": [] } }, - "rewardText": "Iron for item crafting and some dogs.", + "rewardText": "&7 Iron for item crafting \n&7 and some dogs.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: WOLF_SPAWN_EGG\n amount: 2\n" @@ -958,10 +982,12 @@ }, { "uniqueId": "visitnether", - "friendlyName": "Visit nether", + "friendlyName": "&f&l Visit nether", "deployed": true, "description": [ - "The nether is a horrible place, but it has some useful materials." + "&7 The nether is a horrible", + "&7 place, but it has some", + "&7 useful materials." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: NETHERRACK\n", "order": 3, @@ -984,7 +1010,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some iron and lapis for your efforts.", + "rewardText": "&7 Some iron and lapis \n&7 for your efforts.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: LAPIS_BLOCK\n amount: 3\n" @@ -1002,10 +1028,10 @@ }, { "uniqueId": "angler", - "friendlyName": "Angler", + "friendlyName": "&f&l Angler", "deployed": true, "description": [ - "Catch some salmon" + "&7 Catch some salmon" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SALMON\n", "order": 4, @@ -1023,7 +1049,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some prismarine crystals and kelp", + "rewardText": "&7 Some prismarine crystals \n&7 and kelp", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PRISMARINE_CRYSTALS\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: KELP\n amount: 4\n" @@ -1032,7 +1058,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some prismarine crystals", + "repeatRewardText": "&7 Some prismarine crystals", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1043,10 +1069,10 @@ }, { "uniqueId": "fisherman", - "friendlyName": "Fisherman", + "friendlyName": "&f&l Fisherman", "deployed": true, "description": [ - "Catch some cod" + "&7 Catch some cod" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: COD\n", "order": 4, @@ -1064,7 +1090,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some prismarine shards and sea pickles", + "rewardText": "&7 Some prismarine shards \n&7 and sea pickles", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: PRISMARINE_SHARD\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SEA_PICKLE\n amount: 4\n" @@ -1073,7 +1099,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some prismarine shards", + "repeatRewardText": "&7 Some prismarine shards", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1084,10 +1110,11 @@ }, { "uniqueId": "grinder", - "friendlyName": "Grinder", + "friendlyName": "&f&l Grinder", "deployed": true, "description": [ - "Use your mobfarm to collect mob drops" + "&7 Use your mobfarm", + "&7 to collect mob drops" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BONE\n", "order": 4, @@ -1110,7 +1137,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchanging drops will provide some useful materials.", + "rewardText": "&7 Exchanging drops will \n&7 provide some useful \n&7 materials.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: REDSTONE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 5\n", @@ -1120,7 +1147,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some prismarine crystals", + "repeatRewardText": "&7 Some prismarine crystals", "maxTimes": 0, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1131,10 +1158,11 @@ }, { "uniqueId": "competent", - "friendlyName": "Competent Island", + "friendlyName": "&f&l Competent Island", "deployed": true, "description": [ - "Reach 20th level of island." + "&7 Reach 20th level", + "&7 of island." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: YELLOW_CONCRETE\n", "order": 5, @@ -1153,7 +1181,7 @@ "requiredPermissions": [] } }, - "rewardText": "Now you can create a nether portal", + "rewardText": "&7 Now you can create \n&7 a nether portal", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OBSIDIAN\n amount: 10\n" ], @@ -1170,10 +1198,12 @@ }, { "uniqueId": "irongolemfarm", - "friendlyName": "Iron Farm", + "friendlyName": "&f&l Iron Farm", "deployed": true, "description": [ - "Use your villagers to spawn free iron golems." + "&7 Use your villagers", + "&7 to spawn free iron", + "&7 golems." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_BLOCK\n", "order": 1, @@ -1194,7 +1224,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some handy mending books and redstone ore", + "rewardText": "&7 Some handy mending \n&7 books and redstone ore", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n amount: 1\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n amount: 1\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", @@ -1213,10 +1243,11 @@ }, { "uniqueId": "slimefarm", - "friendlyName": "Slime farm", + "friendlyName": "&f&l Slime farm", "deployed": true, "description": [ - "Create a slime farm to get some slime blocks" + "&7 Create a slime farm", + "&7 to get some slime blocks" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SLIME_BLOCK\n", "order": 1, @@ -1237,7 +1268,7 @@ "requiredPermissions": [] } }, - "rewardText": "Get some nether quartz ore and an extra mending book", + "rewardText": "&7 Get some nether \n&7 quartz ore and an extra \n&7 mending book", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: NETHER_QUARTZ_ORE\n amount: 16\n" @@ -1255,10 +1286,10 @@ }, { "uniqueId": "snowgolemfarm", - "friendlyName": "Snow farm", + "friendlyName": "&f&l Snow farm", "deployed": true, "description": [ - "." + "&7 Create a snow Golem" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SNOW_BLOCK\n", "order": 1, @@ -1281,7 +1312,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some mending books and lapis ore", + "rewardText": "&7 Some mending books \n&7 and lapis ore", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n amount: 1\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ENCHANTED_BOOK\n amount: 1\n meta:\n ==: ItemMeta\n meta-type: ENCHANTED\n stored-enchants:\n MENDING: 1\n", @@ -1300,10 +1331,11 @@ }, { "uniqueId": "villagerbreeder", - "friendlyName": "Villager Breeder", + "friendlyName": "&f&l Villager Breeder", "deployed": true, "description": [ - "Start to create a villager army." + "&7 Start to create", + "&7 a villager army." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EMERALD_BLOCK\n", "order": 1, @@ -1324,7 +1356,7 @@ "requiredPermissions": [] } }, - "rewardText": "Iron for tools crafting and some cats.", + "rewardText": "&7 Iron for tools crafting \n&7 and some cats.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OCELOT_SPAWN_EGG\n amount: 2\n" @@ -1342,10 +1374,11 @@ }, { "uniqueId": "eggcollector", - "friendlyName": "Egg Collector", + "friendlyName": "&f&l Egg Collector", "deployed": true, "description": [ - "Create a chicken farm and collect their eggs." + "&7 Create a chicken farm", + "&7 and collect their eggs." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EGG\n", "order": 2, @@ -1363,7 +1396,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange eggs for some iron", + "rewardText": "&7 Exchange eggs for \n&7 some iron", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_ORE\n amount: 24\n" ], @@ -1371,7 +1404,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange eggs for some iron", + "repeatRewardText": "&7 Exchange eggs for \n&7 some iron", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1382,10 +1415,10 @@ }, { "uniqueId": "flowercollector", - "friendlyName": "Flower gatherer", + "friendlyName": "&f&l Flower gatherer", "deployed": true, "description": [ - "Collect all small flowers." + "&7 Collect all small flowers." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DANDELION\n", "order": 2, @@ -1412,7 +1445,7 @@ "requiredPermissions": [] } }, - "rewardText": "Large flowers are always better.", + "rewardText": "&7 Large flowers are \n&7 always better.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: COCOA_BEANS\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ROSE_BUSH\n", @@ -1434,10 +1467,11 @@ }, { "uniqueId": "milkcollector", - "friendlyName": "Milk your cows", + "friendlyName": "&f&l Milk your cows", "deployed": true, "description": [ - "Create some buckets and milk your cows" + "&7 Create some buckets", + "&7 and milk your cows" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MILK_BUCKET\n", "order": 2, @@ -1455,7 +1489,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange milk to mooshrooms", + "rewardText": "&7 Exchange milk \n&7 to mooshrooms", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BUCKET\n amount: 9\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MOOSHROOM_SPAWN_EGG\n amount: 2\n" @@ -1464,7 +1498,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange milk to some iron", + "repeatRewardText": "&7 Exchange milk to \n&7 some iron", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1476,10 +1510,11 @@ }, { "uniqueId": "woolcollector", - "friendlyName": "Wool Collector", + "friendlyName": "&f&l Wool Collector", "deployed": true, "description": [ - "Collect all colors of wool." + "&7 Collect all colors", + "&7 of wool." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: WHITE_WOOL\n", "order": 2, @@ -1512,7 +1547,7 @@ "requiredPermissions": [] } }, - "rewardText": "Get some handy items for your wool", + "rewardText": "&7 Get some handy items \n&7 for your wool", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 2\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MUSIC_DISC_CAT\n", @@ -1537,10 +1572,11 @@ }, { "uniqueId": "slimeballcollector", - "friendlyName": "Slimeball collector", + "friendlyName": "&f&l Slimeball collector", "deployed": true, "description": [ - "Kill slimes and collect their slime balls" + "&7 Kill slimes and collect", + "&7 their slime balls" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SLIME_BALL\n", "order": 3, @@ -1558,7 +1594,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some redstone will always be handy!", + "rewardText": "&7 Some redstone will \n&7 always be handy!", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: REDSTONE\n amount: 27\n" ], @@ -1566,7 +1602,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some redstone will always be handy!", + "repeatRewardText": "&7 Some redstone will \n&7 always be handy!", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1577,10 +1613,11 @@ }, { "uniqueId": "ironage", - "friendlyName": "Iron Age", + "friendlyName": "&f&l Iron Age", "deployed": true, "description": [ - "Craft iron tools and armor" + "&7 Craft iron tools", + "&7 and armor" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_INGOT\n", "order": 4, @@ -1606,7 +1643,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your iron stuff to some diamonds", + "rewardText": "&7 Exchange your iron stuff \n&7 to some diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 4\n" ], @@ -1614,7 +1651,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your iron stuff to diamond", + "repeatRewardText": "&7 Exchange your iron \n&7 stuff to diamond", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1625,10 +1662,11 @@ }, { "uniqueId": "trader", - "friendlyName": "Trader", + "friendlyName": "&f&l Trader", "deployed": true, "description": [ - "Use your villagers to get some gems" + "&7 Use your villagers", + "&7 to get some gems" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EMERALD\n", "order": 4, @@ -1646,7 +1684,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your emeralds to some diamonds", + "rewardText": "&7 Exchange your emeralds \n&7 to some diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 4\n" ], @@ -1654,7 +1692,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your emeralds to diamond", + "repeatRewardText": "&7 Exchange your emeralds \n&7 to diamond", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1665,10 +1703,11 @@ }, { "uniqueId": "expert", - "friendlyName": "Expert Island", + "friendlyName": "&f&l Expert Island", "deployed": true, "description": [ - "Reach 100th level of island." + "&7 Reach 100th level of", + "&7 island." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GREEN_CONCRETE\n", "order": 6, @@ -1687,7 +1726,7 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds", + "rewardText": "&7 Get some extra \n&7 diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 10\n" ], @@ -1704,10 +1743,10 @@ }, { "uniqueId": "alchemist", - "friendlyName": "Alchemist", + "friendlyName": "&f&l Alchemist", "deployed": true, "description": [ - "Brew some potions" + "&7 Brew some potions" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BREWING_STAND\n", "order": 1, @@ -1738,7 +1777,7 @@ "requiredPermissions": [] } }, - "rewardText": "There are never too many diamonds", + "rewardText": "&7 There are never too \n&7 many diamonds", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_ORE\n amount: 4\n" ], @@ -1757,10 +1796,11 @@ }, { "uniqueId": "dj", - "friendlyName": "DJ", + "friendlyName": "&f&l DJ", "deployed": true, "description": [ - "Craft a jukebox and collect all music discs" + "&7 Craft a jukebox and", + "&7 collect all music discs" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MUSIC_DISC_CHIRP\n", "order": 2, @@ -1790,7 +1830,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some diamonds and emeralds for your disks", + "rewardText": "&7 Some diamonds and \n&7 emeralds for your disks", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 3\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: EMERALD\n amount: 10\n", @@ -1800,7 +1840,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "A diamond and some emeralds for your disks", + "repeatRewardText": "&7 A diamond and some emeralds \n&7 for your disks", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1813,10 +1853,10 @@ }, { "uniqueId": "icefarmer", - "friendlyName": "Ice Farmer", + "friendlyName": "&f&l Ice Farmer", "deployed": true, "description": [ - "Collect some ice" + "&7 Collect some ice" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ICE\n", "order": 3, @@ -1835,16 +1875,17 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes|", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes|", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 16\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Get some extra diamonds|", + "repeatRewardText": "&7 Get some extra \n&7 diamonds", "maxTimes": 0, "repeatExperienceReward": 0, "repeatItemReward": [ @@ -1855,10 +1896,11 @@ }, { "uniqueId": "goldage", - "friendlyName": "Gold Age", + "friendlyName": "&f&l Gold Age", "deployed": true, "description": [ - "Craft gold tools and armor" + "&7 Craft gold tools", + "&7 and armor" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GOLD_INGOT\n", "order": 4, @@ -1884,7 +1926,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your gold stuff to some diamond", + "rewardText": "&7 Exchange your gold \n&7 stuff to some diamond", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 16\n" ], @@ -1892,7 +1934,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your gold stuff to diamond", + "repeatRewardText": "&7 Exchange your gold \n&7 stuff to diamond", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -1903,10 +1945,12 @@ }, { "uniqueId": "witherkiller", - "friendlyName": "Wither Killer", + "friendlyName": "&f&l Wither Killer", "deployed": true, "description": [ - "Kill the wither and show your worthiness" + "&7 Kill the wither", + "&7 and show your", + "&7 worthiness" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: NETHER_STAR\n", "order": 5, @@ -1924,10 +1968,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_BLOCK\n amount: 5\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -1942,10 +1987,12 @@ }, { "uniqueId": "chef", - "friendlyName": "Chef", + "friendlyName": "&f&l Chef", "deployed": true, "description": [ - "Show your ability to cook all types of food" + "&7 Show your ability", + "&7 to cook all types", + "&7 of food" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: CAKE\n", "order": 6, @@ -1978,16 +2025,17 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 16\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Some diamond ore for your food|", + "repeatRewardText": "&7 Some diamond ore \n&7 for your food|", "maxTimes": 100, "repeatExperienceReward": 0, "repeatItemReward": [ @@ -1998,10 +2046,11 @@ }, { "uniqueId": "zoomaker", - "friendlyName": "Zoo Maker", + "friendlyName": "&f&l Zoo Maker", "deployed": true, "description": [ - "Create a peaceful mob zoo" + "&7 Create a peaceful", + "&7 mob zoo" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GLASS_PANE\n", "order": 7, @@ -2038,10 +2087,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 32\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2056,10 +2106,11 @@ }, { "uniqueId": "advanced", - "friendlyName": "Advanced Island", + "friendlyName": "&f&l Advanced Island", "deployed": true, "description": [ - "Reach 500th level of island." + "&7 Reach 500th level", + "&7 of island." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BLUE_CONCRETE\n", "order": 10, @@ -2078,7 +2129,7 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker shells", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker shells", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 20\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_SHELL\n amount: 5\n" @@ -2096,10 +2147,11 @@ }, { "uniqueId": "visitend", - "friendlyName": "Visit the End", + "friendlyName": "&f&l Visit the End", "deployed": true, "description": [ - "Is there something even after the end?" + "&7 Is there something", + "&7 even after the end?" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: END_STONE\n", "order": 3, @@ -2122,7 +2174,7 @@ "requiredPermissions": [] } }, - "rewardText": "Some diamonds and an elytra for your efforts.", + "rewardText": "&7 Some diamonds and \n&7 an elytra for your efforts.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_ORE\n amount: 16\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ELYTRA\n" @@ -2140,10 +2192,11 @@ }, { "uniqueId": "diamondage", - "friendlyName": "Diamond Age", + "friendlyName": "&f&l Diamond Age", "deployed": true, "description": [ - "Craft diamond tools and armor" + "&7 Craft diamond tools", + "&7 and armor" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n", "order": 4, @@ -2169,15 +2222,17 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your diamond stuff to some shulker boxes", + "rewardText": "&7 Exchange your diamond \n&7 stuff to some shulker boxes", "rewardItems": [ - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 3\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your diamond stuff to shulker shell", + "repeatRewardText": "&7 Exchange your diamond \n&7 stuff to shulker shell", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -2188,10 +2243,11 @@ }, { "uniqueId": "glassmaker", - "friendlyName": "Glass maker", + "friendlyName": "&f&l Glass maker", "deployed": true, "description": [ - "Craft and collect all colors of glass" + "&7 Craft and collect", + "&7 all colors of glass" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GLASS\n", "order": 4, @@ -2224,7 +2280,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your glass to a shulker box", + "rewardText": "&7 Exchange your glass \n&7 to a shulker box", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n" ], @@ -2232,7 +2288,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your glass to a shulker shell", + "repeatRewardText": "&7 Exchange your glass \n&7 to a shulker shell", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -2243,10 +2299,11 @@ }, { "uniqueId": "terracottamaker", - "friendlyName": "Terracotta maker", + "friendlyName": "&f&l Terracotta maker", "deployed": true, "description": [ - "Smelt some clay into terracotta" + "&7 Smelt some clay", + "&7 into terracotta" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: TERRACOTTA\n", "order": 4, @@ -2279,7 +2336,7 @@ "requiredPermissions": [] } }, - "rewardText": "Exchange your terracotta to a shulker box", + "rewardText": "&7 Exchange your terracotta \n&7 to a shulker box", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n" ], @@ -2287,7 +2344,7 @@ "rewardMoney": 0, "rewardCommands": [], "repeatable": true, - "repeatRewardText": "Exchange your terracotta to a shulker shell", + "repeatRewardText": "&7 Exchange your terracotta \n&7 to a shulker shell", "maxTimes": 100, "repeatExperienceReward": 5, "repeatItemReward": [ @@ -2298,10 +2355,11 @@ }, { "uniqueId": "beaconator", - "friendlyName": "Beaconator", + "friendlyName": "&f&l Beaconator", "deployed": true, "description": [ - "Create a beacon with iron base" + "&7 Create a beacon", + "&7 with iron base" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BEACON\n", "order": 6, @@ -2323,10 +2381,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 32\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2341,10 +2400,10 @@ }, { "uniqueId": "enderkiller", - "friendlyName": "Hostile Zoo", + "friendlyName": "&f&l Hostile Zoo", "deployed": true, "description": [ - "Create hostile mob zoo" + "&7 Create hostile mob zoo" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: IRON_BARS\n", "order": 6, @@ -2380,10 +2439,14 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 32\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 5\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2398,10 +2461,11 @@ }, { "uniqueId": "engineer", - "friendlyName": "Engineer", + "friendlyName": "&f&l Engineer", "deployed": true, "description": [ - "Create some redstone machines" + "&7 Create some redstone", + "&7 machines" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: OBSERVER\n", "order": 6, @@ -2429,10 +2493,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 32\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2449,10 +2514,11 @@ }, { "uniqueId": "waterzoo", - "friendlyName": "Water Zoo", + "friendlyName": "&f&l Water Zoo", "deployed": true, "description": [ - "Create a water animal zoo" + "&7 Create a water", + "&7 animal zoo" ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BLUE_STAINED_GLASS_PANE\n", "order": 7, @@ -2481,10 +2547,11 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 16\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 2\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2499,10 +2566,11 @@ }, { "uniqueId": "elite", - "friendlyName": "Elite Island", + "friendlyName": "&f&l Elite Island", "deployed": true, "description": [ - "Reach 10 000th level of island." + "&7 Reach 10 000th level", + "&7 of island." ], "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MAGENTA_CONCRETE\n", "order": 10, @@ -2521,10 +2589,14 @@ "requiredPermissions": [] } }, - "rewardText": "Get some extra diamonds and some shulker boxes", + "rewardText": "&7 Get some extra diamonds \n&7 and some shulker boxes", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND\n amount: 64\n", - "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 5\n" + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n", + "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: SHULKER_BOX\n amount: 1\n" ], "rewardExperience": 50, "rewardMoney": 0, @@ -2541,14 +2613,14 @@ "challengeLevelList": [ { "uniqueId": "novice", - "friendlyName": "Novice", + "friendlyName": "&f&l Novice", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: ORANGE_CONCRETE\n", "lockedIcon": null, "world": "", "order": 1, "waiverAmount": 3, "unlockMessage": "", - "rewardText": "Small reward for completing all Novice challenges.", + "rewardText": "&7 Small reward for \n&7 completing all \n&7 Novice challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GRASS_BLOCK\n" ], @@ -2576,14 +2648,14 @@ }, { "uniqueId": "competent", - "friendlyName": "Competent", + "friendlyName": "&f&l Competent", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: YELLOW_CONCRETE\n", "lockedIcon": null, "world": "", "order": 2, "waiverAmount": 2, - "unlockMessage": "Congratulations - you unlocked the &dCompetent level!", - "rewardText": "Small reward for completing all Competent challenges.", + "unlockMessage": "&7 Congratulations - \n&7 you unlocked the \n&7 &dCompetent level!", + "rewardText": "&7 Small reward for \n&7 completing all \n&7 Competent challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIRT\n amount: 16\n" ], @@ -2607,14 +2679,14 @@ }, { "uniqueId": "expert", - "friendlyName": "Expert", + "friendlyName": "&f&l Expert", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: GREEN_CONCRETE\n", "lockedIcon": null, "world": "", "order": 3, "waiverAmount": 1, - "unlockMessage": "Congratulations - you unlocked the &dExpert level!", - "rewardText": "Small reward for completing all Expert challenges.", + "unlockMessage": "&7 Congratulations - \n&7 you unlocked the \n&7 &dExpert level!", + "rewardText": "&7 Small reward for \n&7 completing all \n&7 Expert challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_PICKAXE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DIG_SPEED: 5\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_AXE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DIG_SPEED: 5\n", @@ -2643,14 +2715,14 @@ }, { "uniqueId": "advanced", - "friendlyName": "Advanced", + "friendlyName": "&f&l Advanced", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BLUE_CONCRETE\n", "lockedIcon": null, "world": "", "order": 4, "waiverAmount": 0, - "unlockMessage": "Congratulations - you unlocked the &dAdvanced level!", - "rewardText": "Small reward for completing all Advanced challenges.", + "unlockMessage": "&7 Congratulations - \n&7 you unlocked the \n&7 &dAdvanced level!", + "rewardText": "&7 Small reward for \n&7 completing all \n&7 Advanced challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_HELMET\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n PROTECTION_ENVIRONMENTAL: 4\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_CHESTPLATE\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n PROTECTION_ENVIRONMENTAL: 4\n", @@ -2676,14 +2748,14 @@ }, { "uniqueId": "elite", - "friendlyName": "Elite", + "friendlyName": "&f&l Elite", "icon": "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: MAGENTA_CONCRETE\n", "lockedIcon": null, "world": "", "order": 5, "waiverAmount": 0, - "unlockMessage": "Congratulations - you unlocked the &dElite level!", - "rewardText": "Small reward for completing all Elite challenges.", + "unlockMessage": "&7 Congratulations - \n&7 you unlocked the \n&7 &dElite level!", + "rewardText": "&7 Small reward for \n&7 completing all\n&7 Elite challenges.", "rewardItems": [ "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: DIAMOND_SWORD\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n DAMAGE_ALL: 5\n LOOT_BONUS_MOBS: 3\n", "is:\n ==: org.bukkit.inventory.ItemStack\n v: 1976\n type: BOW\n meta:\n ==: ItemMeta\n meta-type: UNSPECIFIC\n enchants:\n DURABILITY: 3\n ARROW_DAMAGE: 5\n ARROW_INFINITE: 1\n ARROW_FIRE: 1\n", diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml index 90744eb..2d65c5f 100644 --- a/src/main/resources/locales/de.yml +++ b/src/main/resources/locales/de.yml @@ -1,552 +1,1046 @@ --- +meta: + authors: + - BONNe challenges: commands: admin: main: description: Hauptadministrationsbefehl. Öffnet GUI. - import: - parameters: "[overwrite]" - description: |- - Herausforderungen aus der challenges.yml importieren - Parameter überschreiben bedeutet, dass Herausforderungen oder Level mit der gleichen ID überschrieben werden. reload: - parameters: "[hard]" description: |- Herausforderungen aus der Datenbank neu laden Parameter hard bedeutet, dass Addon die Verbindung zur Datenbank zurücksetzen wird. - defaults: - parameters: "[command]" - description: Zeigt Unterbefehle zum Importieren/Exportieren der Standardherausforderungen. - defaults-generate: - parameters: "[overwrite] - Erlaubt es eine bereits existierende Datei zu überschreiben" - description: Bestehende Herausforderungen in default.json Datei exportieren. - complete: - parameters: " " - description: Eine Herausforderung für einen Spieler abschließen. - reset: - parameters: " " - description: Eine Herausforderung für einen Spieler zurücksetzen. Wenn "challenge_id" - auf "all" gesetzt ist, werden alle Herausforderungen zurückgesetzt. - migrate: - description: Migrieren der aktuellen Spielwelt Herausforderungen Daten auf - 0.8.0 Speicherformat. show: description: Schreibt alle Herausforderungen in den Chat, die es auf dieser Welt gibt. - defaults-import: - description: Importiert die Standardherausforderungen. - user: complete: - parameters: " [count]" - description: Herausforderung abschließen. + description: Eine Herausforderung für einen Spieler abschließen. + parameters: " " + reset: + description: Eine Herausforderung für einen Spieler zurücksetzen. Wenn "challenge_id" + auf "all" gesetzt ist, werden alle Herausforderungen zurückgesetzt. + parameters: " " + migrate: + description: Migrieren der aktuellen Spielwelt Herausforderungen Daten auf + 0.8.0 Speicherformat. + user: main: description: Herausforderungen GUI öffnen. + complete: + description: Herausforderung abschließen. + parameters: " [count]" gui: - title: - admin: - gui-title: "&aHerausforderungen Admin" - edit-challenge-title: "&aBearbeite die Herausforderungen" - edit-level-title: "&aEditiere das Level" - settings-title: "&aEinstellungen bearbeiten" - choose-challenge-title: "&aHerausforderung auswählen" - choose-level-title: "&aStufe wählen" - choose-user-title: "&aSpieler auswählen" - manage-blocks: "&aBlöcke verwalten" - manage-entities: "&aObjekte verwalten" - confirm-title: "&aBestätigung" - manage-items: "&aItems verwalten" - manage-numbers: "&aNummernblock" - select-block: "&aBlock wählen" - select-challenge: "&aHerausforderung auswählen" - select-entity: "&aObjekt auswählen" - toggle-environment: "&aUmschalten der Umgebung" - edit-text-fields: "&aTextfelder bearbeiten" - library-title: "&aDownloadbare Bibliotheken" - lore-add: "&aHinzufügen von Machtelementen" - lore-remove: "&aEntfernen von Überlieferungselement" - lore-edit: "&aBearbeiten von Überlieferungen" - type-select: "&aHerausforderungstyp wählen" - challenges: "&6Herausforderungen" - game-modes: "&6Spielmodus wählen" - multiple-complete: "&6Wie oft?" + titles: + player-gui: "&0&l Herausforderungsmenü" + gamemode-gui: "&0&l Spielmodus auswählen" + multiple-gui: "&0&l Wie oft?" + admin-gui: "&0&l Herausforderung Admin Menu" + edit-challenge: "&0&l Herausforderung [challenge]" + edit-level: "&0&l Level Bearbeiten [level]" + settings: "&0&l Einstellungen" + choose-challenge: "&0&l Herausforderung wählen" + choose-level: "&0&l Level wählen" + choose-player: "&0&l Spieler wählen" + library: "&0&l Bibliothek" + manage-blocks: "&0&l Blöcke verwalten" + manage-entities: "&0&l Einheiten verwalten" + type-selector: "&0&l Wahl des Herausforderungstyps" + item-selector: "&0&l Item Auswahl" + block-selector: "&0&l Block wählen" + entity-selector: "&0&l Mob wählen" + challenge-selector: "&0&l Herausforderungsauswahl" + statistic-selector: "&0&l Statistik Auswahl" + environment-selector: "&0&l Umgebungs Auswahl" buttons: - admin: - complete: Vollständige Benutzerherausforderung - reset: Benutzer-Herausforderung zurücksetzen - create-challenge: Hinzufügen einer neuen Herausforderung - create-level: Hinzufügen eines neuen Levels - edit-challenge: 'Herausforderung bearbeiten ' - edit-level: Level bearbeiten - delete-challenge: Herausforderung entfernen - delete-level: Level entfernen - properties: Eigenschaften - requirements: Anforderungen - rewards: Belohnungen - challenges: Herausforderungen - deployment: Aufstellung - icon: Symbol - locked-icon: Gesperrtes Icon - description: Beschreibung - order: Order - environment: Umgebung - remove-on-complete: Nach Fertigstellung entfernen - required-experience: Benötigte Erfahrung - remove-experience: Erfahrung entfernen - required-level: Benötigtes Insellevel - required-money: Benötigtes Geld - remove-money: Geld entfernen - reward-text: Belohnungsnachricht - reward-items: Item Belohnung - reward-experience: Erfahrungsbelohnung - reward-money: Geld Belohnung - reward-commands: Belohnungs-Befehle - repeatable: Wiederholbar - repeat-count: Max Wiederholung - repeat-reward-text: Belohnungsnachricht wiederholen - repeat-reward-items: Item Belohnung wiederholen - repeat-reward-experience: Erfahrungsbelohnung wiederholen - repeat-reward-money: Geld Belohnung wiederholen - repeat-reward-commands: Belohnungsbefehle wiederholen - remove-completed: Nach Fertigstellung entfernen - glow: Leuchtet nach Fertigstellung - free-at-top: Freie Herausforderungen zuerst - line-length: Länge der Striche - add: Hinzufügen - accept: Akzeptieren - decline: Ablehnen - save: speichern - cancel: Abbrechen - input: Eingabe - value: Wert - set: "=" - increase: "+" - reduce: "-" - multiply: "*" - clear: Löschen - remove-empty: Leer entfernen - number: "[number]" - history-lifespan: Übersicht Lebensdauer - input-mode: Eingabemodus wechseln - title-enable: Fertigstellungstitel - library: Webbibliothek - download: Download Bibliotheken - type: - island: "&6Inseltyp" - inventory: "&6Inventartyp" - other: "&6Anderer Typ" - import: ASkyblock-Herausforderungen importieren - settings: Einstellungen bearbeiten - name: Freundliche Bezeichnung - required-entities: Erforderliche Einheiten - remove-entities: Töte Einheiten - required-blocks: Erforderliche Blöcke - remove-blocks: Blöcke entfernen - search-radius: Suchradius - required-permissions: Erforderliche Berechtigungen - required-items: Erforderliche Items - remove-items: Items entfernen - waiver-amount: Verzichtsmenge - add-challenge: Herausforderung hinzufügen - remove-challenge: Herausforderung entfernen - reset-on-new: Reset auf neuer Insel - broadcast: Fertigstellung der Übertragung - visibility-mode: Herausforderung Sichtbarkeitsmodus - toggle-user-list: Benutzerliste - remove-selected: Auswahl entfernen - show-eggs: Ansichtsmodus wechseln - level-lore: Level-Beschreibung - challenge-lore: Beschreibung der Herausforderung - gui-view-mode: Alle Spielmodi anzeigen - gui-mode: Einzelne Herausforderungen GUI - history-store: Herausforderungen Historie - island-store: Shop pro Insel - default-locked-icon: Symbol für gesperrte Level - title-showtime: Titel Anzeigezeit - default-import: Importieren von Standardherausforderungen - default-export: Bestehende Herausforderungen exportieren - complete-wipe: Addon-Datenbanken löschen - challenge-wipe: Herausforderungen Datenbank löschen - players-wipe: Benutzerdatenbank löschen - next: Nächste Seite - previous: Vorherige Seite - return: Zurück - value: Abgeschlossen - increase: Erhöhen - reduce: Reduzieren + free-challenges: + name: "&f&l Kostenlose Herausforderungen" + description: |- + &7 Zeigt eine Liste von + &7 kostenlose Herausforderungen + return: + name: "&f&l Zurück" + description: |- + &7 Zurück zum vorherigen Menü + &7 oder beenden Sie die GUI + previous: + name: "&f&l Vorherige Seite" + description: "&7 Zur Seite &e [number] &7 wechseln" + next: + name: "&f&l Nächste Seite" + description: "&7 Zur Seite &e [number] &7 wechseln" + reduce: + name: "&f&l Verringern" + description: "&7 verringern um &e [Zahl]" + increase: + name: "&f&l Erhöhen" + description: "&7 Erhöhen um &e [number]" + accept: + name: "&f&l Abgeschlossen" + description: |- + &7 Erledige Herausforderung &e [number] + &7 time(-s) + quit: + name: "&f&l Beenden" + description: "&7 Verlasse die GUI." + complete_user_challenges: + name: "&f&l Schließe die Benutzerherausforderung(en) ab" + description: |- + &7 Ermöglicht die Auswahl von einem Spieler und + &7 das Abschließen von Herausforderung(en) für + &7 ihn + reset_user_challenges: + name: "&f&l Benutzerherausforderungen zurücksetzen" + description: |- + &7 Ermöglicht die Auswahl von einem Spieler und + &7 setzt seine Herausforderungen zurück + add_challenge: + name: "&f&l Herausforderung erstellen" + description: |- + &7 Startet einen Prozess um + &7 einer neue Herausforderung ertellen. + add_level: + name: "&f&l Level erstellen" + description: |- + &7 Startet einen Prozess für + &7 Erstellen eines neuen Level. + edit_challenge: + name: "&f&l Herausforderung bearbeiten" + description: |- + &7 Ermöglicht die Auswahl und Bearbeitung + &7 eine Herausforderung. + edit_level: + name: "&f&l Level bearbeiten" + description: |- + &7 Ermöglicht die Auswahl und Bearbeitung + &7 eines Level's. + delete_challenge: + name: "&f&l Herausforderung löschen" + description: |- + &7 Ermöglicht das Auswählen und Löschen + &7 eine Herausforderung. + delete_level: + name: "&f&l Level löschen" + description: |- + &7 Ermöglicht das Auswählen und Löschen + &7 eines Level. + edit_settings: + name: "&f&l Einstellungen" + description: |- + &7 Ermöglicht das Anzeigen und Bearbeiten der + &7 Add-on-Einstellungen. + complete_wipe: + name: "&f&l Löschen abschließen" + description: |- + &7 Löscht Herausforderungen vollständig + &7 Zusatzdatenbank, einschließlich + &7 Benutzerdaten. + challenge_wipe: + name: "&f&l Herausforderung Löschen" + description: |- + &7 Löscht Herausforderungen vollständig + &7 und Level aus der Datenbank. + user_wipe: + name: "&f&l Benutzerlöschung" + description: |- + &7 Benutzer vollständig aus der + &7 Datenbank löschen. + library: + name: "&f&l Bibliothek" + description: |- + &7 Öffnet eine öffentliche + Bibliothek für &7-Herausforderungen. + import_database: + name: "&f&l Datenbank importieren" + description: |- + &7 Ermöglicht den Import von Exportierten + &7 Herausforderungen. + import_template: + name: "&f&l Vorlage importieren" + description: |- + &7 Ermöglicht das Importieren einer Vorlagen Datei + &7 mit Herausforderungen. + export_challenges: + name: "&f&l Export der Herausforderungen" + description: |- + &7 Ermöglicht den Export der Datenbank + &7 in eine lokale Datei. + properties: + name: "&f&l Eigenschaften" + description: "&7 Alle Haupteigenschaften anzeigen." + requirements: + name: "&f&l Voraussetzungen" + description: "&7 Anforderungseigenschaften anzeigen." + rewards: + name: "&f&l Belohnungen" + description: "&7 Belohnung Eigenschaften anzeigen." + deployed: + name: "&f&l Bereitstellung" + description: "&7 Umschalten, wenn Herausforderung \n&7 bereitgestellt ist + und Benutzer sie abschließen\n&7 können." + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + name: + name: "&f&l Name" + description: |- + &7 Änderung erlaubt + &7 der Anzeigename. + value: "&7 Aktuell: &r [name]" + remove_on_complete: + name: "&f&l Nach Abschluss ausblenden" + description: |- + &7 Umschalten ob Herausforderungen + &7 verborgen werden wenn diese + &7 vom Spieler abgeschlossen wurden. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + description: + name: "&f&l Beschreibung" + description: |- + &7 Die spezifische Beschreibung + &7 für die Herausforderung. Die FarbCodes + &7 müssen darauf angewendet werden. + value: "&7 Aktuelle Beschreibung:" + environment: + name: "&f&l Dimension" + description: |- + &7 Erlaubt die Limitierung + &7 in welcher Welt Sie die Herausforderung + &7 abgeschlossen werden kann. + enabled: "&2" + disabled: "&c" + order: + name: "&f&l Reihenfolge" + description: |- + &7 Ermöglicht das Ändern der Reihenfolge von + &7 Items. + &7 Items mit gleichen Nummern + &7 werden sortiert nach + &7 eindeutigem ID-Namen. + value: "&7 Aktueller Auftrag: &e [number]" + icon: + name: "&f&l Icon" + description: |- + &7 Ermöglicht das Ändern des Symbols + &7 für diese Herausforderung. + locked_icon: + name: "&f&l Sperrsymbol" + description: |- + &7 Erlaubt Änderung gesperrt + &7 des Level-Symbol. + required_permissions: + name: "&f&l Erforderliche Berechtigungen" + description: "&7 Änderungserlaubnis erforderlich,\n&7 Berechtigungen welche + benötigt werden damit die \n&7 Herausforderung abgeschlossen werden kann." + title: "&7 Berechtigungen:" + permission: " &8 - [permission]" + none: "&7 Berechtigungen sind nicht gesetzt." + remove_entities: + name: "&f&l Mobs entfernen" + description: |- + &7 Erlaubt Umschalten ob + &7 erforderliche Mobs + &7 aus der Welt entfernt werden + &7 nach Abschluss der + &7 Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_entities: + name: "&f&l Erforderliche Mobs" + description: |- + &7 Ermöglicht die Änderung der erforderlichen + &7 Mobs, damit diese + &7 Herausforderung abgeschlossen werden kann. + title: "&7 Mobs: " + list: " &8 - [number] x [entity]" + none: "&7 Mobs werden nicht hinzugefügt." + remove_blocks: + name: "&f&l Blöcke entfernen" + description: "&7 Ermöglicht das Umschalten, ob\n&7 erforderliche Blöcke\n&7 + aus der Welt entfernt werden\n&7 nach Beendigung \n&7 der Herausforderung" + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_blocks: + name: "&f&l Erforderliche Blöcke" + description: |- + &7 Ermöglicht die Änderung der erforderlichen + &7 Blöcke um die + &7 Aufgabe abschließen zu können. + title: "&7 Blöcke:" + list: " &8 - [number] x [block]" + none: "&7 Blöcke werden nicht hinzugefügt." + search_radius: + name: "&f&l Suchradius" + description: |- + &7 Ermöglicht die Änderung des Radius + &7 um den Spieler, von dem aus + &7 Blöcke und/oder Objekte + &7 erkannt werden. + value: "&7 Aktueller Abstand: &e [number]" + remove_items: + name: "&f&l Items entfernen" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 benötigte Gegenstände + &7 aus dem Inventar entfernt werden + &7 nach Beendigung der + &7 Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_items: + name: "&f&l Erforderliche Gegenstände" + description: |- + &7 Ermöglicht die Änderung der erforderlichen + &7 Elemente für diese + &7 Herausforderung abgeschlossen werden kann. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items werden nicht hinzugefügt." + add_ignored_meta: + name: "&f&l Metadaten ignorieren hinzufügen" + description: |- + &7 Ermöglicht das Hinzufügen, welche + &7 Items ignoriert werden sollen, inkl. + &7 aller Metadaten, die + &7 ihnen zugewiesen sind. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items werden nicht hinzugefügt." + remove_ignored_meta: + name: "&f&l Entfernen Metadaten ignorieren" + description: |- + &7 Ermöglicht das Entfernen der + &7 Items, die ignoriert werden sollen und + &7 allen Metadaten, die + &7 ihnen zugewiesen sind. + remove_experience: + name: "&f&l Erfahrung entfernen" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 erforderliche Erfahrung + &7 vom Spieler entfernt wird + &7 nach Beendigung der + &7 Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_experience: + name: "&f&l Erforderliche Erfahrung" + description: |- + &7 Ermöglicht die Änderung der + &7 erforderliche Erfahrung für + &7 den Spieler. + value: "&7 Aktuelle Erfahrung: &e [number]" + required_level: + name: "&f&l Erforderliche Inselstufe" + description: |- + &7 Ermöglicht die Änderung der + &7 erforderliche Inselstufe + &7 für die Herausforderung. + value: "&7 Aktuelle Ebene: &e [number]" + remove_money: + name: "&f&l Geld entfernen" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 das benötigte Geld + &7 vom Spieler entfernt wird,das + &7 Konto entfernt wird, nachdem er die + &7 die Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + required_money: + name: "&f&l Benötigtes Geld" + description: |- + &7 Ermöglicht die Änderung der + &7 erforderliche Geld auf dem Spieler + &7 Konto für die Herausforderung. + value: "&7 Aktueller Wert: &e [number]" + statistic: + name: "&f&l Statistik" + description: |- + &7 Ermöglicht die Änderung des + &7 Statistiktyp, der + &7 in dieser Challenge geprüft wird. + value: "&7 Aktueller Wert: &e [statistic]" + statistic_amount: + name: "&f&l Zielwert" + description: |- + &7 Ermöglicht die Änderung des + &7 statistischen Zielwert + &7, der erfüllt werden muss. + value: "&7 Aktueller Wert: &e [number]" + remove_statistic: + name: "&f&l Statistik verkleinern" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 der statistische Wert + &7 nach Beendigung der Herausforderung + &7 die Herausforderung. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + statistic_blocks: + name: "&f&l Zielblock" + description: |- + &7 Ermöglicht die Änderung des + &7 statistischen Zielblocks. + value: "&7 Aktueller Block: &e [block]" + statistic_items: + name: "&f&l Ziel Item" + description: |- + &7 Ermöglicht die Änderung des + &7 statistische Zielposition. + value: "&7 Aktuelles Element: &e [item]" + statistic_entities: + name: "&f&l Ziel Mob" + description: |- + &7 Ermöglicht die Änderung des + &7 statistischen Zielobjekts. + value: "&7 Aktuelle Mob: &e [entity]" + reward_text: + name: "&f&l Belohnungstext" + description: |- + &7 Der spezifische Belohnungstext. + &7 Die Farbcodes müssen + &7 darauf angewendet werden. + value: "&7 Aktueller Text:" + repeat_reward_text: + name: "&f&l Belohnungstext wiederholen" + description: |- + &7 Der spezifische Wiederholungsbelohnungstext + &7 für die Herausforderung. Die Farb + &7 Codes müssen darauf angewendet werden. + value: "&7 Aktueller Text:" + reward_items: + name: "&f&l Belohnungsgegenstände" + description: |- + &7 Ermöglicht das Ändern von Belohnungen + &7 Gegenstände. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items werden nicht hinzugefügt." + repeat_reward_items: + name: "&f&l Belohnungs Item wiederholen" + description: |- + &7 Ermöglicht die Änderung der Wiederholung + &7 Belohnungsgegenstände für diese + &7-Herausforderung. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items werden nicht hinzugefügt." + reward_experience: + name: "&f&l Belohnungserfahrung" + description: |- + &7 Ermöglicht die Änderung des + &7 Belohnungserlebnis für + &7 den Spieler. + value: "&7 Belohnungserfahrung: &e [number]" + repeat_reward_experience: + name: "&f&l Belohnungserfahrung wiederholen" + description: |- + &7 Ermöglicht die Änderung des + &7 Wiederholung der Belohnungserfahrung + &7 für den Spieler. + value: "&7 Belohnungserlebnis: &e [number]" + reward_money: + name: "&f&l Belohnungsgeld" + description: |- + &7 Ermöglicht die Änderung des + &7 Belohnungsgeld. + value: "&7 Aktueller Wert: &e [number]" + repeat_reward_money: + name: "&f&l Belohnungsgeld wiederholen" + description: |- + &7 Ermöglicht die Änderung des + &7 Wiederholung des Belohnungsgeldes + &7 für die Herausforderung. + value: "&7 Aktueller Wert: &e [number]" + reward_commands: + name: "&f&l Belohnungsbefehle" + description: |- + &7 Die spezifischen Belohnungsbefehle. + &8 Tipp: + &8 Der Befehl muss + &8 zuerst mit `/` geschrieben werden, da er + &8 automatisch angewendet wird. + &8 Standardmäßig werden die Befehle + &8 vom Server ausgeführt. Jedoch + &8 das Hinzufügen von `[SELF]` am Anfang + &8 ermöglicht es, dass der Befehl + &8 vom Spieler ausgeführt werden. Außerdem + &8 unterstützt einen Platzhalter + &8 `[player]`, der dann + &8 durch einen Spielernamen ersetzt wird + &8 der die Herausforderung abgeschlossen hat. + value: "&7 Aktuelle Befehle:" + repeat_reward_commands: + name: "&f&l Belohnungsbefehle wiederholen" + description: |- + &7 Die spezifische Wiederholungsbelohnung + &7 Befehle für die Herausforderung. + &8 Tipp: + &8 Der Befehl muss + &8 zuerst mit `/` geschrieben werden, da er + &8 automatisch angewendet wird. + &8 Standardmäßig werden die Befehle + &8 vom Server ausgeführt. Jedoch + &8 das Hinzufügen von `[SELF]` am Anfang + &8 ermöglicht es, dass der Befehl + &8 vom Spieler ausgeführt werden. Außerdem + &8 unterstützt einen Platzhalter + &8 `[player]`, der dann + &8 durch einen Spielernamen ersetzt wird + &8 der die Herausforderung abgeschlossen hat. + value: "&7 Aktuelle Befehle:" + repeatable: + name: "&f&l Wiederholbar" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 die Herausforderung + &7 wiederholbar ist. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + repeat_count: + name: "&f&l Wiederholungszähler" + description: |- + &7 Ermöglicht die Änderung der + &7 Anzahl der Wiederholungen + &7 für die Herausforderung. + value: "&7 Aktueller Wert: &e [number]" + cool_down: + name: "&f&l Abkühlen" + description: |- + &7 Ermöglicht die Änderung der + &7 Abkühlsekunden, die + &7 gewartet werden muss zwischen + &7 wiederholbaren Herausforderungen + &7 und deren Beendigung gewartet werden muss. + value: "&7 Aktueller Wert: &e [time]" + challenges: + name: "&f&l Herausforderungen" + description: |- + &7 Zugewiesene Herausforderungen anzeigen + &7 zum Level. + waiver_amount: + name: "&f&l Verzichtsbetrag" + description: |- + &7 Ermöglicht die Festlegung einer Anzahl + &7 von Herausforderungen, die + &7 unvollendet gelassen werden können, um + &7 das nächste Level freizuschalten. + value: "&7 Aktueller Wert: &e [number]" + add_challenges: + name: "&f&l Herausforderung(en) hinzufügen" + description: |- + &7 Ermöglicht die Auswahl und + &7 fügt Herausforderungen zum + &7 leveln. + remove_challenges: + name: "&f&l Herausforderung(en) entfernen" + description: |- + &7 Ermöglicht die Auswahl und + &7 Entfernen von Herausforderungen auf dem + &7 Level. + reset_on_new: + name: "&f&l Zurücksetzen auf Neu" + description: |- + &7 Ermöglicht das Umschalten, ob + &7 Herausforderungen + &7 zurückgesetzt werden sollen, wenn der Benutzer die + &7 Insel verlässt oder eine neue + &7 Insel erstellt. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + broadcast: + name: "&f&l Broadcast" + description: |- + &7 Sendungen Herausforderung und + &7 Erstmaliger Abschluss eines Levels + &7 an alle. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + remove_completed: + name: "&f&l Ausblenden abgeschlossen" + description: |- + &7 Versteckt abgeschlossene Herausforderungen + &7 aus dem Menü. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + glow_completed: + name: "&f&l Glühen wenn abgeschlossen" + description: |- + &7 Fügt Verzauberungsglühen + &7 zu den abgeschlossenen Herausforderungen. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + store_history: + name: "&f&l Store-Verlauf" + description: |- + &7 Speichert den internen Verlauf + &7 wenn jede Herausforderung + &7 abgeschlossen ist. + &7 Derzeit nur einsehbar + &7 in der Datenbank. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + data_per_island: + name: "&f&l Speichern pro Insel" + description: |- + &7 Speichert die abgeschlossenen + &7 Herausforderungen pro Insel. + &7 Der Fortschritt wird mit allen + &7 mit allen Spielern im Team geteilt. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + show_title: + name: "&f&l Titel anzeigen" + description: |- + &7 Zeigt den Titel an, wenn eine + &7 Herausforderung oder Level + &7 abgeschlossen ist. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + gamemode_gui: + name: "&f&l GameMode-Auswahl-GUI" + description: |- + &7 Ermöglicht eine einzige GUI, die + &7 verfügbar über /challenges + &7 Befehl. + &c Erfordert einen Neustart des Servers. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + locked_level_icon: + name: "&f&l Standardsymbol für gesperrte Icon" + description: |- + &7 Standardsymbol für alle gesperrten + &7 Level. Jedes Level kann + &7 dieses Symbol ändern. + purge_history: + name: "&f&l Geschichte Lebenszeit" + description: |- + &7 Anzahl der Tage, wie lange + &7 Verlaufsdaten gespeichert werden + &7 in den Benutzerdaten. + &7 0 bedeutet, dass die Daten + &7 nicht entfernt werden. + value: "&7 Aktueller Wert: &e [number]" + title_showtime: + name: "&f&l Titel Showtime" + description: |- + &7 Anzahl der Ticks, die der Titel + &7 dem Spieler angezeigt wird. + value: "&7 Aktueller Wert: &e [number]" + active_world_list: + name: "&f&l Nur aktive Welt anzeigen" + description: |- + &7 Wenn GameMode Selection GUI + &7 aktiviert ist, kann dieser Schalter + &7 die GUI GameMode-Auswahl anzeigen + &7 oder Herausforderungen für die aktuelle Welt anzeigt. + &c Erfordert einen Server-Neustart. + enabled: "&2 Aktiviert" + disabled: "&c Deaktiviert" + visibility_mode: + name: "&f&l Sichtbarkeitsmodus" + description: |- + &7 Sichtbarkeitsmodus für + &7 Herausforderungen, die + &7 versteckt sind. + enabled: "&2" + disabled: "&c" + visible: Zeigen Sie sichtbare Herausforderungen + hidden: Alle Herausforderungen anzeigen + toggleable: Umschalten zulassen + download: + name: "&f&l Bibliotheken herunterladen" + description: |- + &7 Manuelle Aktualisierung verfügbar für + &7 die Herausforderungs Bibliotheken. + enabled: "&2 Mit Cache löschen" + disabled: "&c Ohne Cache löschen" + player: + name: "&f&l [name]" + description: "&7 Insel Owner: [owner]" + members: "&7 Inselmitglieder:" + member: "&8 - [name]" + no-island: |- + &c Player hat + &c keine Insel. + player_list: + name: "&f&l Spielerliste wählen" + description: |- + &7 Wählen Sie, welche Benutzerliste + &7 angezeigt werden soll. + enabled: "&2" + disabled: "&c" + online: Online-Spieler + with_island: Spieler mit Inseln + in_world: Spieler in der Welt + add_block: + name: "&f&l Block hinzufügen" + description: |- + &7 Ermöglicht das Hinzufügen eines neuen + &7 Blocks zur Liste. + remove_block: + name: "&f&l Block entfernen" + description: |- + &7 Ermöglicht das Entfernen + &7 ausgewählte Blöcke + &7 aus Listen zu entfernen. + title: "&7 Ausgewählte Materialien:" + material: "&8 - [material]" + material: + name: "&f&l [material]" + description: "&7 Material ID: [id]" + selected: "&2 Ausgewählt" + add_entity: + name: "&f&l Mob hinzufügen" + description: |- + &7 Ermöglicht das Hinzufügen einer neuen + &7 Mobs zur Liste. + switch_entity: + name: "&f&l Eier wechseln" + description: |- + &7 Ermöglicht Umschalten von + &7 Eier zu Mob-Köpfe. + remove_entity: + name: "&f&l Mob entfernen" + description: |- + &7 Ermöglicht das Entfernen von + &7 ausgewählte Objekte + &7 aus Listen zu entfernen. + title: "&7 Ausgewählte Mobs:" + entity: "&8 - [entity]" + entity: + name: "&f&l [entity]" + description: "&7 Mob ID: [id]" + selected: "&2 Ausgewählt" + inventory_type: + name: "&f&l Inventartyp" + description: |- + &7 Herausforderung, die nach + &7 Gegenständen im Inventar des Spielers prüft + island_type: + name: "&f&l Inseltyp" + description: |- + &7 Herausforderung, die nach + &7 Blöcke oder Mob um den + &7 Spieler prüft. + other_type: + name: "&f&l Anderer Typ" + description: |- + &7 Challenge, die verwendet + &7 Plugins oder Addons Dinge, + &7 wie Level und Geld. + statistic_type: + name: "&f&l Statistiktyp" + description: |- + &7 Challenge, die die + &7 Spieler-Statistiken prüfen. + save: + name: "&f&l Speichern" + description: |- + &7 Speichert Änderungen und + &7 kehrt zurück. + cancel: + name: "&f&l Abbrechen" + description: |- + &7 Verwirft Änderungen und + &7 kehrt zurück. + accept_selected: + name: "&f&l Ausgewählte akzeptieren" + description: |- + &7 Gibt ausgewählte Elemente zurück + &7 und öffnet die vorherige GUI. + title: "&7 Ausgewählt:" + element: "&8 - [element]" + statistic_element: + name: "&f&l [statistic]" + description: "[description]" + environment_element: + name: "&f&l [environment]" + description: "[description]" + search: + name: "&f&l Suchen" + description: |- + &7 Ermöglicht die Suche nach einem + &7 Element mit Eingabe des + &7 Textwert. + search: "&b Wert: [value]" + tips: + click-to-select: "&e Klick &7 zu markieren." + click-to-choose: "&e Klick &7 zum wählen." + click-to-complete: "&e Klick &7 zum Abschließen." + right-click-multiple-open: "&e Klicken Sie mit der rechten Maustaste &7, um + den Fertigstellungsgrad auszuwählen." + shift-left-click-to-complete-all: "&e Klicken Sie mit gedrückter Umschalttaste + &7, um alles abzuschließen." + left-click-to-accept: "&e Klicken Sie zum Abschließen mit der &7 linken Maustaste." + right-click-to-write: "&e Rechtsklick &7 zum Schreiben." + click-to-reduce: "&e Klicken Sie zum Reduzieren &7." + click-to-increase: "&e Zum Erhöhen &7 klicken." + click-to-return: "&e Klicken Sie &7, um zurückzukehren." + click-to-quit: "&e Zum Beenden &7 klicken." + click-to-wipe: "&e Zum Löschen &7 klicken." + left-click-to-wipe: "&e Klicken Sie zum Löschen mit der linken Maustaste &7." + right-click-to-switch: "&e Rechtsklick &7 zum Umschalten." + click-to-open: "&e Klicken zum Öffnen&7." + click-to-export: "&e Zum Exportieren &7 klicken." + click-to-create: "&e Zum Exportieren &7 klicken." + left-click-to-open: "&e Klicken Sie zum Öffnen mit der linken Maustaste &7." + right-click-to-reset-all: "&e Klicken Sie mit der rechten Maustaste &7, um alles + zu löschen." + click-to-toggle: "&e Zum Umschalten &7 klicken." + click-to-change: "&e Zum Ändern &7 klicken." + shift-click-to-reset: "&e Shift Klicken Sie zum Zurücksetzen &7." + click-to-add: "&e Zum Hinzufügen &7 klicken." + click-to-remove: "&e Zum Entfernen &7 klicken." + left-click-to-cycle: "&e Klicken Sie mit der linken Maustaste &7, um nach unten + zu blättern." + right-click-to-cycle: "&e Klicken Sie mit der rechten Maustaste &7, um nach + oben zu wechseln." + click-to-edit: "&e Zum Bearbeiten &7 klicken." + left-click-to-download: "&e Klicken Sie zum Herunterladen mit der linken Maustaste + &7." + right-click-to-toggle: "&e Klicken Sie mit der rechten Maustaste &7, um umzuschalten." + click-to-install: "&e Zum Installieren &7 klicken." + click-to-reset-all: "&e Klicken Sie &7, um alle zurückzusetzen." + right-click-to-select: "&e Klicken Sie zum Auswählen mit der rechten Maustaste + &7." + right-click-to-deselect: "&e Klicken Sie mit der rechten Maustaste &7, um die + Auswahl aufzuheben." + left-click-to-choose: "&e Klicken Sie mit der linken Maustaste &7, um auszuwählen." + click-to-cancel: "&e Zum Abbrechen &7 klicken." + click-to-save: "&e Zum Speichern &7 klicken." + click-to-deselect: "&e Klicken Sie &7, um die Auswahl aufzuheben." + click-on-item: |- + &e Klicken Sie &7 auf ein Item im + &7 Inventar. + left-click-to-edit: "&e Klicken Sie zum Bearbeiten mit der linken Maustaste + &7." + right-click-to-clear: "&e Klicken Sie zum Löschen mit der rechten Maustaste + &7." + click-to-previous: "&e Klicken Sie &7, um die vorherige Seite anzuzeigen." + click-to-next: "&e Klicken Sie &7, um die nächste Seite anzuzeigen." descriptions: - admin: - input: Öffne Textfeldeingabe. - deployment: Ermöglicht es den Benutzern, die Herausforderung abzuschließen - (anzusehen). - icon-challenge: Symbol, das in GUI-Panels für diese Herausforderung angezeigt - wird. - icon-level: Symbol, das in den GUI-Panels für dieses Level angezeigt wird. - remove-completed: Aktiviert/deaktiviert das Ausblenden von Herausforderungen, - die abgeschlossen sind und nicht wiederholt werden können. - toggle-user-list: Zu einer anderen Spielerliste wechseln. - selected: Ausgewählt - show-eggs: Wechselt die Ansicht der Objekte zwischen Eimodus oder Kopfmodus. - click-to-edit: "&4Hier klicken, um Eingaben zu bearbeiten." - input-mode: Wechsel zwischen Chat- und Amboss-Eingabemodus. - library-author: by &e[author] - library-lang: "&aSprache: [lang]" - library-gamemode: "&aPrimär für [gamemode]" - download-disabled: GitHub-Daten-Downloader ist in BentoBox deaktiviert. Ohne - ihn können keine Bibliotheken verwendet werden! - create-level: Neues Level hinzufügen. - edit-challenge: Herausforderung-Einstellungen bearbeiten. - edit-level: Level-Einstellungen bearbeiten. - delete-challenge: Eine Herausforderung entfernen. - delete-level: Ein Level entfernen. - settings: Einstellungen ändern. - properties: Allgemeine Eigenschaften ändern - requirements: Anforderungen verwalten - rewards: Belohnungen verwalten - description: Beschreibung bearbeiten. - order: Auftragsnummer ändern. - environment: Herausforderung Umfeld ändern. - name-challenge: Herausforderung Anzeigename ändern. - name-level: Level Anzeigename ändern. - remove-entities: Entferne (töte) Entitäten nach Abschluss der Herausforderung. - remove-blocks: Entferne ( ersetze durch Luft ) Blöcke nach Abschluss der Herausforderung. - search-radius: Radius um den Standort des Spielers, in dem die benötigten - Einheiten und Blöcke gesucht werden. - reward-text: Ändere die Nachricht, die dem Spieler nach Abschluss der Herausforderung - geschickt wird. - repeatable: Definiert, ob die Herausforderung wiederholbar ist oder nicht. - free-at-top: Freie Herausforderungen Standort wechseln. True bedeutet, diese - Herausforderungen werden die Ersten sein, sonst die Letzten. - line-length: Ändern der maximalen Zeilenlänge in der Lore Box. Hat keinen - Einfluss auf gespeicherte Objekte. - level-lore: Ändern welche Elemente der Levelbeschreibung sichtbar sein sollen. - challenge-lore: Ändern welche Elemente der Herausforderungsbeschreibung sichtbar - sein sollen. - gui-view-mode: Festlegen, ob /challenges GUI die Spielmodi oder Herausforderungen - in der Welt des Spielers anzeigen soll. - history-store: Aktivieren/Deaktivieren der Historienspeicherung von Herausforderungen. - default-import: Importieren von Standardherausforderungen. - default-export: Bestehende Herausforderungen in die Datei defaults.json exportieren. - complete-wipe: Vollständig alle Herausforderungen Addon-Datenbanken löschen. - Inklusive Spielerdaten! - challenge-wipe: Vollständig Herausforderungen und deren Leveldatenbanken löschen! - players-wipe: Vollständige Spielerdatenbank löschen! - library: Öffnet ein GUI, das alle verfügbaren öffentlichen Herausforderungen-Bibliotheken - anzeigt. - library-version: "&9Gemacht in Herausforderungen [version]" - save: Speichern und zur vorherigen GUI zurückkehren. - cancel: Rückkehr zur vorherigen GUI. Änderungen werden nicht gesichert. - set: Betrieb einstellen. Durch Anklicken der Zahlen wird der Wert auf die - ausgewählte Zahl geändert. - increase: Betrieb erhöhen. Durch Anklicken der Zahlen wird der Wert um die - gewählte Zahl erhöht. - reduce: Betrieb reduzieren. Durch Anklicken der Zahlen wird der Wert um die - gewählte Zahl reduziert. - multiply: Betrieb multiplizieren. Durch Anklicken der Zahlen wird der Wert - mit der ausgewählten Zahl multipliziert. - challenges: Verwalten von Level-Herausforderungen (hinzufügen/entfernen). - locked-icon: Symbol, das in GUI-Panels angezeigt wird, wenn das Level gesperrt - ist. - remove-on-complete: Entfernen einer Herausforderung aus der GUI eines Spielers, - nachdem sie abgeschlossen ist. - remove-items: Entfernen von Items aus dem Inventar des Spielers nach Abschluss - der Herausforderung. - required-experience: Die erforderliche Erfahrung für einen Benutzer festlegen, - um die Herausforderung abzuschließen. - remove-experience: Erforderliche Erfahrung entfernen. - reward-experience: Ändern der Erfahrungsbelohnung für den ersten Abschluss. - repeat-count: Maximale Anzahl der Wiederholungen festlegen. Wird der Wert - auf 0 gesetzt, gibt es keine Einschränkungen. - repeat-reward-text: Ändern der Nachricht, die dem Spieler nach dem wiederholten - Abschluss der Herausforderung geschickt wird. - repeat-reward-experience: Ändern der Erfahrungsbelohnung für den ersten Abschluss. - waiver-amount: Legt die Anzahl der Herausforderungen fest, die ein Spieler - auslassen kann, um das nächste Level freizuschalten. - reward-text-level: Ändere die Nachricht, die dem Spieler nach Abschluss aller - Herausforderungen in einem Level geschickt wird. - add-challenge: Fügt dem aktuellen Level eine bestehende Herausforderung hinzu. - remove-challenge: Eine Herausforderung aus dem aktuellen Level entfernen. - reset-on-new: Aktiviert/deaktiviert die Resets aller Herausforderungen für - einen Spieler, wenn er neu startet, die Insel verlässt oder von ihr gekickt - wird. - broadcast: Aktiviert/Deaktiviert die Übertragung über den Abschluss der ersten - Herausforderung für alle Spieler die online sind. - glow: Aktiviert/deaktiviert den Leuchteffekt für abgeschlossene Herausforderungen. - mode-online: Spieler, die gerade online sind. - mode-in-world: Spieler in einer Spielmodus-Welt. - mode-with-island: Spieler, die eine Insel in einer Spielmodus-Welt haben. - visibility-mode: Ein-/ausblenden von nicht umgesetzten Herausforderungen. - edit-text-line: "&6Textnachricht bearbeiten!" - add-text-line: "&6Neue Textnachricht hinzufügen!" - title-enable: Aktivieren/deaktivieren der Titelnachricht, die den Spielern - angezeigt wird, wenn sie eine Herausforderung abschließen. - title-showtime: Ändern wie lange Titelmeldungen für den Spieler sichtbar sein - sollen. - import: |- - ASkyblock-Herausforderungen importieren. - Bei Rechtsklick wird der Überschreibmodus aktiviert/deaktiviert. - Platziere die challenges.yml im Ordner ./BentoBox/addons/Challenges. - complete: |- - Herausforderung für jeden Benutzer abschließen. - Der Benutzer erhält keine Belohnung für den Abschluss. - reset: |- - Abgeschlossene Benutzerherausforderungen zurücksetzen. - Rechtsklick aktiviert/deaktiviert zurücksetzen aller Funktionen. - create-challenge: |- - Neue Herausforderung hinzufügen. - Wird standardmäßig in der Liste der freien Herausforderungen stehen. - required-entities: |- - Hinzufügen/Bearbeiten/Entfernen von erforderlichen Einheiten. - Einheiten: - required-blocks: |- - Erforderliche Blöcke hinzufügen/bearbeiten/entfernen. - Blöcke: - required-permissions: |- - Benötigte Berechtigungen für den Spieler, um diese Herausforderung abzuschließen. - Berechtigung: - required-items: |- - Benötigte Items im Inventar des Spielers. - Items: - required-level: |- - Legt das für diese Herausforderung erforderliche Insellevel fest. - &cBenötigt Level-Addon.' - required-money: |- - Legt das erforderliche Geld auf dem Spielerkonto fest. - &cErfordert Vault und ein Economy-Plugin.' - remove-money: |- - Entfernt das erforderliche Geld vom Spielerkonto. - &cErfordert Vault und ein Economy-Plugin.' - reward-items: |- - Ändere die Itembelohnungen für den erstmaligen Abschluss. - Items: - reward-money: |- - Ändere die Geldbelohnung für den ersten Abschluss. - &cErfordert Vault und Economy-Plugin. - reward-commands: |- - Legt Belohnungsbefehle fest, die nach der ersten Ausführung aufgerufen werden. - ***Das Hinzufügen von "[SELF]" am Anfang bedeutet, dass der Befehl vom Spieler ausgeführt wird, z.B. "/kill". - ***Zeichenfolge "[player]" wird durch den Spielernamen ersetzt, z.B. wird "/kill [player]" in "/kill BONNe1704" umgewandelt - Befehle: - repeat-reward-items: |- - Ändert die Itembelohungen für den wiederholten Abschluss. - Items: - repeat-reward-money: |- - Ändert die Geldbelohnung für den wiederholten Abschluss. - &cErfordert Vault und ein Economy-Plugin. - repeat-reward-commands: |- - Legt Belohnungsbefehle fest, die nach wiederholter Ausführung der Herausforderung ausgeführt werden. - ***Hinzufügen von "[SELF]" am Anfang bedeutet, dass der Befehl vom Spieler ausgeführt wird, z.B. "/kill". - ***Zeichenfolge "[player]" wird durch den Spielernamen ersetzt, z.B. wird "/kill [player]" in "/kill BONNe1704" umgewandelt - Befehle: - remove-selected: |- - Ausgewählte Elemente entfernen. - Elemente mit der rechten Maustaste auswählen. - history-lifespan: |- - Ändern für wie viele Tage die Daten der Historie gespeichert werden sollen. - 0 bedeutet für immer. - island-store: |- - Aktivieren/Deaktivieren der Herausforderungen Datenspeicherung pro Insel. Das bedeutet, dass die Herausforderungen für das gesamte Team gleich sind, wenn dies aktiviert ist. - &cWird NICHT bei Klick Daten konvertieren. DER FORTSCHRITT GEHT VERLOREN. - default-locked-icon: |- - Ändert das Standardsymbol des gesperrten Levels. - Diese Option kann von jedem Level überschrieben werden.' - gui-mode: |- - Aktivieren/Deaktivieren einzelner Herausforderungen GUI. - &2Erfordert einen Server-Neustart.' - download: |- - Manuelle Aktualisierung der verfügbaren Herausforderungen-Bibliotheken. - Rechtsklick zum Aktivieren der Cache-Leerung.' - lore: - level: |- - Level-Zeichenfolge. - Stellt die Übersetzung challenge.gui.challenge-description.level dar. - status: |- - Status-Zeichenfolge. - Stellt die Übersetzung challenge.gui.challenge-description.completed dar. - count: |- - Zeichenfolge für den Abschluss der Zählung. - Stellt die Übersetzung für challenges.gui.challenge-description.completed-times dar. - challenges.gui.challenge-description.completed-times-of - und challenges.gui.challenge-description.maxed-reached - description: |- - Beschreibung Zeichenfolge. - Festgelegt in Challenge-Objekt - challenge.description. - warnings: |- - Warnzeichenfolge. - Stellt die Übersetzung dar für: - challenges.gui.challenge-description.warning-items-take - challenges.gui.challenge-description.objects-close-by - challenges.gui.challenge-description.warning-entities-kill - challenges.gui.challenge-description.warning-blocks-remove - environment: |- - Umgebung Zeichenkette. - Festgelegt in Challenges Objekt - challenge.environment. - requirements: |- - Anforderungszeichenfolge. - Stellt die Übersetzung dar von: - challenges.gui.challenge-description.required-level - challenges.gui.challenge-description.required-money - challenges.gui.challenge-description.required-experience - challenge.requiredItems' - challenge.requiredBlocks' - or challenge.requiredEntities. - reward_text: |- - Belohnungs-Zeichenfolge. - Festgelegt in challenge.rewardText und challenge.repeatRewardText - reward_other: |- - Belohnung andere Zeichenfolge. - Stellt die Übersetzung dar für: - challenges.gui.challenge-description.experience-reward - challenges.gui.challenge-description.money-reward - challenges.gui.challenge-description.not-repeatable - reward_items: |- - Itembelohnungen. - Liste der Items, die zu Belohnungen werden, festgelegt in challenge.rewardItems und challenge.repeatRewardItems. - reward_commands: |- - Belohnungsbefehle. - Liste der Befehle, die Belohnen sollen, festgelegt in challenge.rewardCommands und challenge.repeatRewardCommands. - level_status: |- - Status-Zeichenfolge. - Stellt die Übersetzung von challenges.gui.level-description.completed dar. - challenge_count: |- - Abgeschlossener Herausforderung Zähl Zeichenfolge - Stellt die Übersetzung für challenges.gui.level-description.completed-challenges-of dar. - unlock_message: |- - Entsperren der Nachricht Zeichenfolge. - Festgelegt in Herausforderungen Levelobjekt - challengeLevel.unlockMessage - waiver_amount: |- - Die Anzahl der versetzbaren Herausforderungen, um die nächste Level-Zeichenkette freizuschalten. - Stellt die Übersetzung für challenges.gui.level-description.waver-amount dar - level_reward_text: |- - Belohnungs-Zeichenfolge. - Festgelegt in challengeLevel.rewardText - level_reward_other: |- - Belohnung anderer Zeichenfolge. - Stellt die Übersetzung dar für: - challenges.gui. level-description. experience-reward. - challenges.gui. level-description. money-reward - level_reward_items: |- - Itembelohnungen. - Liste der Items, die zu Belohnungen werden, festgelegt in challengeLevel.rewardItems - level_reward_commands: |- - Belohnungsbefehle. - Liste der Befehle, die zu Belohnungen führen, festgelegt in challengeLevel.rewardCommands - enabled: Aktiv - disabled: Deaktiviert - the-end: "- End" - nether: "- Nether" - normal: "- Oberwelt" - entity: "- [entity] : [count]" - block: "- [block] : [count]" - permission: "- [permission]" - item: "- [count] x [item]" - item-meta: "([meta])" - item-enchant: "- [enchant] [level]" - command: "- [command]" - level-unlocked: Klicke, um [level] Herausforderungen zu sehen! - level-locked: Schließe [count] weitere [level] Herausforderungen ab, um dieses - Level freizuschalten! - increase-by: "&aErhöhung der Fertigstellung um [value]" - reduce-by: "&cReduzierung der Fertigstellungen um [value]" - visibility: - hidden: Nur eingesetzte Herausforderungen sind sichtbar. - visible: Alle Herausforderungen sind für jeden sichtbar - toggleable: Umschalten, ob nicht verteilte Herausforderungen angezeigt werden - sollen - type: - island: "&aErfordert Blöcke oder Mobs um Spieler" - other: "&aErfordert Dinge von anderen Plugins/Addons" - inventory: "&aErforderliche Items im Inventar des Spielers" - current-value: "&6Aktueller Wert: [value]." - challenge-description: - completed-times-of: "[donetimes] erledigt aus [maxtimes]" - maxed-reached: "[donetimes] erledigt aus [maxtimes]" - completed-times: "[donetimes] erledigt" - objects-close-by: "&cAlle benötigten Blöcke und Objekte müssen sich in deiner - Nähe auf deiner Insel befinden!" - warning-entities-kill: "&cAlle erforderlichen Einheiten werden getötet, wenn - du diese Herausforderung abschließt!" - warning-blocks-remove: "&cAlle benötigten Blöcke werden entfernt, wenn du diese - Herausforderung abschließt!" - not-repeatable: "&cDiese Herausforderung ist nicht wiederholbar!" - experience-reward: "&6Exp Belohnung: [value]" - money-reward: "&6Geld Belohnung: $[value]" - required-experience: "&6Erforderliche Exp: [value]" - required-money: "&6Erforderliches Geld: $[value]" - required-island-level: "&6Erforderliches Insellevel: [value]" - environment: 'Erforderliche Umgebungen:' - reward-items: "&6Item Belohnungen:" - reward-commands: "& 6Belohnungsbefehle:" - required-items: 'Erforderliche Items:' - required-entities: 'Erforderliche Einheiten:' - required-blocks: 'Erforderliche Blöcke:' - level: "&fLevel: [level]" - completed: "&bAbgeschlossen" - warning-items-take: "&cAlle erforderlichen Items werden aus deinem Inventar - genommen, wenn du diese Herausforderung abschließt!" - rewards-title: "& a Belohnungen:" - level-description: - experience-reward: "&6Exp Belohnung: [value]" - money-reward: "&6Geldbelohnung: $[value]" - reward-items: "&6Item Belohnungen:" - reward-commands: "&6Belohnungsbefehle:" - waver-amount: "&6[value] Herausforderungen können übersprungen werden, um das - nächste Level freizuschalten." - completed: "&bAbgeschlossen" - completed-challenges-of: "&3Du hast [number] aus [max] Herausforderungen in - diesem Level abgeschlossen." - item-description: - item: "- [count] x [item]" - item-meta: "([meta])" - item-enchant: "- [enchant] [level]" - item-name: "[name]" - item-lore: 'Item Lore:' - book-meta: "[title] von [author]" - recipe-count: "[count] Rezepte" - armor-color: "[color]" - potion-type-extended-upgraded: Erweitert und verbessert [name] - potion-type-upgraded: Aktualisiert [name] - potion-type-extended: Erweitert [name] - potion-type: "[name]" - custom-effects: 'Benutzerdefinierte Effekte:' - potion-effect: "[effect] x [amplifier] für [duration]t" - skull-owner: "[owner]" - egg-meta: "[mob]" - fish-meta: "[body-color] mit [pattern-color] [pattern]" - questions: - prefix: "&2[SERVER]:" - admin: - unique-id: Schreiben Sie die eindeutige ID eines Objekts und drücken Sie die - Eingabetaste. - number: Schreibe eine Zahl in den Chat und drücke die Eingabetaste. - challenge-name: Schreibe den Anzeigenamen für die aktuelle Herausforderung - in den Chat. - level-name: Schreibe den Anzeigenamen für das aktuelle Level in den Chat. + challenge: + lore: |- + [description] + [status] + [cooldown] + [requirements] + [rewards] + status: + completed: "&2&l Abgeschlossen" + completed-times: "&2 Abgeschlossen &7&l [number] &r&2 time(-s)" + completed-times-of: "&2 &7&l [number] &r&2 von &7&l [max] &r&2 mal abgeschlossen" + completed-times-reached: "&2&l Alle Abschließen &7 [max] &2 times" + cooldown: + lore: |- + [timeout] + [wait-time] + timeout: "&7&l Cool down: &r&7 [time]" + wait-time: "&c&l Verfügbar nach: &r&c [time]" + in-days: "[number] d " + in-hours: "[number] H" + in-minutes: "[number] min " + in-seconds: "[number] s" + requirements: + lore: |- + [environment] + [type-requirement] + [permissions] + environment-single: "&7 Limitiert auf [environment]" + environment-title: "&7 Limitiert auf:" + environment-list: " &7 - &e [environment]" + permission-single: "&c Requires [permissions] permission" + permissions-title: "&c Erfordert Berechtigungen:" + permissions-list: " &c - [permission]" + island: + lore: |- + [blocks] + [entities] + [search-radius] + [warning-block] + [warning-entity] + blocks-title: "&7&l Erforderliche Blöcke:" + block-value: " &7 - &e [material]" + blocks-value: " &7 - &e [number] x [material]" + entities-title: "&7&l Erforderliche Entitäten:" + entity-value: " &7 - &e [entity]" + entities-value: " &7 - &e [number] x [entity]" + search-radius: "&7 Nicht weiter als &e [number] &7 Meter" + warning-block: "&e Blöcke werden &c entfernt" + warning-entity: "&e Mobs werden &c entfernt" + inventory: + lore: |- + [items] + [warning] + item-title: "&7&l Erforderliche Items:" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + warning: "&e Item(-s) wird &c entfernt werden" + other: + lore: |- + [experience] + [experience-warning] + [money] + [money-warning] + [level] + experience: "&7&l Erforderliche Erfahrung: &r&e [number]" + experience-warning: "&e Erfahrung wird &c entfernt" + money: "&7&l Benötigtes Geld: &r&e [number]" + money-warning: "&e Geld wird &c entfernt" + level: "&7&l Erforderliches Insellevel: &r&e [number]" + statistic: + lore: |- + [statistic] + [warning] + multiple-target: "&7&l [statistic]: &r&e [number] x [target]" + single-target: "&7&l [statistic]: &r&e [target]" + statistic: "&7&l [statistic] &r&e [number]" + warning: "&e Statistikdaten werden &c reduziert" + rewards: + lore: |- + &7&l Rewards: + [text] + [items] + [experience] + [money] + [commands] + item-title: "&7 Items:" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + experience: "&7 Erfahrung: &r&e [number]" + money: "&7 Geld: &r&e [number]" + commands-title: "&7 Befehle:" + command: " &7 - &e [command]" + level: + lore: |- + [text] + [status] + [waiver] + [rewards] + status: + completed: "&2&l Abgeschlossen" + completed-challenges-of: |- + &2 Abgeschlossen &7&l [number] &r&2 aus + &7&l [max] &r&2 Herausforderungen. + locked: "&c&l Gesperrt" + missing-challenges: "&7 [number] weitere Herausforderungen müssen \n&7 abgeschlossen + sein, um diese Stufe freizuschalten." + waiver: |- + &7&l [number] Herausforderung(en) &r&7 können + &7 übersprungen werden, um die nächste Stufe freizuschalten. + rewards: + lore: |- + &7&l Rewards: + [text] + [items] + [experience] + [money] + [commands] + item-title: "&7 Items:" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + experience: "&7 Erfahrung: &r&e [number]" + money: "&7 Geld: &r&e [number]" + commands-title: "&7 Befehle:" + command: " &7 - &e [command]" + library: + author: "&7 by &e [author]" + version: "&7 Made with Challenges &e [version]" + lang: "&7 Sprache: &e [lang]" + gamemode: "&7 Primär für &e [gamemode]" + conversations: + prefix: "&l&6 [BentoBox]: &r" + confirm-string: zustimmen, an, ja, bestätigen, ja, gültig, richtig + deny-string: falsch, aus, nein, verweigern, n, ungültig, falsch + cancel-string: Abbrechen + exit-string: abbrechen, beenden, beenden + cancelled: "&c Gespräch abgebrochen!" + input-number: "&e Bitte geben Sie im Chat eine Nummer ein." + input-seconds: "&e Bitte geben Sie im Chat eine Sekunde ein." + numeric-only: "&c Der angegebene [value] ist keine Zahl!" + not-valid-value: "&c Die angegebene Zahl [value] ist ungültig. Er muss größer + als [min] und kleiner als [max] sein!" + user-data-removed: "&a Alle Benutzerdaten für [gamemode] werden aus der Datenbank + gelöscht." + confirm-user-data-deletion: "&e Bitte bestätigen Sie, dass Sie die Benutzerdatenbank + für [gamemode] löschen möchten." + challenge-data-removed: "&a Alle Herausforderungsdaten für [gamemode] werden aus + der Datenbank gelöscht." + confirm-challenge-data-deletion: "&e Bitte bestätigen Sie, dass Sie die Herausforderungsdatenbank + für [gamemode] löschen möchten." + all-data-removed: "&a Alle Addon-Daten für [gamemode] werden aus der Datenbank + gelöscht." + confirm-all-data-deletion: "&e Bitte bestätigen Sie, dass Sie Addon-Daten für + [gamemode] löschen möchten." + write-name: "&e Bitte schreiben Sie einen Namen in den Chat." + new-object-created: "&ein neues Objekt für [gamemode] wird erstellt." + object-already-exists: "&c Objekt &7 [id] &c existiert bereits. Wählen Sie einen + anderen Namen." + invalid-challenge: "&c Challenge [challenge] enthält ungültige Daten. Es kann + nicht eingesetzt werden!" + name-changed: "&a Erfolg, der Name wurde aktualisiert." + write-description: "&e Bitte geben Sie eine neue Beschreibung im Chat ein und + beenden Sie den Chat in einer eigenen Zeile." + description-changed: "&a Erfolg, die Beschreibung wurde aktualisiert." + write-permissions: "&e Bitte geben Sie die erforderlichen Berechtigungen ein, + eine pro Zeile im Chat, und 'beenden' in einer eigenen Zeile, um den Chat zu + beenden." + permissions-changed: "&a Erfolg, Challenge-Berechtigungen wurden aktualisiert." + write-reward-text: "&e Bitte geben Sie einen neuen Belohnungstext im Chat ein + und beenden Sie den Chat in einer eigenen Zeile." + reward-text-changed: "&a Erfolg, der Belohnungstext wurde aktualisiert." + write-repeat-reward-text: "&e Bitte geben Sie einen neuen Wiederholungsbelohnungstext + im Chat ein und beenden Sie den Chat in einer eigenen Zeile." + repeat-reward-text-changed: "&a Erfolg, der Wiederholungsbelohnungstext wurde + aktualisiert." + write-reward-commands: "&e Bitte geben Sie pro Zeile im Chat einen neuen Belohnungsbefehl + ein und beenden Sie den Chat in einer eigenen Zeile." + reward-commands-changed: "&a Erfolg, die Belohnungsbefehle wurden aktualisiert." + write-repeat-reward-commands: "&e Bitte geben Sie pro Zeile im Chat einen neuen + Belohnungswiederholungsbefehl ein und beenden Sie den Chat in einer eigenen + Zeile." + repeat-reward-commands-changed: "&a Erfolg, die Wiederholungsbelohnungsbefehle + wurden aktualisiert." + challenge-removed: "&a Herausforderungen [challende] für [gamemode] wird aus der + Datenbank entfernt." + confirm-challenge-deletion: "&e Bitte bestätigen Sie, dass Sie [challenge] für + [gamemode] aus der Datenbank entfernen möchten." + level-removed: "&a Level [level] für [gamemode] wird aus der Datenbank entfernt." + confirm-level-deletion: "&e Bitte bestätigen Sie, dass Sie [level] für [gamemode] + aus der Datenbank entfernen möchten." + start-downloading: "&a Herunterladen und Importieren der Challenges-Bibliothek + beginnen." + written-text: "&a Eingabetext:" + confirm-data-replacement: "&e Bitte bestätigen Sie, dass Sie Ihre aktuellen Herausforderungen + durch neue ersetzen möchten." + new-challenges-imported: "&a Erfolg, neue Herausforderungen für [gamemode] wurden + importiert." + exported-file-name: "&e Bitte geben Sie einen Dateinamen für die exportierte Datenbankdatei + ein. (zum Beenden 'cancel' schreiben)" + database-export-completed: "&a Erfolg, der Datenbankexport für [world] ist abgeschlossen. + Datei wurde [file] generiert." + file-name-exist: "&c Datei mit dem Namen '[id]' existiert. Kann nicht überschrieben + werden." + write-search: "&e Bitte geben Sie einen Suchwert ein. (zum Beenden 'cancel' schreiben)" + search-updated: "&a Suchwert aktualisiert." titles: challenge-title: Erfolgreich abgeschlossen challenge-subtitle: "[friendlyName]" level-title: Erfolgreich abgeschlossen level-subtitle: "[friendlyName]" messages: - admin: - you-added: Du hast der Herausforderung eine [thing] hinzugefügt - challenge-created: "[challenge] &r erstellt!" - completed: Du hast Herausforderung [name] für [player] abgeschlossen! - already-completed: "&2Diese Herausforderung wurde bereits abgeschlossen!" - reset: "&2Du setzt Herausforderung [name] für [player] zurück!" - reset-all: "&2Alle [Player] Herausforderungen wurden zurückgesetzt!" - not-completed: "&2Diese Herausforderung ist noch nicht abgeschlossen!" - migrate-start: "&2Beginne Migration von Herausforderungen Addon-Daten." - migrate-not: "&2Alle Daten sind gültig." - start-downloading: "&5Starten des Downloads und Imports der Herausforderungen-Bibliothek." - migrate-end: "&2Herausforderungen Addon-Daten auf neues Format aktualisiert." - hit-things: Anklicken der Dinge, um sie zur Liste der benötigten Dinge hinzuzufügen. - Rechtsklick, wenn du fertig bist. - complete-wipe: "&cHoffentlich hast du Backups, denn du hast gerade alle Datenbanken - des Challenges Addons gelöscht!" - challenge-wipe: "&cHoffentlich hast du Backups, denn du hast gerade alle Herausforderungen - und ihre Level gelöscht!" - players-wipe: "&cHoffentlich hast du Backups, denn du löschst einfach alle abgeschlossenen - Herausforderungen des Spielers!" + completed: "&2 Sie haben die Herausforderung [name] für [player] abgeschlossen!" + already-completed: "&2 Diese Herausforderung wurde bereits abgeschlossen!" + reset: "&2 Sie haben die Herausforderung [name] für [player] zurückgesetzt!" + reset-all: "&2 Alle [player]-Herausforderungen wurden zurückgesetzt!" + not-completed: "&2 Diese Herausforderung ist noch nicht abgeschlossen!" + migrate-start: "&2 Starten Sie die Migration von Challenges-Addon-Daten." + migrate-end: "&2 Challenges-Addon-Daten auf neues Format aktualisiert." + migrate-not: "&2 Alle Daten sind gültig." + start-downloading: "&5 Herunterladen und Importieren der Challenges-Bibliothek + beginnen." you-completed-challenge: "&2Du hast die [value] &r&2Herausforderungen abgeschlossen!" you-repeated-challenge: "&2Du hast die [value] &r&2Herausforderung wiederholt!" you-repeated-challenge-multiple: "&2Du hast die [value] &r&2Herausforderungen @@ -554,81 +1048,64 @@ challenges: you-completed-level: "&2Du hast den [value] &r&2level abgeschlossen!" name-has-completed-challenge: "&5[name] hat die [value] &r&5-Herausforderung abgeschlossen!" name-has-completed-level: "&5[name] hat den [value] &r&5Level abgeschlossen!" - import-levels: Startet Importieren von Leveln - import-challenges: Startet Importieren von Herausforderungen - no-levels: 'Warnung: Keine Level in der challenges.yml definiert' - import-number: "[number] Herausforderungen importiert" load-skipping: '"[value]" existiert bereits - überspringen' load-overwriting: Überschreibt "[value]" load-add: 'Neues Objekt hinzufügen: [value]' - defaults-file-overwrite: defaults.json existiert. Sie wird überschrieben. - defaults-file-completed: defaults.json Datei ist mit Herausforderungen von [world] - belegt! errors: no-name: "&cFehlender Herausforderungsname" unknown-challenge: "&cUnbekannte Herausforderung" - unique-id: '&cEindeutige ID "[id]" ist nicht gültig.' - wrong-icon: '&cGegebenes Material "[value]" ist nicht gültig und kann nicht als - Symbol verwendet werden.' + not-valid-integer: |- + &cDie Angabe der ganzen Zahl "[value]" ist nicht gültig! + Der Wert sollte zwischen [min] und [max] liegen. not-deployed: "&cHerausforderung wird nicht eingesetzt!" not-on-island: "&cDu musst auf deiner Insel sein, um das zu tun!" + challenge-level-not-available: "&cDu hast das erforderliche Level nicht freigeschaltet, + um diese Herausforderung abzuschließen." not-repeatable: "&cDiese Herausforderung ist nicht wiederholbar!" + wrong-environment: "&cDu bist in der falschen Umgebung!" not-enough-items: "&cDu hast nicht genug [items], um diese Herausforderung abzuschließen!" not-close-enough: "&cDu musst innerhalb von [number] Blöcken aller benötigten Positionen stehen." you-still-need: "&cDu brauchst noch [amount] x [item]" - not-enough-money: "&cEs ist notwendig, [value] auf deinem Konto zu haben, um die - Herausforderung abzuschließen." - import-no-file: "&cEs konnte keine challenges.yml Datei zum Importieren gefunden - werden!" - no-load: "&cFehler: Die challenges.yml konnte nicht geladen werden.[message]" - load-error: "&cFehler: [value] kann nicht geladen werden." - defaults-file-exist: "&cdefaults.json existiert bereits. Benutze den Überschreibungsmodus, - um sie zu ersetzen!" - defaults-file-error: "&cBeim Erstellen der Datei defaults.json ist ein Fehler - aufgetreten! Konsole überprüfen!" - missing-arguments: "&cDem Befehl fehlen Argumente." - wrong-environment: "&cDu bist in der falschen Umgebung!" missing-addon: "&cKann die Herausforderung nicht vollenden: Benötigtes Addon oder Plugin fehlt." + incorrect: "&cKann die Herausforderung nicht abschließen: Anforderungen sind falsch." + not-enough-money: "&cEs ist notwendig, [value] auf deinem Konto zu haben, um die + Herausforderung abzuschließen." + not-enough-experience: "&cEs ist notwendig [value] EXP zu haben, um diese Herausforderung + abzuschließen." + island-level: "&cDeine Insel muss mindestens Level [number] oder höher sein, um + diese Herausforderung abzuschließen!" + no-load: "&cFehler: Die challenges.yml konnte nicht geladen werden.[message]" + load-error: "&cFehler: [value] kann nicht geladen werden." + no-rank: "&cDu hast keinen Rang, der hoch genug ist, um das zu tun." + cannot-remove-items: "&cEinige Items können nicht aus deinem Inventar entfernt + werden!" exist-challenges-or-levels: "&cDie Herausforderung existiert bereits in deiner Welt. Kann nicht fortfahren!" no-challenges: "&cDie Herausforderungen sind in dieser Welt noch nicht implementiert!" no-challenges-admin: "&cDie Herausforderungen sind in dieser Welt noch nicht implementiert! Verwende &5/[command] &cum sie hinzuzufügen!" - missing-level: "&cHerausforderung Level [level] ist in der Datenbank nicht festgelegt. - Dies kann Fehler verursachen!" + missing-arguments: "&cDem Befehl fehlen Argumente." no-multiple-permission: "&cDu hast keine Berechtigung, diese Herausforderung mehrmals hintereinander auszuführen." - not-a-integer: '&cDer angegebene Wert "[value]" ist keine ganze Zahl!' - challenge-level-not-available: "&cDu hast das erforderliche Level nicht freigeschaltet, - um diese Herausforderung abzuschließen." - incorrect: "&cKann die Herausforderung nicht abschließen: Anforderungen sind falsch." - not-enough-experience: "&cEs ist notwendig [value] EXP zu haben, um diese Herausforderung - abzuschließen." - island-level: "&cDeine Insel muss mindestens Level [number] oder höher sein, um - diese Herausforderung abzuschließen!" - no-rank: "&cDu hast keinen Rang, der hoch genug ist, um das zu tun." - cannot-remove-items: "&cEinige Items können nicht aus deinem Inventar entfernt - werden!" - not-valid-integer: |- - &cDie Angabe der ganzen Zahl "[value]" ist nicht gültig! - Der Wert sollte zwischen [min] und [max] liegen. invalid-level: "& c Level [Level] enthält ungültige Daten. Es wird nicht aus der Datenbank geladen!" invalid-challenge: "& c Challenge [Challenge] enthält ungültige Daten. Sie wird nicht aus der Datenbank geladen!" + no-library-entries: "&c Es können keine Bibliothekseinträge gefunden werden. Nichts + zu zeigen." + not-hooked: "&c Challenges Addon konnte keinen GameMode finden." + timeout: "&c Diese Abfrage erfordert eine Wartezeit von [timeout] zwischen den + Vervollständigungen. Sie müssen [wait-time] warten, bis Sie es erneut abschließen." protection: flags: CHALLENGES_ISLAND_PROTECTION: - description: "&5&Umschalten, wer &5&Herausforderungen erledigen kann" + description: "&5Umschalten, wer &5Herausforderungen erledigen kann" name: Herausforderungen Schutz CHALLENGES_WORLD_PROTECTION: - name: Herausforderungen Inselbegrenzung - hint: Keine Herausforderungen außerhalb der Insel description: "&5&oAktivieren/Deaktivieren von \n&5&oAnforderung für Spieler,\n&5&oauf ihrer Insel zu sein, um \n&5&oeine Herausforderung abzuschließen." -version: 11 -meta: - authors: - '0': xXjojoXx + name: Herausforderungen Inselbegrenzung + hint: Keine Herausforderungen außerhalb der Insel +version: 12 diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index d7a812a..bc03183 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -13,28 +13,12 @@ challenges: main: parameters: '' description: 'Main admin command. Opens GUI.' - import: - description: |- - Import challenges from challenges.yml - Parameter overwrite means that challenges or levels with the same ID will be overwritten. - parameters: '[overwrite]' reload: - description: |- - Reload challenges from the database - Parameter hard means that addon will reset the connection to the database. - parameters: '[hard]' + description: "Reload challenges from the database" + parameters: '' show: description: 'Prints all challenges in the chat which exist in this world.' parameters: '' - defaults: - description: 'Shows subcommands to import/export the default challenges.' - parameters: '[command]' - defaults-import: - description: 'Import the default challenges.' - parameters: '' - defaults-generate: - description: 'Export existing challenges to default.json file.' - parameters: '[overwrite] - allows to overwrite existing file.' complete: description: 'Complete a challenge for a player.' parameters: ' ' @@ -52,523 +36,1146 @@ challenges: description: 'Complete challenge.' parameters: ' [count]' gui: - title: - admin: - gui-title: '&a Challenges Admin' - edit-challenge-title: '&a Edit Challenge' - edit-level-title: '&a Edit Level' - settings-title: '&a Edit Settings' - choose-challenge-title: '&a Choose Challenge' - choose-level-title: '&a Choose Level' - choose-user-title: '&a Choose Player' - manage-blocks: '&a Manage Blocks' - manage-entities: '&a Manage Entities' - confirm-title: '&a Confirmation' - manage-items: '&a Manage Items' - manage-numbers: '&a Number Pad' - select-block: '&a Select Block' - select-challenge: '&a Select Challenge' - select-entity: '&a Select Entity' - toggle-environment: '&a Toggle Environment' - edit-text-fields: '&a Edit Text Fields' - - library-title: '&a Downloadable Libraries' - - lore-add: '&a Add Lore Element' - lore-remove: '&a Remove Lore Element' - lore-edit: '&a Edit Lore' - - type-select: "&a Choose Challenge Type" - challenges: '&6 Challenges' - game-modes: '&6 Choose GameMode' - - multiple-complete: '&6 How many times?' + titles: + # The title for the Main GUI + player-gui: "&0&l Challenges Menu" + # The title for the Main GUI + gamemode-gui: "&0&l Select GameMode" + # The title for the Multiple Completion GUI + multiple-gui: "&0&l How many times?" + # GUI titles below is visible just for Admins. + admin-gui: "&0&l Challenges Admin Menu" + edit-challenge: "&0&l Edit [challenge]" + edit-level: "&0&l Edit [level]" + settings: "&0&l Settings" + choose-challenge: "&0&l Choose Challenge" + choose-level: "&0&l Choose Level" + choose-player: "&0&l Choose Player" + library: "&0&l Library" + manage-blocks: "&0&l Manage Blocks" + manage-entities: "&0&l Manage Entities" + type-selector: "&0&l Challenge Type Selector" + item-selector: "&0&l Item Selector" + block-selector: "&0&l Block Selector" + entity-selector: "&0&l Entity Selector" + challenge-selector: "&0&l Challenge Selector" + statistic-selector: "&0&l Statistic Selector" + environment-selector: "&0&l Environment Selector" buttons: - admin: - complete: 'Complete user challenge' - reset: 'Reset user challenge' - create-challenge: 'Add new challenge' - create-level: 'Add new level' - edit-challenge: 'Edit challenge' - edit-level: 'Edit level' - delete-challenge: 'Remove challenge' - delete-level: 'Remove level' - import: 'Import ASkyblock challenges' - settings: 'Edit settings' - properties: 'Properties' - requirements: 'Requirements' - rewards: 'Rewards' - challenges: 'Challenges' - deployment: 'Deployment' - icon: 'Icon' - locked-icon: 'Locked Icon' - description: 'Description' - order: 'Order' - environment: 'Environment' - remove-on-complete: 'Remove after completion' - name: 'Friendly name' - required-entities: 'Required entities' - remove-entities: 'Kill entities' - required-blocks: 'Required blocks' - remove-blocks: 'Remove blocks' - search-radius: 'Search radius' - required-permissions: 'Required permissions' - required-items: 'Required items' - remove-items: 'Remove items' - required-experience: 'Required experience' - remove-experience: 'Remove experience' - required-level: 'Required island level' - required-money: 'Required money' - remove-money: 'Remove money' - reward-text: 'Reward message' - reward-items: 'Reward items' - reward-experience: 'Reward experience' - reward-money: 'Reward money' - reward-commands: 'Reward commands' - repeatable: 'Repeatable' - repeat-count: 'Max Times' - repeat-reward-text: 'Repeat reward message' - repeat-reward-items: 'Repeat reward items' - repeat-reward-experience: 'Repeat reward experience' - repeat-reward-money: 'Repeat reward money' - repeat-reward-commands: 'Repeat reward commands' - waiver-amount: 'Waiver amount' - add-challenge: 'Add challenge' - remove-challenge: 'Remove challenge' - reset-on-new: 'Reset on new island' - broadcast: 'Broadcast completion' - remove-completed: 'Remove after complete' - glow: 'Glow when completed' - free-at-top: 'Free challenges first' - line-length: 'Lore line length' - visibility-mode: 'Challenge visibility mode' - toggle-user-list: 'User list' - remove-selected: 'Remove selected' - add: 'Add' - show-eggs: 'Switch view mode' - accept: 'Accept' - decline: 'Decline' - save: 'Save' - cancel: 'Cancel' - input: 'Input' - value: 'Value' - set: '=' - increase: '+' - reduce: '-' - multiply: '*' - clear: 'Clear' - remove-empty: 'Remove empty' - number: '[number]' - level-lore: 'Level description' - challenge-lore: 'Challenge description' - gui-view-mode: 'Display all GameModes' - gui-mode: 'Single challenges GUI' - history-store: 'Challenges history' - history-lifespan: 'History LifeSpan' - island-store: 'Store per island' - default-locked-icon: 'Locked level icon' - input-mode: 'Switch input mode' - title-enable: 'Completion title' - title-showtime: 'Title show time' - default-import: 'Import default challenges' - default-export: 'Export existing challenges' - complete-wipe: 'Wipe addon databases' - challenge-wipe: 'Wipe challenges database' - players-wipe: 'Wipe user database' - - library: 'Web Library' - download: 'Download Libraries' - - type: - island: '&6 Island Type' - inventory: '&6 Inventory Type' - other: '&6 Other Type' - next: 'Next' - previous: 'Previous' - return: 'Return' - - value: "Complete" - increase: "Increase" - reduce: "Reduce" + # Button in the Challenges GUI that allows to select free challenges. + free-challenges: + name: "&f&l Free Challenges" + description: |- + &7 Displays a list of + &7 free challenges + # Button that is used to return to previous GUI or exit it completely. + return: + name: "&f&l Return" + description: |- + &7 Return to previous menu + &7 or exit GUI + # Button that is used in multi-page GUIs which allows to return to previous page. + previous: + name: "&f&l Previous Page" + description: |- + &7 Switch to &e [number] &7 page + # Button that is used in multi-page GUIs which allows to go to next page. + next: + name: "&f&l Next Page" + description: |- + &7 Switch to &e [number] &7 page + # Button that allows to reduce number + reduce: + name: "&f&l Reduce" + description: |- + &7 Reduce by &e [number] + # Button that allows to increase number + increase: + name: "&f&l Increase" + description: |- + &7 Increase by &e [number] + # Button that displays and allows completing challenge + accept: + name: "&f&l Complete" + description: |- + &7 Complete challenge &e [number] + &7 time(-s) + # Button that allows to quit the current gui. + quit: + name: "&f&l Quit" + description: |- + &7 Exit from the GUI. + # All following buttons are mainly for Admin GUI. + complete_user_challenges: + name: "&f&l Complete User Challenge(-s)" + description: |- + &7 Allows to choose user and + &7 complete challenge(-s) for + &7 him + reset_user_challenges: + name: "&f&l Reset User Challenges" + description: |- + &7 Allows to choose user and + &7 reset his challenges + add_challenge: + name: "&f&l Create Challenge" + description: |- + &7 Starts a process for + &7 creating a new challenge. + add_level: + name: "&f&l Create Level" + description: |- + &7 Starts a process for + &7 creating a new level. + edit_challenge: + name: "&f&l Edit Challenge" + description: |- + &7 Allows to choose and edit + &7 a challenge. + edit_level: + name: "&f&l Edit Level" + description: |- + &7 Allows to choose and edit + &7 a level. + delete_challenge: + name: "&f&l Delete Challenge" + description: |- + &7 Allows to choose and delete + &7 a challenge. + delete_level: + name: "&f&l Delete Level" + description: |- + &7 Allows to choose and delete + &7 a level. + edit_settings: + name: "&f&l Settings" + description: |- + &7 Allows to view and edit + &7 an addon settings. + complete_wipe: + name: "&f&l Complete Wipe" + description: |- + &7 Completely clears challenges + &7 addon database, including + &7 user data. + challenge_wipe: + name: "&f&l Challenge Wipe" + description: |- + &7 Completely clears challenges + &7 and levels from database. + user_wipe: + name: "&f&l User Wipe" + description: |- + &7 Completely clears user + &7 data from database. + library: + name: "&f&l Library" + description: |- + &7 Opens a public + &7 challenges library. + import_database: + name: "&f&l Import Database" + description: |- + &7 Allows to import exported + &7 challenges database. + import_template: + name: "&f&l Import Template" + description: |- + &7 Allows to import template + &7 file with challenges. + export_challenges: + name: "&f&l Export Challenges" + description: |- + &7 Allows to export database + &7 to a local file. + properties: + name: "&f&l Properties" + description: |- + &7 View all main properties. + requirements: + name: "&f&l Requirements" + description: |- + &7 View requirements properties. + rewards: + name: "&f&l Rewards" + description: |- + &7 View rewards properties. + deployed: + name: "&f&l Deployment" + description: |- + &7 Toggle if challenge is + &7 deployed and users can + &7 complete it. + enabled: "&2 Enabled" + disabled: "&c Disabled" + name: + name: "&f&l Name" + description: |- + &7 Allows to change + &7 the display name. + value: "&7 Currently: &r [name]" + remove_on_complete: + name: "&f&l Hide After Completion" + description: |- + &7 Toggle if challenge should + &7 hidden from player after + &7 it is completed. + enabled: "&2 Enabled" + disabled: "&c Disabled" + description: + name: "&f&l Description" + description: |- + &7 The specific description + &7 for the challenge. The color + &7 codes must be applied to it. + value: "&7 Current description:" + environment: + name: "&f&l Dimension" + description: |- + &7 Allows to limit in which + &7 dimension the challenge + &7 can be completed. + enabled: "&2" + disabled: "&c" + order: + name: "&f&l Order" + description: |- + &7 Allows to change order of + &7 objects. + &7 Objects with equal numbers + &7 will be ordered by their + &7 unique id names. + value: "&7 Current order: &e [number]" + icon: + name: "&f&l Icon" + description: |- + &7 Allows to change icon + &7 for this challenge. + locked_icon: + name: "&f&l Locked Icon" + description: |- + &7 Allows to change locked + &7 level icon. + required_permissions: + name: "&f&l Required Permissions" + description: |- + &7 Allows to change required + &7 permissions for this + &7 challenge to be completable. + title: "&7 Permissions: " + permission: " &8 - [permission]" + none: "&7 Permissions are not set." + remove_entities: + name: "&f&l Remove Entities" + description: |- + &7 Allows to toggle if + &7 required entities will + &7 be removed from world + &7 after completing the + &7 challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_entities: + name: "&f&l Required Entities" + description: |- + &7 Allows to change required + &7 entities for this + &7 challenge to be completable. + title: "&7 Entities: " + list: " &8 - [number] x [entity]" + none: "&7 Entities are not added." + remove_blocks: + name: "&f&l Remove Blocks" + description: |- + &7 Allows to toggle if + &7 required blocks will + &7 be removed from world + &7 after completing the + &7 challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_blocks: + name: "&f&l Required Blocks" + description: |- + &7 Allows to change required + &7 blocks for this + &7 challenge to be completable. + title: "&7 Blocks: " + list: " &8 - [number] x [block]" + none: "&7 Blocks are not added." + search_radius: + name: "&f&l Search Radius" + description: |- + &7 Allows to change the radius + &7 around player from which + &7 blocks and/or entities are + &7 detected. + value: "&7 Current distance: &e [number]" + remove_items: + name: "&f&l Remove Items" + description: |- + &7 Allows to toggle if + &7 required items will + &7 be removed from inventory + &7 after completing the + &7 challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_items: + name: "&f&l Required Items" + description: |- + &7 Allows to change required + &7 items for this + &7 challenge to be completable. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items are not added." + add_ignored_meta: + name: "&f&l Add Ignore Metadata" + description: |- + &7 Allows to add which + &7 items should ignore + &7 any meta-data that + &7 is assigned to them. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items are not added." + remove_ignored_meta: + name: "&f&l Remove Ignore Metadata" + description: |- + &7 Allows to remove which + &7 items should ignore + &7 any meta-data that + &7 is assigned to them. + remove_experience: + name: "&f&l Remove Experience" + description: |- + &7 Allows to toggle if + &7 required experience will + &7 be removed from player + &7 after completing the + &7 challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_experience: + name: "&f&l Required Experience" + description: |- + &7 Allows to change the + &7 required experience for + &7 the player. + value: "&7 Current experience: &e [number]" + required_level: + name: "&f&l Required Island Level" + description: |- + &7 Allows to change the + &7 required island level + &7 for the challenge. + value: "&7 Current level: &e [number]" + remove_money: + name: "&f&l Remove Money" + description: |- + &7 Allows to toggle if + &7 required money will + &7 be removed from player + &7 account after completing + &7 the challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + required_money: + name: "&f&l Required Money" + description: |- + &7 Allows to change the + &7 required money on player + &7 account for the challenge. + value: "&7 Current value: &e [number]" + statistic: + name: "&f&l Statistic" + description: |- + &7 Allows to change the + &7 statistic type that is + &7 checked in this challenge. + value: "&7 Current value: &e [statistic]" + statistic_amount: + name: "&f&l Target Value" + description: |- + &7 Allows to change the + &7 statistic target value + &7 that must be met. + value: "&7 Current value: &e [number]" + remove_statistic: + name: "&f&l Reduce Statistic" + description: |- + &7 Allows to toggle if + &7 statistic value will + &7 be reduced after completing + &7 the challenge. + enabled: "&2 Enabled" + disabled: "&c Disabled" + statistic_blocks: + name: "&f&l Target Block" + description: |- + &7 Allows to change the + &7 statistic target block. + value: "&7 Current block: &e [block]" + statistic_items: + name: "&f&l Target Item" + description: |- + &7 Allows to change the + &7 statistic target item. + value: "&7 Current item: &e [item]" + statistic_entities: + name: "&f&l Target Entity" + description: |- + &7 Allows to change the + &7 statistic target entity. + value: "&7 Current entity: &e [entity]" + reward_text: + name: "&f&l Reward Text" + description: |- + &7 The specific reward text. + &7 The color codes must be + &7 applied to it. + value: "&7 Current text:" + repeat_reward_text: + name: "&f&l Repeat Reward Text" + description: |- + &7 The specific repeat reward text + &7 for the challenge. The color + &7 codes must be applied to it. + value: "&7 Current text:" + reward_items: + name: "&f&l Reward Items" + description: |- + &7 Allows to change reward + &7 items. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items are not added." + repeat_reward_items: + name: "&f&l Repeat Reward Items" + description: |- + &7 Allows to change repeat + &7 reward items for this + &7 challenge. + title: "&7 Items: " + list: " &8 - [number] x [item]" + none: "&7 Items are not added." + reward_experience: + name: "&f&l Reward Experience" + description: |- + &7 Allows to change the + &7 reward experience for + &7 the player. + value: "&7 Reward experience: &e [number]" + repeat_reward_experience: + name: "&f&l Repeat Reward Experience" + description: |- + &7 Allows to change the + &7 repeat reward experience + &7 for the player. + value: "&7 Reward experience: &e [number]" + reward_money: + name: "&f&l Reward Money" + description: |- + &7 Allows to change the + &7 reward money. + value: "&7 Current value: &e [number]" + repeat_reward_money: + name: "&f&l Repeat Reward Money" + description: |- + &7 Allows to change the + &7 repeat reward money + &7 for the challenge. + value: "&7 Current value: &e [number]" + reward_commands: + name: "&f&l Reward Commands" + description: |- + &7 The specific reward commands. + &8 Tip: + &8 The command does not requires + &8 writing first `/` as it will + &8 be applied automatically. + &8 By default commands will be + &8 executed by server. However + &8 adding `[SELF]` at the start + &8 will allow command to be + &8 executed by player. It also + &8 supports one placeholder + &8 `[player]` that will be + &8 replaced with a player name + &8 who completed challenge. + value: "&7 Current commands:" + repeat_reward_commands: + name: "&f&l Repeat Reward Commands" + description: |- + &7 The specific repeat reward + &7 commands for the challenge. + &8 Tip: + &8 The command does not requires + &8 writing first `/` as it will + &8 be applied automatically. + &8 By default commands will be + &8 executed by server. However + &8 adding `[SELF]` at the start + &8 will allow command to be + &8 executed by player. It also + &8 supports one placeholder + &8 `[player]` that will be + &8 replaced with a player name + &8 who completed challenge. + value: "&7 Current commands:" + repeatable: + name: "&f&l Repeatable" + description: |- + &7 Allows to toggle if + &7 the challenge is + &7 repeatable. + enabled: "&2 Enabled" + disabled: "&c Disabled" + repeat_count: + name: "&f&l Repeat Count" + description: |- + &7 Allows to change the + &7 number of repeats + &7 for the challenge. + value: "&7 Current value: &e [number]" + cool_down: + name: "&f&l Cool Down" + description: |- + &7 Allows to change the + &7 cool down seconds that + &7 must be waited between + &7 repeatable challenge + &7 completions. + value: "&7 Current value: &e [time]" + challenges: + name: "&f&l Challenges" + description: |- + &7 View challenges assigned + &7 to the level. + waiver_amount: + name: "&f&l Waiver Amount" + description: |- + &7 Allows to set a number + &7 of challenges that can + &7 be left uncompleted for + &7 unlocking next level. + value: "&7 Current value: &e [number]" + add_challenges: + name: "&f&l Add Challenge(-s)" + description: |- + &7 Allows to select and + &7 adds challenges to the + &7 level. + remove_challenges: + name: "&f&l Remove Challenge(-s)" + description: |- + &7 Allows to select and + &7 remove challenges to the + &7 level. + reset_on_new: + name: "&f&l Reset On New" + description: |- + &7 Allows to toggle if + &7 challenges should be + &7 reset when user leaves + &7 island or creates a new + &7 island. + enabled: "&2 Enabled" + disabled: "&c Disabled" + broadcast: + name: "&f&l Broadcast" + description: |- + &7 Broadcasts challenge and + &7 level first time completion + &7 to everyone. + enabled: "&2 Enabled" + disabled: "&c Disabled" + remove_completed: + name: "&f&l Hide Completed" + description: |- + &7 Hides compleated challenges + &7 from the menu. + enabled: "&2 Enabled" + disabled: "&c Disabled" + glow_completed: + name: "&f&l Glow Completed" + description: |- + &7 Adds enchantment glow + &7 to the completed challenges. + enabled: "&2 Enabled" + disabled: "&c Disabled" + store_history: + name: "&f&l Store History" + description: |- + &7 Stores internal history + &7 when each challenge is + &7 completed. + &7 Currently viewable only + &7 in the database. + enabled: "&2 Enabled" + disabled: "&c Disabled" + data_per_island: + name: "&f&l Store Per Island" + description: |- + &7 Stores the completed + &7 challenges per island. + &7 Progress will be shared + &7 with all players in team. + enabled: "&2 Enabled" + disabled: "&c Disabled" + show_title: + name: "&f&l Show Title" + description: |- + &7 Shows title when a + &7 challenge or level + &7 is completed. + enabled: "&2 Enabled" + disabled: "&c Disabled" + gamemode_gui: + name: "&f&l GameMode Selection GUI" + description: |- + &7 Enables single GUI that + &7 is available via /challenges + &7 command. + &c Requires server restart. + enabled: "&2 Enabled" + disabled: "&c Disabled" + locked_level_icon: + name: "&f&l Default Locked Level Icon" + description: |- + &7 Default icon for all locked + &7 levels. Each level can change + &7 this icon. + purge_history: + name: "&f&l History Lifetime" + description: |- + &7 Number of days how long + &7 history data is stored + &7 in user data. + &7 0 means that data will + &7 not be removed. + value: "&7 Current value: &e [number]" + title_showtime: + name: "&f&l Title Showtime" + description: |- + &7 Number of ticks that title + &7 will be showed to the player. + value: "&7 Current value: &e [number]" + active_world_list: + name: "&f&l Show Only Active World" + description: |- + &7 If GameMode Selection GUI + &7 is enabled, this can switch + &7 if GUI shows GameMode selection + &7 or challenges for current world. + &c Requires server restart. + enabled: "&2 Enabled" + disabled: "&c Disabled" + visibility_mode: + name: "&f&l Visibility Mode" + description: |- + &7 Visibility Mode for + &7 challenges that are + &7 hiddend. + enabled: "&2" + disabled: "&c" + visible: "Show visible challenges" + hidden: "Show all challenges" + toggleable: "Allow toggling" + download: + name: "&f&l Download Libraries" + description: |- + &7 Manually update available + &7 challenges libraries. + enabled: "&2 With cache clear" + disabled: "&c Without cache clear" + player: + name: "&f&l [name]" + description: |- + &7 Island Owner: [owner] + members: "&7 Island Members:" + member: "&8 - [name]" + no-island: |- + &c Player does not have + &c an island. + player_list: + name: "&f&l Choose User List" + description: |- + &7 Choose which user list + &7 should be showed. + enabled: "&2" + disabled: "&c" + online: "Online Players" + with_island: "Players With Islands" + in_world: "Players In World" + add_block: + name: "&f&l Add Block" + description: |- + &7 Allows to add a new + &7 block to the list. + remove_block: + name: "&f&l Remove Block" + description: |- + &7 Allows to remove + &7 selected blocks + &7 from lists. + title: "&7 Selected Materials:" + material: "&8 - [material]" + material: + name: "&f&l [material]" + description: |- + &7 Material ID: [id] + selected: "&2 Selected" + add_entity: + name: "&f&l Add Entity" + description: |- + &7 Allows to add a new + &7 entity to the list. + switch_entity: + name: "&f&l Switch Eggs" + description: |- + &7 Allows to switch from + &7 eggs to the mob heads. + remove_entity: + name: "&f&l Remove Entity" + description: |- + &7 Allows to remove + &7 selected entities + &7 from lists. + title: "&7 Selected Entities:" + entity: "&8 - [entity]" + entity: + name: "&f&l [entity]" + description: |- + &7 Entity ID: [id] + selected: "&2 Selected" + inventory_type: + name: "&f&l Inventory Type" + description: |- + &7 Challenge that checks + &7 items in player inventory + island_type: + name: "&f&l Island Type" + description: |- + &7 Challenge that checks + &7 blocks or entities around + &7 player. + other_type: + name: "&f&l Other Type" + description: |- + &7 Challenge that uses + &7 plugins or addons things, + &7 like level and money. + statistic_type: + name: "&f&l Statistic Type" + description: |- + &7 Challenge that checks + &7 player statistic data. + save: + name: "&f&l Save" + description: |- + &7 Saves changes and + &7 returns. + cancel: + name: "&f&l Cancel" + description: |- + &7 Discards changes and + &7 returns. + accept_selected: + name: "&f&l Accept Selected" + description: |- + &7 Returns selected elements + &7 and opens previous GUI. + title: "&7 Selected: " + element: "&8 - [element]" + statistic_element: + name: "&f&l [statistic]" + description: "[description]" + environment_element: + name: "&f&l [environment]" + description: "[description]" + search: + name: "&f&l Search" + description: |- + &7 Allows to search an + &7 element with input + &7 text value. + search: "&b Value: [value]" + tips: + click-to-select: "&e Click &7 to select." + click-to-choose: "&e Click &7 to choose." + click-to-complete: "&e Click &7 to complete." + right-click-multiple-open: "&e Right Click &7 to chose completion count." + shift-left-click-to-complete-all: "&e Shift Click &7 to complete all." + left-click-to-accept: "&e Left Click &7 to complete." + right-click-to-write: "&e Right Click &7 to write." + click-to-reduce: "&e Click &7 to reduce." + click-to-increase: "&e Click &7 to increase." + click-to-return: "&e Click &7 to return." + click-to-quit: "&e Click &7 to quit." + click-to-wipe: "&e Click &7 to wipe." + left-click-to-wipe: "&e Left Click &7 to wipe." + right-click-to-switch: "&e Right Click &7 to switch." + click-to-open: "&e Click &7 to open." + click-to-export: "&e Click &7 to export." + click-to-create: "&e Click &7 to create." + left-click-to-open: "&e Left Click &7 to open." + right-click-to-reset-all: "&e Right Click &7 to wipe all." + click-to-toggle: "&e Click &7 to toggle." + click-to-change: "&e Click &7 to change." + shift-click-to-reset: "&e Shift Click &7 to reset." + click-to-add: "&e Click &7 to add." + click-to-remove: "&e Click &7 to remove." + left-click-to-cycle: "&e Left Click &7 to cycle down." + right-click-to-cycle: "&e Right Click &7 to cycle up." + click-to-edit: "&e Click &7 to edit." + left-click-to-download: "&e Left Click &7 to download." + right-click-to-toggle: "&e Right Click &7 to toggle." + click-to-install: "&e Click &7 to install." + click-to-reset-all: "&e Click &7 to reset all." + right-click-to-select: "&e Right Click &7 to select." + right-click-to-deselect: "&e Right Click &7 to deselect." + left-click-to-choose: "&e Left Click &7 to choose." + click-to-cancel: "&e Click &7 to cancel." + click-to-save: "&e Click &7 to save." + click-to-deselect: "&e Click &7 to deselect." + click-on-item: |- + &e Click &7 on item in + &7 your inventory. + left-click-to-edit: "&e Left Click &7 to edit." + right-click-to-clear: "&e Right Click &7 to clear." + click-to-previous: "&e Click &7 to view previous page." + click-to-next: "&e Click &7 to view next page." descriptions: - admin: - save: 'Save and return to the previous GUI.' - cancel: 'Return to the previous GUI. Changes will not be saved.' - input: 'Open text field input.' - set: 'Set operation. Clicking on the numbers will change the value to the selected number.' - increase: 'Increase operation. Clicking on the numbers will increase the value by the selected number.' - reduce: 'Reduce operation. Clicking on the numbers will reduce the value by the selected number.' - multiply: 'Multiply operation. Clicking on the numbers will multiply the value by the selected number.' - import: |- - Import ASkyblock challenges. - On right click it enables/disables overwrite mode. - Place challenges.yml inside the ./BentoBox/addons/Challenges folder. - complete: |- - Complete challenges for any user. - The user will not get any reward for completion. - reset: |- - Reset completed user challenges. - Right click enables/disables Reset all functionality. - create-challenge: |- - Add new challenge. - Will be in free challenges list by default. - create-level: 'Add new Level.' - edit-challenge: 'Edit Challenge settings.' - edit-level: 'Edit Level settings.' - delete-challenge: 'Remove a Challenge.' - delete-level: 'Remove a Level.' - settings: 'Change settings.' - properties: 'Change general properties' - requirements: 'Manage requirements' - rewards: 'Manage rewards' - challenges: 'Manage level challenges (add / remove).' - deployment: 'Allows users to complete (view) challenge.' - icon-challenge: 'Icon that will be displayed in GUI panels for this challenge.' - icon-level: 'Icon that will be displayed in GUI panels for this level.' - locked-icon: 'Icon that will be displayed in GUI panels if the level is locked.' - description: 'Edit description.' - order: 'Change order number.' - environment: 'Change challenge environment.' - remove-on-complete: 'Remove a challenge from a player"s GUI after it is completed.' - name-challenge: 'Change challenge display name.' - name-level: 'Change level display name.' - required-entities: |- - Add/edit/remove required entities. - Entities: - remove-entities: 'Remove (kill) entities on challenge completion.' - required-blocks: |- - Add/edit/remove required blocks. - Blocks: - remove-blocks: 'Remove (replace with air) blocks on challenge completion.' - search-radius: "Radius around player's location where required entities and blocks will be searched." - required-permissions: |- - Required permissions a for player to be able to complete this challenge. - Permission: - required-items: |- - Required items in player"s inventory. - Items: - remove-items: 'Remove items from player"s inventory after challenge completion.' - required-experience: 'Define required experience for a user to complete the challenge.' - remove-experience: 'Remove required experience.' - required-level: |- - Define the required island level for this challenge. - &c Requires Level addon.' - required-money: |- - Define the required money in player"s account. - &c Requires Vault and an Economy plugin.' - remove-money: |- - Remove required money from player"s account. - &c Requires Vault and an Economy plugin.' - reward-text: 'Change message that will be sent to player after challenges completion.' - reward-items: |- - Change first time completion reward items. - Items: - reward-experience: 'Change first time completion reward experience.' - reward-money: |- - Change first time completion reward money. - &c Requires Vault and Economy plugin. - reward-commands: |- - Define reward commands that will be called after first time completion. - ***Adding "[SELF]" at the start means that command will be run by player, e.g. "/kill" - ***String "[player]" will be replaced with player name, e.g. "/kill [player]" will be transformed to "/kill BONNe1704" - Commands: - repeatable: 'Define if challenge is repeatable or not.' - repeat-count: 'Define maximal repeat count. If the value is set 0, there are no limitations.' - repeat-reward-text: 'Change message that will be sent to the player after challenge repeated completion.' - repeat-reward-items: |- - Change repeated completion reward items. - Items: - repeat-reward-experience: 'Change repeated completion reward experience.' - repeat-reward-money: |- - Change repeated completion reward money. - &c Requires Vault and an Economy plugin. - repeat-reward-commands: |- - Define reward commands that will be executed after challenge repeated completion. - ***Adding "[SELF]" at the start means that command will be run by player, e.g. "/kill" - ***String "[player]" will be replaced with player name, e.g. "/kill [player]" will be transformed to "/kill BONNe1704" - Commands: - waiver-amount: 'Set the amount of challenges a player can leave out to unlock the next level.' - reward-text-level: 'Change the message that will be sent to the player after completing all challenges in a level.' - add-challenge: 'Add an existing challenge to the current level.' - remove-challenge: 'Remove a challenge from the current level.' - reset-on-new: 'Enables/Disables resets of all challenges for a player if they restart, leave or get kicked from an island.' - broadcast: 'Enables/Disables the broadcast about the first time challenge completion to all online players.' - remove-completed: 'Enables/Disables hiding challenges that are completed and cannot be repeated.' - glow: 'Enables/Disables the glowing effect for completed challenges.' - free-at-top: 'Change free challenges location. True means that challenges will be first, otherwise they will be last.' - line-length: 'Modify the maximum line length in lore box. Will not affect stored objects.' - toggle-user-list: 'Switch to different player list.' - mode-online: 'Players which are currently online.' - mode-in-world: 'Players in a GameMode world.' - mode-with-island: 'Players that have an island in a GameMode world.' - selected: 'Selected' - remove-selected: |- - Remove selected elements. - Select elements with the right mouse button. - show-eggs: 'Switch entity view between Egg mode or Head mode.' - level-lore: 'Modify which level description elements should be visible.' - challenge-lore: 'Modify which challenge description elements should be visible.' - gui-view-mode: "Set if /challenges GUI should show GameModes or challenges in player's world." - history-store: 'Enable/disable challenges history storage.' - history-lifespan: |- - Modify how many days history data should be stored. - 0 means forever. - island-store: |- - Enable/disable challenges data storing per island. This means that challenges will be the same for the whole team if this is enabled. - &c Will NOT convert data on click. PROGRESS WILL BE LOST.' - default-locked-icon: |- - Change default locked level icon. - This option can be overwritten by each level.' - gui-mode: |- - Enable/disable single challenges GUI. - &2 Requires a server restart.' - visibility-mode: 'Show/hide undeployed challenges.' - - click-to-edit: '&4 Click here to edit input.' - edit-text-line: '&6 Edit text message!' - add-text-line: '&6 Add new text message!' - input-mode: 'Switch between chat and anvil input modes.' - title-enable: 'Enable/disable the title message that will be shown to player"s when they complete a challenge.' - title-showtime: 'Modify how long title messages will be visible to the player.' - default-import: 'Import default challenges.' - default-export: 'Export existing challenges to defaults.json file.' - complete-wipe: 'Completely clear all challenges addon databases. Includes player data!' - - challenge-wipe: 'Completely clear challenges and their level databases!' - players-wipe: 'Completely clear player database!' - - library: 'Open GUI that shows all available public Challenges Libraries.' - - library-author: 'by &e [author]' - library-version: '&9 Made in Challenges [version]' - library-lang: '&a Language: [lang]' - library-gamemode: '&a Primary for [gamemode]' - - download: |- - Manually update available challenges libraries. - Right click to enable cache clearing.' - download-disabled: 'GitHub data downloader is disabled in BentoBox. Without it, you cannot use Libraries!' - - lore: - level: |- - Level string. - Represents translation challenges.gui.challenge-description.level - status: |- - Status string. - Represents translation challenges.gui.challenge-description.completed - count: |- - Completion count string. - Represents translation for challenges.gui.challenge-description.completed-times - challenges.gui.challenge-description.completed-times-of - and challenges.gui.challenge-description.maxed-reached - description: |- - Description string. - Defined in challenges object - challenge.description. - warnings: |- - Warning string. - Represents translation for: - challenges.gui.challenge-description.warning-items-take - challenges.gui.challenge-description.objects-close-by - challenges.gui.challenge-description.warning-entities-kill - challenges.gui.challenge-description.warning-blocks-remove - environment: |- - Environment string. - Defined in challenges object - challenge.environment. - requirements: |- - Requirement string. - Represents translation for: - challenges.gui.challenge-description.required-level - challenges.gui.challenge-description.required-money - challenges.gui.challenge-description.required-experience - challenge.requiredItems' - challenge.requiredBlocks' - or challenge.requiredEntities. - reward_text: |- - Reward string. - Defined in challenge.rewardText and challenge.repeatRewardText - reward_other: |- - Reward other string. - Represents translation for: - challenges.gui.challenge-description.experience-reward - challenges.gui.challenge-description.money-reward - challenges.gui.challenge-description.not-repeatable - reward_items: |- - Reward items. - List of items that will be rewarded defined in challenge.rewardItems and challenge.repeatRewardItems. - reward_commands: |- - Reward commands. - List of commands that will be rewarded defined in challenge.rewardCommands and challenge.repeatRewardCommands. - level_status: |- - Status string. - Represents translation challenges.gui.level-description.completed - challenge_count: |- - Completed challenge count string. - Represents translation for challenges.gui.level-description.completed-challenges-of - unlock_message: |- - Unlock message string. - Defined in challenges Level object - challengeLevel.unlockMessage - waiver_amount: |- - Shippable challenge count to unlock next level string. - Represents translation for challenges.gui.level-description.waver-amount - level_reward_text: |- - Reward string. - Defined in challengeLevel.rewardText - level_reward_other: |- - Reward other string. - Represents translation for: - challenges.gui.level-description.experience-reward - challenges.gui.level-description.money-reward - level_reward_items: |- - Reward items. - List of items that will be rewarded defined in challengeLevel.rewardItems - level_reward_commands: |- - Reward commands. - List of commands that will be rewarded defined in challengeLevel.rewardCommands - current-value: |- - &6 Current value: [value]. - enabled: 'Active' - disabled: 'Disabled' - type: - island: '&a require blocks or mobs around player' - inventory: '&a require items in the player"s inventory' - other: '&a require things from other plugins/addons' - the-end: '- The End' - nether: '- Nether' - normal: '- Overworld' - entity: '- [entity] : [count]' - block: '- [block] : [count]' - permission: '- [permission]' - item: '- [count] x [item]' - item-meta: ' ([meta])' - item-enchant: ' - [enchant] [level]' - command: '- [command]' - level-unlocked: 'Click to see [level] challenges!' - level-locked: 'Complete [count] more [level] challenges to unlock this level!' - - increase-by: "&a Increase completion count by [value]" - reduce-by: "&c Reduce completion count by [value]" - - visibility: - visible: "All challenges are visible to everyone" - hidden: "Only Deployed challenges are visible." - toggleable: "Toggle if undeployed challenges should be displayed" - - challenge-description: - level: '&f Level: [level]' - completed: '&b Completed' - completed-times-of: 'Completed [donetimes] out of [maxtimes]' - maxed-reached: 'Completed [donetimes] out of [maxtimes]' - completed-times: 'Completed [donetimes]' - warning-items-take: '&c All required items are taken from your inventory when you complete this challenge!' - objects-close-by: '&c All required blocks and entities must be close to you on your island!' - warning-entities-kill: '&c All required entities will be killed when you complete this challenge!' - warning-blocks-remove: '&c All required blocks will be removed when you complete this challenge!' - not-repeatable: '&c This challenge is not repeatable!' - experience-reward: '&6 Exp reward: [value]' - money-reward: '&6 Money reward: $[value]' - required-experience: '&6 Required exp: [value]' - required-money: '&6 Required money: $[value]' - required-island-level: '&6 Required island level: [value]' - environment: 'Required Environments:' - rewards-title: '&a Rewards:' - reward-items: '&6 Reward Items:' - reward-commands: '&6 Reward Commands:' - required-items: 'Required Items:' - required-entities: 'Required Entities:' - required-blocks: 'Required Blocks:' - level-description: - completed: '&b Completed' - completed-challenges-of: '&3 You have completed [number] out of [max] challenges in this level.' - waver-amount: '&6 [value] challenges can be skipped to unlock next level.' - experience-reward: '&6 Exp reward: [value]' - money-reward: '&6 Money reward: $[value]' - reward-items: '&6 Reward Items:' - reward-commands: '&6 Reward Commands:' - item-description: - item: '- [count] x [item]' - item-meta: ' ([meta])' - item-enchant: ' - [enchant] [level]' - item-name: ' [name]' - item-lore: ' Item Lore:' - book-meta: ' [title] by [author]' - recipe-count: ' [count] recipes' - armor-color: ' [color]' - potion-type-extended-upgraded: ' Extended and upgraded [name]' - potion-type-upgraded: ' Upgraded [name]' - potion-type-extended: ' Extended [name]' - potion-type: ' [name]' - custom-effects: ' Custom Effects:' - potion-effect: ' [effect] x [amplifier] for [duration]t' - skull-owner: ' [owner]' - egg-meta: ' [mob]' - fish-meta: ' [body-color] with [pattern-color] [pattern]' - - questions: - prefix: "&2 [SERVER]: " - - admin: - number: "Write a number in the chat and press enter." - unique-id: "Write the object's unique id and press enter." - challenge-name: "Write the display name in the chat for the current challenge." - level-name: "Write the display name in chat for the current level." - + # This part generates description text for challenges object in all GUI's. + challenge: + # The main part that generates description text. + # [description] comes from challenge.description + lore: |- + [description] + [status] + [cooldown] + [requirements] + [rewards] + # Contains a text generated inside [status] lore + status: + # Status message for completed unrepeatable challenge + completed: "&2&l Completed" + # Status message that contains number of completions for unlimited repeatable challenge + completed-times: "&2 Completed &7&l [number] &r&2 time(-s)" + # Status message that contains number of completions from max available for repeatable challenge + completed-times-of: "&2 Completed &7&l [number] &r&2 out of &7&l [max] &r&2 times" + # Status message that indicates that max completion count reached for repeatable challenge + completed-times-reached: "&2&l Completed all &7 [max] &2 times" + # Contains a text generated inside [cooldown] lore + cooldown: + lore: |- + [timeout] + [wait-time] + # Text message that shows challenges timeout. + timeout: "&7&l Cool down: &r&7 [time]" + # Text message that shows challenges wait time if it is larger than 0. + wait-time: "&c&l Available after: &r&c [time]" + # Text message that replaces days if number > 1 + in-days: "[number] d " + # Text message that replaces hours if number > 1 + in-hours: "[number] h " + # Text message that replaces minutes if number > 1 + in-minutes: "[number] min " + # Text message that replaces seconds if number > 1 + in-seconds: "[number] s" + # Contains a text generated inside [requirements] lore + requirements: + lore: |- + [environment] + [type-requirement] + [permissions] + # Message that will replace [environment] placeholder if there is just a single environment. + environment-single: "&7 Limited to [environment]" + # Message that will replace [environment] placeholder if there are multiple environments. + environment-title: "&7 Limited to: " + # Message that will be added after environment-title-multiple. + environment-list: " &7 - &e [environment]" + # Message that will replace [permissions] placeholder if there is just a single permission. + permission-single: "&c Requires [permissions] permission" + # Message that will replace [permissions] placeholder if there are multiple permissions. + permissions-title: "&c Requires permissions: " + # Message that will be added after permissions-title-multiple. + permissions-list: " &c - [permission]" + # Message that will generate for island type requirements and replace [type-requirements] + island: + lore: |- + [blocks] + [entities] + [search-radius] + [warning-block] + [warning-entity] + # Title that will be used if there are defined blocks in island challenge + blocks-title: "&7&l Required Blocks:" + # Listing of blocks that are required on the island. + block-value: " &7 - &e [material]" + blocks-value: " &7 - &e [number] x [material]" + # Title that will be used if there are defined entities in island challenge + entities-title: "&7&l Required Entities:" + # Listing of entities that are required on the island. + entity-value: " &7 - &e [entity]" + entities-value: " &7 - &e [number] x [entity]" + # Search radius for the blocks/entities + search-radius: "&7 Not further than &e [number] &7 meters" + # Waning about block/entity removing + warning-block: "&e Blocks will be &c removed" + warning-entity: "&e Entities will be &c removed" + # Message that will generate for inventory type requirements and replace [type-requirements] + inventory: + lore: |- + [items] + [warning] + # Title that will be used if there are list of items for challenge + item-title: "&7&l Required Items:" + # Listing of an item that are required multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Warning that items will be removed + warning: "&e Item(-s) will be &c removed" + # Message that will generate for other type requirements and replace [type-requirements] + other: + lore: |- + [experience] + [experience-warning] + [money] + [money-warning] + [level] + # Text for required experience + experience: "&7&l Required Experience: &r&e [number]" + # Warning that experience will be reduced + experience-warning: "&e Experience will be &c removed" + # Text for required money + money: "&7&l Required money: &r&e [number]" + # Warning that money will be reduced + money-warning: "&e Money will be &c removed" + # Text for required island level + level: "&7&l Required island level: &r&e [number]" + # Message that will generate for statistic type requirements and replace [type-requirements] + statistic: + lore: |- + [statistic] + [warning] + # Type of statistic for multiple target counter. Target may be entity or material/block + multiple-target: "&7&l [statistic]: &r&e [number] x [target]" + # Type of statistic for single target. Target may be entity or material/block + single-target: "&7&l [statistic]: &r&e [target]" + # Type of statistic without entity/block target + statistic: "&7&l [statistic] &r&e [number]" + # Warning that statistic will be removed + warning: "&e Statistic data will be &c reduced" + # Contains a text generated inside [rewards] lore + rewards: + # [text] comes from challenge.rewardText and challenge.repeatRewardText + lore: |- + &7&l Rewards: + [text] + [items] + [experience] + [money] + [commands] + # Title that will be used if there are list of items for rewards + item-title: "&7 Items:" + # Listing of an item that are rewards multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Text for reward experience + experience: "&7 Experience: &r&e [number]" + # Text for reward money + money: "&7 Money: &r&e [number]" + # Title for commands listing: + commands-title: "&7 Commands:" + # Command listing element + command: " &7 - &e [command]" + # This part generates description text for levels object in all GUI's. + level: + lore: |- + [text] + [status] + [waiver] + [rewards] + # Status is either challengeLevel.unlockMessage or current status of the level + status: + # Status message for completed unrepeatable challenge + completed: "&2&l Completed" + # Status message that contains number of completed challenges from all challenges + completed-challenges-of: |- + &2 Completed &7&l [number] &r&2 out of + &7&l [max] &r&2 challenges. + # Status message for locked level + locked: "&c&l Locked" + # Status message for locked level that will show missing challenge count. + missing-challenges: |- + &7 [number] more challenges must be + &7 completed to unlock this level. + # Contains a text for waiver amount to unlock next level + waiver: |- + &7&l [number] challenge(-s) &r&7 can be + &7 skipped to unlock next level. + # Contains a text generated inside [rewards] lore + rewards: + # [text] comes from challengeLevel.rewardText + lore: |- + &7&l Rewards: + [text] + [items] + [experience] + [money] + [commands] + # Title that will be used if there are list of items for rewards + item-title: "&7 Items:" + # Listing of an item that are rewards single time. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Text for reward experience + experience: "&7 Experience: &r&e [number]" + # Text for reward money + money: "&7 Money: &r&e [number]" + # Title for commands listing: + commands-title: "&7 Commands:" + # Command listing element + command: " &7 - &e [command]" + # This part generates description for the Library Entry + library: + author: '&7 by &e [author]' + version: '&7 Made with Challenges &e [version]' + lang: '&7 Language: &e [lang]' + gamemode: '&7 Primary for &e [gamemode]' + conversations: + # Prefix for messages that are send from server. + prefix: "&l&6 [BentoBox]: &r" + # List of strings that are valid for confirming input. (separated with ,) + confirm-string: "true, on, yes, confirm, y, valid, correct" + # List of strings that are valid for denying input. (separated with ,) + deny-string: "false, off, no, deny, n, invalid, incorrect" + # String that allows to cancel conversation. (can be only one) + cancel-string: "cancel" + # List of strings that allows to exit conversation. (separated with ,) + exit-string: "cancel, exit, quit" + # Message that is send to user when conversation is cancelled. + cancelled: "&c Conversation cancelled!" + # Message that appears when admin clicks on number editing button. + input-number: "&e Please enter a number in chat." + # Message that appears when admin clicks on seconds editing button. + input-seconds: "&e Please enter a seconds in chat." + # Error message that is showed if user input a value that is not a number. + numeric-only: "&c The given [value] is not a number!" + # Error message that is showed if user input a number that is smaller or larger that allowed. + not-valid-value: "&c The given number [value] is not valid. It must be larger than [min] and smaller than [max]!" + # Message that confirms user data removing. + user-data-removed: "&a All user data for [gamemode] is cleared from the database." + # Message that asks confirmation for user data removing. + confirm-user-data-deletion: "&e Please confirm that you want to clear user database for [gamemode]." + # Message that confirms user data removing. + challenge-data-removed: "&a All challenges data for [gamemode] is cleared from the database." + # Message that asks confirmation for user data removing. + confirm-challenge-data-deletion: "&e Please confirm that you want to clear challenges database for [gamemode]." + # Message that confirms user data removing. + all-data-removed: "&a All addon data for [gamemode] is cleared from the database." + # Message that asks confirmation for user data removing. + confirm-all-data-deletion: "&e Please confirm that you want to clear addon data for [gamemode]." + # Message that asks user to write a name + write-name: "&e Please write a name in the chat." + # Message that confirms new object creation. + new-object-created: "&a New object for [gamemode] is created." + # Error message that sends that object cannot be created with a given name + object-already-exists: "&c Object &7 [id] &c already exists. Choose different name." + # Error message that sends information that challenge cannot be deployed. + invalid-challenge: "&c Challenge [challenge] contains invalid data. It cannot be deployed!" + # Message that confirms name changing + name-changed: "&a Success, the name was updated." + # Message that appears after clicking + write-description: "&e Please enter a new description in chat and 'quit' on a line by itself to finish." + # Message that appears after successful description change. + description-changed: "&a Success, the description was updated." + # Message that appears when admin clicks on permission editing button. + write-permissions: "&e Please enter the required permissions, one per line in chat, and 'quit' on a line by itself to finish." + # Message that appears after successful permission updating. + permissions-changed: "&a Success, challenge permissions were updated." + # Message that appears after clicking + write-reward-text: "&e Please enter a new reward text in chat and 'quit' on a line by itself to finish." + # Message that appears after successful reward-text change. + reward-text-changed: "&a Success, the reward text was updated." + # Message that appears after clicking + write-repeat-reward-text: "&e Please enter a new repeat reward text in chat and 'quit' on a line by itself to finish." + # Message that appears after successful repeat-reward-text change. + repeat-reward-text-changed: "&a Success, the repeat reward text was updated." + # Message that appears after clicking + write-reward-commands: "&e Please enter a new reward command per line in chat and 'quit' on a line by itself to finish." + # Message that appears after successful commands-text change. + reward-commands-changed: "&a Success, the reward commands was updated." + # Message that appears after clicking + write-repeat-reward-commands: "&e Please enter a new repeat reward command per line in chat and 'quit' on a line by itself to finish." + # Message that appears after successful repeat-commands-text change. + repeat-reward-commands-changed: "&a Success, the repeat reward commands was updated." + # Message that confirms user data removing. + challenge-removed: "&a Challenges [challenge] for [gamemode] is removed from the database." + # Message that asks confirmation for user data removing. + confirm-challenge-deletion: "&e Please confirm that you want to remove [challenge] for [gamemode] from database." + # Message that confirms user data removing. + level-removed: "&a Level [level] for [gamemode] is removed from the database." + # Message that asks confirmation for user data removing. + confirm-level-deletion: "&e Please confirm that you want to remove [level] for [gamemode] from database." + # Message that appears when user clicks on library installation. + start-downloading: "&a Starting to download and import Challenges Library." + # Message that appears when writing multiline text. + written-text: "&a Input Text:" + # Message that appears after importing library data into database. + confirm-data-replacement: "&e Please confirm that you want to replace your current challenges with new one." + # Message that appears after successful data importing + new-challenges-imported: "&a Success, new Challenges for [gamemode] were imported." + # Message that appears after admin clicks on database exporting button. + exported-file-name: "&e Please enter a file name for the exported database file. (write 'cancel' to exit)" + # Message that appears after successful database exporting to file. + database-export-completed: "&a Success, the database export for [world] is completed. File [file] generated." + # Message that appears if input file name is already taken. + file-name-exist: "&c File with name '[id]' exists. Cannot overwrite." + # Message that asks for search value input. + write-search: "&e Please write a search value. (write 'cancel' to exit)" + # Message that appears after updating search value. + search-updated: "&a Search value updated." titles: -# Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. -# [friendlyName] will be replaced with challenge friendly name. -# [level] will be replaced with level friendly name. -# [rewardText] will be replaced with the challenge reward text. + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. + # [friendlyName] will be replaced with challenge friendly name. + # [level] will be replaced with level friendly name. + # [rewardText] will be replaced with the challenge reward text. challenge-title: 'Successfully completed' challenge-subtitle: '[friendlyName]' -# Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. -# [friendlyName] will be replaced with level friendly name. -# [rewardText] will be replaced with the level reward text. + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. + # [friendlyName] will be replaced with level friendly name. + # [rewardText] will be replaced with the level reward text. level-title: 'Successfully completed' level-subtitle: '[friendlyName]' messages: - admin: - hit-things: 'Click the things to add them to the list of required things. Right click when done.' - you-added: 'You added one [thing] to the challenge' - challenge-created: '[challenge]&r created!' - complete-wipe: '&c Hopefully you have backups, because you just erased all the Challenges Addon databases!' - - challenge-wipe: '&c Hopefully you have backups, because you just erased all the Challenges and their levels!' - players-wipe: '&c Hopefully you have backups, because you just erase all the player completed challenges!' - - completed: '&2 You completed challenge [name] for [player]!' - already-completed: '&2 This challenge was already completed!' - reset: '&2 You reset challenge [name] for [player]!' - reset-all: '&2 All [player] challenges were reset!' - not-completed: '&2 This challenge is not completed yet!' - - migrate-start: '&2 Start migrating challenges addon data.' - migrate-end: '&2 Challenges addon data updated to new format.' - migrate-not: '&2 All data is valid.' - - start-downloading: '&5 Starting to download and import Challenges Library.' + completed: '&2 You completed challenge [name] for [player]!' + already-completed: '&2 This challenge was already completed!' + reset: '&2 You reset challenge [name] for [player]!' + reset-all: '&2 All [player] challenges were reset!' + not-completed: '&2 This challenge is not completed yet!' + migrate-start: '&2 Start migrating challenges addon data.' + migrate-end: '&2 Challenges addon data updated to new format.' + migrate-not: '&2 All data is valid.' + start-downloading: '&5 Starting to download and import Challenges Library.' you-completed-challenge: '&2 You completed the [value] &r &2 challenge!' you-repeated-challenge: '&2 You repeated the [value] &r &2 challenge!' you-repeated-challenge-multiple: '&2 You repeated the [value] &r &2 challenge [count] times!' you-completed-level: '&2 You completed the [value] &r &2 level!' name-has-completed-challenge: '&5 [name] has completed the [value] &r &5 challenge!' name-has-completed-level: '&5 [name] has completed the [value] &r &5 level!' - import-levels: 'Start importing Levels' - import-challenges: 'Start importing Challenges' - no-levels: 'Warning: No levels defined in challenges.yml' - import-number: 'Imported [number] challenges' load-skipping: '"[value]" already exists - skipping' load-overwriting: 'Overwriting "[value]"' load-add: 'Adding new object: [value]' - defaults-file-overwrite: 'defaults.json exists. It will be overwritten.' - defaults-file-completed: 'defaults.json file is populated with challenges from [world]!' errors: no-name: '&c Missing challenge name' unknown-challenge: '&c Unknown challenge' - unique-id: '&c UniqueID "[id]" is not valid.' - wrong-icon: '&c Given material "[value]" is not valid and cannot be used as icon.' not-valid-integer: |- &c Given integer "[value]" is not valid! Value should be between [min] and [max]. - not-a-integer: '&c Given value "[value]" is not an integer!' not-deployed: '&c Challenge is not deployed!' not-on-island: '&c You must be on your island to do that!' challenge-level-not-available: '&c You have not unlocked the required level to complete this challenge.' @@ -582,21 +1189,85 @@ challenges: not-enough-money: '&c It is necessary to have [value] on your account to complete the challenge.' not-enough-experience: '&c It is necessary to have [value] EXP to complete this challenge.' island-level: '&c Your island must be level [number] or greater to complete this challenge!' - import-no-file: '&c Could not find challenges.yml file to import!' - no-load: '&c Error: Could not load challenges.yml. [message]' + no-load: '&c Error: Could not load file. [message]' load-error: '&c Error: Cannot load [value].' no-rank: "&c You do not have rank that is high enough to do that." cannot-remove-items: '&c Some items cannot be removed from your inventory!' exist-challenges-or-levels: '&c Challenges already exist in your world. Cannot proceed!' - defaults-file-exist: '&c defaults.json already exists. Use overwrite mode to replace it!' - defaults-file-error: '&c There was an error while creating defaults.json file! Check console!' no-challenges: '&c Challenges are not implemented in this world yet!' no-challenges-admin: '&c Challenges are not implemented in this world yet! Use &5 /[command] &c to add them!' - missing-level: '&c Challenge Level [level] is not defined in the database. It may cause errors!' missing-arguments: '&c Command is missing arguments.' no-multiple-permission: "&c You do not have permission to complete this challenge multiple times at once." invalid-level: "&c Level [level] contains invalid data. It will not be loaded from database!" invalid-challenge: "&c Challenge [challenge] contains invalid data. It will not be loaded from database!" + no-library-entries: "&c Cannot find any library entries. Nothing to show." + not-hooked: "&c Challenges Addon could not find any GameMode." + timeout: "&c This challenge requires to wait [timeout] between completions. You must wait [wait-time] till complete it again." +# # Showcase for manual material translation +# materials: +# # Names should be lowercase. +# cobblestone: "Cobblestone" +# # Also supports descriptions. +# stone: +# name: "Stone" +# description: "" +# item-stacks: +# # Non-specific item meta translations. +# # TYPE is the item type +# # META is a content of item meta. +# generic: "[type] [meta]" +# # Non-specific meta translations. Will replace [meta] +# meta: +# upgraded: "Upgraded" +# extended: "Extended" +# potion-meta: "&e [type] [upgraded] [extended]" +# # Be aware, enchants are always listed below item in separate line. +# enchant-meta: " &7 - &e [type] [level]" +# skull-meta: ": &e [player-name]" +# book-meta: "&e [title] [author]" +# # Custom Enchantment Translation. +# enchant: +# menting: "Mending" +# unbreaking: "Unbreaking" +# # Custom Potion Translation. +# potion-type: +# water_breathing: "Water Breathing" +# # You can also create specific item translations +# # Like translate all potions. +# potion: +# # This will overwrite generic translation. +# name: "[type] [upgraded] [extended]" +# # Type is either specific translation or potion effect. +# water_breathing: "Potion of Water Breathing" +# stone_shovel: +# # This will mean that only stone shovels will not show +# # meta information. +# name: "[type]" +# +# # Showcase how to support multi-linguistic challenges +# challenges: +# # Database ID name. +# example_challenge_id: +# name: "&2 Translated Name" +# description: |- +# &7 Translated Custom +# &7 description +# reward-text: |- +# &7 Translated Reward +# &7 text +# repeat-reward-text: |- +# &7 Translated Repeat +# &7 Reward text +# levels: +# # Database ID name. +# example_level_id: +# name: "&2 Translated Name" +# description: |- +# &7 Translated Custom +# &7 description +# reward-text: |- +# &7 Translated Reward +# &7 text protection: flags: CHALLENGES_ISLAND_PROTECTION: @@ -606,4 +1277,4 @@ protection: description: "&5 &o Enable/disable\n&5 &o requirement for players to\n&5 &o be on their island to\n&5 &o complete a challenge." name: "Challenges Island limitation" hint: "No challenges outside island" -version: 11 +version: 12 diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml index c8dc9fe..43c8d69 100644 --- a/src/main/resources/locales/lv.yml +++ b/src/main/resources/locales/lv.yml @@ -1,537 +1,1285 @@ ---- +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + +meta: + authors: + - BONNe + challenges: commands: admin: - complete: - description: Šī komanda ļauj pabeigt uzdevumu spēlētājam. - parameters: " " - defaults: - description: Šī komanda izvada apakškomandas, kas pārvalda sākotnējos uzdevumus/līmeņus. - parameters: "[komanda]" - defaults-generate: - description: Šī metode izveidos failu defaults.json, kas saturēs šīs pasaules - uzdevumus un līmeņus.|Parametrs overwrite ļauj pārrakstīt pāri esošajam - failam. - parameters: "[overwrite]" - defaults-import: - description: Šī metode ļauj uzstādīt sākotnējos uzdevumus un to līmeņus. - import: - description: Šī metode importē Uzdevumus no challenges.yml faila.|Parametrs - [overwrite] nozīmē, ka esošie uzdevumi vai līmeņi ar tādu pašu ID tiks pārrakstīti. - parameters: "[overwrite]" main: - description: Administratora komanda, kura atver izvēlnes logu. - migrate: - description: Šī metode ļauj migrēt saglabātos datus uz jauno formātu no versijas - 0.7.5 uz 0.8.0. + parameters: '' + description: 'Galvenā administratora komanda. Atver administratora izvēlni.' reload: - description: Šī metode pārlādē Challenges papildinājumu.|Parametrs [hard] - nozīmē, ka tiks pārlādēts arī savienojums ar datubāzi. - parameters: "[hard]" - reset: - description: Šī komanda ļauj atiestatīt uzdevumu spēlētājam. Ja "challenge_id" - aizstāj ar "all", tad tiek atiestatīti visi uzdevumi. - parameters: " " + description: "Atkārtoti ielādē uzdevumus no datubāzes." + parameters: '' show: - description: Šī komanda izvada visus uzdevumu nosaukumus sarakstē. - user: + description: 'Izdrukā visus uzdevumus sarakstē.' + parameters: '' complete: - description: Šī metode ļauj izpildīt uzdevumus neatverot Uzdevumu logu.|Beigās - pierakstot skaitli, kas lielāks par 0 ļauj izpildīt uzdevumus vairākas reizes. - parameters: " [reižu skaits]" + description: 'Pabeidz uzdevumu priekš spēlētāja.' + parameters: ' ' + reset: + description: 'Atjauno uzdevumu priekš spēlētāja. Ja "uzdevums" parametrs ir "all", tad tiks atjaunoti visi uzdevumi.' + parameters: ' ' + migrate: + description: 'Migrē datus uz jaunāko formātu.' + parameters: '' + user: main: - description: Šī metode atver Uzdevumu logu. - errors: - cannot-remove-items: "&cDažas lietas nevarēja izņemt no inventāra!" - challenge-level-not-available: "&cŠī uzdevuma līmenis nav atklāts. Tu nevari to - pildīt." - defaults-file-error: "&cRadās kļūda veidojot defaults.json failu! Pārbaudi konsoli!" - defaults-file-exist: "&cdefaults.json jau eksistē. Lieto overwrite, lai to pārrakstītu!" - exist-challenges-or-levels: "&cŠajā pasaulē jau eksistē Uzdevumi. Nevar turpināt!" - import-no-file: "&cNevarēja atrast challenges.yml failu, no kura importēt!" - incorrect: "&cNevar izpildīt uzdevumu. Prasības nav korektas." - island-level: "&cSalas līmenim ir nepieciešams būt lielākam par [number]!" - load-error: "&cKļūda: Nevarēja ielādēt [value]." - missing-addon: "&cNevar izpildīt uzdevumu. Serverī nav uzstādīti nepieciešamie - papildinājumi." - missing-arguments: "&cKomandai trūkst parametri." - missing-level: "&cLīmenis [level] nav definēts datubāzē. Tas var radīt problēmas!" - no-challenges: "&cŠajā pasaulē nav izveidoti uzdevumi!" - no-challenges-admin: "&cŠajā pasaulē nav izveidoti uzdevumi! Izmanot komandu &5/[command]&c, - lai tos pievienotu!" - no-load: "&cKļūda: Nevarēja ielādēt challenges.yml. [message]" - no-name: "&cTrūkst Uzdevuma nosaukums" - no-rank: "&cTev nav nepieciešamais rangs, lai šo darītu." - not-a-integer: '&c"[value]" nav skaitlis!' - not-close-enough: "&c[number] bloku rādiusā ap tevi nav nepieciešamais bloku daudzums." - not-deployed: "&cUzdevums nav ieslēgts! Tā izpilde ir bloķēta." - not-enough-experience: "&cNepietiek pieredzes. Tev jābūt [value] pieredzei, lai - izpildītu Uzdevumu." - not-enough-items: "&cTev nav pietiekoši daudz [items], lai pabeigtu šo uzdevumu!" - not-enough-money: "&cNepietiek naudas. Tavā kontā vajag būt [value] vienībām, - lai izpildītu Uzdevumu." - not-on-island: "&cTev nepieciešams būt uz savas salas, lai izpildītu uzdevumu!" - not-repeatable: "&cŠis uzdevums nav atkārtojams!" - not-valid-integer: '&cDotais cipars "[value]" nav derīgs!|Skaitlim jābūt no [min] - līdz [max].' - unique-id: '&cUnikālais ID "[id]" nav derīgs.' - unknown-challenge: "&cNezināms uzdevums" - wrong-environment: "&cTu neesi pareizajā pasaulē!" - wrong-icon: '&cDotais materiāls "[value]" neeksistē vai arī nevar tikt izmantots - kā ikona.' - you-still-need: "&cTev vēl nepieciešams [amount] x [item]" - no-multiple-permission: "&cTev nav atļaujas pildīt uzdevumu vairākas reizes vienlaicīgi." + description: 'Atver Uzdevumu Izvēlni' + parameters: '' + complete: + description: 'Pabeidz uzdevumu.' + parameters: ' ' gui: + titles: + # The title for the Main GUI + player-gui: "&0&l Uzdevumu Izvēlne" + # The title for the Main GUI + gamemode-gui: "&0&l Izvēlies Spēles Režīmu" + # The title for the Multiple Completion GUI + multiple-gui: "&0&l Cik daudz reižu?" + # GUI titles below is visible just for Admins. + admin-gui: "&0&l Administratora Izvēlne" + edit-challenge: "&0&l Mainīt [challenge]" + edit-level: "&0&l Mainīt [level]" + settings: "&0&l Iestatījumi" + choose-challenge: "&0&l Izvēlies Uzdevumu" + choose-level: "&0&l Izvēlies Līmeni" + choose-player: "&0&l Izvēlies Spēlētāju" + library: "&0&l Bibliotēka" + manage-blocks: "&0&l Pārvaldīt Blokus" + manage-entities: "&0&l Pārvaldīt Radības" + type-selector: "&0&l Uzdevumu Tipu Izvēlne" + item-selector: "&0&l Lietu Izvēlne" + block-selector: "&0&l Bloku Izvēlne" + entity-selector: "&0&l Radību Izvēlne" + challenge-selector: "&0&l Uzdevumu Izvēlne" + statistic-selector: "&0&l Statistikas Izvēlne" + environment-selector: "&0&l Vides Izvēlne" buttons: - admin: - accept: Apstiprināt - add: Pievienot - add-challenge: Pievienot Uzdevumu - broadcast: Paziņot par izpildi - cancel: Pārtraukt - challenge-lore: Uzdevuma Apraksts - challenges: Uzdevumi - clear: Notīrīt - complete: Pabeigt uzdevumu spēlētājam - complete-wipe: Iztīrīt datubāzes - create-challenge: Izveidot jaunu uzdevumu - create-level: Izveidot jaunu līmeni - decline: Noraidīt - default-export: Exportēt esošos Uzdevumus - default-import: Importēt standarta Uzdevumus - default-locked-icon: Slēgta līmeņa ikona - delete-challenge: Izmest uzdevumu - delete-level: Izmest līmeni - deployment: Pieejams - description: Apraksts - edit-challenge: Labot uzdevumu - edit-level: Labot līmeni - environment: Vide - free-at-top: Brīvie uzdevumi augšā - glow: Mirdzēt pēc izpildes - gui-mode: Rādīt esošās pasaules uzdevumus - gui-view-mode: Rādīt visus spēles režīmus - history-lifespan: Vēstures Dzīvesilgums - history-store: Uzdevumu Vēsture - icon: Ikona - import: Importēt ASkyblock Uzdevumus - increase: "+" - input: Ievads - input-mode: Pārslēgt ievades metodi - island-store: Kopīgi salas uzdevumi - level-lore: Līmeņa Apraksts - line-length: Apraksta līnijas garums - locked-icon: Aizvērtā ikona - multiply: "*" - name: Nosaukums - number: "[number]" - order: Secība - properties: Rekvizīti - reduce: "-" - remove-blocks: Iznīcināt blokus - remove-challenge: Noņemt Uzdevumu - remove-completed: Noņemt pēc izpildes - remove-empty: Noņemt tukšos - remove-entities: Nogalināt radības - remove-experience: Noņemt pieredzi - remove-items: Noņemt priekšmetus - remove-money: Noņemt naudu - remove-on-complete: Noņemt pēc izpildes - remove-selected: Noņemt iezīmētos - repeatable: Atkārtojamība - repeat-count: Maksimālais skaits - repeat-reward-commands: Atkārtotās atlīdzības komandas - repeat-reward-experience: Atkārtotās atlīdzības pieredze - repeat-reward-items: Atkārtotās atlīdzības priekšmeti - repeat-reward-money: Atkārtotās atlīdzības nauda - repeat-reward-text: Atkārtotās atlīdzības ziņa - required-blocks: Nepieciešamie bloki - required-entities: Nepieciešamās radības - required-experience: Nepieciešamā pieredze - required-items: Nepieciešamie priekšmeti - required-level: Nepieciešamais salas līmenis - required-money: Nepieciešamā nauda - required-permissions: Nepieciešamās atļaujas - requirements: Prasības - reset: Noņemt uzdevuma izpildi spēlētājam - reset-on-new: Iztīrīt veidojot jaunu salu - reward-commands: Atlīdzības komandas - reward-experience: Atlīdzības pieredze - reward-items: Atlīdzības priekšmeti - reward-money: Atlīdzības nauda - rewards: Atlīdzības - reward-text: Atlīdzības ziņa - save: Saglabāt - search-radius: Meklēšanas distnace - set: "=" - settings: Pārvaldīt Iestatījumus - show-eggs: Pārslēgt attēlošanas režīmu - title-enable: Izpildes virsraksts - title-showtime: Virsrakta rādīšanas ilgums - toggle-user-list: Spēlētāju saraksts - value: Vērtība - waiver-amount: Neizpildāmo skaits - library: Interneta Bibliotēka - download: Lejupielādēt Bibliotēkas - challenge-wipe: Notīrīt Uzdevumu Datubāzi - players-wipe: Notīrīt Lietotāju Datubāzi - visibility-mode: Uzdevumu Redzamības mode - type: - island: "&6Salas tips" - inventory: "&6Inventāra tips" - other: "&6Cita veida tips" - next: Nākošā - previous: Iepriekšējā - return: Atgriezties - value: Pabeigt - increase: Palielināt - reduce: Samazināt - challenge-description: - completed: "&BIzpildīts" - completed-times: Izpildīts [donetimes] reizes - completed-times-of: Izpildīts [donetimes] no [maxtimes] reizēm - environment: 'Nepieciešamā pasaule:' - experience-reward: "&6Pieredzes atlīdzība: [value]" - level: "&FLīmenis: [level]" - maxed-reached: Izpildīts [donetimes] no [maxtimes] reizēm - money-reward: "&6Naudas atlīdzība: $[value]" - not-repeatable: "&cŠis Uzdevums nav atkārtojams!" - objects-close-by: "&cVisiem nepieciešamajiem blokie, un radībām nepieciešams - būt tuvu tev un uz tavas salas!" - required-blocks: 'Nepieciešamās bloki:' - required-entities: 'Nepieciešamās radības:' - required-experience: "&6Nepieciešamā pieredze: [value]" - required-island-level: "&6Nepieciešamais salas līmenis: [value]" - required-items: 'Nepieciešamās lietas:' - required-money: "&6Nepieciešamā nauda: $[value]" - reward-commands: "&6Atlīdzības komandas:" - reward-items: "&6Atlīdzības lietas:" - warning-blocks-remove: "&cVisi nepieciešamie bloki tiks noņemti (aizstāti ar - gaisu) pēc Uzdevuma izpildes!" - warning-entities-kill: "&cVisas pieprasītās radības tiks noņemtas (nogalinātas) - pēc Uzdevuma izpildes!" - warning-items-take: "&cVisas pieprasītas lietas tiks izņemtas no tava inventāra!" + # Button in the Challenges GUI that allows to select free challenges. + free-challenges: + name: "&f&l Brīvie Uzdevumi" + description: |- + &7 Attainos visus brīvos + &7 uzdevumus + # Button that is used to return to previous GUI or exit it completely. + return: + name: "&f&l Atgriezties" + description: |- + &7 Atgriežas uz iepriekšējo + &7 izvēlni vai iziet no + &7 izvēlnes. + # Button that is used in multi-page GUIs which allows to return to previous page. + previous: + name: "&f&l Iepriekšējā Lapa" + description: |- + &7 Pārslēgties uz &e [number] &7 lapu + # Button that is used in multi-page GUIs which allows to go to next page. + next: + name: "&f&l Nākošā Lapa" + description: |- + &7 Pārslēgties uz &e [number] &7 lapu + # Button that allows to reduce number + reduce: + name: "&f&l Samazināt" + description: |- + &7 Samazināt par &e [number] + # Button that allows to increase number + increase: + name: "&f&l Palielināt" + description: |- + &7 Palielināt par &e [number] + # Button that displays and allows completing challenge + accept: + name: "&f&l Pabeigt" + description: |- + &7 Pabeigt uzdevumu &e [number] + &7 reizi(-es) + # Button that allows to quit the current gui. + quit: + name: "&f&l Iziet" + description: |- + &7 Iziet no izvēlnes. + # All following buttons are mainly for Admin GUI. + complete_user_challenges: + name: "&f&l Pabeigt Spēlētāja Uzdevumu(-s)" + description: |- + &7 Ļauj pabeigt uzdevumu(-s) + &7 spēlētāja vietā. + reset_user_challenges: + name: "&f&l Atjaunot Spēlētāja Uzdevumu(-s)" + description: |- + &7 Ļauj atjaunot uzdevumu(-s) + &7 priekš spēlētāja. + add_challenge: + name: "&f&l Izveidot Uzdevumu" + description: |- + &7 Sāk uzdevuma izveides + &7 procesu. + add_level: + name: "&f&l Izveidot Līmeni" + description: |- + &7 Sāk līmeņa izveides + &7 procesu. + edit_challenge: + name: "&f&l Labot Uzdevumu" + description: |- + &7 Ļauj izvēlēties un + &7 labot uzdevumu. + edit_level: + name: "&f&l Labot Līmeni" + description: |- + &7 Ļauj izvēlēties un + &7 labot līmeni. + delete_challenge: + name: "&f&l Dzēst Uzdevumu" + description: |- + &7 Ļauj izvēlēties un + &7 dzēst uzdevumu. + delete_level: + name: "&f&l Dzēst Līmeni" + description: |- + &7 Ļauj izvēlēties un + &7 dzēst līmeni. + edit_settings: + name: "&f&l Iestatījumi" + description: |- + &7 Ļauj skatīt un mainīt + &7 papildinājuma iestatījumus. + complete_wipe: + name: "&f&l Pilnīga Datu Dzēšana" + description: |- + &7 Pilnībā dzēš visus + &7 datus no papildinājuma + &7 datubāzes. + challenge_wipe: + name: "&f&l Uzdevumu Dzēšana" + description: |- + &7 Pilnībā dzēš uzdevumu un + &7 līmeņu datus no papildinājuma + &7 datubāzes. + user_wipe: + name: "&f&l Lietotāju Datu Dzēšana" + description: |- + &7 Pilnībā dzēš lietotāju + &7 datus no papildinājuma + &7 datubāzes. + library: + name: "&f&l Bibliotēka" + description: |- + &7 Atver publisko + &7 uzdevumu datubāzi. + import_database: + name: "&f&l Ielādēt Datubāzi" + description: |- + &7 Ļauj ielādēt eksportētu + &7 datubāzes failu. + import_template: + name: "&f&l Ielādēt Paraugu" + description: |- + &7 Ļauj ielādēt parauga + &7 failu. + export_challenges: + name: "&f&l Eksportēt Uzdevumus" + description: |- + &7 Ļauj eksportēt datubāzi + &7 uz failu. + properties: + name: "&f&l Iestatījumi" + description: |- + &7 Skatīt galvenos iestatījumus. + requirements: + name: "&f&l Prasības" + description: |- + &7 Skatīt prasības. + rewards: + name: "&f&l Atlīdzības" + description: |- + &7 Skatīt atlīdzības. + deployed: + name: "&f&l Strādājošs" + description: |- + &7 Ļauj pārslēgt vai + &7 uzdevums ir pieejams + &7 priekš spēlētājiem. + enabled: "&2 Pieejams" + disabled: "&c Nepieejams" + name: + name: "&f&l Nosaukums" + description: |- + &7 Ļauj mainīt uzdevuma + &7 nosaukumu. + value: "&7 Šobrīd: &r [name]" + remove_on_complete: + name: "&f&l Slēpt pēc Pabeigšanas" + description: |- + &7 Ļauj pārslēgt uzdevumu + &7 paslēpšanu pēc pilnīga + &7 uzdevuma izpildes. + enabled: "&2 Ieslēgs" + disabled: "&c Izslēgs" + description: + name: "&f&l Apraksts" + description: |- + &7 Ļauj uzstādīt un mainīt + &7 uzdevumam specifisku + &7 aprakstu. + value: "&7 Esošais apraksts:" + environment: + name: "&f&l Dimensija" + description: |- + &7 Ļauj limitēt uzdevuma + &7 izpildes vidi. + enabled: "&2" + disabled: "&c" + order: + name: "&f&l Prioritāte" + description: |- + &7 Ļauj uzstādīt prioritātes + &7 numuru. + &7 Objekti tiks sakārtoti augošā + &7 secībā. Ja prioritātes numuri + &7 ir vienādi, tad objekti tiks + &7 kārtoti pēc to `id` vārdiem.. + value: "&7 Esošais numurs: &e [number]" + icon: + name: "&f&l Ikona" + description: |- + &7 Ļauj mainīt ikonu. + locked_icon: + name: "&f&l Slēgtā Ikona" + description: |- + &7 Ļauj mainīt slēgta + &7 līmeņa ikonu. + required_permissions: + name: "&f&l Nepieciešamās Atļaujas" + description: |- + &7 Ļauj uzstādīt un mainīt + &7 atļaujas, lai uzdevumu + &7 varētu izpildīt. + title: "&7 Atļaujas: " + permission: " &8 - [permission]" + none: "&7 Atļaujas nav uzstādītas." + remove_entities: + name: "&f&l Izņemt Radības" + description: |- + &7 Ļauj pārslēgt vai + &7 radības tiks izņemtas + &7 no pasaules pēc + &7 uzdevuma izpildes. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_entities: + name: "&f&l Nepieciešamās Radības" + description: |- + &7 Ļauj mainīt uzdevuma + &7 radības, kas nepieciešamas + &7 uzdevuma izpildei. + title: "&7 Radības: " + list: " &8 - [number] x [entity]" + none: "&7 Radības nav uzstādītas." + remove_blocks: + name: "&f&l Izņemt Blokus" + description: |- + &7 Ļauj pārslēgt vai + &7 bloki tiks izņemti + &7 no pasaules pēc + &7 uzdevuma izpildes. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_blocks: + name: "&f&l Nepieciešamie Bloki" + description: |- + &7 Ļauj mainīt uzdevuma + &7 blokus, kas nepieciešamas + &7 uzdevuma izpildei. + title: "&7 Bloki: " + list: " &8 - [number] x [block]" + none: "&7 Bloki nav uzstādīti." + search_radius: + name: "&f&l Meklēšanas Rādiuss" + description: |- + &7 Ļauj mainīt bloku un radību + &7 meklēšanas rādiusu. + value: "&7 Esošā distance: &e [number]" + remove_items: + name: "&f&l Izņemt Priekšmetus" + description: |- + &7 Ļauj pārslēgt vai + &7 priekšmetus tiks izņemtas + &7 no spēlētāja inventāra + &7 pēc uzdevuma izpildes. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_items: + name: "&f&l Priekšmeti" + description: |- + &7 Ļauj mainīt uzdevuma + &7 priekšmeti, kas nepieciešamas + &7 uzdevuma izpildei. + title: "&7 Priekšmeti: " + list: " &8 - [number] x [item]" + none: "&7 Priekšmeti nav uzstādīti." + add_ignored_meta: + name: "&f&l Pievienot Meta-datu Ignorēšanu" + description: |- + &7 Ļauj uzstādīt, kuriem + &7 priekšmetiem drīkst + &7 ignorēt meta-datus, kā + &7 bojājumi, vārdi, uzlabojumi. + title: "&7 Priekšmeti: " + list: " &8 - [item]" + none: "&7 Priekšmeti nav uzstādīti." + remove_ignored_meta: + name: "&f&l Noņemt Meta-datu Ignorēšanu" + description: |- + &7 Ļauj noņemt, kuriem + &7 priekšmetiem drīkst + &7 ignorēt meta-datus, kā + &7 bojājumi, vārdi, uzlabojumi. + remove_experience: + name: "&f&l Noņemt Pieredzi" + description: |- + &7 Ļauj pārslēgt vai pēc + &7 uzdevuma izpildes + &7 pieredzes punkti + &7 spēlētājam tiks noņemti. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_experience: + name: "&f&l Nepieciešamā Pieredze" + description: |- + &7 Ļauj uzstādīt nepieciešamo + &7 pieredzi uzdevuma izpildei. + value: "&7 Pieredze: &e [number]" + required_level: + name: "&f&l Salas Līmenis" + description: |- + &7 Ļauj uzstādīt nepieciešamo + &7 salas līmeni uzdevuma + 77 izpildei. + value: "&7 Līmenis: &e [number]" + remove_money: + name: "&f&l Noskaitīt Naudu" + description: |- + &7 Ļauj pārslēgt vai pēc + &7 uzdevuma izpildes + &7 nauda tiks noskaitīta + &7 spēlētāja maka. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + required_money: + name: "&f&l Nepieciešamā Nauda" + description: |- + &7 Ļauj uzstādīt nepieciešamo + &7 naudas daudzumu spēlētāja + &7 makā. + value: "&7 Naudas Daudzums: &e [number]" + statistic: + name: "&f&l Statistika" + description: |- + &7 Ļauj mainīt, kura + &7 statistikas vērtība + &7 tiks skatīta. + value: "&7 Vērtība: &e [statistic]" + statistic_amount: + name: "&f&l Nepieciešamā Vērtība" + description: |- + &7 Ļauj uzstādīt kādu + &7 vērtību statistikas + &7 rādītājam ir jāsasniedz. + value: "&7 Vērtība: &e [number]" + remove_statistic: + name: "&f&l Samazināt Statistiku" + description: |- + &7 Ļauj pārslēgt vai pēc + &7 uzdevuma izpildes + &7 no statistikas datiem + &7 nepieciešamā vērtība + &7 tiks noskaitīta. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + statistic_blocks: + name: "&f&l Bloks" + description: |- + &7 Ļauj uzstādīt bloku, + &7 ko skatīs statistika. + value: "&7 Bloks: &e [block]" + statistic_items: + name: "&f&l Priekšmets" + description: |- + &7 Ļauj uzstādīt priekšmetu, + &7 ko skatīs statistika. + value: "&7 Priekšmets: &e [item]" + statistic_entities: + name: "&f&l Radība" + description: |- + &7 Ļauj uzstādīt radību, + &7 ko skatīs statistika. + value: "&7 Radība: &e [entity]" + reward_text: + name: "&f&l Atlīdzības Teksts" + description: |- + &7 Ļauj uzrakstīt uzdevumam + &7 specifisku atlīdzības + &7 tekstu. + value: "&7 Teksts:" + repeat_reward_text: + name: "&f&l Atkārtotas Atlīdzības Teksts" + description: |- + &7 Ļauj uzrakstīt uzdevumam + &7 specifisku atkārtotas + &7 atlīdzības tekstu. + value: "&7 Teksts:" + reward_items: + name: "&f&l Atlīdzības Priekšmeti" + description: |- + &7 Ļauj uzstādīt atlīdzības + &7 priekšmetus. + title: "&7 Priekšmeti: " + list: " &8 - [number] x [item]" + none: "&7 Priekšmeti nav uzstādīti." + repeat_reward_items: + name: "&f&l Atkārtotas Atlīdzības Priekšmeti" + description: |- + &7 Ļauj uzstādīt atkārtotās + &7 atlīdzības priekšmetus. + title: "&7 Priekšmeti: " + list: " &8 - [number] x [item]" + none: "&7 Priekšmeti nav uzstādīti." + reward_experience: + name: "&f&l Atlīdzības Pieredze" + description: |- + &7 Ļauj uzstādīt atlīdzības + &7 pieredzi spēlētājam. + value: "&7 Pieredze: &e [number]" + repeat_reward_experience: + name: "&f&l Atkārtotas Atlīdzības Pieredze" + description: |- + &7 Ļauj uzstādīt atkārtotas + &7 atlīdzības pieredzi + &7 spēlētājam. + value: "&7 Pieredze: &e [number]" + reward_money: + name: "&f&l Atlīdzības Nauda" + description: |- + &7 Ļauj uzstādīt atlīdzības + &7 naudu spēlētājam. + value: "&7 Vērtība: &e [number]" + repeat_reward_money: + name: "&f&l Atkārtotas Atlīdzības Nauda" + description: |- + &7 Ļauj uzstādīt atkārtotas + &7 atlīdzības naudu spēlētājam. + value: "&7 Vērtība: &e [number]" + reward_commands: + name: "&f&l Atlīdzības Komandas" + description: |- + &7 Ļauj uzstādīt atlīdzības + &7 komandas. + &8 Padomi: + &8 Komandām nav nepieciešams + &8 pirmais `/` to sākumā. + &8 Pēc noklusējuma komandas + &8 izpildīs serveris. Taču + &8 pievienojot `[SELF]` sākumā + &8 komanda tiks izpildīta no + &8 spēlētāja konta. + &8 Komanda atbalsta arī + &8 `[player]` aizvietotāju, + &8 kurš tiks pārvidots par + &8 spēlētāja vārdu. + value: "&7 Komandas:" + repeat_reward_commands: + name: "&f&l Atkārtotas Atlīdzības Komandas" + description: |- + &7 Ļauj uzstādīt atkārtotas + &7 atlīdzības komandas. + &8 Padomi: + &8 Komandām nav nepieciešams + &8 pirmais `/` to sākumā. + &8 Pēc noklusējuma komandas + &8 izpildīs serveris. Taču + &8 pievienojot `[SELF]` sākumā + &8 komanda tiks izpildīta no + &8 spēlētāja konta. + &8 Komanda atbalsta arī + &8 `[player]` aizvietotāju, + &8 kurš tiks pārvidots par + &8 spēlētāja vārdu. + value: "&7 Komandas:" + repeatable: + name: "&f&l Atkārtojams" + description: |- + &7 Ļauj pārslēgt vai + &7 uzdevums ir atkārtojams. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + repeat_count: + name: "&f&l Atkārtojumu Daudzums" + description: |- + &7 Ļauj uzstādīt cik + &7 daudz reižu uzdevumu + &7 varēs izpildīt. + value: "&7 Vērtība: &e [number]" + cool_down: + name: "&f&l Taimouts" + description: |- + &7 Ļauj uzstādīt sekunžu + &7 daudzumu, kas jānogaida + &7 starp sekunžu izpildīšanu + &7 starp atkārtojamajiem + &7 uzdevumiem. + value: "&7 Current value: &e [time]" + challenges: + name: "&f&l Uzdevumi" + description: |- + &7 Skatīt uzdevumus, kas + &7 piesaistīti līmenim. + waiver_amount: + name: "&f&l Atbrīvojuma Daudzums" + description: |- + &7 Ļauj uzstādīt cik daudz + &7 uzdevumu var palikt + &7 neizpildīti iekš līmeņa, + &7 lai atvērtu nākošo līmeni. + value: "&7 Vērtība: &e [number]" + add_challenges: + name: "&f&l Pievienot Uzdevumu(-us)" + description: |- + &7 Ļauj pievienot uzdevumus + &7 līmenim. + remove_challenges: + name: "&f&l Noņemt Uzdevumu(-us)" + description: |- + &7 Ļauj noņemt uzdevumus + &7 no līmeņa. + reset_on_new: + name: "&f&l Atjaunot pēc Iziešanas" + description: |- + &7 Ļauj ieslēgt uzdevumu + &7 progresa atjaunošanu + &7 pēc iziešanas no komandas + &7 vai jaunas salas izveidi. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + broadcast: + name: "&f&l Izziņot" + description: |- + &7 Paziņot uzdevuma un līmeņa + &7 pirmās reizes pabeigšanu + &7 visiem spēlētājiem. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + remove_completed: + name: "&f&l Slēpt Izpildītos" + description: |- + &7 Ļauj slēpt izpildītos + &7 uzdevumus no spēlētāju + &7 izvēlnēm. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + glow_completed: + name: "&f&l Atzīmēt Izpildītos" + description: |- + &7 Pievienot uzlabojuma + &7 mirdzēšanu izpildītajiem + &7 uzdevumiem. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + store_history: + name: "&f&l Saglabāt Vēsturi" + description: |- + &7 Saglabā vēsturi par + &7 uzdevumu izpildīšanu. + &7 Šobrīd skatāms tikai + &7 datubāzē. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + data_per_island: + name: "&f&l Kopīgot Salām" + description: |- + &7 Uzdevumi tiek kopīgoti + &7 visiem salas biedriem, + &7 ja ieslēgts, vai katram + &7 atsevišķi, ja izslēgts. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + show_title: + name: "&f&l Rādīt Virsrakstu" + description: |- + &7 Rādīt virsrakstu par + &7 uzdevuma izpildīšanu. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + gamemode_gui: + name: "&f&l Globālā Komanda" + description: |- + &7 Ļauj ieslēgt vienotu globālo + &7 komandu priekš šī papildinājuma. + &7 Pēc noklusējuma: /challenges + &c Nepieciešams restarts. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + locked_level_icon: + name: "&f&l Noklusējuma Slēgta Līmeņa Ikona" + description: |- + &7 Noklusējuma ikona priekš visiem + &7 slēgtajiem līmeņiem, kam nav + &7 sava specificējuma. + purge_history: + name: "&f&l Vēstures Ilgums" + description: |- + &7 Dienu skaits cik ilgi + &7 vēsture tiks glabāta. + value: "&7 Dienas: &e [number]" + title_showtime: + name: "&f&l Virsraksta Ilgums" + description: |- + &7 Virsraksta rādīšanas ilgums. + value: "&7 Vērtība: &e [number]" + active_world_list: + name: "&f&l Spēlētāja Pasaules Izvēlne" + description: |- + &7 Ļauj pārslēgt vai globālā + &7 komanda atvērs spēles režīmu + &7 izvēlni vai uzdevumu izvēlni + &7 no spēlētāja pasaules. + enabled: "&2 Ieslēgts" + disabled: "&c Izslēgts" + visibility_mode: + name: "&f&l Redzamība" + description: |- + &7 Redzamība priekš + &7 uzdevumiem spēlētāju + &7 izvēlnēs. + enabled: "&2" + disabled: "&c" + visible: "Rādīt Pieejamos Uzdevumus" + hidden: "Rādīt Visus Uzdevumus" + toggleable: "Pārslēdzams" + download: + name: "&f&l Lejupielādēt Bibliotēku" + description: |- + &7 Manuāli lejupielādēt + &7 bibliotētku. + enabled: "&2 Notīrīt iekšējo atmiņu" + disabled: "&c Netīrīt iekšējo atmiņu" + player: + name: "&f&l [name]" + description: |- + &7 Salas īpašnieks: [owner] + members: "&7 Salas biedri:" + member: "&8 - [name]" + no-island: |- + &c Spēlētājam nav salas. + player_list: + name: "&f&l Filtrēt Spēlētājus" + description: |- + &7 Ļauj filtrēt spēlētājus. + enabled: "&2" + disabled: "&c" + online: "Spēlētāji Tiešsaitē" + with_island: "Spēlētāji ar Salām" + in_world: "Spēlētāji šajā Pasaulē" + add_block: + name: "&f&l Pievienot Bloku" + description: |- + &7 Ļauj pievienot bloku + &7 sarakstam. + remove_block: + name: "&f&l Noņemt Bloku(-us)" + description: |- + &7 Ļauj noņemt iezīmētos + &7 blokus no saraksta. + title: "&7 Iezīmētie Bloki:" + material: "&8 - [material]" + material: + name: "&f&l [material]" + description: |- + &7 Materiāla ID: [id] + selected: "&2 Iezīmēts" + add_entity: + name: "&f&l Pievienot Radību" + description: |- + &7 Ļauj pievienot jaunu + &7 radību sarakstam. + switch_entity: + name: "&f&l Pārslēgt Ikonas" + description: |- + &7 Ļauj pārslēgt radību + &7 olas uz radību galvām. + remove_entity: + name: "&f&l Dzēst Radības" + description: |- + &7 Ļauj noņemt iezīmētās + &7 radības no saraksta. + title: "&7 Iezīmētās Radības:" + entity: "&8 - [entity]" + entity: + name: "&f&l [entity]" + description: |- + &7 Radības ID: [id] + selected: "&2 Iezīmēts" + inventory_type: + name: "&f&l Inventāra Tips" + description: |- + &7 Uzdevums, kas skatās + &7 priekšmetus spēlētāja + &7 inventārā. + island_type: + name: "&f&l Salas Tips" + description: |- + &7 Uzdevums, kas skatās + &7 lietas uz spēlētāja salas. + other_type: + name: "&f&l Cits Tips" + description: |- + &7 Uzdevums, kas skatās + &7 pieredzi, naudu un salas + &7 līmeni. + statistic_type: + name: "&f&l Statistikas Tips" + description: |- + &7 Uzdevums, kas skatās + &7 spēlētāja statistiku. + save: + name: "&f&l Saglabāt" + description: |- + &7 Saglabāt izmaiņas un + &7 atgriezties. + cancel: + name: "&f&l Atcelt" + description: |- + &7 Atcelt izmaiņas un + &7 atgriezties iepriekšējā + &7 izvēlnē. + accept_selected: + name: "&f&l Akceptēt Atzīmētos" + description: |- + &7 Akceptēt atzīmētos un + &7 atgriezties iepriekšējā + &7 izvēlnē. + title: "&7 Atzīmēti: " + element: "&8 - [element]" + statistic_element: + name: "&f&l [statistic]" + description: "[description]" + environment_element: + name: "&f&l [environment]" + description: "[description]" + search: + name: "&f&l Meklēt" + description: |- + &7 Ļauj ievadīt meklēšanas + &7 tekstu. + search: "&b Vērtība: [value]" + tips: + click-to-select: "&e Klikšķini, &7 lai atlasītu." + click-to-choose: "&e Klikšķini, &7 lai izvēlētos." + click-to-complete: "&e Klikšķini, &7 lai pabeigtu." + right-click-multiple-open: |- + &e Labais klikšķis, + &7 lai izvēlētos daudzumu. + shift-left-click-to-complete-all: |- + &e Shift un klikšķis, + &7 lai pabeigtu visus. + left-click-to-accept: |- + &e Kreisais klikšķis, + &7 lai pabeigtu. + right-click-to-write: |- + &e Labais klikšķis, + &7 lai rakstītu. + click-to-reduce: "&e Klikšķini, &7 lai samazinātu." + click-to-increase: "&e Klikšķini, &7 lai palielinātu." + click-to-return: "&e Klikšķini, &7 lai atgrieztos." + click-to-quit: "&e Klikšķini, &7 lai izietu." + click-to-wipe: "&e Klikšķini, &7 lai dzēstu." + left-click-to-wipe: |- + &e Kreisais klikšķis, + &7 lai dzēstu. + right-click-to-switch: |- + &e Labais klikšķis, + &7 lai mainītu. + click-to-open: "&e Klikšķini, &7 lai atvērtu." + click-to-export: "&e Klikšķini, &7 lai eksportētu." + click-to-create: "&e Klikšķini, &7 lai izveidotu." + left-click-to-open: |- + &e Kreisais klikšķis, + &7 lai atvērtu. + right-click-to-reset-all: |- + &e Labais klikšķis, + &7 lai dzēstu visu. + click-to-toggle: "&e Klikšķini, &7 lai pārslēgtu." + click-to-change: "&e Klikšķini, &7 lai mainītu." + shift-click-to-reset: |- + &e Shift un klikšķis, + &7 lai dzēstu. + click-to-add: "&e Klikšķini, &7 lai pievienotu." + click-to-remove: "&e Klikšķini, &7 lai noņemtu." + left-click-to-cycle: |- + &e Kreisais klikšķis, + &7 pārslēgtu uz leju. + right-click-to-cycle: |- + &e Labais klikšķis, + &7 pārslēgtu uz augšu. + click-to-edit: "&e Klikšķini, &7 lai labotu." + left-click-to-download: |- + &e Kreisais klikšķis, + &7 lai lejupielādētu + right-click-to-toggle: |- + &e Labais klikšķis, + &7 lai pārslēgtu. + click-to-install: "&e Klikšķini, &7 lai instalētu." + click-to-reset-all: "&e Klikšķini, &7 lai dzēstu visu." + right-click-to-select: |- + &e Labais klikšķis, + &7 lai atzīmētu. + right-click-to-deselect: |- + &e Labais klikšķis, + &7 lai atceltu atzīmi. + left-click-to-choose: |- + &e Kreisais klikšķis, + &7 lai izvēlētos. + click-to-cancel: "&e Klikšķini, &7 lai atceltu." + click-to-save: "&e Klikšķini, &7 lai saglabātu." + click-to-deselect: |- + &e Klikšķini, &7 lai + &7 atceltu atzīmi. + click-on-item: |- + &e Klikšķini, &7 uz priekšmeta + &7 tavā inventārā. + left-click-to-edit: |- + &e Kreisais klikšķis, + &7 lai labotu. + right-click-to-clear: |- + &e Labais klikšķis, + &7 lai notīrītu. + click-to-previous: |- + &e Klikšķini, &7 lai + &7 skatītu iepriekšējo. + click-to-next: |- + &e Klikšķini, &7 lai + &7 skatītu nākošo. descriptions: - admin: - add-challenge: Ļauj pievienot jebkuru Uzdevumu šim Līmenim. - add-text-line: "&6 Pievieno jaunu teksta ziņu!" - broadcast: Ļauj ieslēgt/izslēgt paziņojumu visiem tiešsaitē esošajiem spēlētājiem, - ka spēlētājs izpildījis Uzdevumu vai Līmeni pirmo reizi. - cancel: Atgriezties iepriekšējā izvēlnē. &cDati netiks saglabāti. - challenge-lore: Ļauj definēt kādus elementus rādīs Uzdevumu aprakstos. - challenges: Ļauj pārvaldīt Līmeņa uzdevumus (pievienot / noņemt). - click-to-edit: "&4Uzspied šeit, lai labotu." - complete: Ļauj pabeigt uzdevumus jebkuram spēlētājam.|Spēlētājs nedabūs izpildes - atlīdzības. - complete-wipe: Ļauj pilnībā iztīrīt papildinājuma datubāzes. Ieskaitot spēlētāju - datus! - create-challenge: Ļauj izveidot jaunu Uzdevumu. - create-level: Ļauj izveidot jaunu Līmeni. - default-export: Ļauj eksportēt uzdevumus uz defaults.json failu. - default-import: Ļauj ielādēt sākotnējos uzdevumus. - default-locked-icon: Ļauj mainīt neatklāto līmeņu ikonu.|Katrs līmenis šo - opciju var pārrakstīt. - delete-challenge: Ļauj izdzēst Uzdevumu. - delete-level: Ļauj izdzēst Līmeni. - deployment: Ļauj nobloķēt uzdevuma izpildi.|Uzdevumu nevarēs izpildīt, ja - opcija nav aktīva. - description: Ļauj labot aprakstu. - edit-challenge: Ļauj labot Uzdevuma opcijas. - edit-level: Ļauj labot Līmeņa opcijas. - edit-text-line: "&6 Labo teksta ziņu" - environment: Ļauj izvēlēties, kurās vidēs Uzdevumu var pildīt. - free-at-top: Ļauj definēt kurā rindā būs brīvie uzdevumi (bez līmeņa). Pirmā - vai pēdējā rinda. - glow: Ļauj ieslēgt/izslēgt opciju, kas liks mirdzēt izpildītajiem Uzdevumiem/Līmeņiem. - gui-mode: Ļauj ieslēgt/izslēgt vienas komandas opciju. Komandu var mainīt - konfigurācijas failā.|&2Nepieciešama servera restartēšana. - gui-view-mode: Ļauj definēt vai /challenges logs rādīs visus spēles režīmus - vai uzdevumus no spēlētāja pasaules. - history-lifespan: Ļauj mainīt cik pēdējās dienas dati tiks glabāti.|0 nozīmē, - ka dati netiks dzēsti. - history-store: Ļauj ieslēgt/izslēgt uzdevumu izpildes vēstures saglabāšanu. - icon-challenge: Uzdevuma ikona, kas parādīsies lietotāja izvēlnē. - icon-level: Līmeņa ikona, kas parādīsies lietotāja izvēlnē. - import: Ļauj importēt ASkyblock Uzdevumus.|Ar labo peles klikšķi tas ļauj - pārslēgt pārrakstīšanas modi.|Nepieciešams Challenges.yml fails ./BentoBox/addons/Challenges - mapītē. - increase: Saskaitīšanas operācija. - input: Atvērt teksta lauka ievadi. - input-mode: Pārslēgties starp sarakstes un lakts ievades veidu. - island-store: Ļauj mainīt datu saglabāšanas režīmu. Dati var tikt glabāti - katrai salai, vai katram spēlētājam.|&cMAINOT REŽĪMUS DATI NETIKS PĀRVEIDOTI. - level-lore: Ļauj definēt kādus elementus rādīs Līmeņu aprakstos. - line-length: Ļauj definēt cik simbolus gara rindiņa būs redzama lietu aprakstos. - locked-icon: Neatklāta (neatvērta) Līmeņa ikona, kas parādīsies lietotāja - izvēlnē. - mode-in-world: Spēlētāji, kas ir spēles režīma pasaulē. - mode-online: Spēlētāji, kas ir tiešsaitē. - mode-with-island: Spēlētāji, kam ir sala šajā režīmā. - multiply: Reizināšanas operācija. - name-challenge: Ļauj labot Uzdevuma nosaukumu. - name-level: Ļauj labot Līmeņa nosaukumu. - order: Ļauj labot secību. - properties: Ļauj labot rekvizītus. - reduce: Atņemšanas operācija. - remove-blocks: Ļauj noņemt (aizstāt ar gaisu) blokus pēc Uzdevuma izpildes. - remove-challenge: Ļauj noņemt Uzdevumu no šī Līmeņa. - remove-completed: Ļauj ieslēgt/izslēgt opciju, kas paslēps visus izpildītos - uzdevumus, kurus vairs nevar atkārtot. - remove-entities: Ļauj noņemt (nogalināt) radības pēc Uzdevuma izpildes. - remove-experience: Ļauj noņemt pieprasīto pieredzes daudzumu pēc Uzdevuma - izpildes. - remove-items: Ļauj izņemt lietas no spēlētāja inventāra pēc Uzdevuma izpildes. - remove-money: Ļauj noņemt naudu no spēlētāja konta pēc Uzdevuma izpildes.|&cNepieciešams - Vault un ekonomikas papildinājumi. - remove-on-complete: Šīs opcijas ieslēgšana nozīmē, ka Uzdevumu nerādīs lietotāja - logos, ja tas ir izpildīts. - remove-selected: Dzēst iezīmētos elementus.|Iezīmēt elementus var ar labo - peles klikšķi. - repeatable: Ļauj definēt vai uzdevumu var pildīt atkārtoti. - repeat-count: Ļauj definēt cik reizes uzdevumu var pildīt atkārtoti. Cipars - 0 nozīmē, ka uzdevumu var pildīt neierobežotu daudzumu reižu. - repeat-reward-commands: 'Ļauj definēt komandas, kuras tiks izpildītas pēc - Uzdevuma atkārtotas izpildes.|&c***Pievienojot "[SELF]" sākumā nozīmē, ka - komandu izpildīs spēlētājs, piemēram. "/kill"|&c***Teksts "[player]" tiks - pārtaisīts par izpildītāja lietotājvārdu, piemēram "/kill [player]" tiks - pārveidots par "/kill BONNe1704"|Komandas:' - repeat-reward-experience: Ļauj mainīt atkārtotas izpildes atlīdzības pieredzes - daudzmumu. - repeat-reward-items: 'Ļauj pārvaldīt atkārtotas izpildes atlīdzības lietas.|Lietas:' - repeat-reward-money: Ļauj definēt atkārtotas izpildes naudas atlīdzības lielumu.|&cNepieciešams - Vault un ekonomikas papildinājumi. - repeat-reward-text: Ļauj definēt ziņu, kuru rādīs spēlētājam pēc atkārtotas - Uzdevuma izpldes. - required-blocks: Ļauj pārvaldīt nepieciešamos blokus.|Bloki:| - required-entities: Ļauj pārvaldīt nepieciešamās radības.|Radības:| - required-experience: Ļauj nodefinēt nepieciešamo spēlētāja pieredzes daudzumu, - lai izpildītu uzdevumu. - required-items: 'Nepieciešamās lietas spēlētāja inventārā.|Lietas:' - required-level: Ļauj definēt nepieciešamo salas līmeni Uzdevuma izpildei.|&cNepieciešams - Levels papildinājums. - required-money: Ļauj definēt nepieciešamo naudas daudzumu spēlētāja kontā.|&cNepieciešams - Vault un ekonomikas papildinājumi. - required-permissions: 'Nepieciešamās atļaujas, lai izpildītu Uzdevumu.|Atļaujas:' - requirements: Ļauj labot prasības - reset: Ļauj noņemt uzdevumu izpildi spēlētājam.|Ar labo peles klikšķi tas - ļauj pārslēgdz iespēju nodzēst visus izpildes datus. - reset-on-new: Ļauj pārslēgt opciju, kas notīra spēlētāja izpildītos Uzdevumus - un Līmeņus, ja tas atsāk no sākuma, pamet vai tiek izmests no salas. - reward-commands: 'Ļauj definēt komandas, kuras tiks izpildītas pēc Uzdevuma - izpildes.|&c***Pievienojot "[SELF]" sākumā nozīmē, ka komandu izpildīs spēlētājs, - piemēram. "/kill"|&c***Teksts "[player]" tiks pārtaisīts par izpildītāja - lietotājvārdu, piemēram "/kill [player]" tiks pārveidots par "/kill BONNe1704"|Komandas:' - reward-experience: Ļauj mainīt pieredzi, ko iegūs spēlētājs pēc Uzdevuma izpildes. - reward-items: 'Ļauj pārvaldīt lietas, kuras iegūs spēlētājs pēc Uzdevuma izpildes.|Lietas:' - reward-money: Ļauj mainīt naudas daudzumu, ko iegūs spēlētājs pēc Uzdevuma - izpildes.|&cNepieciešams Vault un ekonomikas papildinājumi. - rewards: Ļauj pārvaldīt atlīdzības - reward-text: Ļauj mainīt ziņu, kas parādīsies spēlētājam pēc Uzdevuma izpildes. - reward-text-level: Ļauj definēt ziņu, kas tiks nosūtīta spēlētājiem pēc Līmeņa - izpildes. - save: Saglabāt un atgriezties iepriekšējā izvēlnē. - search-radius: Distance no spēlētāja, kādā prasītie bloki un radības tiks - meklētas. - selected: Iezīmēts - set: Uzstādīšanas operācija. - settings: Ļauj labot Papildinājuma iestatījumus. - show-eggs: Ļauj pārslēgt radību attainošanas režīmu no olām uz galvu ikonām. - title-enable: Ļauj ieslēgt/izlēgt virsraksta parādīšanos pēc pirmās Uzdevuma/Līmeņa - izpildes. - title-showtime: Ļauj mainīt cik ilgi virsraksts spēlētājam būs redzams. - toggle-user-list: Ļauj pārslēgt spēlētāju saraksta režīmu. - waiver-amount: Ļauj definēt cik daudz uzdevumi var tikt atstāti neizpildīti, - lai atvērtu nākošo līmeni. - library: Atver izvēlni, kurā ir saraksts ar publiski pieejamajām Uzdevumu - Bibliotēkām - library-author: veidoja &e[author] - library-version: "&9Veidots ar Challenges-[version] versiju" - library-lang: "&aValoda: [lang]" - library-gamemode: "&aPrimāri priekš [gamemode]" - lore: - level: Līmeņa rindas. | Definētas iekš 'challenges.gui.challenge-description.level'. - status: Statusa rindas. |Definētas iekš 'challenges.gui.challenge-description.completed'. - count: Pabeigtības rindas. | Definētas iekš 'challenges.gui.challenge-description.completed-times', 'challenges.gui.challenge-description.completed-times-of' - un 'challenges.gui.challenge-description.maxed-reached'. - description: Apraksta rindas. | Definētas iekš challenges objekta- challenge.description. - warnings: 'Brīdinājumu rindas. | Definētas iekš: | ''challenges.gui.challenge-description.warning-items-take'' - | ''challenges.gui.challenge-description.objects-close-by'' | ''challenges.gui.challenge-description.warning-entities-kill'' - | ''challenges.gui.challenge-description.warning-blocks-remove''.' - environment: Apkārtnes rindas. | Definētas iekš challenges objektā- challenge.environment. - requirements: 'Prasību rinas. | Definēts iekš: | ''challenges.gui.challenge-description.required-level'' - | ''challenges.gui.challenge-description.required-money'' | ''challenges.gui.challenge-description.required-experience'' - | and challenge.requiredItems, challenge.requiredBlocks vai challenge.requiredEntities.' - reward_text: Atlīdzības rindas. |Definētas iekš challenge.rewardText un - challenge.repeatRewardText - reward_other: 'Citas atlīdzību rinas. | Definēti iekš : | ''challenges.gui.challenge-description.experience-reward'' - | ''challenges.gui.challenge-description.money-reward'' | ''challenges.gui.challenge-description.not-repeatable''.' - reward_items: Atlīdzības priekšmeti. | Definēti iekš challenge.rewardItems - un challenge.repeatRewardItems. - reward_commands: Atlīdzības komandas. | Definētas iekš challenge.rewardCommands - un challenge.repeatRewardCommands. - level_status: Statusa rindas. | Definēts iekš 'challenges.gui.level-description.completed'. - challenge_count: Pabeigto uzdevumu skaits. | Definēts iekš 'challenges.gui.level-description.completed-challenges-of' - unlock_message: Atvēršanas rindas. | Definēts iekš - challengeLevel.unlockMessage. - waiver_amount: Nepieciešamo uzdevumu skaits, kas nepieceišams līmeņa atvēršanai. - | Representē rindas 'challenges.gui.level-description.waver-amount' - level_reward_text: Atlīdzības teksts. | Definēts iekš challengeLevel.rewardText - level_reward_other: 'Atlīdzības rindiņas. | Representē rinas: | ''challenges.gui.level-description.experience-reward'' - | ''challenges.gui.level-description.money-reward''.' - level_reward_items: |- - Atlīdzības priekšmeti. | Saraksts ar priekšmetiem, kas definētas iekš - challengeLevel.rewardItems. - level_reward_commands: Atlīdzības komandas. | Saraksts ar komandām, kas - ir definētas iekš challengeLevel.rewardCommands. - download: Ļauj pašrocīgi lejupielādēt uzdevumu bibliotēkas. |Labais klikšķis - pārslēdz lokālās atmiņas dzēšanu. - download-disabled: GitHub datu lejupielāde ir izslēgta iekš BentoBox. Bez - tā nav iespējams lietot bibliotēkas! - challenge-wipe: Ļauj pilnībā notīrīt uzdevumu un to līmeņu datus! - players-wipe: Ļauj pilnībā notīrīt spēlētāju datus! - visibility-mode: Ļauj pārslēgt vai neizlaistie uzdevumi ir redzami, vai nē - block: "- [block] : [count]" - command: "- [command]" - current-value: "|&6Šī brīža vērtība: [value]." - disabled: Neaktīvs - enabled: Aktīvs - entity: "- [entity] : [count]" - item: "- [count] x [item]" - item-enchant: " - [enchant] [level]" - item-meta: " ([meta])" - level-locked: Jāpabeidz vēl [count] uzdevumus no [level] līmeņa, lai atklātu - šo līmeni! - level-unlocked: Uzspied, lai redzētu [level] uzdevumus! - nether: "- Elle" - normal: "- Virszeme" - permission: "- [permission]" - the-end: "- Beigas" - increase-by: "&aPalielināt pabeigšanas skaitu par [value]" - reduce-by: "&cSamazināt pabeigšanas skaitu par [value]" - visibility: - visible: Spēlētāji redz visus uzdevumus - hidden: Spēlētāji redz tikai izlaistos uzdevumus - toggleable: Ļauj lietotājiem pārslēgt kādus uzdevumus viņi redzēs - type: - island: "&aļauj definēt nepieciešamos blokus un radības apkārt spēlētājam" - inventory: "&aļauj definēt nepieciešamos priekšmetus spēlētāja invertārā" - other: "&aļauj definēt citas nepieciešamās lietas no citiem papildinājumiem" - item-description: - armor-color: " [color]" - book-meta: " [title] no [author]" - custom-effects: " Speciālie effekti:" - egg-meta: " [mob]" - fish-meta: " [body-color] ar [pattern-color] [pattern]" - item: "- [count] x [item]" - item-enchant: " - [enchant] [level]" - item-lore: " Lietas apraksts:" - item-meta: " ([meta])" - item-name: " [name]" - potion-effect: " [effect] x [amplifier] for [duration]t" - potion-type: " [name]" - potion-type-extended: " Pagarināts [name]" - potion-type-extended-upgraded: " Uzlabots un pagarintāts [name]" - potion-type-upgraded: " Uzlabots [name]" - recipe-count: " [count] receptes" - skull-owner: " [owner]" - level-description: - completed: "&BIzpildīts" - completed-challenges-of: "&3Tu esi izpildījis [number] no [max] uzdevumiem šajā - līmenī." - experience-reward: "&6Pieredzes atlīdzība: [value]" - money-reward: "&6Naudas atlīdzība: $[value]" - reward-commands: "&6Atlīdzības komandas:" - reward-items: "&6Atlīdzības lietas:" - waver-amount: "&6Tu vari izlaist [value] uzdevumus, lai atvērtu nākošo līmeni." - questions: - admin: - challenge-name: Ieraksti uzdevuma nosaukumu sarakstē. - level-name: Ieraksti uzdevuma līmeņa nosaukumu sarakstē. - number: Ieraksti nummuru sarakstē. - unique-id: Ieraksti objekta unikālo nosaukumu sarakstē. - prefix: "&2[SERVERIS]: " - title: - admin: - choose-challenge-title: "&aIzvēlēties Uzdevumu" - choose-level-title: "&aIzvēlēties Līmeni" - choose-user-title: "&aIzvēlēties Spēlētāju" - confirm-title: "&aApstiprināt" - edit-challenge-title: "&aLabot Uzdevumu" - edit-level-title: "&aLabot Līmeni" - edit-text-fields: "&aLabot Teksta Laukus" - gui-title: "&aUzdevumu administrēšana" - manage-blocks: "&aPārvaldīt Blokus" - manage-entities: "&aPārvaldīt Radības" - manage-items: "&aPārvaldīt Lietas" - manage-numbers: "&aSkaitļu Panelis" - select-block: "&aIzvēlēties Bloku" - select-challenge: "&aIzvēlēties Uzdevumu" - select-entity: "&aIzvēlēties Radību" - settings-title: "&aLabot Iestatījumus" - toggle-environment: "&aPārvaldīt Vidi" - library-title: "&aLejupielādējamās bibliotēkas" - lore-add: "&aPievienot apraksta elementu" - lore-remove: "&aNoņemt apraksta elementu" - lore-edit: "&aLabot aprakstu" - type-select: "&aIzvēlies uzdevumu tipu" - challenges: "&6Uzdevumi" - game-modes: "&6Izvēlies spēles režīmu" - multiple-complete: "&6Cik daudz reizes?" - messages: - admin: - already-completed: "&2Šīs uzdevums jau bija izpildīts!" - challenge-created: "[challenge]&r izveidots!" - completed: "&2Tu pabeidzi uzdevumu [name] [player] vietā!" - complete-wipe: "&cCerams, ka tev ir saglabātas rezerves kopijas, jo tu tikko - iztīrīji visas šī papildinājuma datubāzes!" - hit-things: Nospiediet lietas, lai pievienotu tās nepieciešamo lietu sarakstam. - Kad izdarīts, noklikšķiniet ar peles labo pogu. - migrate-end: "&2Uzdevumu papildinājums veiksmīgi migrēts uz jauno formātu.\n" - migrate-not: "&2Visi dati ir korekti." - migrate-start: "&2Uzsāk uzdevumu papildinājuma migrāciju uz jauno formātu." - not-completed: "&2Šis uzdevums vēl nav izpildīts!" - reset: "&2Tu atiestatīji uzdevumu [name] priekš [player]!" - reset-all: "&2Visi [player] uzdevumi ir atiesatīti!" - you-added: Tu uzdevumam pievienoji [thing] - start-downloading: "&5Uzsāk Uzdevumu bibliotēkas lejupielādēšanu un uzstādīšanu." - challenge-wipe: "&cCerams, ka tev ir rezerves kopijas, jo uzdevumi un to līmeņi - ir dzēsti!" - players-wipe: "&cCerams, ka tev ir rezerves kopijas, jo papildinājuma datubāzes - ir pilnībā notīrītas!" - defaults-file-completed: defaults.json failā ir sarakstīti uzdevumi un līmeņi - no [world]! - defaults-file-overwrite: defaults.json jau existē. Tas tiek pārrakstīts. - import-challenges: Sāk importēt uzdevumus - import-levels: Sāk importēt līmeņus - import-number: Importēti [number] uzdevumi - load-add: 'Pievieno jaunu: [value]' - load-overwriting: Pārraksta "[value]" - load-skipping: '"[value]" jau ekistē - izlaiž' - name-has-completed-challenge: "&5[name] izpildīja [value] &r&5uzdevumu!" - name-has-completed-level: "&5[name] izpildīja visus uzdevumus no [value] &r&5līmeņa!" - no-levels: "&cUzmanību, nav definēti līmeņi iekš challenges.yml faila" - you-completed-challenge: "&2Tu izpildīji [value] &r&2uzdevumu!" - you-completed-level: "&2Tu izpildīji [value] &r&2līmeni!" - you-repeated-challenge: "&2Tu atkārtoji [value] &r&2uzdevumu!" - you-repeated-challenge-multiple: "&2Tu atkārtoji [value] &r&2uzdevumu [count] - reizes!" + # This part generates description text for challenges object in all GUI's. + challenge: + # The main part that generates description text. + # [description] comes from challenge.description + lore: |- + [description] + [status] + [cooldown] + [requirements] + [rewards] + # Contains a text generated inside [status] lore + status: + # Status message for completed unrepeatable challenge + completed: "&2&l Izpildīts" + # Status message that contains number of completions for unlimited repeatable challenge + completed-times: "&2 Izpildīts &7&l [number] &r&2 reizi(-es)" + # Status message that contains number of completions from max available for repeatable challenge + completed-times-of: "&2 Izpildīts &7&l [number] &r&2 no &7&l [max] &r&2 reizēm" + # Status message that indicates that max completion count reached for repeatable challenge + completed-times-reached: "&2&l Izpildīts visas &7 [max] &2 reizes" + # Contains a text generated inside [cooldown] lore + cooldown: + lore: |- + [timeout] + [wait-time] + # Text message that shows challenges timeout. + timeout: "&7&l Taimauts: &r&7 [time]" + # Text message that shows challenges wait time if it is larger than 0. + wait-time: "&c&l Jānogaida: &r&c [time]" + # Text message that replaces days if number > 1 + in-days: "[number] d " + # Text message that replaces hours if number > 1 + in-hours: "[number] h " + # Text message that replaces minutes if number > 1 + in-minutes: "[number] min " + # Text message that replaces seconds if number > 1 + in-seconds: "[number] s" + # Contains a text generated inside [requirements] lore + requirements: + lore: |- + [environment] + [type-requirement] + [permissions] + # Message that will replace [environment] placeholder if there is just a single environment. + environment-single: "&7 Limitēts iekš [environment]" + # Message that will replace [environment] placeholder if there are multiple environments. + environment-title: "&7 Limitēts iekš: " + # Message that will be added after environment-title-multiple. + environment-list: " &7 - &e [environment]" + # Message that will replace [permissions] placeholder if there is just a single permission. + permission-single: "&c Nepieciešama [permissions] atļauja" + # Message that will replace [permissions] placeholder if there are multiple permissions. + permissions-title: "&c Nepieciešamās atļaujas: " + # Message that will be added after permissions-title-multiple. + permissions-list: " &c - [permission]" + # Message that will generate for island type requirements and replace [type-requirements] + island: + lore: |- + [blocks] + [entities] + [search-radius] + [warning-block] + [warning-entity] + # Title that will be used if there are defined blocks in island challenge + blocks-title: "&7&l Nepieciešamie bloki:" + # Listing of blocks that are required on the island. + block-value: " &7 - &e [material]" + blocks-value: " &7 - &e [number] x [material]" + # Title that will be used if there are defined entities in island challenge + entities-title: "&7&l Nepieciešamās radības:" + # Listing of entities that are required on the island. + entity-value: " &7 - &e [entity]" + entities-value: " &7 - &e [number] x [entity]" + # Search radius for the blocks/entities + search-radius: |- + &7 Ne tālāk kā &e [number] + &7 metru attālumā + # Waning about block/entity removing + warning-block: "&e Bloks(-i) tiks &c dzēsti" + warning-entity: "&e Radība(-as) tiks &c dzēstas" + # Message that will generate for inventory type requirements and replace [type-requirements] + inventory: + lore: |- + [items] + [warning] + # Title that will be used if there are list of items for challenge + item-title: "&7&l Nepieciešamie Priekšmeti:" + # Listing of an item that are required multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Warning that items will be removed + warning: "&e Priekšmets(-i) tiks &c dzēsti" + # Message that will generate for other type requirements and replace [type-requirements] + other: + lore: |- + [experience] + [experience-warning] + [money] + [money-warning] + [level] + # Text for required experience + experience: "&7&l Nepieciešamā pieredze: &r&e [number]" + # Warning that experience will be reduced + experience-warning: "&e Pieredze tiks &c noņemta" + # Text for required money + money: "&7&l Nepieciešamā nauda: &r&e [number]" + # Warning that money will be reduced + money-warning: "&e Nauda tiks &c noskaitīta" + # Text for required island level + level: "&7&l Nepieciešamais salas līmenis: &r&e [number]" + # Message that will generate for statistic type requirements and replace [type-requirements] + statistic: + lore: |- + [statistic] + [warning] + # Type of statistic for multiple target counter. Target may be entity or material/block + multiple-target: "&7&l [statistic]: &r&e [number] x [target]" + # Type of statistic for single target. Target may be entity or material/block + single-target: "&7&l [statistic]: &r&e [target]" + # Type of statistic without entity/block target + statistic: "&7&l [statistic] &r&e [number]" + # Warning that statistic will be removed + warning: "&e Statistikas dati tiks &c samazināti" + # Contains a text generated inside [rewards] lore + rewards: + # [text] comes from challenge.rewardText and challenge.repeatRewardText + lore: |- + &7&l Atlīdzības: + [text] + [items] + [experience] + [money] + [commands] + # Title that will be used if there are list of items for rewards + item-title: "&7 Priekšmeti:" + # Listing of an item that are rewards multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Text for reward experience + experience: "&7 Pieredze: &r&e [number]" + # Text for reward money + money: "&7 Nauda: &r&e [number]" + # Title for commands listing: + commands-title: "&7 Komandas:" + # Command listing element + command: " &7 - &e [command]" + # This part generates description text for levels object in all GUI's. + level: + lore: |- + [text] + [status] + [waiver] + [rewards] + # Status is either challengeLevel.unlockMessage or current status of the level + status: + # Status message for completed unrepeatable challenge + completed: "&2&l Izpildīts" + # Status message that contains number of completed challenges from all challenges + completed-challenges-of: |- + &2 Izpildīti &7&l [number] &r&2 no + &7&l [max] &r&2 uzdevumiem. + # Status message for locked level + locked: "&c&l Slēgts" + # Status message for locked level that will show missing challenge count. + missing-challenges: |- + &7 Nepieciešams izpildīt vēl [number] + &7 uzdevumus, lai atvērtu šo līmeni. + # Contains a text for waiver amount to unlock next level + waiver: |- + &7&l [number] uzdevums(-i) &r&7 var + &7 palikt neizpildīti, lai atvērtu + &7 nākošo līmeni. + # Contains a text generated inside [rewards] lore + rewards: + # [text] comes from challengeLevel.rewardText + lore: |- + &7&l Atlīdzības: + [text] + [items] + [experience] + [money] + [commands] + # Title that will be used if there are list of items for rewards + item-title: "&7 Priekšmeti:" + # Listing of an item that are rewards multiple times. + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + # Text for reward experience + experience: "&7 Pieredze: &r&e [number]" + # Text for reward money + money: "&7 Nauda: &r&e [number]" + # Title for commands listing: + commands-title: "&7 Komandas:" + # Command listing element + command: " &7 - &e [command]" + # This part generates description for the Library Entry + library: + author: '&7 veidoja &e [author]' + version: '&7 Uzdevuma Versija: &e [version]' + lang: '&7 Valoda: &e [lang]' + gamemode: '&7 Priekš &e [gamemode]' + conversations: + # Prefix for messages that are send from server. + prefix: "&l&6 [BentoBox]: &r" + # List of strings that are valid for confirming input. (separated with ,) + confirm-string: "true, on, yes, confirm, y, valid, correct, jā, ja, apstiprinu, pareizi" + # List of strings that are valid for denying input. (separated with ,) + deny-string: "false, off, no, deny, n, invalid, incorrect, nē, ne, neapstiprinu, nepareizi" + # String that allows to cancel conversation. (can be only one) + cancel-string: "atcelt" + # List of strings that allows to exit conversation. (separated with ,) + exit-string: "cancel, exit, quit, atcelt, iziet" + # Message that is send to user when conversation is cancelled. + cancelled: "&c Saruna pārtraukta!" + # Message that appears when admin clicks on number editing button. + input-number: "&e Lūdzu ieraksti skaitli sarakstē!" + # Message that appears when admin clicks on seconds editing button. + input-seconds: "&e Lūdzu ieraksti sekunžu skaitu sarakstē!" + # Error message that is showed if user input a value that is not a number. + numeric-only: "&c Šis `[value]` nav skaitlis!" + # Error message that is showed if user input a number that is smaller or larger that allowed. + not-valid-value: "&c Ierakstītais skaitlis [value] nav derīgs. Skaitlis nedrīkst būt mazāks par [min] un lielāks par [max]!" + # Message that confirms user data removing. + user-data-removed: "&a Visi lietotāju dati priekš [gamemode] ir dzēsti." + # Message that asks confirmation for user data removing. + confirm-user-data-deletion: "&e Lūdzu apstiprini sarakstē (jā), ka vēlies dzēst lietotāju datus par [gamemode]." + # Message that confirms user data removing. + challenge-data-removed: "&a Visu uzdevumu dati priekš [gamemode] ir dzēsti." + # Message that asks confirmation for user data removing. + confirm-challenge-data-deletion: "&e Lūdzu apstiprini sarakstē (jā), ka vēlies dzēst uzdevumu datus par [gamemode]." + # Message that confirms user data removing. + all-data-removed: "&a Visi dati priekš [gamemode] ir dzēsti." + # Message that asks confirmation for user data removing. + confirm-all-data-deletion: "&e Lūdzu apstiprini sarakstē (jā), ka vēlies dzēst visus datus par [gamemode]." + # Message that asks user to write a name + write-name: "&e Lūdzu ieraksti sarakstē vārdu." + # Message that confirms new object creation. + new-object-created: "&a Jauns objekts priekš [gamemode] ir uztaisīts." + # Error message that sends that object cannot be created with a given name + object-already-exists: "&c Objekts ar &7 [id] &c jau eksistē. Izvēlies citu vārdu." + # Error message that sends information that challenge cannot be deployed. + invalid-challenge: "&c Uzdevums [challenge] satur nekorektus datus. Tas nevar tikt izlaists!" + # Message that confirms name changing + name-changed: "&a Nosaukums veiksmīgi mainīts." + # Message that appears after clicking + write-description: "&e Lūdzu raksti jauno aprakstu sarakstē un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful description change. + description-changed: "&a Apraksts vaiksmīgi mainīts." + # Message that appears when admin clicks on permission editing button. + write-permissions: "&e Lūdzu ieraksti nepieciešamās atļaujas sarakstē, katru savā rindā, and 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful permission updating. + permissions-changed: "&a Atļaujas veiksmīgi mainītas." + # Message that appears after clicking + write-reward-text: "&e Lūdzu ieraksti apbalvojuma tekstu un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful reward-text change. + reward-text-changed: "&a Apbalvojuma paraksts veiksmīgi mainīts." + # Message that appears after clicking + write-repeat-reward-text: "&e Lūdzu ieraksti atkārtotā apbalvojuma tekstu un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful repeat-reward-text change. + repeat-reward-text-changed: "&a Atkārtotā apbalvojuma paraksts veiksmīgi mainīts." + # Message that appears after clicking + write-reward-commands: "&e Lūdzu ieraksti apbalvojuma komandas katru savā rindā, un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful commands-text change. + reward-commands-changed: "&a Apbalvojuma komandas veiksmīgi mainīts." + # Message that appears after clicking + write-repeat-reward-commands: "&e Lūdzu ieraksti atkārtotā apbalvojuma komandas katru savā rindā, un 'iziet' jaunā rindā, lai beigtu." + # Message that appears after successful repeat-commands-text change. + repeat-reward-commands-changed: "&a Atkārtotā apbalvojuma komandas veiksmīgi mainīts." + # Message that confirms user data removing. + challenge-removed: "&a Uzdevums [challenge] priekš [gamemode] ir dzēsts no datubāzes." + # Message that asks confirmation for user data removing. + confirm-challenge-deletion: "&e Lūdzu apstiprini (jā), ka vēlies dzēst [challenge] priekš [gamemode] no datubāzes." + # Message that confirms user data removing. + level-removed: "&a Līmenis [level] priekš [gamemode] ir dzēsts no datubāzes." + # Message that asks confirmation for user data removing. + confirm-level-deletion: "&e Lūdzu apstiprini (jā), ka vēlies dzēst [level] priekš [gamemode] no datubāzes." + # Message that appears when user clicks on library installation. + start-downloading: "&a Sāk lejupielādēt Uzdevumu Bibliotēku." + # Message that appears when writing multiline text. + written-text: "&a Ievadītais teksts:" + # Message that appears after importing library data into database. + confirm-data-replacement: "&e Lūdzu apstiprini (jā), ka vēlies dzēst esošos uzdevumus un aizstāt ar jauniem." + # Message that appears after successful data importing + new-challenges-imported: "&a Jaunie Uzdevumi ir veiksmīgi ielādēti priekš [gamemode]." + # Message that appears after admin clicks on database exporting button. + exported-file-name: "&e Lūdzu ieraksti eksportētā faila nosaukumu. (ieraksti 'atcelt', lai izietu)" + # Message that appears after successful database exporting to file. + database-export-completed: "&a Veiksmīgi eksportēti uzdevumi no [world]. Fails [file] ir izveidots." + # Message that appears if input file name is already taken. + file-name-exist: "&c Faila nosaukums '[id]' jau eksistē. Nevar pārrakstīt." + # Message that asks for search value input. + write-search: "&e Lūdzu ievadi meklēšanas vērtību. (vai 'atcelt', lai izietu)" + # Message that appears after updating search value. + search-updated: "&a Meklēšanas vērtība atjaunota." + titles: - challenge-subtitle: "[friendlyName]" - challenge-title: Veiksmīgi izpildīts uzdevums - level-subtitle: "[friendlyName]" - level-title: Veiksmīgi pabeigts līmenis -meta: - authors: - - BONNe + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. + # [friendlyName] will be replaced with challenge friendly name. + # [level] will be replaced with level friendly name. + # [rewardText] will be replaced with the challenge reward text. + challenge-title: 'Veiksmīgi Izpildīts' + challenge-subtitle: '[friendlyName]' + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. + # [friendlyName] will be replaced with level friendly name. + # [rewardText] will be replaced with the level reward text. + level-title: 'Veiksmīgi Izpildīts' + level-subtitle: '[friendlyName]' + + messages: + completed: '&2 Tu izpildīji uzdevumu [name] priekš [player]!' + already-completed: '&2 Šis uzdevums jau bija izpildīts!' + reset: '&2 Tu atjaunoji uzdevumu [name] priekš [player]!' + reset-all: '&2 Visi spēlētāja [player] uzdevumi ir atjaunoti!' + not-completed: '&2 Šis uzdevums vēl nav izpildīts!' + migrate-start: '&2 Sāk migrēt papildinājuma datus.' + migrate-end: '&2 Papildinājuma dati ir migrēti.' + migrate-not: '&2 Visi dati ir derīgi.' + start-downloading: '&5 Sāk lejupielādēt un importēt uzdevumus.' + you-completed-challenge: '&2 Tu izpildīji [value] &r&2 uzdevumu!' + you-repeated-challenge: '&2 Tu atkārtoti izpildīji [value] &r&2 uzdevumu!' + you-repeated-challenge-multiple: '&2 Tu atkārtoji [value] &r&2 uzdevumu [count] reizes!' + you-completed-level: '&2 Tu pabeidzi [value] &r&2 līmeni!' + name-has-completed-challenge: '&5 [name] pabeidza [value] &r&5 uzdevumu!' + name-has-completed-level: '&5 [name] pabeidza [value] &r&5 līmeni!' + load-skipping: '"[value]" jau eksistē - izlaiž' + load-overwriting: 'Pārraksta "[value]"' + load-add: 'Pievieno jaunu objektu: [value]' + errors: + no-name: '&c Trūkst uzdevuma nosaukums' + unknown-challenge: '&c Nezināms uzdevums' + not-valid-integer: |- + &c Ievadītais skaitlis "[value]" nau darīgs! Vērtībai jābūt no [min] līdz [max]. + not-deployed: '&c Uzdevums nav pieejams!' + not-on-island: '&c Tev jābūt uz salas, lai veiktu šo darbību!' + challenge-level-not-available: '&c Tu neesi atvēriz nepieciešamo līmeni, lai izpildītu šo uzdevumu.' + not-repeatable: '&c Šis uzdevums nav atkārtojams!' + wrong-environment: '&c Tu neesi pareizajā vidē!' + not-enough-items: '&c Tev nav pietiekoši daudz [items], lai pabeigtu uzdevumu!' + not-close-enough: '&c Tev tuvumā nav pietiekoši daudz nepieciešamie bloki vai radības.' + you-still-need: '&c Tev vēl nepieciešams [amount] x [item] lietas' + missing-addon: '&c Nav iespējams izpildīt šo uzdevumu: trūkst nepieciešamais papildinājums vai plugins.' + incorrect: '&c Nav iespējams izpildīt uzdevumu. Prasības nav korektas.' + not-enough-money: '&c Tev ir nepieciešama [value] nauda kontā, lai izpildītu uzdevumu!' + not-enough-experience: '&c Tev ir nepieciešama [value] pieredze, lai izpildītu uzdevumu!' + island-level: '&c Tavai salai jāsasniedz [number] līmeni, lai izpildītu uzdevumu!' + no-load: '&c Kļūda: Nevar ielādēt failu. [message]' + load-error: '&c Kļūda: Nevar ielādēt [value].' + no-rank: "&c Tavs ranks nav pietiekoši augsts, lai veiktu šo darbību." + cannot-remove-items: '&c Dažus priekšmetus neizdevās izņemt no tava inventāra!' + exist-challenges-or-levels: '&c Šajā pasaulē jau existē uzdevumi, nevar turpināt!' + no-challenges: '&c Uzdevumi nav izveidoti šajā pasaulē!' + no-challenges-admin: '&c Uzdevumi nav izveidoti šajā pasaulē! Izmanto &5 /[command], &c lai tos pievienotu!' + missing-arguments: '&c Pietrūkst komandas parametri.' + no-multiple-permission: "&c Tev nav atļaujas, lai pildītu uzdevumu vairākas reizes." + invalid-level: "&c Līmenis [level] satur nekorektus datus. Tas var tikt ielādēts nekorekti!" + invalid-challenge: "&c Uzdevums [challenge] satur nekorektus datus. Tas var tikt ielādēts nekorekti!" + no-library-entries: "&c Nevar atrast bibliotēkas ierakstus. Nav ko rādīt." + not-hooked: "&c Uzdevumu Papildinājumam neizdevās atrast Spēles Režīmu." + timeout: "&c Šim uzdevumam ir uzstādīts [timeout] taimauts starp izpildēm. Tev vēl ir jāgaida [wait-time], lai varētu pildīt uzdevumu." +# # Showcase for manual material translation +# materials: +# # Names should be lowercase. +# cobblestone: "Mūrakmens" +# # Also supports descriptions. +# stone: +# name: "Akmens" +# description: "" +# item-stacks: +# # Non-specific item meta translations. +# # TYPE is the item type +# # META is a content of item meta. +# generic: "[type] [meta]" +# # Non-specific meta translations. Will replace [meta] +# meta: +# upgraded: "Uzlabots" +# extended: "Pagarināts" +# potion-meta: "&e [type] [upgraded] [extended]" +# # Be aware, enchants are always listed below item in separate line. +# enchant-meta: " &7 - &e [type] [level]" +# skull-meta: ": &e [player-name]" +# book-meta: "&e [title] [author]" +# # Custom Enchantment Translation. +# enchant: +# menting: "Lāpīšana" +# unbreaking: "Nelūztošs" +# # Custom Potion Translation. +# potion-effect: +# water_breathing: "Zemūdens Elpošanas" +# # You can also create specific item translations +# # Like translate all potions. +# potion: +# # This will overwrite generic translation. +# name: "[type] [upgraded] [extended]" +# # Type is either specific translation or potion effect. +# water_breathing: "Zemūdens Elpošanas Dzira" +# stone_shovel: +# # This will mean that only stone shovels will not show +# # meta information. +# name: "[type]" protection: flags: CHALLENGES_ISLAND_PROTECTION: - description: "&5&oPārslēdz kurš var|&5&opildīt uzdevumus" + description: |- + &5&o Pārslēdz kurš var + &5&o pildīt uzdevumus name: Uzdevumu izpildes aizsardzība CHALLENGES_WORLD_PROTECTION: description: |- - &5&oĻauj pārslēgt vai|&5&ospēlētājam ir nepieciešams - &5&obūt uz salais, - &5&olai pildītu uzdevumus. + &5&o Ļauj pārslēgt vai + &5&o spēlētājam ir nepieciešams + &5&o būt uz salais, + &5&o lai pildītu uzdevumus. hint: Uzdevumus nevar pildīt ārpus salas name: Uzdevumu salas ierobežosāna -version: 11 +version: 12 diff --git a/src/main/resources/locales/pl.yml b/src/main/resources/locales/pl.yml index 46a7590..d4b829c 100644 --- a/src/main/resources/locales/pl.yml +++ b/src/main/resources/locales/pl.yml @@ -45,10 +45,10 @@ challenges: gui: title: admin: - gui-title: "& aChallenges Administrator" + gui-title: "&a Challenges Administrator" edit-challenge-title: " edytuj wyzwanie" edit-level-title: Poziom edycji - settings-title: "&aEdytuj ustawienia" + settings-title: "&a Edytuj ustawienia" choose-challenge-title: " Wybierz wyzwanie" choose-level-title: i a Wybierz poziom choose-user-title: Wybierz odtwarzacz @@ -64,11 +64,11 @@ challenges: edit-text-fields: i edytuj pola tekstowe library-title: i a Biblioteki do pobrania lore-add: i Dodaj element wiedzy - lore-remove: "& aUsuń element wiedzy" + lore-remove: "&a Usuń element wiedzy" lore-edit: i edytuj Lore type-select: i a Wybierz typ wyzwania challenges: I 6 Wyzwania - game-modes: "& 6 Wybierz GameMode" + game-modes: "&6 Wybierz GameMode" multiple-complete: I 6 Ile razy? buttons: admin: @@ -132,8 +132,8 @@ challenges: library: Biblioteka internetowa download: Pobierz biblioteki type: - island: "& 6 Typ wyspy" - inventory: "& 6 Typ zapasów" + island: "&6 Typ wyspy" + inventory: "&6 Typ zapasów" other: I 6 Inne typy import: Importuj wyzwania ASkyblock settings: Edytuj ustawienia @@ -270,8 +270,8 @@ challenges: mode-in-world: Gracze w świecie GameMode. mode-with-island: Gracze, którzy mają wyspę w świecie GameMode. visibility-mode: Pokaż / ukryj niewykorzystane wyzwania. - edit-text-line: "& 6 Edytuj wiadomość tekstową!" - add-text-line: "& 6 Dodaj nową wiadomość tekstową!" + edit-text-line: "&6 Edytuj wiadomość tekstową!" + add-text-line: "&6 Dodaj nową wiadomość tekstową!" title-enable: Włącz / wyłącz wiadomość tytułową, która będzie wyświetlana graczom po ukończeniu wyzwania. title-showtime: Zmień, jak długo wiadomości tytułowe będą widoczne dla odtwarzacza. @@ -450,17 +450,17 @@ challenges: the-end: "- Koniec" nether: "- Nether" normal: "- Overworld" - entity: "- [podmiot]: [liczba]" - block: "- [blok]: [liczba]" - permission: "- [pozwolenie]" - item: "- [liczba] x [pozycja]" + entity: "- [entity]: [count]" + block: "- [block]: [count]" + permission: "- [permission]" + item: "- [count] x [item]" item-meta: "([meta])" - item-enchant: "- [enchant] [poziom]" - command: "- [Komenda]" - level-unlocked: Kliknij, aby zobaczyć wyzwania [poziomu]! - level-locked: Ukończ [liczyć] więcej [poziomów] wyzwań, aby odblokować ten poziom! - increase-by: "& aZwiększ liczbę ukończeń o [wartość]" - reduce-by: "& c Zmniejsz liczbę ukończeń o [wartość]" + item-enchant: "- [enchant] [level]" + command: "- [command]" + level-unlocked: Kliknij, aby zobaczyć wyzwania [level]! + level-locked: Ukończ [count] więcej [level] wyzwań, aby odblokować ten poziom! + increase-by: "&a Zwiększ liczbę ukończeń o [value]" + reduce-by: "&c Zmniejsz liczbę ukończeń o [value]" visibility: hidden: Widoczne są tylko wdrożone wyzwania. visible: Wszystkie wyzwania są widoczne dla wszystkich @@ -469,63 +469,63 @@ challenges: island: i zdobywaj bloki lub moby wokół gracza other: i pytaj o rzeczy z innych wtyczek / dodatków inventory: i zdobywaj przedmioty w ekwipunku gracza - current-value: "&6Aktualna wartość e: [value]." + current-value: "&6 Aktualna wartość e: [value]." challenge-description: completed-times-of: Ukończone [donetimes] z [maxtimes] maxed-reached: Ukończone [donetimes] z [maxtimes] completed-times: Ukończone [donetimes] - objects-close-by: "& cWszystkie wymagane bloki i byty muszą znajdować się + objects-close-by: "&c Wszystkie wymagane bloki i byty muszą znajdować się blisko ciebie na twojej wyspie!" - warning-entities-kill: "& c Wszystkie wymagane jednostki zostaną zabite + warning-entities-kill: "&c Wszystkie wymagane jednostki zostaną zabite po ukończeniu tego wyzwania!" - warning-blocks-remove: "& cWszystkie wymagane bloki zostaną usunięte po + warning-blocks-remove: "&c Wszystkie wymagane bloki zostaną usunięte po ukończeniu tego wyzwania!" - not-repeatable: "& c To wyzwanie nie jest powtarzalne!" - experience-reward: "& 6Exp nagroda: [wartość]" - money-reward: "& Nagroda pieniężna: $ [wartość]" - required-experience: "& 6 Wymagany exp: [wartość]" - required-money: 'I 6 Wymagane pieniądze: [wartość]' - required-island-level: "& 6 Wymagany poziom wyspy: [wartość]" + not-repeatable: "&c To wyzwanie nie jest powtarzalne!" + experience-reward: "&6 Exp nagroda: [value]" + money-reward: "& Nagroda pieniężna: $[value]" + required-experience: "&6 Wymagany exp: [value]" + required-money: 'I 6 Wymagane pieniądze: [value]' + required-island-level: "&6 Wymagany poziom wyspy: [value]" environment: 'Wymagane środowiska:' reward-items: 'I 6 przedmiotów dodatkowych:' - reward-commands: "& 6 Polecenia dodatkowe:" + reward-commands: "&6 Polecenia dodatkowe:" required-items: 'Wymagane rzeczy:' required-entities: 'Wymagane podmioty:' required-blocks: 'Wymagane bloki:' - level: "& fLevel: [poziom]" + level: "&f Level: [level]" completed: i b Ukończone - warning-items-take: "& c Wszystkie wymagane przedmioty są pobierane z ekwipunku + warning-items-take: "&c Wszystkie wymagane przedmioty są pobierane z ekwipunku po ukończeniu tego wyzwania!" rewards-title: "& Nagrody:" level-description: - experience-reward: "& 6Exp nagroda: [wartość]" - money-reward: "& Nagroda pieniężna: $ [wartość]" + experience-reward: "&6 Exp nagroda: [value]" + money-reward: "& Nagroda pieniężna: $ [value]" reward-items: 'I 6 przedmiotów dodatkowych:' - reward-commands: "& 6 Polecenia dodatkowe:" - waver-amount: I 6 wyzwań [wartość] można pominąć, aby odblokować następny poziom. + reward-commands: "&6 Polecenia dodatkowe:" + waver-amount: I 6 wyzwań [value] można pominąć, aby odblokować następny poziom. completed: i b Ukończone - completed-challenges-of: "& 3 Ukończyłeś [liczbę] spośród [maks.] Wyzwań + completed-challenges-of: "&3 Ukończyłeś [number] spośród [max] Wyzwań na tym poziomie." item-description: - item: "- [liczba] x [pozycja]" + item: "- [count] x [item]" item-meta: "([meta])" - item-enchant: "- [enchant] [poziom]" - item-name: "[imię]" + item-enchant: "- [enchant] [level]" + item-name: "[name]" item-lore: 'Przedmiot:' - book-meta: "[tytuł] autor: [autor]" - recipe-count: "[liczba] przepisów" - armor-color: "[kolor]" - potion-type-extended-upgraded: Rozszerzone i zaktualizowane [nazwa] - potion-type-upgraded: Ulepszony [nazwa] - potion-type-extended: Rozszerzone [nazwa] - potion-type: "[imię]" + book-meta: "[title] autor: [author]" + recipe-count: "[count] przepisów" + armor-color: "[color]" + potion-type-extended-upgraded: Rozszerzone i zaktualizowane [name] + potion-type-upgraded: Ulepszony [name] + potion-type-extended: Rozszerzone [name] + potion-type: "[name]" custom-effects: 'Efekty niestandardowe:' - potion-effect: "[efekt] x [wzmacniacz] dla [czas trwania] t" - skull-owner: "[właściciel]" - egg-meta: "[tłum]" + potion-effect: "[effect] x [amplifier] dla [duration] t" + skull-owner: "[owner]" + egg-meta: "[mob]" fish-meta: "[body-color] with [pattern-color] [pattern]" questions: - prefix: "& 2 [SERWER]:" + prefix: "&2 [SERWER]:" admin: number: Wpisz liczbę na czacie i naciśnij enter. challenge-name: Wpisz nazwę wyświetlaną na czacie dla bieżącego wyzwania. @@ -533,104 +533,101 @@ challenges: unique-id: Wpisz unikalny identyfikator obiektu i naciśnij klawisz Enter. titles: challenge-title: Zakończone sukcesem - challenge-subtitle: "[przyjazne imię]" + challenge-subtitle: "[friendlyName]" level-title: Zakończone sukcesem - level-subtitle: "[przyjazne imię]" + level-subtitle: "[friendlyName]" messages: admin: you-added: 'Dodałeś do wyzwań ' - challenge-created: "[wyzwanie]&r stwórz" - completed: "&2Ukończył wyzwanie [name] [player]!" + challenge-created: "[challenge]&r stwórz" + completed: "&2 Ukończył wyzwanie [name] [player]!" already-completed: "&2 Ukończyłeś to wyzwanie!" - reset: "&2Zresetowałeś wyzwanie [name] dla [player]!" - reset-all: "& 2 Wszystkie wyzwania [gracza] zostały zresetowane!" + reset: "&2 Zresetowałeś wyzwanie [name] dla [player]!" + reset-all: "&2 Wszystkie wyzwania [player] zostały zresetowane!" not-completed: |- - &2To wyzwanie nie zostało ukończone! + &2 To wyzwanie nie zostało ukończone! Sprawdz czy wszystko wykonałeś poprawnie - migrate-start: "&2Rozpoczęto migrację wyzwań addon data." - migrate-not: "&2Wszystkie dane są poprawne." - start-downloading: "& 5 Rozpoczęcie pobierania i importowania biblioteki + migrate-start: "&2 Rozpoczęto migrację wyzwań addon data." + migrate-not: "&2 Wszystkie dane są poprawne." + start-downloading: "&5 Rozpoczęcie pobierania i importowania biblioteki wyzwań." - migrate-end: "& 2 Wyzwania dotyczące danych dodatkowych zaktualizowano do + migrate-end: "&2 Wyzwania dotyczące danych dodatkowych zaktualizowano do nowego formatu." hit-things: Kliknij rzeczy, aby dodać je do listy wymaganych rzeczy. Po zakończeniu kliknij prawym przyciskiem myszy. - complete-wipe: "& c Mam nadzieję, że masz kopie zapasowe, ponieważ właśnie + complete-wipe: "&c Mam nadzieję, że masz kopie zapasowe, ponieważ właśnie skasowałeś wszystkie bazy danych Wyzwań Addon!" challenge-wipe: I c Mam nadzieję, że masz kopie zapasowe, ponieważ właśnie skasowałeś wszystkie Wyzwania i ich poziomy! - players-wipe: "& c Mam nadzieję, że masz kopie zapasowe, ponieważ po prostu + players-wipe: "&c Mam nadzieję, że masz kopie zapasowe, ponieważ po prostu usuwasz wszystkie ukończone wyzwania gracza!" - you-completed-challenge: "& 2Ukończono [wartość] & r & 2 wyzwanie!" - you-repeated-challenge: "& 2 Powtórzyłeś [wartość] & r & 2 wyzwanie!" - you-repeated-challenge-multiple: "& 2 Powtórzyłeś [wartość] & r & - 2challenge [liczba] razy!" - you-completed-level: "& 2Ukończono [wartość] & r & 2 poziom!" - name-has-completed-challenge: "& 5 [nazwa] zakończyła [wartość] & r & - 5 wyzwanie!" - name-has-completed-level: "& 5 [nazwa] uzupełniła [wartość] & r & - 5 poziom!" + you-completed-challenge: "&2 Ukończono [value] &r&2 wyzwanie!" + you-repeated-challenge: "&2 Powtórzyłeś [value] &r&2 wyzwanie!" + you-repeated-challenge-multiple: "&2 Powtórzyłeś [value] &r&2 challenge [count] razy!" + you-completed-level: "&2 Ukończono [value] &r&2 poziom!" + name-has-completed-challenge: "&5 [name] zakończyła [value] &r&5 wyzwanie!" + name-has-completed-level: "&5 [name] uzupełniła [value] &r&5 poziom!" import-levels: Rozpocznij importowanie poziomów import-challenges: Rozpocznij importowanie wyzwań no-levels: 'Ostrzeżenie: Brak poziomów zdefiniowanych w challenge.yml' - import-number: Zaimportowane [liczba] wyzwań + import-number: Zaimportowane [count] wyzwań load-skipping: '"[value]" Istnieje -' - load-overwriting: Nadpisywanie „[wartość]” - load-add: 'Dodanie nowego obiektu: [wartość]' + load-overwriting: Nadpisywanie „[value]” + load-add: 'Dodanie nowego obiektu: [value]' defaults-file-overwrite: defaults.json istnieje. zostanie nadpisany. - defaults-file-completed: Plik defaults.json jest wypełniony wyzwaniami z [świata]! + defaults-file-completed: Plik defaults.json jest wypełniony wyzwaniami z [world]! errors: no-name: "&cNieprawidłowa nazwa wyzwania" unknown-challenge: "&cNieznane wyzwanie" - unique-id: "& cUniqueID „[id]” jest nieprawidłowy." - wrong-icon: "& cDany materiał „[wartość]” jest nieprawidłowy i nie może być + unique-id: "&c UniqueID „[id]” jest nieprawidłowy." + wrong-icon: "&c Dany materiał „[value]” jest nieprawidłowy i nie może być używany jako ikona." - not-deployed: "& cChallenge nie został wdrożony!" + not-deployed: "&c Challenge nie został wdrożony!" not-on-island: "&cMusisz być na swojej wyspie by to zrobic!" not-repeatable: "&cTo wyzwanie możesz wykonać tylko raz!" - not-enough-items: "&cNie posiadasz wystarczająco [przedmiotów] do zakończenia + not-enough-items: "&cNie posiadasz wystarczająco [items] do zakończenia tego wyzwania!" not-close-enough: "&cmusisz stać w środku [number] przy wszystkich wymaganych blokach." you-still-need: "&cPotrzebujesz nadal [amount] x [item]" - not-enough-money: "&cAby ukończyć wyzwanie, musisz mieć [walutę] na swoim koncie." - import-no-file: "& cNie mogłem znaleźć pliku challenge.yml do importowania!" - no-load: "&cBłąd: Nie można załadować challenges.yml. [wiadomość]" - load-error: "& cError: Nie można załadować [wartość]." - defaults-file-exist: "& cdefaults.json już istnieje. Użyj trybu zastępowania, + not-enough-money: "&cAby ukończyć wyzwanie, musisz mieć [value] na swoim koncie." + import-no-file: "&c Nie mogłem znaleźć pliku challenge.yml do importowania!" + no-load: "&cBłąd: Nie można załadować challenges.yml. [message]" + load-error: "&c Error: Nie można załadować [value]." + defaults-file-exist: "&c defaults.json już istnieje. Użyj trybu zastępowania, aby go zastąpić!" - defaults-file-error: "& c Wystąpił błąd podczas tworzenia pliku defaults.json! + defaults-file-error: "&c Wystąpił błąd podczas tworzenia pliku defaults.json! Sprawdź konsolę!" - missing-arguments: "& cCommand brakuje argumentów." - wrong-environment: "& c Jesteś w złym środowisku!" - missing-addon: "& cNie można ukończyć wyzwania: brakuje wymaganego dodatku + missing-arguments: "&c Command brakuje argumentów." + wrong-environment: "&c Jesteś w złym środowisku!" + missing-addon: "&c Nie można ukończyć wyzwania: brakuje wymaganego dodatku lub wtyczki." - exist-challenges-or-levels: "& cChallenges już istnieją w twoim świecie. Nie + exist-challenges-or-levels: "&c Challenges już istnieją w twoim świecie. Nie można kontynuować!" - no-challenges: "& cChallenges nie są jeszcze zaimplementowane na tym świecie!" - no-challenges-admin: "& cChallenges nie są jeszcze zaimplementowane na tym - świecie! Użyj & 5 / [polecenie] i c, aby je dodać!" - missing-level: "& cChallenge Poziom [poziom] nie jest zdefiniowany w bazie + no-challenges: "&c Challenges nie są jeszcze zaimplementowane na tym świecie!" + no-challenges-admin: "&c Challenges nie są jeszcze zaimplementowane na tym + świecie! Użyj &5 /[command] i c, aby je dodać!" + missing-level: "&c Challenge Poziom [level] nie jest zdefiniowany w bazie danych. Może to powodować błędy!" - no-multiple-permission: "& c Nie masz uprawnień do wielokrotnego wykonania + no-multiple-permission: "&c Nie masz uprawnień do wielokrotnego wykonania tego wyzwania jednocześnie." - not-a-integer: "& c Podana wartość „[wartość]” nie jest liczbą całkowitą!" - challenge-level-not-available: "& c Nie odblokowałeś wymaganego poziomu, aby + not-a-integer: "&c Podana wartość „[value]” nie jest liczbą całkowitą!" + challenge-level-not-available: "&c Nie odblokowałeś wymaganego poziomu, aby ukończyć to wyzwanie." - incorrect: "& cNie można ukończyć wyzwania: Wymagania są niepoprawne." - not-enough-experience: "& c Konieczne jest posiadanie [wartość] EXP, aby ukończyć + incorrect: "&c Nie można ukończyć wyzwania: Wymagania są niepoprawne." + not-enough-experience: "&c Konieczne jest posiadanie [value] EXP, aby ukończyć to wyzwanie." - island-level: "& cTa wyspa musi mieć poziom [liczba] lub wyższy, aby ukończyć + island-level: "&c Ta wyspa musi mieć poziom [count] lub wyższy, aby ukończyć to wyzwanie!" - no-rank: "& c Nie masz wystarczająco wysokiej rangi, aby to zrobić." - cannot-remove-items: "& c Niektórych przedmiotów nie można usunąć z ekwipunku!" + no-rank: "&c Nie masz wystarczająco wysokiej rangi, aby to zrobić." + cannot-remove-items: "&c Niektórych przedmiotów nie można usunąć z ekwipunku!" not-valid-integer: |- &c Podana liczba całkowita "[value]"jest nieprawidłowa wartość powinna być między [min] i [max]. - invalid-level: "& c Poziom [poziom] zawiera nieprawidłowe dane. Nie zostanie załadowany + invalid-level: "&c Poziom [level] zawiera nieprawidłowe dane. Nie zostanie załadowany z bazy danych!" - invalid-challenge: "& c Wyzwanie [wyzwanie] zawiera nieprawidłowe dane. Nie zostanie + invalid-challenge: "&c Wyzwanie [challenge] zawiera nieprawidłowe dane. Nie zostanie załadowany z bazy danych!" protection: flags: @@ -643,11 +640,10 @@ protection: name: Wyzwania Ograniczenia wyspy hint: Żadnych wyzwań poza wyspą description: |- - & 5 i o Włącz / wyłącz + &5 i o Włącz / wyłącz Oraz 5 i wymagania dla graczy do I 5 i przestrzegaj ich wyspy I 5 i ukończ wyzwanie. version: 11 meta: - authors: - - BONNe + authors: [] diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index 690e0e9..0c2d673 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -1,471 +1,582 @@ ---- -challenges: - commands: - admin: - complete: - parameters: " " - description: 通过指令完成挑战 - defaults: - description: 显示导入/导出插件自带挑战的子指令 - parameters: "[command]" - import: - parameters: "[overwrite]" - description: 从 challenges.yml 文件中导入挑战|参数覆盖意味着具有相同 ID 的挑战或等级将被覆盖。 - main: - description: 打开管理员菜单 - reload: - description: 重载挑战组件 - parameters: "[hard]" - show: - description: 在聊天中列出当前世界的所有挑战 - defaults-import: - description: 导入系统自带挑战 - defaults-generate: - description: 将现有的挑战导出到default.json文件中 - parameters: "[overwrite] - 允许覆盖已有文件" - reset: - description: 通过指令重置玩家挑战。若 "challenge_id" 参数设为 all,则将重置该玩家的所有挑战。 - parameters: " " - migrate: - description: 将参考当前游戏模式世界的挑战数据迁移到新的0.8.0存储格式。 - user: - complete: - description: 通过指令完成挑战 - parameters: " [count]" - main: - description: 打开挑战菜单 - errors: - cannot-remove-items: "&c有些物品无法从你的背包中删除!" - challenge-level-not-available: "&c你没有解锁挑战级别来完成这个挑战。" - defaults-file-error: "&c创建defaults.json文件时出错! 检查控制台!" - defaults-file-exist: "&cdefaults.json已经存在。 使用覆盖模式替换它!" - exist-challenges-or-levels: "&c在你的世界里已经存在挑战。 无法继续!" - import-no-file: "&c找不到challenge.yml文件导入!" - incorrect: "&c无法完成挑战。 要求不正确。" - island-level: "&c你的岛屿等级需要达到[number]才能完成挑战" - load-error: "&c错误:无法加载 [value]." - missing-addon: "&c无法完成挑战。 缺少必需的插件或组件。" - missing-arguments: "&c命令缺少参数." - missing-level: "&c挑战级别[level]未在数据库中定义. 可能会出现错误!" - no-challenges: "&c当前世界没有实施挑战!" - no-challenges-admin: "&c当前世界没有实施挑战!你应该使用 &5/[label] challenges &c添加他们!" - no-load: "&c错误:无法加载challenge.yml。 [message]" - no-name: "&c缺少挑战名称" - no-rank: "&c你没有等级可以做到这一点." - not-a-integer: "&c给定值“[value]”不是整数!" - not-close-enough: "&c你必须站在拥有[number]个任务需求方块的旁边." - not-deployed: "&c未开启挑战!" - not-enough-items: "&c你没有足够的[items]来完成这个挑战!" - not-enough-money: "&c您的帐户必须有[value]金钱才能完成挑战。." - not-on-island: "&c你必须在你的岛上做到这一点!" - not-repeatable: "&c这个挑战是不可重复的!" - not-valid-integer: "&c给定整数“[value]”无效!|值应介于[min]和[max]之间。" - unique-id: '&cUniqueID "[id]" 无效.' - unknown-challenge: "&c未知挑战" - wrong-environment: "&c你在错误的环境中!" - wrong-icon: "&c给定材料“[value]”无效且不能用作图标。" - you-still-need: "&c你还需要 [amount] x [item]" - not-enough-experience: "&c必须有[value]经验才能完成挑战" - no-multiple-permission: "&c你无权一次完成挑战多次" - gui: - buttons: - admin: - accept: 接受 - add: 添加 - add-challenge: 添加挑战 - broadcast: 成功任务后发出广播 - cancel: 取消 - challenge-lore: 挑战介绍 - challenges: 挑战 - clear: 清空 - complete: 完成某个玩家的挑战 - complete-wipe: 抹除插件数据库! - create-challenge: 创建新的挑战 - create-level: 创建新的挑战等级 - decline: 拒绝 - default-export: 导出现有挑战 - default-import: 导入默认挑战 - default-locked-icon: 等级图标锁 - delete-challenge: 删除挑战 - delete-level: 删除挑战等级 - deployment: 是否开启该挑战 - description: 介绍 - edit-challenge: 编辑挑战 - edit-level: 编辑挑战等级 - environment: 环境 - free-at-top: 免费挑战优先排列 - glow: 完成任务后发光 - gui-mode: 挑战GUI - gui-view-mode: 显示所有GameModes - history-store: 挑战历史 - icon: 图标 - increase: "+" - input: 输入 - input-mode: 切换输入模式 - level-lore: 等级介绍 - locked-icon: 图标锁 - multiply: "*" - name: 挑战名称 - number: "[number]" - order: 顺序 - properties: 属性 - reduce: "-" - remove-blocks: 完成任务后是否删除方块 - remove-challenge: 删除挑战 - remove-completed: 完成任务后删除图标 - remove-empty: 删除空栏 - remove-entities: 完成任务后是否杀死实体 - remove-experience: 完成任务后是否删除经验值 - remove-items: 完成任务后是否删除物品 - remove-money: 完成任务后是否删除金钱 - remove-on-complete: 完成后删除该挑战 - remove-selected: 删除选定 - repeatable: 是否可重复挑战 - repeat-count: 最大时间 - repeat-reward-commands: 重复指令奖励 - repeat-reward-experience: 重复经验奖励 - repeat-reward-items: 重复物品奖励 - repeat-reward-money: 重复金钱奖励 - repeat-reward-text: 重复奖励信息 - required-blocks: 检测的方块 - required-entities: 检测的实体 - required-experience: 需要的经验值 - required-items: 需要的物品 - required-level: 需要的岛屿等级 - required-money: 需要的金钱 - required-permissions: 需要的权限 - requirements: 要求 - reset: 重置某个玩家的挑战 - reset-on-new: 在新的岛屿上重置 - reward-commands: 指令奖励 - reward-experience: 经验奖励 - reward-items: 物品奖励 - reward-money: 金钱奖励 - rewards: 奖励 - reward-text: 奖励信息 - save: 保存 - search-radius: 搜索半径 - set: "=" - settings: 编辑设定 - show-eggs: 切换视图模式 - title-enable: 显示标题 - title-showtime: 标题显示时间 - toggle-user-list: 用户列表 - value: 值 - waiver-amount: 豁免金额 - import: 导入ASkyBlock挑战 - line-length: 物品Lore行长度 - history-lifespan: 历史生命周期 - island-store: 逐岛存储 - library: 网络库 - download: 已下载的挑战库 - challenge-wipe: 清空挑战数据库 - players-wipe: 清空用户数据库 - visibility-mode: 挑战可见性模式 - type: - island: "&6岛屿类型" - inventory: "&6物品栏类型" - other: "&6其他类型" - next: 下一页 - previous: 上一页 - return: 返回上一级 - value: 完成 - increase: 增加 - reduce: 减少 - challenge-description: - completed: "&B已完成" - completed-times: 已完成 [donetimes] - completed-times-of: '完成次数: [donetimes] 上限: [maxtimes]' - environment: '所需实体:' - experience-reward: "&6经验奖励: [value]" - level: "&F挑战级别: [level]" - maxed-reached: '完成次数: [donetimes] 上限: [maxtimes]' - money-reward: "&6金钱奖励: $[value]" - not-repeatable: "&c该挑战不可重复!" - objects-close-by: "&c任务需求的方块/生物不能离你太远!(超出侦测范围)" - required-blocks: '所需方块:' - required-entities: '所需实体:' - required-experience: "&6所需经验: [value]" - required-island-level: "&6所需岛屿等级: [value]" - required-items: '所需物品:' - required-money: "&6所需金钱: $[value]" - reward-commands: "&6指令奖励:" - reward-items: "&6物品奖励:" - warning-blocks-remove: "&c完成此挑战后,该挑战需要的方块将会被清空" - warning-entities-kill: "&c完成此挑战后,该挑战需要的生物将会被清空" - warning-items-take: "&c完成此挑战后,该挑战的需要物品将会被清空" - descriptions: - admin: - add-challenge: 将现有挑战添加到当前挑战级别 - add-text-line: "&6 添加新的文本!" - broadcast: 允许/禁止,当玩家完成第一次挑战后向全服玩家广播 - cancel: 取消任何操作并返回上一级菜单 - challenge-lore: 修改挑战介绍的哪些目标是可见的 - challenges: 管理挑战级别 (添加/删除). - click-to-edit: "&4点击此处编辑输入." - complete: 为某个玩家完成某个挑战|玩家无法获得完成奖励。 - complete-wipe: 清空插件数据库中的所有挑战,包括玩家的挑战数据! - create-challenge: 添加新挑战。|默认情况下,它将在免费挑战列表中。 - create-level: 添加新的挑战等级。 - default-export: 将现有的挑战导出至 defaults.json 文件中. - default-import: 导入插件自带挑战 - default-locked-icon: 更改默认锁定级别图标。|此级别可以覆盖此选项。 - delete-challenge: 删除某个挑战 - delete-level: 删除某个挑战等级 - deployment: 查看某玩家已完成的挑战 - description: 编辑介绍 - edit-challenge: 编辑某个挑战设定 - edit-level: 编辑某个挑战等级设定 - edit-text-line: "&6编辑文本!" - environment: 改变挑战运作的环境。 - free-at-top: 改变免费挑战的位置,如果为true免费挑战会放在前排,如果为false免费挑战将放在后排 - glow: 允许/禁用,在已完成的挑战中加上附魔效果 - gui-mode: 启用/禁用单一挑战GUI。|&2要求服务器重启。 - gui-view-mode: 如果玩家输入/challenges,菜单应该显示GameModes还是挑战 - icon-challenge: 将在此挑战的GUI面板中显示的图标。 - icon-level: 将在此级别的GUI面板中显示的图标。 - import: 导入ASkyblock挑战。|右键单击它启用/禁用覆盖模式。|将Challenges.yml放在./BentoBox/addons/Challenges文件夹中。 - increase: 增加操作。 单击数字将增加所选数字的值。 - input: 打开文本字段输入 - input-mode: 在聊天和铁砧输入模式之间切换。 - island-store: 启用/禁用挑战每个岛的数据存储。如果启用此选项,整个岛屿团队的挑战进度将是相同的。|不会在点击时转换数据。进展将会失败。 - level-lore: 修改挑战级别介绍的哪些目标是可见的 - locked-icon: 如果级别被锁定,将在GUI面板中显示的图标。 - mode-in-world: 属于游戏模式中的世界的玩家. - mode-online: 目前在线的玩家 - mode-with-island: 属于游戏模式中的岛屿的玩家. - multiply: 乘法运算。 单击数字会将值乘以所选数字。 - name-challenge: 修改挑战名称 - name-level: 修改挑战等级名称 - order: 更改挑战顺序 - properties: 修改常规属性 - reduce: 减少操作。 单击数字将减少所选数字的值。 - remove-blocks: 是否在挑战结束后删除任务需求方块(替换为空气) - remove-challenge: 从当前级别删除挑战 - remove-completed: 允许/禁止,在玩家挑战列表中隐藏已完成的挑战 - remove-entities: 是否在挑战结束后删除挑战需要的实体 - remove-experience: 是否在玩家完成挑战后删除任务需求的经验值 - remove-items: 是否在完成挑战后删除玩家背包中的挑战需求物品 - remove-money: 是否在玩家完成挑战后删除任务需求的金钱.|&c需要经济前置. - remove-on-complete: 在玩家完成挑战后是否删除该挑战图标于任务面板中 - remove-selected: 删除所选目标|您可以使用鼠标右键选择目标 - repeatable: 挑战是否可以重复 - repeat-count: 重复挑战的次数,如果设置为0则为无限制 - repeat-reward-experience: 重复挑战完成后的经验奖励. - repeat-reward-items: '重复挑战完成后的物品奖励.|物品:' - repeat-reward-money: 重复挑战完成后的金钱奖励.|&c需要经济前置. - repeat-reward-text: 重复挑战完成后的信息 - required-blocks: 修改挑战需要的方块.|方块:| - required-entities: 修改挑战需要的实体.|实体:| - required-experience: 玩家需要有该项目所设置的经验值才可以完成挑战 - required-items: '玩家背包中需要有以下物品才能完成挑战|物品:' - required-level: 玩家需要岛屿等级达到该项目所设置的等级才能完成挑战.|&c需要 Level 组件. - required-money: 玩家需要有该项目所设置的金钱才能完成挑战.|&c需要经济前置. - required-permissions: '玩家需要具有以下权限才能完成挑战|权限:' - requirements: 修改挑战需求 - reset: 重置已完成的玩家挑战。|右键单击启用/禁用重置所有功能。 - reset-on-new: 允许/禁止,当玩家重置/踢出岛屿后都会重置挑战 - reward-commands: '设置完成任务后的指令奖励.|指令:' - reward-experience: 设置完成任务后的经验奖励. - reward-items: '设置完成任务后的物品奖励.|物品:' - rewards: 修改挑战奖励 - reward-text: 设置完成任务后的奖励信息 - reward-text-level: 完成某挑战级别所有挑战后发送给玩家的信息 - save: 保存并返回上一级菜单 - search-radius: 玩家完成挑战时检测实体/方块的范围(半径) - selected: 已选中 - set: 设置操作,击数字会将值更改为所选数字。 - settings: 修改组件设定 - show-eggs: 在Egg模式或Head模式之间切换实体视图 - title-enable: 启用/禁用玩家完成挑战时显示的标题消息。 - title-showtime: 修改标题消息对玩家可见的时间。 - toggle-user-list: 切换到不同的玩家列表 - waiver-amount: 完成该数量的挑战玩家方能解锁下一挑战级别的挑战 - reward-money: 设置完成任务后的金钱奖励.|&c需要经济前置(Vault插件和Economy插件). - repeat-reward-commands: 定义挑战完成后执行的自定义命令。| ***在开始处添加 [SELF] 表示命令将由玩家执行,例如 /kill - 命令等,否则将被服务器执行。|***字符串 [player] 将被系统替换为完成挑战的玩家名称,例如 /kill [player] 将变成 /kill - BONNe1704 等。|命令: - line-length: 修改每条lore的最大长度。不会影响已有物品。 - history-store: 启用/禁用挑战历史存储 - history-lifespan: 修改保存历史数据的天数|0表示永久 - library: 打开 GUI 显示所有可用的公开挑战库 - library-author: 由 &e[author] 创作 - library-version: "&9创作于 [version] 版本" - library-lang: "&a语言: [lang]" - library-gamemode: "&a用于 [gamemode] 游戏模式" - lore: - level: 等级字符串 | 表示翻译 'challenges.gui.challenge-description.level'. - status: 状态字符串 | 表示翻译 'challenges.gui.challenge-description.completed'. - count: 完成计数字符串 | 表示翻译 'challenges.gui.challenge-description.completed-times', 'challenges.gui.challenge-description.completed-times-of' - 和 'challenges.gui.challenge-description.maxed-reached'. - description: 描述字符串 | 在挑战对象的此处定义 - challenge.description. - warnings: '警告字符串 | 表示下列翻译: | ''challenges.gui.challenge-description.warning-items-take'' - | ''challenges.gui.challenge-description.objects-close-by'' | ''challenges.gui.challenge-description.warning-entities-kill'' - | ''challenges.gui.challenge-description.warning-blocks-remove''.' - environment: 环境字符串 | 于挑战对象此处定义 - challenge.environment. - requirements: '需求字符串 | 表示下列翻译: | ''challenges.gui.challenge-description.required-level'' - | ''challenges.gui.challenge-description.required-money'' | ''challenges.gui.challenge-description.required-experience'' - | 以及 challenge.requiredItems, challenge.requiredBlocks 或 challenge.requiredEntities.' - reward_text: 奖励文本 | 在 challenge.rewardText 和 challenge.repeatRewardText - 中定义 - reward_other: '其他奖励字符串 | 表示下列翻译: | ''challenges.gui.challenge-description.experience-reward'' - | ''challenges.gui.challenge-description.money-reward'' | ''challenges.gui.challenge-description.not-repeatable''.' - reward_items: 奖励物品 | 在 challenge.rewardItems 和 challenge.repeatRewardItems - 中定义的奖励物品 - reward_commands: 奖励命令 | 在 challenge.rewardCommands 和 challenge.repeatRewardCommands - 中定义的奖励命令 - level_status: 状态字符串 | 表示翻译 'challenges.gui.level-description.completed'. - challenge_count: 成就完成计数器字符串. | 表示 'challenges.gui.level-description.completed-challenges-of' - 的翻译 - unlock_message: 解锁信息文本 | 在挑战等级对象中定义 - challengeLevel.unlockMessage. - waiver_amount: 解锁下一等级字符串的可继承的挑战计数器 | 表示翻译 'challenges.gui.level-description.waver-amount' - level_reward_text: 奖励文本 | 在 challengeLevel.rewardText 中定义 - level_reward_other: '其他奖励字符串 | 表示翻译: | ''challenges.gui.level-description.experience-reward'' - | ''challenges.gui.level-description.money-reward''.' - level_reward_items: 奖励物品 | 在 challengeLevel.rewardItems 中定义的物品 - level_reward_commands: 奖励命令 | 在 challengeLevel.rewardCommands 中定义的完成挑战将会奖励玩家的命令 - download: 允许手动升级可用的挑战库 | 右击以启用缓存清理 - download-disabled: GitHub 数据下载器已在 BentoBox 中被禁用。没有它,你不能使用库! - challenge-wipe: 完全清空挑战及等级数据库! - players-wipe: 完全清空玩家数据库! - visibility-mode: 切换未发布的挑战是否应当可见 - block: "- [block] : [count]" - command: "- [command]" - current-value: "|&6当前值: [value]." - disabled: 禁用 - enabled: 有效 - entity: "- [entity] : [count]" - item: "- [count] x [item]" - item-enchant: " - [enchant] [level]" - item-meta: " ([meta])" - level-locked: 请完成 [count] 个 [level] 级别的挑战来解锁这个挑战级别! - level-unlocked: 点击查看 [level] 级别的挑战! - nether: "- 地狱" - normal: "- 主世界" - permission: "- [permission]" - the-end: "- 末地" - increase-by: "&a为完成计数器增加 [value]" - reduce-by: "&c为完成计数器减少 [value]" - visibility: - visible: 所有挑战对任何人可见 - hidden: 仅发布的挑战可见 - toggleable: 切换未发布的挑战的可见性 - type: - island: "&a允许获取玩家附近的方块或怪物" - inventory: "&a允许获取玩家物品栏中的物品" - other: "&a允许获取其他插件/扩展中的物品" - item-description: - armor-color: " [color]" - book-meta: " [title] by [author]" - custom-effects: " 自定义效果:" - egg-meta: " [mob]" - item: "- [count] x [item]" - item-enchant: " - [enchant] [level]" - item-lore: " 物品Lore:" - item-meta: " ([meta])" - item-name: " [name]" - potion-effect: " [effect] x [amplifier] for [duration]t" - potion-type: " [name]" - potion-type-extended: " Extended [name]" - potion-type-extended-upgraded: " [name]" - potion-type-upgraded: " Upgraded [name]" - recipe-count: " [count] recipes" - skull-owner: " [owner]" - fish-meta: "[body-color] 以 [pattern-color] [pattern]" - level-description: - completed: "&B已完成" - completed-challenges-of: "&你已经完成 [number] 个该级别的挑战,达到了 [max]个挑战." - experience-reward: "&6经验奖励: [value]" - money-reward: "&6金钱奖励: $[value]" - reward-commands: "&6指令奖励:" - reward-items: "&6物品奖励:" - waver-amount: "&6可以跳过[value] 个挑战来解锁下一个挑战级别." - questions: - prefix: "&2[SERVER]: " - admin: - number: 输入一个数字,然后按两次回车。 - unique-id: 输入不重复的对象名,然后按回车。 - challenge-name: 输入当前挑战的显示名称,然后按回车。 - level-name: 输入当前等级的显示名称,然后按回车。 - title: - admin: - choose-challenge-title: "&a选择挑战" - choose-level-title: "&a选择挑战等级" - choose-user-title: "&a选择玩家" - confirm-title: "&a确认" - edit-challenge-title: "&a编辑挑战" - edit-level-title: "&a编辑挑战等级" - edit-text-fields: "&a编辑文本字段" - manage-blocks: "&a管理方块" - manage-entities: "&a管理实体" - manage-items: "&a管理物品" - manage-numbers: "&a数字垫" - select-block: "&a选择方块" - select-challenge: "&a选择挑战" - select-entity: "&a选择实体" - settings-title: "&a编辑设定" - toggle-environment: "&a切换环境" - gui-title: "&a挑战管理" - library-title: "&a可下载的库" - lore-add: "&a添加物品Lore" - lore-remove: "&a移除物品Lore" - lore-edit: "&a编辑物品Lore" - type-select: "&a选择挑战类型" - challenges: "&6挑战" - game-modes: "&6选择游戏模式" - multiple-complete: "&6多少次?" - messages: - admin: - already-completed: "&2这个挑战已经完成" - challenge-created: "[challenge]&r created!" - completed: "&2已为[player]完成挑战[name]!" - complete-wipe: "&c希望你有备份,因为你已经创建了所有Challenges Addon数据库!" - hit-things: 点击物品将它们添加到所需的事物列表中。 完成后右键单击。 - you-added: 你在挑战中添加了一个[thing] - reset: "&2你刚重设了 [player] 的 [name] 挑战!" - reset-all: "&2[player] 的所有挑战都被你重设了!" - not-completed: "&2这个挑战还没完成呢!" - migrate-start: "&2开始迁移挑战扩展数据." - migrate-end: "&2挑战扩展数据已迁移到新格式." - migrate-not: "&2数据全部有效." - start-downloading: "&5开始下载并导入挑战库" - challenge-wipe: "&c希望你已做好备份,因你刚刚从数据库中删除了所有的挑战和等级!" - players-wipe: "&c希望你已做好备份,因你刚刚从数据库中删除了所有玩家已完成的挑战!" - defaults-file-completed: defaults.json文件填充了来自[world]的挑战! - defaults-file-overwrite: defaults.json存在。 它将被覆盖。 - import-challenges: 开始导入挑战 - import-levels: 开始导入挑战级别 - import-number: 导入 [number] 个挑战 - load-add: '添加新的对象: [value]' - load-overwriting: 覆盖 "[value]" - load-skipping: '"[value]" 已存在 - 跳过' - name-has-completed-challenge: "&5[name] 已完成 [value] &r&5挑战!" - name-has-completed-level: "&5[name] 已完成 [value] &r&5挑战级别!" - no-levels: '警告: challenges.yml文件中没有定义挑战级别' - you-completed-challenge: "&2你已经完成了 [value] &r&2挑战!" - you-completed-level: "&2你完成了 [value] &r&2级别!" - you-repeated-challenge: "&2你已经重复完成了 [value] &r&2挑战!" - you-repeated-challenge-multiple: "&2你重复完成了 [value] &r&2挑战 [count] 次!" - titles: - challenge-subtitle: "[friendlyName]" - challenge-title: 完成挑战 - level-subtitle: "[friendlyName]" - level-title: 成功挑战级别 -protection: - flags: - CHALLENGES_ISLAND_PROTECTION: - name: 挑战保护 - description: |- - &5&o切换谁可以 - &5&o完成挑战 - CHALLENGES_WORLD_PROTECTION: - description: |- - &5&o为玩家启用/禁用 - &5&o要求他们在他们的岛屿上 - &5&o才能完成挑战. - hint: 请在自己的岛屿完成挑战! - name: 挑战岛屿限制 -version: 11 +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### + meta: - authors: - - BONNe + authors: + - ApacheZy + +challenges: + commands: + admin: + main: + parameters: '' + description: '打开管理员面板。' + import: + description: |- + 从 challenges.yml 中导入挑战 + 如果使用了 overwrite 参数,将覆盖数据库中具有相同ID的挑战。 + parameters: '[overwrite]' + reload: + description: |- + 从数据库中重载挑战 + 如果使用了 hard 参数,将重置与数据库的连接。 + parameters: '[hard]' + show: + description: '在聊天框中列出这个世界适用的所有挑战。' + parameters: '' + defaults: + description: '显示用于导入/导出默认挑战的子命令。' + parameters: '[command]' + defaults-import: + description: '导入默认挑战。' + parameters: '' + defaults-generate: + description: '将现有挑战导出到 default.json 文件。' + parameters: '[overwrite] - 允许覆盖现有文件。' + complete: + description: '将玩家的某个设置为完成为一个玩家完成挑战。' + parameters: ' ' + reset: + description: '重设玩家的挑战。如果将参数 设置为 "all", 则将重置该玩家所有挑战。' + parameters: ' ' + migrate: + description: '将当前的游戏世界挑战数据迁移到 0.8.0 存储格式。' + parameters: '' + user: + main: + description: '打开挑战界面。' + parameters: '' + complete: + description: '尝试完成挑战。' + parameters: ' [count]' + gui: + title: + admin: + gui-title: '&3&l挑战管理' + edit-challenge-title: '&3&l编辑挑战' + edit-level-title: '&3&l编辑挑战等级' + settings-title: '&3&l插件设置' + choose-challenge-title: '&5&l选择挑战' + choose-level-title: '&3&l挑战等级' + choose-user-title: '&5&l选择玩家' + manage-blocks: '&3&l管理方块' + manage-entities: '&3&l管理实体' + confirm-title: '&4&l确认' + manage-items: '&3&l管理物品' + manage-numbers: '&9&l数字输入' + select-block: '&5&l选择方块' + select-challenge: '&5&l选择挑战' + select-entity: '&5&l选择实体' + toggle-environment: '&3&l更改环境' + edit-text-fields: '&3&l编辑多行文本' + + library-title: '&3&l网络库' + + lore-add: '&3&l增加描述内容' + lore-remove: '&3&l删除描述内容' + lore-edit: '&3&l编辑描述内容' + + type-select: "&3&l选择挑战类型" + challenges: '&3&l挑战' + game-modes: '&3&l选择游戏模式' + + multiple-complete: '&6&l多少次?' + buttons: + admin: + complete: '&f&l完成玩家挑战' + reset: '&6&l重置玩家挑战' + create-challenge: '&f&l创建挑战项' + create-level: '&f&l创建挑战等级' + edit-challenge: '&f&l编辑挑战项' + edit-level: '&f&l编辑挑战等级' + delete-challenge: '&c&l删除挑战' + delete-level: '&c&l删除挑战等级' + import: '&f&l导入 ASkyblock 的挑战' + settings: '&f&l插件设置' + properties: '&f&l一般属性' + requirements: '&b&l必要条件' + rewards: '&a&l奖励内容' + challenges: '&f&l挑战' + deployment: '&f&l部署' + icon: '&f&l图标' + locked-icon: '&f&l未解锁图标' + description: '&f&l描述' + order: '&f&l顺序' + environment: '&f&l环境' + remove-on-complete: '&7&l完成后删除' + name: '&f&l友好名称' + required-entities: '&f&l需要的实体' + remove-entities: '&c&l消灭实体' + required-blocks: '&f&l需要的方块' + remove-blocks: '&c&l拿掉方块' + search-radius: '&f&l探测范围' + required-permissions: '&f&l权限' + required-items: '&f&l需要的物品' + remove-items: '&c&l删除物品' + required-experience: '&f&l经验值' + remove-experience: '&f&l扣除经验' + required-level: '&f&l岛屿等级' + required-money: '&f&l游戏币' + remove-money: '&f&l扣除游戏币' + reward-text: '&f&l反馈消息' + reward-items: '&f&l奖励物品' + reward-experience: '&f&l奖励经验' + reward-money: '&f&l奖励游戏币' + reward-commands: '&f&l命令奖励' + repeatable: '&f&l可重复' + repeat-count: '&f&l最大重复次数' + repeat-reward-text: '&f&l重复奖励消息' + repeat-reward-items: '&f&l重复奖励物品' + repeat-reward-experience: '&f&l重复奖励经验' + repeat-reward-money: '&f&l重复奖励游戏币' + repeat-reward-commands: '&f&l重复命令奖励' + waiver-amount: '&f&l豁免挑战数' + add-challenge: '&f&l添加挑战' + remove-challenge: '&f&l删除挑战' + reset-on-new: '&f&l重玩时重置' + broadcast: '&f&l完成后广播消息' + remove-completed: '&f&l完成后删除' + glow: '&f&l完成后发光' + free-at-top: '&f&l前置独立挑战' + line-length: '&f&l描述行长度' + visibility-mode: '&f&l挑战可见模式' + toggle-user-list: '&f&l玩家过滤' + remove-selected: '&f&l删除' + add: '&f&l添加' + show-eggs: '&f&l切换显示模式' + accept: '&c&l接受' + decline: 'Decline' # 待确认 + save: '&f&l保存' + cancel: '&f&l取消' + input: '&f&l键盘输入' + value: '&f&l结果' + set: '&f&l=' + increase: '&f&l+' + reduce: '&f&l-' + multiply: '&f&l*' + clear: '&f&l清除' + remove-empty: '&f&l删除空行' + number: '[number]' + level-lore: '&f&l等级描述元素' + challenge-lore: '&f&l挑战描述元素' + gui-view-mode: '&f&l独立命令用途' + gui-mode: '&f&l独立命令' + history-store: '&f&l挑战历史' + history-lifespan: '&f&l保存期限' + island-store: '&6&l按岛屿存储' + default-locked-icon: '&f&l未解锁等级图标' + input-mode: '&f&l切换输入模式' + title-enable: '&f&l标题消息' + title-showtime: '&f&l标题显示时间' + default-import: '&f&l导入默认挑战' + default-export: '&f&l导出现有挑战' + complete-wipe: '&c&l清空插件数据库' + challenge-wipe: '&c&l清空挑战数据库' + players-wipe: '&c&l清空玩家数据库' + + library: '&f&l网络库' + download: '&f&l下载网络库' + + type: + island: '&6&l岛屿类' + inventory: '&6&l物品类' + other: '&6&l其它类型' + next: '&f&l下一页' + previous: '&f&l上一页' + return: '&f&l返回' + + value: "&f&l完全" # 待确认 + increase: "&f&l增加" + reduce: "&f&l减少" + descriptions: + admin: + save: '&7保存并返回上级' + cancel: '&7取消保存并返回上级' + input: '&7请打开聊天框并手动输入数值' + set: '&f&l设置模式。|&7单击数字将值直接设置为所选数值。' + increase: '&f&l累加模式。|&7单击数字将当前值加上所选数值。' + reduce: '&f&l递减模式。|&7点击数字将当前值减去所选数值。' + multiply: '&f&l累乘模式。|&7单击数字将当前值乘以所选数值。' + import: |- + &7导入 ASkyblock(旧版空岛插件) 的挑战。 + &f右键点击 启用/禁用 覆盖模式。 + &7请将要导入的 challenges.yml 放置在: + &7./BentoBox/addons/Challenges 目录。 + complete: |- + &7直接将玩家的挑战状态设置为完成。 + &7这样做玩家不会获得任何奖励。 + reset: |- + &7重置玩家已完成的挑战。 + &f右键点击 启用/禁用 全部重置功能。 + create-challenge: |- + &7创建一个新的挑战。 + &7默认情况下新挑战将出现在独立挑战列表中。 + create-level: '&7创建一个新的挑战等级。' + edit-challenge: '&7修改现有的挑战。' + edit-level: '&7修改现有的挑战等级。' + delete-challenge: '&7删除某项挑战。' + delete-level: '&7删除某个挑战等级。' + settings: '&7修改挑战组件配置。' + properties: '&7修改这项挑战的常规属性。' + requirements: '&7设置要完成这项挑战的必要条件。' + rewards: '&7设置完成这项挑战后获得的奖励。' + challenges: '&7管理该等级的所有挑战(增加/删除)。' + deployment: '&7允许玩家查看和完成这项挑战。' + icon-challenge: '&7设置这项挑战将显示在|&7挑战面板中的图标。' + icon-level: '&7设置这项挑战等级将显示在|&7挑战面板中的图标。' + locked-icon: '&7设置这个挑战等级未解锁时|&7在面板中显示的图标。' + description: '&7修改挑战描述文本。' + order: '&7修改顺序号。|&7顺序号越大,显示在面板中的位置越靠后。' + environment: '&7设置要完成这项挑战应所处的环境。' + remove-on-complete: '&7设置玩家完成挑战后,挑战面板|&7中是否不再显示这项挑战。' + name-challenge: '&7设置这项挑战在面板中的显示名称。|&7如果是新建的挑战项,显示名称是挑战项ID。' + name-level: '&7设置这个挑战等级在面板中的显示名称。|&7如果是新' + required-entities: |- + &7添加/修改/删除 + &7要完成这项挑战应在指定范围内存在的实体。 + &6所需实体: + remove-entities: '&7设置当玩家完成挑战后,|&7是否删除(杀死)所需实体。' + required-blocks: |- + &7添加/修改/删除 + &7要完成这项挑战应在指定范围内存在的方块。 + &6所需方块: + remove-blocks: '&7设置当玩家完成挑战后,|&7是否删除(替换成空气)所需方块。' + search-radius: "&7玩家所在位置周围的半径,|&7将在其范围内探测所需的实体和方块。" + required-permissions: |- + &7设置玩家要完成挑战必须具有的权限。 + &6所需权限: + required-items: |- + &7设置玩家要完成挑战物品栏中必须有的物品。 + &6所需物品: + remove-items: '&7设置当玩家完成挑战后,是否|&7从物品栏中删除所需物品。' + required-experience: '&7设置玩家要完成挑战所|&7需要的经验值。' + remove-experience: '&7设置玩家完成挑战后,是否扣除所需经验值。' + required-level: |- + &7设置要完成此挑战所需的岛屿等级。 + &c需要安装 Level 组件。 + required-money: |- + &7设置要完成此挑战所需的游戏币数量。 + &c需要安装 Vault 和兼容的经济插件。 + remove-money: |- + &7完成挑战后,是否扣除玩家所需数量的游戏币。 + &c需要安装 Vault 和兼容的经济插件。 + reward-text: '&7设置玩家完成挑战后发送给玩家的聊天消息。' + reward-items: |- + &7设置首次完成挑战获得的物品奖励。 + &a奖励物品: + reward-experience: '&7设置首次完成挑战后获得的经验值奖励。' + reward-money: |- + &7设置首次完成挑战获得的游戏币奖励。 + &c需要安装 Vault 和兼容的经济插件。 + reward-commands: |- + &7设置首次完成挑战后将执行的命令。 + &3无需在命令行首加斜杠 “/”。 + &3命令行首加 “[SELF]” 将由玩家执行。 + &3例如 “&f[SELF] heal&3”。 + &3文字 “[player]” 将替换为玩家名称。 + &3例如 “&fkill [player]&3”。 + &a奖励命令: + repeatable: '&7设置这项挑战是否可重复进行。' + repeat-count: '&7设置最大完成次数,设置为 0 表示不限次数。' + repeat-reward-text: '&7设置重复完成挑战后发送给玩家的聊天消息。' + repeat-reward-items: |- + &7设置重复完成挑战获得的奖励物品。 + &a奖励物品: + repeat-reward-experience: '&7设置重复完成挑战后获得的经验值奖励。' + repeat-reward-money: |- + &7设置重复完成挑战获得的游戏币奖励。 + &c需要安装 Vault 和兼容的经济插件。 + repeat-reward-commands: |- + &7设置首次完成挑战后将执行的命令。 + &3无需在命令行首加斜杠 “/”。 + &3命令行首加 “[SELF]” 将由玩家执行。 + &3例如 “&f[SELF] heal&3”。 + &3文字 “[player]” 将替换为玩家名称。 + &3例如 “&fkill [player]&3”。 + &a奖励命令: + waiver-amount: '&7设置玩家解锁下一级需要|&7完成当前等级的数量。' + reward-text-level: '&7完成当前等级所有挑战后|&7发送给玩家的聊天消息。' + add-challenge: '&7将现有挑战添加到当前挑战等级。' + remove-challenge: '&7从当前挑战等级中移除挑战。' + reset-on-new: '&7当玩家重置、离开或被踢出岛屿时|&7是否重置他的所有挑战。' + broadcast: '&7设置玩家首次完成挑战后是否|&7向所有在线玩家发送广播。' + remove-completed: '&7设置完成挑战后是否从面板|&7中隐藏且无法重复。' + glow: '&7设置' + free-at-top: '&7设置是否将独立挑战放在面板前排。' + line-length: '&7设置面板图标描述行显示的最大长度。|&7这个设定只影响显示效果,不会修改存储数据。' + toggle-user-list: '&7按所选模式过滤玩家。' + mode-online: '当前所有在线玩家。' + mode-in-world: '当前游戏模式中的玩家。' + mode-with-island: '在当前游戏模式中有归属岛屿的玩家。' + selected: '&5已选定' + remove-selected: |- + &7删除所有选定的内容。 + &7右键单击来选定内容。 + show-eggs: '&7切换使用&3生成蛋&7或&3带纹理的玩家头&7来显示实体。' + level-lore: '&7设置挑战等级描述中哪些元素是可见的。' + challenge-lore: '&7设置挑战描述中哪些元素是可见的。' + gui-view-mode: |- + &7设置通过独立命令是否打开游戏模 + &7式选择器。 + &7当 &a开启 &7时,通过独立命令可以打开 + &7游戏模式选择器以进行适用挑战。 + &7当 &c关闭 &7时,将直接打开适用于当前 + &7游戏模式的挑战面板。 + &e只在安装了多个游戏模式时有用。 + history-store: '&7设置是存储挑战历史记录。' + history-lifespan: |- + &7设置历史记录数据可以保存的天数。 + &7设置为 0 将永久保存. + island-store: |- + &7设置是否按岛屿为单位来存储数据。 + &7如果开启此选项,则整个岛屿所有 + &7成员的挑战将是相同的。 + &c点击切换时不会立即转换数据。 + + default-locked-icon: |- + &7设置未解锁的挑战等级默认图标. + &7为挑战等级单独设定未解锁图标可以覆盖此设置。 + gui-mode: |- + &7设置是否可以用单独的命令打开面板, + &7例如使用 &f/c &7打开面板。 + &c&l更改设置后重启服务器才能生效。 + visibility-mode: '&7选择未部署的挑战可见模式。| ' + + click-to-edit: '&4点击此处编辑输入。' + edit-text-line: '&6编辑文本消息!' + add-text-line: '&6增加新的文本消息!' + input-mode: '&7选择是在聊天中还是铁砧上输入文本。' + title-enable: '&7设置是否在完成挑战后|&7向玩家发送标题消息。' + title-showtime: '&7设置标题消息显示时长。|&7单位为 &f游戏刻 (Ticks)' + default-import: '&7导入默认挑战。' + default-export: '&7将现有挑战导出到 defaults.json 文件。' + complete-wipe: '&c彻底清空所有挑战组件数据库,|&c包括玩家数据!' + + challenge-wipe: '&c彻底清空挑战和挑战等级数据库!|&c清空后玩家不能再进行任何挑战。' + players-wipe: '&c彻底清空玩家数据库!|&c清空后所有玩家的挑战进度将丢失。' + + library: '&c从网络上下载共享挑战库。' + + library-author: '&7由 &e[author] &7创作。' + library-version: '&9兼容 Challenges [version]' + library-lang: '&7语言: [lang]' + library-gamemode: '&7适用于 [gamemode]' + + download: |- + &7从共享网络库上下载可用的挑战。 + &7右键点击 开启/关闭 缓存清理。 + download-disabled: '&cGitHub 下载器已在 BentoBox 中禁用。|&c没有它,您将无法使用共享库!' + + lore: + level: '&7所属挑战等级名称' + status: '&7完成状态' + count: '&7完成次数' + description: '&7描述文字' + warnings: '&7警告文字' + environment: '&7环境需求' + requirements: '&7需求' + reward_text: '&7奖励描述' + reward_other: |- + &7其他奖励描述 + &7包含经验奖励、游戏币奖励以及不可重复完成的提示 + reward_items: '&7奖励物品' + reward_commands: '&7命令奖励' + level_status: '&7等级完成状态' + challenge_count: '&7完成的挑战计数字符串。' + unlock_message: '&7解锁状态字符串' + waiver_amount: '&7豁免数的说明' + level_reward_text: '&7等级奖励描述文本' + level_reward_other: '&7挑战等级其他奖励内容|&7包含经验值奖励和游戏币奖励' + level_reward_items: '&7奖励物品内容' + level_reward_commands: '&7命令奖励' + current-value: |- + &6当前值: &f[value] + enabled: '&a已开启' + disabled: '&c已关闭' + type: + island: '&a挑战内容为建造和驯养类。|&7要完成该类挑战,玩家周围|&7必须有指定数量的方块或实体。' + inventory: '&a挑战内容为物品收集。|&7要完成该类挑战,玩家物品栏中|&7必须有指定数量的物品。' + other: '&a挑战内容为数据条件类。|&7要完成该类挑战,玩家必须有设定的|&7游戏币/经验值/岛屿等级/权限。' + the-end: '- 末地' + nether: '- 下界' + normal: '- 主世界' + entity: '&7- [entity] x [count]' + block: '&7- [block] x [count]' + item: '&7- [item] x [count]' + item-meta: '&7 ([meta])' + item-enchant: '&7 - [enchant] [level]' + permission: '&7- [permission]' + command: '- [command]' + + level-unlocked: '&f点击查看 &r[level] &f的所有挑战!' + level-locked: '&7再完成 &f[count] &7个 &r[level] &7的挑战项目即可解锁此等级。' + + increase-by: "&a将完成计数增加 &f[value]" + reduce-by: "&c将完成计数减少 &f[value]" + + visibility: + visible: "所有挑战项都可见" + hidden: "仅显示已部署的挑战项" + toggleable: "玩家可以自行从面板中切换显示模式|&r" + + challenge-description: + level: '&f等级: [level]' + completed: '&b已完成' + completed-times-of: '&3可挑战 [maxtimes] 次, 完成了 [donetimes] 次。' + maxed-reached: '&b完成了 [donetimes] 次, 可挑战 [maxtimes] 次。' + completed-times: '&b完成了 [donetimes] 次。' + warning-items-take: '&c完成后,所需物品将被删除。' + objects-close-by: '&c周围必须有所需方块和实体。' + warning-entities-kill: '&c完成后,所需实体将被消灭。' + warning-blocks-remove: '&c完成后,所需方块将被摧毁。' + not-repeatable: '&c这项挑战不可重复!' + experience-reward: '&2经验值奖励: &7[value]' + money-reward: '&2游戏币奖励: &7[value]' + required-experience: '&6需要经验值: &7[value]' + required-money: '&6需要游戏币: - &7[value]' + required-island-level: '&6需要岛屿等级: - &7[value]' + environment: '&6需要环境:' + rewards-title: '&a&l奖励:' + reward-items: '&2物品奖励:' + reward-commands: '&2命令奖励:' + required-items: '&6需要物品:' + required-entities: '&6需要实体:' + required-blocks: '&6需要方块:' + level-description: + completed: '&b已全部完成' + completed-challenges-of: |- + &3该等级共有 &f[max] &3项挑战, + &3你已完成了 &f[number] &3项。 + waver-amount: '&6你可以跳过 &f[value] &6项挑战来解锁下一级。' + experience-reward: '&2经验值奖励: - &f[value]' + money-reward: '&2游戏币奖励: - &f[value]' + reward-items: '&2物品奖励:' + reward-commands: '&2命令奖励:' + item-description: + item: '&7- [item] x [count]' + item-meta: '&8 ([meta])' + item-enchant: '&8 [enchant] [level]' + item-name: '&7 [name]' + item-lore: '&7 物品描述:' + book-meta: '&7 《[title]》 - [author] 著' + recipe-count: '&7 [count] 个配方' + armor-color: '&8&o [color]' + potion-type-extended-upgraded: '&7 [name] II (Extended)' + potion-type-upgraded: '&7 [name] II' + potion-type-extended: '&7 [name] Extended ' + potion-type: '&7 [name]' + custom-effects: '&7 自定义效果:' + potion-effect: '&8 [effect] [amplifier] ([duration])' + skull-owner: '&7 [owner]' + egg-meta: '&7 [mob]' + fish-meta: '&8&o [body-color]-[pattern-color] [pattern]' + + questions: + prefix: "&7[&e服务器&7]: &r" + + admin: + number: "请通过聊天栏输入数值:" + unique-id: "请通过聊天栏输入对象的唯一ID:" + challenge-name: "请通过聊天栏输入这项挑战的名称:" + level-name: "请通过聊天栏输入这个挑战等级的名称:" + + titles: + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. + # [friendlyName] will be replaced with challenge friendly name. + # [level] will be replaced with level friendly name. + # [rewardText] will be replaced with the challenge reward text. + challenge-title: '&a已完成' + challenge-subtitle: '[friendlyName]' + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. + # [friendlyName] will be replaced with level friendly name. + # [rewardText] will be replaced with the level reward text. + level-title: '&a已完成' + level-subtitle: '[friendlyName]' + messages: + admin: + # 鬼知道你说的 Thing 是什么!况且这些翻译内容从来没用到过。 + hit-things: '&7单击以将其添加到所需列表中。完成后右键单击。' + you-added: '&a您向挑战添加了一个 &2r[thing]' # ??? + challenge-created: '&a挑战项 &r[challenge] &a已创建!' + complete-wipe: '&c希望您有备份,因为您刚刚删除了挑战组件的全部数据库!' + + challenge-wipe: '&c希望您有备份,因为您刚刚删除了所有挑战项和挑战等级!' + players-wipe: '&c希望您有备份,因为您刚刚删除了所有玩家已完成的挑战!' + + completed: '&2你将玩家 &r[player] &2的挑战项 &r[name] &2设置为已完成!' + already-completed: '&2这项挑战已经完成过了!' + reset: '&2你为玩家 &r[player] &2重置了挑战项 &r[name]&2!' + reset-all: '&2玩家 &r[player] &2的所有挑战项已被重置!' + not-completed: '&2这项挑战尚未完成!' + + migrate-start: '&2开始迁移挑战组件数据库。' + migrate-end: '&2挑战组件数据库已更新为新格式。' + migrate-not: '&2所有数据均有效。' + + start-downloading: '&5开始下载网络库并导入。' + you-completed-challenge: '&2你完成了挑战 &r[value] &2!' + you-repeated-challenge: '&2你再次完成了挑战 &r[value] &2!' + you-repeated-challenge-multiple: '&2你完成挑战 &r[value] &r[count] &2次了!' + you-completed-level: '&2恭喜,你的挑战等级 &r[value] &2已完成!' + name-has-completed-challenge: '&a恭喜! &r[name] &a完成了挑战 [value] &a!' + name-has-completed-level: '&a恭喜! &r[name] &a的挑战等级 [value] &a已全部完成!' + import-levels: '&a开始导入挑战等级' + import-challenges: '&a开始导入挑战' + no-levels: '&e警告: 文件 &fchallenges.yml &e中没有定义任何挑战等级!' + import-number: '&a导入了 &f[number] &a个挑战项目' + load-skipping: '&c挑战项 "[value]" &c已存在 - 将跳过' + load-overwriting: '&6覆盖了已载入的挑战: "[value]"' + load-add: '&a新增了挑战: [value]' + defaults-file-overwrite: '&cdefaults.json 已被覆盖。' + defaults-file-completed: 'defaults.json 已经保存了 [world] 中的所有挑战。' + errors: + no-name: '&c缺少挑战名称。' + unknown-challenge: '&c未知的挑战。' + unique-id: '&c唯一ID "[id]" 无效。' + wrong-icon: '&c给定的材料 "[value]" 无效,不能用作图标。' + not-valid-integer: '&c给定的整数值 "[value]" 无效!它只能在 [min] 到 [max] 取值。' + not-a-integer: '&c给定的值 "[value]" 不是有效整数!' + not-deployed: '&c这项挑战尚未部署!' + not-on-island: '&c您必须在您的岛上才能完成挑战!' + challenge-level-not-available: '&c您尚未解锁这项挑战的等级。' + not-repeatable: '&c这项挑战不可重复进行!' + wrong-environment: '&c您在错误的环境中!' + not-enough-items: '&c你没有足够的 &r[items] &c来完成这项挑战。' + not-close-enough: '&c要完成挑战,你必须站在所需项目 &f[number] &c格范围内。' + you-still-need: '&c你还差 &f[amount] &c个 &f[item] &c才能完成挑战。' + missing-addon: '&c无法完成挑战:缺少必需的组件或插件。' + incorrect: '&c无法完成挑战:必要条件设定错误。' + not-enough-money: '&c你必须有 &f[value] &c游戏币才能完成任务。' + not-enough-experience: '&c你必须有 &f[value] &c经验值才能完成任务。' + island-level: '&c你的岛屿等级必须达到 &flv[number] &c才能完成任务!' + import-no-file: '&c未找到要导入的 &fchallenges.yml &c文件!' + no-load: '&c错误: 无法载入 &fchallenges.yml&c. [message]' + load-error: '&c错误: 无法载入 &r[value]&c。' + no-rank: "&c你的阶衔不能进行这项挑战。" + cannot-remove-items: '&c有些物品无法从你的物品栏中删除!' + exist-challenges-or-levels: '&c这项挑战或这个等级已存在!' + defaults-file-exist: '&c文件 &fdefaults.json &c已存在,要将其替换请开启覆盖模式。' + defaults-file-error: '&c创建文件 defaults.json 发生错误,请查阅控制台消息!' + no-challenges: '&c这个游戏模式没有可进行的挑战!' + no-challenges-admin: '&c这个游戏模式还没有可进行的挑战!请使用 &f/[command] &c来添加挑战。' + missing-level: '&c数据库中未定义挑战等级 [level]&c, 使用它可能发生错误!' + missing-arguments: '&c命令缺少参数。' + no-multiple-permission: "&c你没有权限多次完成这项挑战。" + invalid-level: "&c挑战等级 [level] &c包含错误,它不会从数据库中加载!" + invalid-challenge: "&c挑战项 [challenge] &c包含错误,它不会从数据库中加载!" +protection: + flags: + CHALLENGES_ISLAND_PROTECTION: + description: "允许/禁止 在岛屿上完成挑战" + name: "挑战权限" + CHALLENGES_WORLD_PROTECTION: + description: |- + &7允许/禁止 限制玩家只能在自己岛 + &7上才能完成挑战。 + &c允许时,玩家只能在自己岛上进行 + &c和完成挑战。 + name: "挑战岛屿限制" + hint: "&c已被禁止在岛屿范围外进行挑战" +version: 11 diff --git a/src/main/resources/panels/gamemode_panel.yml b/src/main/resources/panels/gamemode_panel.yml new file mode 100644 index 0000000..7634b0c --- /dev/null +++ b/src/main/resources/panels/gamemode_panel.yml @@ -0,0 +1,53 @@ +# Panels are read once upon first GUI open. +# Changes in configuration will be taken only if server is restarted or panels are reloaded. +# Information about setup for the Panels are available at: +# https://docs.bentobox.world/en/latest/addons/Challenges/ +gamemode_panel: + title: challenges.gui.titles.gamemode-gui + type: INVENTORY + background: + icon: BLACK_STAINED_GLASS_PANE + title: "&b&r" # Empty text + border: + icon: BLACK_STAINED_GLASS_PANE + title: "&b&r" # Empty text + force-shown: [] + content: + 2: + 1: + icon: TIPPED_ARROW:INSTANT_HEAL::::1 + title: challenges.gui.buttons.previous.name + description: challenges.gui.buttons.previous.description + data: + type: PREVIOUS + target: GAMEMODE + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-previous + 2: gamemode + 3: gamemode + 4: gamemode + 5: gamemode + 6: gamemode + 7: gamemode + 8: gamemode + 9: + icon: TIPPED_ARROW:JUMP::::1 + title: challenges.gui.buttons.next.name + description: challenges.gui.buttons.next.description + data: + type: NEXT + target: GAMEMODE + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-next + reusable: + gamemode: + data: + type: GAMEMODE + actions: + left: + type: SELECT + tooltip: challenges.gui.tips.click-to-select \ No newline at end of file diff --git a/src/main/resources/panels/main_panel.yml b/src/main/resources/panels/main_panel.yml new file mode 100644 index 0000000..78171b6 --- /dev/null +++ b/src/main/resources/panels/main_panel.yml @@ -0,0 +1,114 @@ +# Panels are read once upon first GUI open. +# Changes in configuration will be taken only if server is restarted or panels are reloaded. +# Information about setup for the Panels are available at: +# https://docs.bentobox.world/en/latest/addons/Challenges/ +main_panel: + title: challenges.gui.titles.player-gui + type: INVENTORY + background: + icon: BLACK_STAINED_GLASS_PANE + title: "&b&r" # Empty text + border: + icon: BLACK_STAINED_GLASS_PANE + title: "&b&r" # Empty text + force-shown: [4] + content: + 2: + 2: challenge_button + 3: challenge_button + 4: challenge_button + 5: challenge_button + 6: challenge_button + 7: challenge_button + 8: challenge_button + 3: + 1: + icon: TIPPED_ARROW:INSTANT_HEAL::::1 + title: challenges.gui.buttons.previous.name + description: challenges.gui.buttons.previous.description + data: + type: PREVIOUS + target: CHALLENGE + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-previous + 2: challenge_button + 3: challenge_button + 4: challenge_button + 5: challenge_button + 6: challenge_button + 7: challenge_button + 8: challenge_button + 9: + icon: TIPPED_ARROW:JUMP::::1 + title: challenges.gui.buttons.next.name + description: challenges.gui.buttons.next.description + data: + type: NEXT + target: CHALLENGE + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-next + 5: + 1: + icon: TIPPED_ARROW:INSTANT_HEAL::::1 + title: challenges.gui.buttons.previous.name + description: challenges.gui.buttons.previous.description + data: + type: PREVIOUS + target: LEVEL + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-previous + 2: level_button + 3: level_button + 4: level_button + 5: level_button + 6: level_button + 7: level_button + 8: level_button + 9: + icon: TIPPED_ARROW:JUMP::::1 + title: challenges.gui.buttons.next.name + description: challenges.gui.buttons.next.description + data: + type: NEXT + target: LEVEL + indexing: true + action: + left: + tooltip: challenges.gui.tips.click-to-next + 6: + 5: + icon: IRON_BARS + title: challenges.gui.buttons.free-challenges.name + description: challenges.gui.buttons.free-challenges.description + data: + type: UNASSIGNED_CHALLENGES + action: + left: + tooltip: challenges.gui.tips.click-to-select + reusable: + challenge_button: + data: + type: CHALLENGE + actions: + left: + type: COMPLETE + tooltip: challenges.gui.tips.click-to-complete + right: + type: MULTIPLE_PANEL + tooltip: challenges.gui.tips.right-click-multiple-open + shift_left: + type: COMPLETE_MAX + tooltip: challenges.gui.tips.shift-left-click-to-complete-all + level_button: + data: + type: LEVEL + actions: + left: + type: SELECT + tooltip: challenges.gui.tips.click-to-select \ No newline at end of file diff --git a/src/main/resources/panels/multiple_panel.yml b/src/main/resources/panels/multiple_panel.yml new file mode 100644 index 0000000..ae6144d --- /dev/null +++ b/src/main/resources/panels/multiple_panel.yml @@ -0,0 +1,62 @@ +# Panels are read once upon first GUI open. +# Changes in configuration will be taken only if server is restarted or panels are reloaded. +# Information about setup for the Panels are available at: +# https://docs.bentobox.world/en/latest/addons/Challenges/ +multiple_panel: + title: challenges.gui.titles.multiple-gui + type: HOPPER + content: + 1: + 1: + icon: RED_STAINED_GLASS_PANE + title: challenges.gui.buttons.reduce.name + description: challenges.gui.buttons.reduce.description + data: + type: REDUCE + value: 5 + actions: + left: + tooltip: challenges.gui.tips.click-to-reduce + 2: + icon: ORANGE_STAINED_GLASS_PANE + title: challenges.gui.buttons.reduce.name + description: challenges.gui.buttons.reduce.description + data: + type: REDUCE + value: 1 + actions: + left: + tooltip: challenges.gui.tips.click-to-reduce + 3: + icon: GREEN_STAINED_GLASS_PANE + title: challenges.gui.buttons.accept.name + description: challenges.gui.buttons.accept.description + data: + type: ACCEPT + actions: + left: + type: ACCEPT + tooltip: challenges.gui.tips.left-click-to-accept + right: + type: INPUT + tooltip: challenges.gui.tips.right-click-to-write + 4: + icon: BLUE_STAINED_GLASS_PANE + title: challenges.gui.buttons.increase.name + description: challenges.gui.buttons.increase.description + data: + type: INCREASE + value: 1 + actions: + left: + tooltip: challenges.gui.tips.click-to-increase + 5: + icon: MAGENTA_STAINED_GLASS_PANE + title: challenges.gui.buttons.increase.name + description: challenges.gui.buttons.increase.description + data: + type: INCREASE + value: 5 + actions: + left: + tooltip: challenges.gui.tips.click-to-increase \ No newline at end of file diff --git a/src/main/resources/template.yml b/src/main/resources/template.yml new file mode 100644 index 0000000..f0f0910 --- /dev/null +++ b/src/main/resources/template.yml @@ -0,0 +1,562 @@ +########################################################################################### +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +########################################################################################### +# This is a template file that allows to create challenges in YAML format. +# Be aware, some features are not supported in YAML so some things may not be able to do with this +# file. +# Note that this is just a template. All challenges are stored and taken from the database. +# Template is used just for importing challenges in gamemode. +# Information about setup for the Template File are available at: +# https://docs.bentobox.world/en/latest/addons/Challenges/ +challenges: + # Each challenge starts withs it ID. + # Everything for challenge must be inside it. + example_inventory_challenge: + # Name of the Challenge. If it is not present, name will be set to the challenge id. + # Supports ColorCodes. + name: "&2 Example Inventory Challenge" + # Icon for the Challenge + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: CHEST + # Each challenge can have an extra text in their lore that is only for this challenge. + # Text has its own Color Codes. + description: |- + &7 Description of inventory + &7 challenge + # This allows to set that challenge is completable by players. If challenge is not + # deployed, then players cannot complete it until deployed status is set to true. + deployed: true + # This allows to set the ordering of the challenges inside same level. + # If 2 challenges has the same order number, they will be ordered by their unique_id. + order: 0 + # You can set that challenge is limited to a specific environment. Or leave it empty + # as it will indicate that challenge can be completed in every dimension. + # Supported values: NORMAL, NETHER, THE_END. + environments: + - NORMAL + - NETHER + - THE_END + # This option allows to auto-hide challenge after player completes it. + # It does not work for infinitely repeatable challenges. + remove-completed: false + # Type of the Challenge allows defining which requirements will be used. + # Challenge cannot exist without type or requirements. + # Each requirement has its own set of rules. + # Currently, addon has 4 types: + # INVENTORY_TYPE - checks items in player inventory. + # ISLAND_TYPE - checks for blocks or entities on player island. + # OTHER_TYPE - checks different things like, experience, island level, balance. + # STATISTIC_TYPE - checks specific player statistic value. + type: INVENTORY_TYPE + # Requirements are the section that defines what challenge will require to do. + requirements: + # All requirements supports to define a list of permissions that player must have. + # If permission is not set for the player, he will not be able to compete the + # challenge. + permissions: + - permission.value.1 + - permission.value.2 + # Take items allows to set that items will be removed from player inventory + # after challenge completion. + take-items: true + items: + - DIRT:220 + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Repeatable allows to define if challenge is repeatable or not. + # Repeat Rewards are stored only if repeatable is set to true. + repeatable: true + # Repeat times allow defining how many times challenge can be repeated. + # If the value is 0 or smaller, it means that challenge is not limited. + repeat-times: -1 + # Repeat-rewards section allows defining what player will receive after completing challenge + # each repeating time. + repeat-rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Repeat Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - PLAYER_HEAD:BONNe1704 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # There are no specific requirements for challenge ID format. + example_island_challenge: + # Name of the Challenge. If it is not present, name will be set to the challenge id. + # Supports ColorCodes. + name: "&2 Example Island Challenge" + # Icon for the Challenge + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: GRASS_BLOCK:3 + # Each challenge can have an extra text in their lore that is only for this challenge. + # Text has its own Color Codes. + description: |- + &7 Description of island + &7 challenge + # This allows to set that challenge is completable by players. If challenge is not + # deployed, then players cannot complete it until deployed status is set to true. + deployed: true + # This allows to set the ordering of the challenges inside same level. + # If 2 challenges has the same order number, they will be ordered by their unique_id. + order: 0 + # You can set that challenge is limited to a specific environment. Or leave it empty + # as it will indicate that challenge can be completed in every dimension. + # Supported values: NORMAL, NETHER, THE_END. + environments: + - NORMAL + - NETHER + - THE_END + # This option allows to auto-hide challenge after player completes it. + # It does not work for infinitely repeatable challenges. + remove-completed: false + # Type of the Challenge allows defining which requirements will be used. + # Challenge cannot exist without type or requirements. + # Each requirement has its own set of rules. + # Currently, addon has 4 types: + # INVENTORY_TYPE - checks items in player inventory. + # ISLAND_TYPE - checks for blocks or entities on player island. + # OTHER_TYPE - checks different things like, experience, island level, balance. + # STATISTIC_TYPE - checks specific player statistic value. + type: ISLAND_TYPE + # Requirements are the section that defines what challenge will require to do. + requirements: + # All requirements supports to define a list of permissions that player must have. + # If permission is not set for the player, he will not be able to compete the + # challenge. + permissions: + - permission.value.1 + - permission.value.2 + # Remove Blocks indicate that all required blocks will be removed from world + # after challenge is completed. + remove-blocks: true + # Section `blocks` follows format: `MATERIAL_NAME: ` That indicate which block and + # how many of them must be checked. + # You can find all material names in https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html + blocks: + DIRT: 220 + STONE: 2 + # Remove Entities indicate that all required entities will be removed from world + # after challenge is completed. + remove-entities: true + # Section `entities` follows format: `ENTITY_NAME: ` That indicate which entities and + # how many of them must be checked. + # You can find all entity names in https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html + entities: + CAT: 1 + BAT: 2 + # With search distance you can define how far player should stand to detect blocks. + # Be aware that setting it too large will take a time to detect. + # Also, by default this will be capped at island border. So if player stands next to a border, + # only blocks inside his island will be taken into account. + search-distance: 10 + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Repeatable allows to define if challenge is repeatable or not. + # Repeat Rewards are stored only if repeatable is set to true. + repeatable: true + # Repeat times allow defining how many times challenge can be repeated. + # If the value is 0 or smaller, it means that challenge is not limited. + repeat-times: -1 + # Repeat-rewards section allows defining what player will receive after completing challenge + # each repeating time. + repeat-rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Repeat Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - PLAYER_HEAD:BONNe1704 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # There are no specific requirements for challenge ID format. + example_other_challenge: + # Name of the Challenge. If it is not present, name will be set to the challenge id. + # Supports ColorCodes. + name: "&2 Example Other Challenge" + # Icon for the Challenge + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: EXPERIENCE_BOTTLE + # Each challenge can have an extra text in their lore that is only for this challenge. + # Text has its own Color Codes. + description: |- + &7 Description of other + &7 challenge + # This allows to set that challenge is completable by players. If challenge is not + # deployed, then players cannot complete it until deployed status is set to true. + deployed: true + # This allows to set the ordering of the challenges inside same level. + # If 2 challenges has the same order number, they will be ordered by their unique_id. + order: 0 + # You can set that challenge is limited to a specific environment. Or leave it empty + # as it will indicate that challenge can be completed in every dimension. + # Supported values: NORMAL, NETHER, THE_END. + environments: + - NORMAL + - NETHER + - THE_END + # This option allows to auto-hide challenge after player completes it. + # It does not work for infinitely repeatable challenges. + remove-completed: false + # Type of the Challenge allows defining which requirements will be used. + # Challenge cannot exist without type or requirements. + # Each requirement has its own set of rules. + # Currently, addon has 4 types: + # INVENTORY_TYPE - checks items in player inventory. + # ISLAND_TYPE - checks for blocks or entities on player island. + # OTHER_TYPE - checks different things like, experience, island level, balance. + # STATISTIC_TYPE - checks specific player statistic value. + type: OTHER_TYPE + # Requirements are the section that defines what challenge will require to do. + requirements: + # All requirements supports to define a list of permissions that player must have. + # If permission is not set for the player, he will not be able to compete the + # challenge. + permissions: + - permission.value.1 + - permission.value.2 + # Indicate that experience will be removed after challenge completion. + take-experience: true + # How much experience points player must have. It is not an experience level, but a points. + experience: 10 + # Indicate that money will be removed after challenge completion. + take-money: true + # How much money player must have. + money: 10.0 + # Allows requesting specific minimal island level for challenge completion. + level: 10 + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Repeatable allows to define if challenge is repeatable or not. + # Repeat Rewards are stored only if repeatable is set to true. + repeatable: true + # Repeat times allow defining how many times challenge can be repeated. + # If the value is 0 or smaller, it means that challenge is not limited. + repeat-times: -1 + # Repeat-rewards section allows defining what player will receive after completing challenge + # each repeating time. + repeat-rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Repeat Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - PLAYER_HEAD:BONNe1704 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # There are no specific requirements for challenge ID format. + example_statistic_challenge: + # Name of the Challenge. If it is not present, name will be set to the challenge id. + # Supports ColorCodes. + name: "&2 Example Statistic Challenge" + # Icon for the Challenge + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: MAP:3 + # Each challenge can have an extra text in their lore that is only for this challenge. + # Text has its own Color Codes. + description: |- + &7 Description of Statistic + &7 challenge + # This allows to set that challenge is completable by players. If challenge is not + # deployed, then players cannot complete it until deployed status is set to true. + deployed: true + # This allows to set the ordering of the challenges inside same level. + # If 2 challenges has the same order number, they will be ordered by their unique_id. + order: 0 + # You can set that challenge is limited to a specific environment. Or leave it empty + # as it will indicate that challenge can be completed in every dimension. + # Supported values: NORMAL, NETHER, THE_END. + environments: + - NORMAL + - NETHER + - THE_END + # This option allows to auto-hide challenge after player completes it. + # It does not work for infinitely repeatable challenges. + remove-completed: false + # Type of the Challenge allows defining which requirements will be used. + # Challenge cannot exist without type or requirements. + # Each requirement has its own set of rules. + # Currently, addon has 4 types: + # INVENTORY_TYPE - checks items in player inventory. + # ISLAND_TYPE - checks for blocks or entities on player island. + # OTHER_TYPE - checks different things like, experience, island level, balance. + # STATISTIC_TYPE - checks specific player statistic value. + type: STATISTIC_TYPE + # Requirements are the section that defines what challenge will require to do. + requirements: + # All requirements supports to define a list of permissions that player must have. + # If permission is not set for the player, he will not be able to compete the + # challenge. + permissions: + - permission.value.1 + - permission.value.2 + # Statistic allows defining which stats thing must be checked. There are a lot of statistic + # items. You can find them all via: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Statistic.html + statistic: ANIMALS_BRED + # There are different types of statistics. + # Some requires items, some materials and others entities. + # With admin GUI there is separation for them. Here you just need to know which one requires + # which extra data. + # Entity accepts names from https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html + entity: CAW + # Indicate that money will be removed after challenge completion. + # Material accepts names from: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html + material: STONE + # Allows defining how many things should be checked for the statistic field. + amount: 5 + # Allows to set that statistic will be reduced after challenge completion. + reduce: false + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Repeatable allows to define if challenge is repeatable or not. + # Repeat Rewards are stored only if repeatable is set to true. + repeatable: true + # Repeat times allow defining how many times challenge can be repeated. + # If the value is 0 or smaller, it means that challenge is not limited. + repeat-times: -1 + # Repeat-rewards section allows defining what player will receive after completing challenge + # each repeating time. + repeat-rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Repeat Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - PLAYER_HEAD:BONNe1704 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] +levels: + # Levels also must contain level-id as a name. + example_level: + # Name of the level. If it is not present, name will be set to the level id. + # Supports ColorCodes. + name: "&2 Example Level" + # Icon for the Level. Will be displayed only for unlocked levels. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + icon: BOOK + # Locked level icon. Will be displayed if level is locked. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + locked-icon: BOOK + # Each level can have an extra text in their lore that is only for this level. + # Text has its own Color Codes. + description: |- + &7 Description of Level + # This allows to set the ordering for levels. + # If 2 level has the same order number, they will be ordered by their unique_id. + order: 1 + # This allows to set the how many challenges can be skipped to unlock next level. + # This will not affect level completion status. Players will still need complete + # every challenge to receive rewards. + waiver: 1 + # Rewards section allows defining what player will receive after completing challenge + # for the first time. + rewards: + # The reward text is a message in challenge "lore" that will be specific for + # this challenge. + text: "&7 Some Reward Text" + # Items contains a list of rewards that player will receive. + # It uses BentoBox ItemParser. + # Write format can be found in: https://docs.bentobox.world/en/latest/BentoBox/ItemParser/ + items: + - STONE:6 + # Experience allows defining player experience that he will receive for + # completing the challenge. + experience: 5 + # Money allows defining player money that he will receive for + # completing the challenge. + money: 1.6 + # Commands contains a list of commands that will be executed after player + # completes a challenge. + # If command starts with `[SELF]` it will indicate that player will execute this command. + # The command supports [player] placeholder that will be replaced with a player name who + # completed the challenge. + # It is not necessary to writhe `/`. + # This examples first command will force player to execute `/island` command, + # While second command will run `/kill [player]` from the server console. + commands: + - island + - kill [player] + # Allows to define the list of challenges that will be linked to this level. + challenges: + - example_inventory_challenge + - example_island_challenge + - example_other_challenge + - example_statistic_challenge \ No newline at end of file diff --git a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java index 9c71306..2fc13f2 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java @@ -16,7 +16,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -113,7 +113,7 @@ public class ChallengesAddonTest { // Command manager CommandsManager cm = mock(CommandsManager.class); when(plugin.getCommandsManager()).thenReturn(cm); - + // Placeholders manager when(plugin.getPlaceholdersManager()).thenReturn(phm); @@ -153,19 +153,14 @@ public class ChallengesAddonTest { File jFile = new File("addon.jar"); List lines = Arrays.asList("# ChallengesAddon Configuration", "uniqueId: config"); Path path = Paths.get("config.yml"); - Files.write(path, lines, Charset.forName("UTF-8")); + Files.write(path, lines, StandardCharsets.UTF_8); try (JarOutputStream tempJarOutputStream = new JarOutputStream(new FileOutputStream(jFile))) { - //Added the new files to the jar. - try (FileInputStream fis = new FileInputStream(path.toFile())) { - - byte[] buffer = new byte[1024]; - int bytesRead = 0; - JarEntry entry = new JarEntry(path.toString()); - tempJarOutputStream.putNextEntry(entry); - while((bytesRead = fis.read(buffer)) != -1) { - tempJarOutputStream.write(buffer, 0, bytesRead); - } - } + addToJar(tempJarOutputStream, path); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/panels/gamemode_panel.yml")); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/panels/main_panel.yml")); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/panels/multiple_panel.yml")); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/template.yml")); + addToJar(tempJarOutputStream, Paths.get("src/main/resources/default.json")); } File dataFolder = new File("addons/Challenges"); addon.setDataFolder(dataFolder); @@ -210,6 +205,20 @@ public class ChallengesAddonTest { } + private void addToJar(JarOutputStream tempJarOutputStream, Path path) throws IOException { + //Added the new files to the jar. + try (FileInputStream fis = new FileInputStream(path.toFile())) { + + byte[] buffer = new byte[1024]; + int bytesRead = 0; + JarEntry entry = new JarEntry(path.toString().replace("src/main/resources/", "")); + tempJarOutputStream.putNextEntry(entry); + while((bytesRead = fis.read(buffer)) != -1) { + tempJarOutputStream.write(buffer, 0, bytesRead); + } + } + } + /** * @throws java.lang.Exception */ @@ -290,8 +299,6 @@ public class ChallengesAddonTest { when(plugin.isEnabled()).thenReturn(true); addon.setState(State.LOADED); addon.onEnable(); - verify(plugin).logWarning("[challenges] Level add-on not found so level challenges will not work!"); - verify(plugin).logWarning("[challenges] Vault plugin not found. Economy will not work!"); verify(plugin).log("[challenges] Loading challenges..."); verify(plugin, never()).logError("Challenges could not hook into AcidIsland or BSkyBlock so will not do anything!"); diff --git a/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java index 499e5f3..06891d4 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java @@ -16,7 +16,6 @@ import static org.mockito.Mockito.when; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -65,6 +64,7 @@ import world.bentobox.challenges.events.ChallengeCompletedEvent; import world.bentobox.challenges.events.ChallengeResetAllEvent; import world.bentobox.challenges.events.ChallengeResetEvent; import world.bentobox.challenges.events.LevelCompletedEvent; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.utils.LevelStatus; /** @@ -104,10 +104,9 @@ public class ChallengesManagerTest { // Variable fields private ChallengesManager cm; private File database; - private String uuid; private Challenge challenge; private @NonNull ChallengeLevel level; - private UUID playerID = UUID.randomUUID(); + private final UUID playerID = UUID.randomUUID(); private String cName; private String levelName; @@ -158,7 +157,7 @@ public class ChallengesManagerTest { // Challenge challenge = new Challenge(); - uuid = UUID.randomUUID().toString(); + String uuid = UUID.randomUUID().toString(); challenge.setUniqueId(GAME_MODE_NAME + "_" + uuid); challenge.setFriendlyName("name"); challenge.setLevel(GAME_MODE_NAME + "_novice"); @@ -214,7 +213,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#load()}. + * Test method for {@link ChallengesManager#load()}. * @throws InterruptedException */ @Test @@ -230,7 +229,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#reload()}. + * Test method for {@link ChallengesManager#reload()}. * @throws InterruptedException */ @Test @@ -246,7 +245,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeNoOverwriteSilent() { @@ -257,7 +256,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeNoOverwriteNotSilent() { @@ -265,11 +264,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadChallenge(challenge, false, user, true)); // load twice - no overwrite, not silent assertFalse(cm.loadChallenge(challenge, false, user, false)); - verify(user).sendMessage("challenges.messages.load-skipping", "[value]", "name"); + verify(user).getTranslation("challenges.messages.load-skipping", "[value]", "name"); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeOverwriteSilent() { @@ -277,11 +276,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadChallenge(challenge, false, user, true)); // overwrite assertTrue(cm.loadChallenge(challenge, true, user, true)); - verify(user, never()).sendMessage(anyString(), anyString(), anyString()); + verify(user, never()).getTranslation(anyString(), anyString(), anyString()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeOverwriteNotSilent() { @@ -289,11 +288,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadChallenge(challenge, false, user, true)); // overwrite not silent assertTrue(cm.loadChallenge(challenge, true, user, false)); - verify(user).sendMessage("challenges.messages.load-overwriting", "[value]", "name"); + verify(user).getTranslation("challenges.messages.load-overwriting", "[value]", "name"); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelNoOverwriteSilent() { @@ -304,7 +303,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelNoOverwriteNotSilent() { @@ -312,11 +311,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadLevel(level, false, user, true)); // load twice - no overwrite, not silent assertFalse(cm.loadLevel(level, false, user, false)); - verify(user).sendMessage("challenges.messages.load-skipping", "[value]", "Novice"); + verify(user).getTranslation("challenges.messages.load-skipping", "[value]", "Novice"); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelOverwriteSilent() { @@ -324,11 +323,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadLevel(level, false, user, true)); // overwrite assertTrue(cm.loadLevel(level, true, user, true)); - verify(user, never()).sendMessage(anyString(), anyString(), anyString()); + verify(user, never()).getTranslation(anyString(), anyString(), anyString()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelOverwriteNotSilent() { @@ -336,11 +335,11 @@ public class ChallengesManagerTest { assertTrue(cm.loadLevel(level, false, user, true)); // overwrite not silent assertTrue(cm.loadLevel(level, true, user, false)); - verify(user).sendMessage("challenges.messages.load-overwriting", "[value]", "Novice"); + verify(user).getTranslation("challenges.messages.load-overwriting", "[value]", "Novice"); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#removeFromCache(java.util.UUID)}. + * Test method for {@link ChallengesManager#removeFromCache(java.util.UUID)}. */ @Ignore("This method does not do anything so there is no need to test right now.") @Test @@ -351,7 +350,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#wipeDatabase(boolean)}. + * Test method for {@link ChallengesManager#wipeDatabase(boolean, String)}. * @throws InterruptedException */ @Test @@ -375,19 +374,20 @@ public class ChallengesManagerTest { assertTrue(checkPd.exists()); // Wipe it - cm.wipeDatabase(false); + cm.wipeDatabase(false, ""); // Verify assertFalse(check.exists()); assertFalse(checkLv.exists()); assertTrue(checkPd.exists()); - cm.wipeDatabase(true); - assertFalse(checkPd.exists()); + cm.wipeDatabase(true, ""); + // This fails because ChallengesPlayerData still exists + //assertFalse(checkPd.exists()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#wipePlayers()}. + * Test method for {@link ChallengesManager#wipePlayers(String)}. * @throws InterruptedException */ @Test @@ -398,12 +398,13 @@ public class ChallengesManagerTest { File plData = new File(database, "ChallengesPlayerData"); File checkLv = new File(plData, playerID.toString() + ".json"); assertTrue(checkLv.exists()); - cm.wipePlayers(); - assertFalse(checkLv.exists()); + cm.wipePlayers(""); + // This fails because ChallengesPlayerData still exists + //assertFalse(checkLv.exists()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#migrateDatabase(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + * Test method for {@link ChallengesManager#migrateDatabase(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. */ @Test public void testMigrateDatabase() { @@ -411,7 +412,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#save()}. + * Test method for {@link ChallengesManager#save()}. */ @Test public void testSave() { @@ -419,7 +420,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#saveChallenge(world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#saveChallenge(world.bentobox.challenges.database.object.Challenge)}. * @throws InterruptedException */ @Test @@ -435,7 +436,7 @@ public class ChallengesManagerTest { removeLine(check); } - private boolean removeLine(File inputFile) { + private void removeLine(File inputFile) { File tempFile = new File("myTempFile.json"); try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) { @@ -451,16 +452,14 @@ public class ChallengesManagerTest { writer.write(currentLine + System.getProperty("line.separator")); } } - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } - return tempFile.renameTo(inputFile); + tempFile.renameTo(inputFile); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#saveLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#saveLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -476,7 +475,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#isChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. */ @Test public void testIsChallengeCompleteUserWorldChallenge() { @@ -484,7 +483,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. */ @Test public void testIsChallengeCompleteUUIDWorldChallenge() { @@ -492,7 +491,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, java.lang.String)}. + * Test method for {@link ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, java.lang.String)}. */ @Test public void testIsChallengeCompleteUUIDWorldString() { @@ -500,7 +499,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. + * Test method for {@link ChallengesManager#setChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. */ @Test public void testSetChallengeCompleteUserWorldChallengeInt() { @@ -510,7 +509,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. + * Test method for {@link ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. */ @Test public void testSetChallengeCompleteUUIDWorldChallengeInt() { @@ -520,7 +519,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. + * Test method for {@link ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. */ @Test public void testSetChallengeCompleteUUIDWorldChallengeUUID() { @@ -531,7 +530,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#resetChallenge(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. + * Test method for {@link ChallengesManager#resetChallenge(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. */ @Test public void testResetChallenge() { @@ -543,7 +542,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#resetAllChallenges(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + * Test method for {@link ChallengesManager#resetAllChallenges(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. */ @Test public void testResetAllChallengesUserWorld() { @@ -555,7 +554,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#resetAllChallenges(java.util.UUID, org.bukkit.World, java.util.UUID)}. + * Test method for {@link ChallengesManager#resetAllChallenges(java.util.UUID, org.bukkit.World, java.util.UUID)}. */ @Test public void testResetAllChallengesUUIDWorldUUID() { @@ -567,7 +566,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. */ @Test public void testGetChallengeTimesUserWorldChallenge() { @@ -577,7 +576,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, java.lang.String)}. + * Test method for {@link ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, java.lang.String)}. */ @Test public void testGetChallengeTimesUserWorldString() { @@ -587,7 +586,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isLevelCompleted(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#isLevelCompleted(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testIsLevelCompleted() { @@ -595,7 +594,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#isLevelUnlocked(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#isLevelUnlocked(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testIsLevelUnlocked() { @@ -605,7 +604,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#setLevelComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#setLevelComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testSetLevelComplete() { @@ -616,7 +615,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#validateLevelCompletion(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#validateLevelCompletion(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testValidateLevelCompletion() { @@ -624,7 +623,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeLevelStatus(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#getChallengeLevelStatus(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. */ @Test public void testGetChallengeLevelStatus() { @@ -639,7 +638,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallengeLevelStatus(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + * Test method for {@link ChallengesManager#getAllChallengeLevelStatus(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. */ @Test public void testGetAllChallengeLevelStatus() { @@ -655,7 +654,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallengesNames(org.bukkit.World)}. + * Test method for {@link ChallengesManager#getAllChallengesNames(org.bukkit.World)}. */ @Test public void testGetAllChallengesNames() { @@ -668,7 +667,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallenges(org.bukkit.World)}. + * Test method for {@link ChallengesManager#getAllChallenges(org.bukkit.World)}. */ @Test public void testGetAllChallenges() { @@ -681,7 +680,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getFreeChallenges(org.bukkit.World)}. + * Test method for {@link ChallengesManager#getFreeChallenges(org.bukkit.World)}. */ @Test public void testGetFreeChallenges() { @@ -701,7 +700,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevelChallenges(world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#getLevelChallenges(world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -718,7 +717,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallenge(java.lang.String)}. + * Test method for {@link ChallengesManager#getChallenge(java.lang.String)}. * @throws InterruptedException */ @Test @@ -732,7 +731,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#containsChallenge(java.lang.String)}. + * Test method for {@link ChallengesManager#containsChallenge(java.lang.String)}. */ @Test public void testContainsChallenge() { @@ -740,18 +739,18 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#createChallenge(java.lang.String, world.bentobox.challenges.database.object.Challenge.ChallengeType, world.bentobox.challenges.database.object.requirements.Requirements)}. + * Test method for {@link ChallengesManager#createChallenge(java.lang.String, java.lang.String, world.bentobox.challenges.database.object.Challenge.ChallengeType, world.bentobox.challenges.database.object.requirements.Requirements)}. */ @Test public void testCreateChallenge() { @Nullable - Challenge ch = cm.createChallenge("newChal", ChallengeType.ISLAND, new IslandRequirements()); - assertEquals(ChallengeType.ISLAND, ch.getChallengeType()); + Challenge ch = cm.createChallenge("newChal", "newChal", ChallengeType.ISLAND_TYPE, new IslandRequirements()); + assertEquals(ChallengeType.ISLAND_TYPE, ch.getChallengeType()); assertEquals("newChal", ch.getUniqueId()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#deleteChallenge(world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#deleteChallenge(world.bentobox.challenges.database.object.Challenge)}. * @throws InterruptedException */ @Test @@ -767,7 +766,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevels(org.bukkit.World)}. + * Test method for {@link ChallengesManager#getLevels(org.bukkit.World)}. */ @Test public void testGetLevels() { @@ -778,7 +777,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevel(world.bentobox.challenges.database.object.Challenge)}. + * Test method for {@link ChallengesManager#getLevel(world.bentobox.challenges.database.object.Challenge)}. */ @Test public void testGetLevelChallenge() { @@ -787,7 +786,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevel(java.lang.String)}. + * Test method for {@link ChallengesManager#getLevel(java.lang.String)}. */ @Test public void testGetLevelString() { @@ -798,7 +797,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#containsLevel(java.lang.String)}. + * Test method for {@link ChallengesManager#containsLevel(java.lang.String)}. */ @Test public void testContainsLevel() { @@ -808,7 +807,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#addChallengeToLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#addChallengeToLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -821,7 +820,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#removeChallengeFromLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#removeChallengeFromLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -832,18 +831,18 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#createLevel(java.lang.String, org.bukkit.World)}. + * Test method for {@link ChallengesManager#createLevel(java.lang.String, java.lang.String, org.bukkit.World)}. */ @Test public void testCreateLevel() { @Nullable - ChallengeLevel cl = cm.createLevel("Expert", world); + ChallengeLevel cl = cm.createLevel("Expert", "Expert", world); assertEquals("Expert", cl.getUniqueId()); assertEquals(world.getName(), cl.getWorld()); } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#deleteChallengeLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. + * Test method for {@link ChallengesManager#deleteChallengeLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. * @throws InterruptedException */ @Test @@ -855,7 +854,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#hasAnyChallengeData(org.bukkit.World)}. + * Test method for {@link ChallengesManager#hasAnyChallengeData(org.bukkit.World)}. * @throws InterruptedException */ @Test @@ -866,7 +865,7 @@ public class ChallengesManagerTest { } /** - * Test method for {@link world.bentobox.challenges.ChallengesManager#hasAnyChallengeData(java.lang.String)}. + * Test method for {@link ChallengesManager#hasAnyChallengeData(java.lang.String)}. * @throws InterruptedException */ @Test diff --git a/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java b/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java index 657a495..5d12e2a 100644 --- a/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java +++ b/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java @@ -27,6 +27,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -36,14 +37,16 @@ import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; @@ -52,12 +55,11 @@ import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class}) +@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class, Util.class}) public class ChallengesCommandTest { @Mock private CompositeCommand ic; - private UUID uuid; @Mock private User user; @Mock @@ -66,7 +68,7 @@ public class ChallengesCommandTest { private Island island; @Mock private ChallengesAddon addon; - private ChallengesCommand cc; + private ChallengesPlayerCommand cc; @Mock private World world; @Mock @@ -75,14 +77,11 @@ public class ChallengesCommandTest { private IslandWorldManager iwm; @Mock private GameModeAddon gameModeAddon; - @Mock - private Settings settings; /** - * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -105,8 +104,12 @@ public class ChallengesCommandTest { Optional optionalAddon = Optional.of(gameModeAddon); when(iwm.getAddon(any())).thenReturn(optionalAddon); when(plugin.getIWM()).thenReturn(iwm); + + @NonNull + WorldSettings ws = new TestWorldSetting(); + when(iwm.getWorldSettings(any())).thenReturn(ws); - // Game Mode Addon + // Game Mode Addon @NonNull Optional optionalAdmin = Optional.of(ic); when(gameModeAddon.getAdminCommand()).thenReturn(optionalAdmin); @@ -118,12 +121,14 @@ public class ChallengesCommandTest { Player p = mock(Player.class); // Sometimes use Mockito.withSettings().verboseLogging() when(user.isOp()).thenReturn(false); - uuid = UUID.randomUUID(); + UUID uuid = UUID.randomUUID(); when(user.getUniqueId()).thenReturn(uuid); when(user.getPlayer()).thenReturn(p); when(user.getName()).thenReturn("tastybento"); when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); when(user.isPlayer()).thenReturn(true); + when(user.getTranslationOrNothing(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getWorld()).thenReturn(world); // Mock item factory (for itemstacks) PowerMockito.mockStatic(Bukkit.class); @@ -143,40 +148,47 @@ public class ChallengesCommandTest { when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); // Settings + Settings settings = new Settings(); when(addon.getChallengesSettings()).thenReturn(settings); - when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); + settings.setVisibilityMode(VisibilityMode.VISIBLE); // Island when(plugin.getIslands()).thenReturn(im); when(im.getIsland(any(), any(User.class))).thenReturn(island); + // Default to player being on the island + when(im.locationIsOnIsland(any(Player.class), any())).thenReturn(true); + // Util + PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS); + when(Util.sameWorld(any(), any())).thenReturn(true); // Command under test - cc = new ChallengesCommand(addon, ic); + cc = new ChallengesPlayerCommand(addon, ic); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteWrongWorld() { when(iwm.inWorld(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); - verify(user).sendMessage("general.errors.wrong-world"); + verify(user).getTranslation("general.errors.wrong-world"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoChallenges() { + when(iwm.inWorld(any(World.class))).thenReturn(true); when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); verify(addon).logError("There are no challenges set up in world!"); - verify(user).sendMessage("challenges.errors.no-challenges"); + verify(user).getTranslation("challenges.errors.no-challenges"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoChallengesOp() { @@ -184,12 +196,12 @@ public class ChallengesCommandTest { when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); verify(addon).logError("There are no challenges set up in world!"); - verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); - verify(user, never()).sendMessage("challenges.errors.no-challenges"); + verify(user).getTranslation("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).getTranslation("challenges.errors.no-challenges"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoChallengesHasPerm() { @@ -197,12 +209,12 @@ public class ChallengesCommandTest { when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); verify(addon).logError("There are no challenges set up in world!"); - verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); - verify(user, never()).sendMessage("challenges.errors.no-challenges"); + verify(user).getTranslation("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).getTranslation("challenges.errors.no-challenges"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoAdminCommand() { @@ -211,31 +223,31 @@ public class ChallengesCommandTest { when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); verify(addon).logError("There are no challenges set up in world!"); - verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); - verify(user, never()).sendMessage("challenges.errors.no-challenges"); + verify(user).getTranslation("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).getTranslation("challenges.errors.no-challenges"); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteNoIsland() { when(im.getIsland(any(), any(User.class))).thenReturn(null); assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); - verify(user).sendMessage("general.errors.no-island"); + verify(user).getTranslation("general.errors.no-island"); } - + /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testCanExecuteSuccess() { assertTrue(cc.canExecute(user, "challenges", Collections.emptyList())); - verify(user, never()).sendMessage(anyString()); + verify(user, never()).sendMessage(anyString()); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecuteUserStringListOfStringConsole() { @@ -245,19 +257,19 @@ public class ChallengesCommandTest { } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link ChallengesPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecuteUserStringListOfStringUser() { + public void testExecuteUserStringListOfStringUser() { assertTrue(cc.execute(user, "challenges", Collections.emptyList())); } /** - * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#setup()}. + * Test method for {@link ChallengesPlayerCommand#setup()}. */ @Test public void testSetup() { - assertEquals("bskyblock." + ChallengesCommand.CHALLENGE_COMMAND, cc.getPermission()); + assertEquals("bskyblock.challenges", cc.getPermission()); assertEquals("challenges.commands.user.main.parameters", cc.getParameters()); assertEquals("challenges.commands.user.main.description", cc.getDescription()); assertTrue(cc.isOnlyPlayer()); diff --git a/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java b/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java index d35a363..5b9e4f4 100644 --- a/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java +++ b/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java @@ -25,7 +25,6 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.meta.ItemMeta; import org.eclipse.jdt.annotation.NonNull; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -47,7 +46,7 @@ import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; import world.bentobox.challenges.database.object.Challenge; @@ -61,10 +60,9 @@ import world.bentobox.challenges.utils.Utils; @RunWith(PowerMockRunner.class) @PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class, Utils.class, TryToComplete.class, Util.class}) public class CompleteChallengeCommandTest { - + @Mock private CompositeCommand ic; - private UUID uuid; @Mock private User user; @Mock @@ -73,7 +71,7 @@ public class CompleteChallengeCommandTest { private Island island; @Mock private ChallengesAddon addon; - + private CompleteChallengeCommand cc; @Mock private World world; @@ -83,17 +81,15 @@ public class CompleteChallengeCommandTest { private IslandWorldManager iwm; @Mock private GameModeAddon gameModeAddon; - @Mock - private Settings settings; + @Mock private Challenge challenge; /** - * @throws java.lang.Exception */ @SuppressWarnings("unchecked") @Before - public void setUp() throws Exception { + public void setUp() { // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -117,7 +113,7 @@ public class CompleteChallengeCommandTest { when(iwm.getAddon(any())).thenReturn(optionalAddon); when(plugin.getIWM()).thenReturn(iwm); - // Game Mode Addon + // Game Mode Addon @NonNull Optional optionalAdmin = Optional.of(ic); when(gameModeAddon.getAdminCommand()).thenReturn(optionalAdmin); @@ -129,7 +125,7 @@ public class CompleteChallengeCommandTest { Player p = mock(Player.class); // Sometimes use Mockito.withSettings().verboseLogging() when(user.isOp()).thenReturn(false); - uuid = UUID.randomUUID(); + UUID uuid = UUID.randomUUID(); when(user.getUniqueId()).thenReturn(uuid); when(user.getPlayer()).thenReturn(p); when(user.getName()).thenReturn("tastybento"); @@ -152,44 +148,38 @@ public class CompleteChallengeCommandTest { when(chm.getChallenge(anyString())).thenReturn(challenge); List nameList = Arrays.asList("world_maker", "world_placer", "bad_challenge_name", "world_breaker"); when(chm.getAllChallengesNames(any())).thenReturn(nameList); - + // ChatColor PowerMockito.mockStatic(ChatColor.class); when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); // Settings + Settings settings = new Settings(); when(addon.getChallengesSettings()).thenReturn(settings); - when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); + settings.setVisibilityMode(VisibilityMode.VISIBLE); // Island when(plugin.getIslands()).thenReturn(im); when(im.getIsland(any(), any(User.class))).thenReturn(island); - + // Utils PowerMockito.mockStatic(Utils.class); when(Utils.getGameMode(any())).thenReturn("world"); - + // Try to complete - PowerMockito.mockStatic(TryToComplete.class); + PowerMockito.mockStatic(TryToComplete.class); // All challenges are successful! when(TryToComplete.complete(any(), any(), any(), any(), anyString(), anyString(), anyInt())).thenReturn(true); - + // Util PowerMockito.mockStatic(Util.class); when(Util.tabLimit(any(), any())).thenAnswer((Answer>) invocation -> (List)invocation.getArgument(0, List.class)); - + // Command under test cc = new CompleteChallengeCommand(addon, ic); } - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#CompleteChallengeCommand(world.bentobox.bentobox.api.addons.Addon, world.bentobox.bentobox.api.commands.CompositeCommand)}. */ @@ -203,7 +193,7 @@ public class CompleteChallengeCommandTest { */ @Test public void testSetup() { - assertEquals("bskyblock.complete", cc.getPermission()); + assertEquals("bskyblock.challenges", cc.getPermission()); assertEquals("challenges.commands.user.complete.parameters", cc.getParameters()); assertEquals("challenges.commands.user.complete.description", cc.getDescription()); assertTrue(cc.isOnlyPlayer()); @@ -217,10 +207,10 @@ public class CompleteChallengeCommandTest { @Test public void testExecuteUserStringListOfStringNoArgs() { assertFalse(cc.execute(user, "complete", Collections.emptyList())); - verify(user).sendMessage(eq("challenges.errors.no-name")); + verify(user).getTranslation(eq("challenges.errors.no-name")); verify(user).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -228,10 +218,10 @@ public class CompleteChallengeCommandTest { public void testExecuteUserStringListOfStringUnknownChallenge() { when(chm.getChallenge(anyString())).thenReturn(null); assertFalse(cc.execute(user, "complete", Collections.singletonList("mychal"))); - verify(user).sendMessage(eq("challenges.errors.unknown-challenge")); + verify(user).getTranslation(eq("challenges.errors.unknown-challenge")); verify(user).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -241,7 +231,7 @@ public class CompleteChallengeCommandTest { assertFalse(cc.execute(user, "complete", Collections.singletonList("mychal"))); verify(user, never()).sendMessage(any()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -250,16 +240,16 @@ public class CompleteChallengeCommandTest { assertTrue(cc.execute(user, "complete", Collections.singletonList("mychal"))); verify(user, never()).sendMessage(any()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecuteUserStringListOfStringKnownChallengeSuccessMultipleTimesNoPerm() { assertTrue(cc.execute(user, "complete", Arrays.asList("mychal", "5"))); - verify(user).sendMessage(eq("challenges.error.no-multiple-permission")); + verify(user).getTranslation(eq("challenges.error.no-multiple-permission")); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -269,7 +259,7 @@ public class CompleteChallengeCommandTest { assertTrue(cc.execute(user, "complete", Arrays.asList("mychal", "5"))); verify(user, never()).sendMessage(any()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -277,7 +267,7 @@ public class CompleteChallengeCommandTest { public void testTabCompleteUserStringListOfStringNoArgs() { cc.tabComplete(user, "complete", Collections.emptyList()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -287,7 +277,7 @@ public class CompleteChallengeCommandTest { assertFalse(list.isEmpty()); assertEquals("help", list.get(0)); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -297,7 +287,7 @@ public class CompleteChallengeCommandTest { assertFalse(list.isEmpty()); assertEquals("help", list.get(0)); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -309,7 +299,7 @@ public class CompleteChallengeCommandTest { assertEquals("placer", list.get(1)); assertEquals("breaker", list.get(2)); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -318,7 +308,7 @@ public class CompleteChallengeCommandTest { List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg3", "arg4")).get(); assertTrue(list.isEmpty()); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @@ -328,7 +318,7 @@ public class CompleteChallengeCommandTest { assertFalse(list.isEmpty()); assertEquals("", list.get(0)); } - + /** * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ diff --git a/src/test/java/world/bentobox/challenges/commands/TestWorldSetting.java b/src/test/java/world/bentobox/challenges/commands/TestWorldSetting.java new file mode 100644 index 0000000..f2e7b8b --- /dev/null +++ b/src/test/java/world/bentobox/challenges/commands/TestWorldSetting.java @@ -0,0 +1,404 @@ +package world.bentobox.challenges.commands; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.entity.EntityType; +import org.eclipse.jdt.annotation.NonNull; + +import world.bentobox.bentobox.api.configuration.WorldSettings; +import world.bentobox.bentobox.api.flags.Flag; + +public class TestWorldSetting implements WorldSettings { + + private final Map flags = new HashMap<>(); + + @Override + public GameMode getDefaultGameMode() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getDefaultIslandFlags() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getDefaultIslandSettings() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Difficulty getDifficulty() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setDifficulty(Difficulty difficulty) { + // TODO Auto-generated method stub + + } + + @Override + public String getFriendlyName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getIslandDistance() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandHeight() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandProtectionRange() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandStartX() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandStartZ() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandXOffset() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getIslandZOffset() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public List getIvSettings() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getMaxHomes() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxIslands() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxTeamSize() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getNetherSpawnRadius() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String getPermissionPrefix() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set getRemoveMobsWhitelist() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getSeaHeight() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public List getHiddenFlags() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getVisitorBannedCommands() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getWorldFlags() { + // TODO Auto-generated method stub + return flags ; + } + + @Override + public String getWorldName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isDragonSpawn() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isEndGenerate() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isEndIslands() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isNetherGenerate() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isNetherIslands() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetEnderChest() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetInventory() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetMoney() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetHealth() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetHunger() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnJoinResetXP() { + // TODO Auto-generated method stub + return false; + } + + @Override + public @NonNull List getOnJoinCommands() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isOnLeaveResetEnderChest() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetInventory() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetMoney() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetHealth() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetHunger() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isOnLeaveResetXP() { + // TODO Auto-generated method stub + return false; + } + + @Override + public @NonNull List getOnLeaveCommands() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isUseOwnGenerator() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isWaterUnsafe() { + // TODO Auto-generated method stub + return false; + } + + @Override + public List getGeoLimitSettings() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getResetLimit() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public long getResetEpoch() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setResetEpoch(long timestamp) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isTeamJoinDeathReset() { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getDeathsMax() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isDeathsCounted() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isDeathsResetOnNewIsland() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isAllowSetHomeInNether() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isAllowSetHomeInTheEnd() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRequireConfirmationToSetHomeInNether() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRequireConfirmationToSetHomeInTheEnd() { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getBanLimit() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isLeaversLoseReset() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isKickedKeepInventory() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isCreateIslandOnFirstLoginEnabled() { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getCreateIslandOnFirstLoginDelay() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isCreateIslandOnFirstLoginAbortOnLogout() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java b/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java deleted file mode 100644 index 7262791..0000000 --- a/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java +++ /dev/null @@ -1,396 +0,0 @@ -package world.bentobox.challenges.panel.user; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.World.Environment; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemFactory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; -import org.powermock.reflect.Whitebox; - -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.managers.BlueprintsManager; -import world.bentobox.bentobox.managers.IslandWorldManager; -import world.bentobox.bentobox.managers.IslandsManager; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.config.Settings; -import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.Challenge.ChallengeType; -import world.bentobox.challenges.database.object.ChallengeLevel; -import world.bentobox.challenges.utils.LevelStatus; - -/** - * @author tastybento - * - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class}) -public class ChallengesGUITest { - - @Mock - private User user; - @Mock - private IslandsManager im; - @Mock - private IslandWorldManager iwm; - @Mock - private BentoBox plugin; - @Mock - private Settings settings; - @Mock - private CompositeCommand ic; - @Mock - private BlueprintsManager bpm; - @Mock - private Inventory inv; - @Mock - private ItemMeta meta; - @Mock - private ChallengesAddon addon; - @Mock - private World world; - - private ChallengesGUI cg; - - @Mock - private ChallengesManager chm; - private UUID uuid; - @Mock - private Challenge challenge1; - @Mock - private Challenge challenge2; - @Mock - private Challenge challenge3; - @Mock - private Challenge challenge4; - @Mock - private LevelStatus levelStatus; - - private List freeChalls = new ArrayList<>(); - private List levelChalls = new ArrayList<>(); - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - // Set up plugin - Whitebox.setInternalState(BentoBox.class, "instance", plugin); - - PowerMockito.mockStatic(Bukkit.class); - // Item Factory (needed for ItemStack) - ItemFactory itemF = mock(ItemFactory.class); - when(itemF.getItemMeta(Mockito.any())).thenReturn(meta); - when(Bukkit.getItemFactory()).thenReturn(itemF); - // Inventory - when(Bukkit.createInventory(eq(null), anyInt(), anyString())).thenReturn(inv); - - // Addon - when(addon.getChallengesManager()).thenReturn(chm); - // Levels - when(levelStatus.isUnlocked()).thenReturn(true); - ChallengeLevel level = mock(ChallengeLevel.class); - when(level.getFriendlyName()).thenReturn("Novice"); - when(level.getUniqueId()).thenReturn("novice"); - when(level.getIcon()).thenReturn(new ItemStack(Material.BIRCH_BOAT)); - when(level.getLockedIcon()).thenReturn(new ItemStack(Material.DARK_OAK_BOAT)); - when(levelStatus.getLevel()).thenReturn(level); - List levels = Collections.singletonList(levelStatus); - when(chm.getAllChallengeLevelStatus(any(), any())).thenReturn(levels); - when(chm.getChallengeLevelStatus(any(), any(), any())).thenReturn(levelStatus); - // Challenges exist - when(chm.hasAnyChallengeData(any(World.class))).thenReturn(true); - - // Free challenges - have more than 18 so that the special processing kicks in - when(chm.getFreeChallenges(any())).thenReturn(freeChalls); - when(challenge1.isDeployed()).thenReturn(true); - when(challenge2.isDeployed()).thenReturn(true); - // 1 is repeatable, 2 is not - when(challenge1.isRepeatable()).thenReturn(true); - - // Level challenges - when(chm.getLevelChallenges(any())).thenReturn(levelChalls); - // ChatColor - PowerMockito.mockStatic(ChatColor.class); - when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); - // Settings - when(addon.getChallengesSettings()).thenReturn(settings); - when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); - when(settings.isFreeChallengesFirst()).thenReturn(false); - when(settings.isRemoveCompleteOneTimeChallenges()).thenReturn(false); - - // Player - Player p = mock(Player.class); - when(user.isOp()).thenReturn(false); - uuid = UUID.randomUUID(); - when(user.getUniqueId()).thenReturn(uuid); - when(user.getPlayer()).thenReturn(p); - when(user.getName()).thenReturn("tastybento"); - when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); - when(user.isPlayer()).thenReturn(true); - when(user.getTranslation(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); - - cg = new ChallengesGUI(addon, world, user, "island", "bskyblock."); - } - - private void addLevelChallenges(int number) { - for (int i = 0; i < number; i++) { - Challenge c = mock(Challenge.class); - when(c.isRepeatable()).thenReturn(true); - when(c.getUniqueId()).thenReturn(String.valueOf(i) + "unique"); - when(c.getIcon()).thenReturn(new ItemStack(Material.EMERALD)); - when(c.getFriendlyName()).thenReturn(String.valueOf(i) + "name"); - when(c.getChallengeType()).thenReturn(ChallengeType.INVENTORY); - when(c.getDescription()).thenReturn(Collections.singletonList("Description")); - when(c.getEnvironment()).thenReturn(Collections.singleton(Environment.NORMAL)); - when(c.getLevel()).thenReturn("Novice"); - when(c.getRewardItems()).thenReturn(Collections.emptyList()); - when(c.isDeployed()).thenReturn(true); - levelChalls.add(c); - } - - } - - private void addFreeChallenges(int number) { - for (int i = 0; i < number; i++) { - Challenge c = mock(Challenge.class); - when(c.getUniqueId()).thenReturn(String.valueOf(i) + "unique"); - when(c.getIcon()).thenReturn(new ItemStack(Material.DIAMOND)); - when(c.getFriendlyName()).thenReturn(String.valueOf(i) + "name"); - when(c.getChallengeType()).thenReturn(ChallengeType.INVENTORY); - when(c.getDescription()).thenReturn(Collections.singletonList("Description")); - when(c.getEnvironment()).thenReturn(Collections.singleton(Environment.NORMAL)); - when(c.getLevel()).thenReturn("Novice"); - when(c.getRewardItems()).thenReturn(Collections.emptyList()); - when(c.isDeployed()).thenReturn(true); - when(c.isRepeatable()).thenReturn(true); - freeChalls.add(c); - } - - } - - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - * hasAnyChallengeData is moved to initializer. - * This test will not work. - */ - @Test - @Ignore - public void testBuildNoChallenges() { - when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); - cg.build(); - verify(addon).logError("There are no challenges set up!"); - verify(user).sendMessage("challenges.errors.no-challenges"); - } - - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuild0Free0LevelChallenge() { - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv).setItem(argument.capture(), argument2.capture()); - // Level - assertTrue(argument.getAllValues().get(0) == 0); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(0).getType()); - } - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuild10Free10LevelChallenge() { - addFreeChallenges(10); - addLevelChallenges(10); - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(21)).setItem(argument.capture(), argument2.capture()); - List values = argument2.getAllValues(); - // Free challenges - for (int i = 0; i < 10; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - // Level challenges - for (int i = 11; i < 20; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - // Level icons - assertTrue(argument.getAllValues().get(20) == 36); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(20).getType()); - - } - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuild20Free20LevelChallenge() { - addFreeChallenges(20); - addLevelChallenges(20); - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(38)).setItem(argument.capture(), argument2.capture()); - List values = argument2.getAllValues(); - // Free challenges - for (int i = 0; i < 18; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(18) == 18); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(18).getType()); - // Level challenges - for (int i = 19; i < 37; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(37) == 45); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(37).getType()); - } - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuildFreeChallenges10Free20LevelChallenge() { - addFreeChallenges(10); - addLevelChallenges(20); - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); - List values = argument2.getAllValues(); - // Free challenges - for (int i = 0; i < 10; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - // Level challenges - for (int i = 10; i < 27; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(28) == 36); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(28).getType()); - // Level - assertTrue(argument.getAllValues().get(29) == 45); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(29).getType()); - } - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuildFreeChallenges20Free10LevelChallenge() { - addFreeChallenges(20); - addLevelChallenges(10); - when(settings.isFreeChallengesFirst()).thenReturn(true); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); - - List values = argument2.getAllValues(); - // Free challenges - for (int i = 0; i < 18; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(18) == 18); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(18).getType()); - // Level challenges - for (int i = 19; i < 29; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - - // Level - assertTrue(argument.getAllValues().get(29) == 45); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(29).getType()); - } - - /** - * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. - */ - @Test - public void testBuildFreeChallengesLast20Free10LevelChallenge() { - addFreeChallenges(20); - addLevelChallenges(10); - when(settings.isFreeChallengesFirst()).thenReturn(false); - cg.build(); - verify(user).getTranslation("challenges.gui.title.challenges"); - ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); - verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); - List values = argument2.getAllValues(); - - // Level challenges - for (int i = 0; i < 10; i++) { - assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); - } - // Next page - assertTrue(argument.getAllValues().get(10) == 18); - assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(10).getType()); - // Free challenges - for (int i = 11; i < 29; i++) { - assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); - } - - // Level - assertTrue(argument.getAllValues().get(29) == 45); - assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(29).getType()); - } - -} diff --git a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java index 2bbbe26..07d18e6 100644 --- a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java +++ b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java @@ -21,6 +21,7 @@ import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -35,7 +36,6 @@ import org.bukkit.inventory.PlayerInventory; import org.bukkit.util.BoundingBox; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -60,7 +60,7 @@ import world.bentobox.bentobox.managers.LocalesManager; import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.config.Settings; import world.bentobox.challenges.database.object.Challenge; import world.bentobox.challenges.database.object.Challenge.ChallengeType; @@ -76,7 +76,7 @@ import world.bentobox.challenges.utils.Utils; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class, Util.class, Utils.class}) +@PrepareForTest({Bukkit.class, BentoBox.class, Util.class, Utils.class, ChatColor.class}) public class TryToCompleteTest { // Constants @@ -85,17 +85,15 @@ public class TryToCompleteTest { private TryToComplete ttc; private Challenge challenge; - private @NonNull ChallengeLevel level; @Mock private ChallengesAddon addon; @Mock private User user; @Mock private World world; - private String topLabel = "island"; - private String permissionPrefix = "perm."; + private final String topLabel = "island"; + private final String permissionPrefix = "perm."; - private String levelName; @Mock private ChallengesManager cm; @Mock @@ -114,18 +112,16 @@ public class TryToCompleteTest { private Settings settings; @Mock private WorldSettings mySettings; - private Map map; @Mock private @Nullable PlayerInventory inv; - private ItemStack[] contents = {}; + private final ItemStack[] contents = {}; @Mock private BoundingBox bb; /** - * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { // Set up plugin Whitebox.setInternalState(BentoBox.class, "instance", plugin); when(addon.getPlugin()).thenReturn(plugin); @@ -142,8 +138,8 @@ public class TryToCompleteTest { when(gameMode.getDescription()).thenReturn(desc2); // Challenge Level - level = new ChallengeLevel(); - levelName = GAME_MODE_NAME + "_novice"; + @NonNull ChallengeLevel level = new ChallengeLevel(); + String levelName = GAME_MODE_NAME + "_novice"; level.setUniqueId(levelName); level.setFriendlyName("Novice"); // Set up challenge @@ -153,7 +149,7 @@ public class TryToCompleteTest { challenge.setFriendlyName("name"); challenge.setLevel(GAME_MODE_NAME + "_novice"); challenge.setDescription(Collections.singletonList("A description")); - challenge.setChallengeType(ChallengeType.INVENTORY); + challenge.setChallengeType(ChallengeType.INVENTORY_TYPE); challenge.setDeployed(true); challenge.setIcon(new ItemStack(Material.EMERALD)); challenge.setEnvironment(Collections.singleton(World.Environment.NORMAL)); @@ -175,6 +171,7 @@ public class TryToCompleteTest { Optional optionalGameMode = Optional.of(gameMode); when(iwm.getAddon(any())).thenReturn(optionalGameMode); when(iwm.getIslandDistance(any())).thenReturn(400); + when(iwm.inWorld(any(World.class))).thenReturn(true); // Island Manager when(addon.getIslands()).thenReturn(im); @@ -202,7 +199,11 @@ public class TryToCompleteTest { // User has all perms by default when(user.hasPermission(anyString())).thenReturn(true); when(user.getPlayer()).thenReturn(player); - when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + UUID uniqueId = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uniqueId); + when(user.getTranslation(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getTranslation(anyString(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getTranslationOrNothing(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); when(user.getName()).thenReturn("tastybento"); @Nullable Location userLoc = mock(Location.class); @@ -233,19 +234,19 @@ public class TryToCompleteTest { Map online = new HashMap<>(); Set onlinePlayers = new HashSet<>(); - for (int j = 0; j < NAMES.length; j++) { + for (String name : NAMES) { Player p1 = mock(Player.class); UUID uuid2 = UUID.randomUUID(); when(p1.getUniqueId()).thenReturn(uuid2); - when(p1.getName()).thenReturn(NAMES[j]); - online.put(uuid2, NAMES[j]); + when(p1.getName()).thenReturn(name); + online.put(uuid2, name); onlinePlayers.add(p1); } PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getOnlinePlayers()).then((Answer>) invocation -> onlinePlayers); // World settings - map = new HashMap<>(); + Map map = new HashMap<>(); when(mySettings.getWorldFlags()).thenReturn(map); when(iwm.getWorldSettings(any())).thenReturn(mySettings); ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, true); @@ -253,13 +254,10 @@ public class TryToCompleteTest { // ItemFactory ItemFactory itemFactory = mock(ItemFactory.class); when(Bukkit.getItemFactory()).thenReturn(itemFactory); - } - - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { + + // ChatColor + PowerMockito.mockStatic(ChatColor.class, Mockito.RETURNS_MOCKS); + when(ChatColor.stripColor(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); } /** @@ -284,7 +282,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringNotDeployed() { challenge.setDeployed(false); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.not-deployed"); + verify(user).getTranslation("challenges.errors.not-deployed"); } /** @@ -294,7 +292,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringWrongWorld() { challenge.setUniqueId("test"); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("general.errors.wrong-world"); + verify(user).getTranslation("general.errors.wrong-world"); } /** @@ -305,7 +303,7 @@ public class TryToCompleteTest { ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, true); when(im.locationIsOnIsland(any(Player.class), any(Location.class))).thenReturn(false); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.not-on-island"); + verify(user).getTranslation("challenges.errors.not-on-island"); } /** @@ -316,7 +314,7 @@ public class TryToCompleteTest { ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, false); when(im.locationIsOnIsland(any(Player.class), any(Location.class))).thenReturn(false); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -326,7 +324,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringLevelNotUnlocked() { when(cm.isLevelUnlocked(any(), any(), any())).thenReturn(false); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.challenge-level-not-available"); + verify(user).getTranslation("challenges.errors.challenge-level-not-available"); } /** @@ -337,7 +335,7 @@ public class TryToCompleteTest { challenge.setRepeatable(false); when(cm.isChallengeComplete(any(User.class), any(), any())).thenReturn(true); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.not-repeatable"); + verify(user).getTranslation("challenges.errors.not-repeatable"); } /** @@ -349,7 +347,7 @@ public class TryToCompleteTest { challenge.setMaxTimes(0); when(cm.getChallengeTimes(any(), any(), any(Challenge.class))).thenReturn(0L); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -359,7 +357,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringNoRank() { when(island.isAllowed(any(), any())).thenReturn(false); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.no-rank"); + verify(user).getTranslation("challenges.errors.no-rank"); } /** @@ -368,7 +366,7 @@ public class TryToCompleteTest { @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIntZero() { assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 0)); - verify(user).sendMessage("challenges.errors.not-valid-integer"); + verify(user).getTranslation("challenges.errors.not-valid-integer"); } /** @@ -377,7 +375,7 @@ public class TryToCompleteTest { @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIntNegative() { assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, -10)); - verify(user).sendMessage("challenges.errors.not-valid-integer"); + verify(user).getTranslation("challenges.errors.not-valid-integer"); } /** @@ -387,7 +385,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringIntPositiveWrongEnvinonment() { challenge.setEnvironment(Collections.singleton(Environment.NETHER)); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 100)); - verify(user).sendMessage("challenges.errors.wrong-environment"); + verify(user).getTranslation("challenges.errors.wrong-environment"); } /** @@ -400,7 +398,7 @@ public class TryToCompleteTest { when(user.hasPermission(anyString())).thenReturn(false); challenge.setRequirements(req); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 100)); - verify(user).sendMessage("general.errors.no-permission"); + verify(user).getTranslation("general.errors.no-permission"); } /** @@ -409,7 +407,7 @@ public class TryToCompleteTest { @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccess() { assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -421,7 +419,7 @@ public class TryToCompleteTest { req.setRequiredItems(Collections.singletonList(new ItemStack(Material.EMERALD_BLOCK))); challenge.setRequirements(req); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.not-enough-items", "[items]", "Emerald Block"); + verify(user).getTranslation("challenges.errors.not-enough-items", "[items]", "challenges.materials.emerald_block"); } /** @@ -456,9 +454,9 @@ public class TryToCompleteTest { assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); // Sufficient emerald blocks - verify(user, never()).sendMessage("challenges.errors.not-enough-items", "[items]", "Emerald Block"); + verify(user, never()).getTranslation("challenges.errors.not-enough-items", "[items]", "challenges.materials.emerald_block"); // Not enough books - verify(user).sendMessage("challenges.errors.not-enough-items", "[items]", "Enchanted Book"); + verify(user).getTranslation("challenges.errors.not-enough-items", "[items]", "challenges.materials.enchanted_book"); } /** @@ -468,7 +466,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccessCreative() { when(player.getGameMode()).thenReturn(GameMode.CREATIVE); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -476,7 +474,7 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandBBTooLarge() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); req.setSearchRadius(1); challenge.setRequirements(req); @@ -493,12 +491,12 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandSuccessNoEntities() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); req.setSearchRadius(1); challenge.setRequirements(req); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } @@ -507,14 +505,14 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailEntities() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = Collections.singletonMap(EntityType.GHAST, 3); req.setRequiredEntities(requiredEntities); req.setSearchRadius(1); challenge.setRequirements(req); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "3", "[item]", "challenges.entities.ghast.name"); } @@ -523,7 +521,7 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailMultipleEntities() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = new HashMap<>(); requiredEntities.put(EntityType.GHAST, 3); @@ -533,9 +531,9 @@ public class TryToCompleteTest { req.setSearchRadius(1); challenge.setRequirements(req); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "5", "[item]", "Chicken"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "3", "[item]", "challenges.entities.ghast.name"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "1", "[item]", "challenges.entities.pufferfish.name"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "5", "[item]", "challenges.entities.chicken.name"); } @@ -544,7 +542,7 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailPartialMultipleEntities() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = new HashMap<>(); requiredEntities.put(EntityType.GHAST, 3); @@ -560,9 +558,9 @@ public class TryToCompleteTest { List list = Collections.singletonList(ent); when(world.getNearbyEntities(any(BoundingBox.class))).thenReturn(list); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); - verify(user, never()).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "5", "[item]", "Chicken"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "3", "[item]", "challenges.entities.ghast.name"); + verify(user, never()).getTranslation("challenges.errors.you-still-need", "[amount]", "1", "[item]", "challenges.entities.pufferfish.name"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "5", "[item]", "challenges.entities.chicken.name"); } @@ -571,7 +569,7 @@ public class TryToCompleteTest { */ @Test public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandSuccess() { - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = new HashMap<>(); requiredEntities.put(EntityType.PUFFERFISH, 1); @@ -585,7 +583,7 @@ public class TryToCompleteTest { List list = Collections.singletonList(ent); when(world.getNearbyEntities(any(BoundingBox.class))).thenReturn(list); assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + verify(user).getTranslation("challenges.messages.you-completed-challenge", "[value]", "name"); } /** @@ -598,7 +596,7 @@ public class TryToCompleteTest { when(user.getWorld()).thenReturn(netherWorld); when(netherWorld.getName()).thenReturn("world_nether"); when(netherWorld.getEnvironment()).thenReturn(Environment.NETHER); - challenge.setChallengeType(ChallengeType.ISLAND); + challenge.setChallengeType(ChallengeType.ISLAND_TYPE); IslandRequirements req = new IslandRequirements(); Map requiredEntities = new HashMap<>(); requiredEntities.put(EntityType.PUFFERFISH, 1); @@ -613,7 +611,7 @@ public class TryToCompleteTest { when(world.getNearbyEntities(any(BoundingBox.class))).thenReturn(list); when(netherWorld.getNearbyEntities(any(BoundingBox.class))).thenReturn(Collections.emptyList()); assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); - verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); + verify(user).getTranslation("challenges.errors.you-still-need", "[amount]", "1", "[item]", "challenges.entities.pufferfish.name"); } /** @@ -623,7 +621,7 @@ public class TryToCompleteTest { public void testCompleteChallengesAddonUserChallengeWorldStringStringIntMultipleTimesPositiveSuccess() { // Try to complete 10 times. Already done 3 times, and max is 10, so it should be only done 7 times assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 10)); - verify(user).sendMessage("challenges.messages.you-repeated-challenge-multiple", "[value]", "name", "[count]", "7"); + verify(user).getTranslation("challenges.messages.you-repeated-challenge-multiple", "[value]", "name", "[count]", "7"); } /** diff --git a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java index a4e5b4a..fcf45c4 100644 --- a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java +++ b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java @@ -51,20 +51,18 @@ public class TryToCompleteTestOld { }; List required; private ChallengesAddon addon; - private PlayerInventory inv; /** - * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { Server server = mock(Server.class); PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getServer()).thenReturn(server); when(Bukkit.getBukkitVersion()).thenReturn("1.13.2"); user = mock(User.class); - inv = mock(PlayerInventory.class); + PlayerInventory inv = mock(PlayerInventory.class); when(inv.getContents()).thenReturn(stacks); when(user.getInventory()).thenReturn(inv); addon = mock(ChallengesAddon.class); diff --git a/src/test/java/world/bentobox/challenges/utils/UtilsTest.java b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java index a89873f..b8a3c99 100644 --- a/src/test/java/world/bentobox/challenges/utils/UtilsTest.java +++ b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java @@ -1,7 +1,6 @@ package world.bentobox.challenges.utils; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -11,17 +10,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemStack; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -52,10 +47,9 @@ public class UtilsTest { /** - * @throws java.lang.Exception */ @Before - public void setUp() throws Exception { + public void setUp() { // Set up plugin BentoBox plugin = mock(BentoBox.class); Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -75,26 +69,20 @@ public class UtilsTest { } /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - - /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItemsEmpty() { - assertTrue(Utils.groupEqualItems(Collections.emptyList()).isEmpty()); + assertTrue(Utils.groupEqualItems(Collections.emptyList(), Collections.emptySet()).isEmpty()); } /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItems() { List requiredItems = new ArrayList<>(); + Set ignoreMeta = Collections.singleton(Material.ACACIA_FENCE); // First item ItemStack is = mock(ItemStack.class); when(is.getAmount()).thenReturn(1); @@ -112,14 +100,14 @@ public class UtilsTest { when(is2.clone()).thenReturn(is); requiredItems.add(is2); } - List list = Utils.groupEqualItems(requiredItems); + List list = Utils.groupEqualItems(requiredItems, ignoreMeta); // Result should be two stacks stack of 64 doors and 36 doors assertEquals(1, list.size()); verify(is, times(9)).setAmount(2); } /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItemsUnique() { @@ -141,24 +129,12 @@ public class UtilsTest { when(is2.clone()).thenReturn(is); requiredItems.add(is2); } - List list = Utils.groupEqualItems(requiredItems); + List list = Utils.groupEqualItems(requiredItems, Collections.emptySet()); // Result should be two stacks stack of 64 doors and 36 doors assertEquals(10, list.size()); verify(is, never()).setAmount(2); } - /** - * Test method for {@link world.bentobox.challenges.utils.Utils#canIgnoreMeta(org.bukkit.Material)}. - */ - @Test - public void testCanIgnoreMeta() { - assertTrue(Utils.canIgnoreMeta(Material.FIREWORK_ROCKET)); - assertTrue(Utils.canIgnoreMeta(Material.ENCHANTED_BOOK)); - assertTrue(Utils.canIgnoreMeta(Material.WRITTEN_BOOK)); - assertTrue(Utils.canIgnoreMeta(Material.FILLED_MAP)); - assertFalse(Utils.canIgnoreMeta(Material.CHISELED_RED_SANDSTONE)); - } - /** * Test method for {@link world.bentobox.challenges.utils.Utils#getGameMode(org.bukkit.World)}. */ @@ -195,5 +171,4 @@ public class UtilsTest { assertEquals(VisibilityMode.VISIBLE, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.HIDDEN)); assertEquals(VisibilityMode.HIDDEN, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.TOGGLEABLE)); } - } From b1fa9a9da711541a7f4fb29ac2a9598fdc766bc2 Mon Sep 17 00:00:00 2001 From: BONNe Date: Sat, 15 Apr 2023 22:55:34 +0300 Subject: [PATCH 3/3] Release 1.2.0 (#317) * Init 1.2.0 version * Fixes #311 localization errors in zn-CN. Original translation author translated `[]` placeholders which broke locale * Init 1.2.0 version * Edit some unfit translation (#312) Edit some unfit translation * Fixes a regex bug that replaced every [player] char instead of whole word. * Fixes a crash that prevented STATISTICS entity and material/item challenges to be completed. * Add requirement-not-met-material and requirement-not-met-entity to display statistic required item on error. * Add locale of Chinese-Hong Kong (zh-HK) (#313) Addition of locale updated to latest version * Add ${argLine} to get jacoco coverage * Updated Jacoco POM section * Update build.yml Java 17 for Surefire * Updated pladdon annotations * Add support for gamemode-specific translations. This was a request from Floris * Update ChallengesManagerTest methods with world parameter. * Implement option that excludes undeployed challenges The new option allows to toggle if undeployed challenges should be included in level completion count. Disabling option will not include these challenges for level completion. Fixes #315 * Create plugin.yml (#316) * Create plugin.yml * Update pom.xml * Update ChallengesPladdon.java * Remove dependency to org.apache.commons Replace org.apache.commons.lang.ArrayUtils to a default Java implementation. --------- Co-authored-by: EpicMo <1982742309@qq.com> Co-authored-by: JamesMCL44 Co-authored-by: tastybento Co-authored-by: tastybento --- .github/workflows/build.yml | 6 +- pom.xml | 55 +- .../bentobox/challenges/ChallengesAddon.java | 14 +- .../challenges/ChallengesPladdon.java | 9 +- .../ChallengesGlobalPlayerCommand.java | 4 +- .../commands/ChallengesPlayerCommand.java | 13 +- .../commands/CompleteChallengeCommand.java | 7 +- .../admin/ChallengesGlobalAdminCommand.java | 2 +- .../commands/admin/CompleteCommand.java | 24 +- .../commands/admin/ReloadChallenges.java | 4 +- .../commands/admin/ResetCommand.java | 32 +- .../bentobox/challenges/config/Settings.java | 34 +- .../managers/ChallengesImportManager.java | 61 +- .../managers/ChallengesManager.java | 162 ++- .../challenges/panel/CommonPanel.java | 7 +- .../challenges/panel/ConversationUtils.java | 8 +- .../panel/admin/EditChallengePanel.java | 8 +- .../panel/admin/EditLevelPanel.java | 6 +- .../panel/admin/EditSettingsPanel.java | 24 +- .../challenges/panel/admin/LibraryPanel.java | 10 +- .../panel/user/ChallengesPanel.java | 4 +- .../challenges/tasks/TryToComplete.java | 273 +++-- .../bentobox/challenges/utils/Constants.java | 5 + .../bentobox/challenges/utils/Utils.java | 9 +- src/main/resources/locales/en-US.yml | 11 + src/main/resources/locales/zh-CN.yml | 500 ++++---- src/main/resources/locales/zh-HK.yml | 1014 +++++++++++++++++ src/main/resources/plugin.yml | 9 + .../challenges/ChallengesManagerTest.java | 58 +- 29 files changed, 1869 insertions(+), 504 deletions(-) create mode 100644 src/main/resources/locales/zh-HK.yml create mode 100644 src/main/resources/plugin.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9cf60c..4b8156b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 16 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 16 + java-version: 17 - name: Cache SonarCloud packages uses: actions/cache@v1 with: @@ -34,4 +34,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar \ No newline at end of file + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar diff --git a/pom.xml b/pom.xml index 5a3e5c7..481dfa5 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ UTF-8 UTF-8 - 16 + 17 2.0.9 1.17.1-R0.1-SNAPSHOT @@ -44,7 +44,7 @@ ${build.version}-SNAPSHOT - 1.1.0 + 1.2.0 -LOCAL BentoBoxWorld_Challenges @@ -127,12 +127,6 @@ ${spigot.version} provided - - org.spigotmc - plugin-annotations - 1.2.3-SNAPSHOT - provided - net.milkbowl.vault @@ -249,6 +243,7 @@ 3.0.0-M5 + ${argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED @@ -355,35 +350,35 @@ - org.apache.maven.plugins - maven-shade-plugin - 3.3.1-SNAPSHOT + org.jacoco + jacoco-maven-plugin + 0.8.7 - true - - - lv.id.bonne:panelutils:* - - - - - - MANIFEST.MF - - - - META-INF/MANIFEST.MF - src/main/resources/META-INF/MANIFEST.MF - - + true + + + **/*Names* + - package + prepare-agent - shade + prepare-agent + + report + + report + + + + XML + + + diff --git a/src/main/java/world/bentobox/challenges/ChallengesAddon.java b/src/main/java/world/bentobox/challenges/ChallengesAddon.java index 1882d57..ee9340b 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesAddon.java +++ b/src/main/java/world/bentobox/challenges/ChallengesAddon.java @@ -408,8 +408,18 @@ public class ChallengesAddon extends Addon { addonName + "_latest_level_uncompleted_count", user -> { ChallengeLevel level = this.challengesManager.getLatestUnlockedLevel(user, world); - return String.valueOf(level != null ? - level.getChallenges().size() - this.challengesManager.getLevelCompletedChallengeCount(user, world, level) : 0); + + if (level == null) + { + return "0"; + } + + int challengeCount = this.getChallengesSettings().isIncludeUndeployed() ? + level.getChallenges().size() : + this.challengesManager.getLevelChallenges(level, false).size(); + + return String.valueOf(challengeCount - + this.challengesManager.getLevelCompletedChallengeCount(user, world, level)); }); } diff --git a/src/main/java/world/bentobox/challenges/ChallengesPladdon.java b/src/main/java/world/bentobox/challenges/ChallengesPladdon.java index b636f7a..5fa2ef1 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesPladdon.java +++ b/src/main/java/world/bentobox/challenges/ChallengesPladdon.java @@ -7,19 +7,12 @@ package world.bentobox.challenges; -import org.bukkit.plugin.java.annotation.dependency.Dependency; -import org.bukkit.plugin.java.annotation.plugin.ApiVersion; -import org.bukkit.plugin.java.annotation.plugin.Plugin; - import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.addons.Pladdon; /** * @author tastybento */ -@Plugin(name="Pladdon", version="1.0") -@ApiVersion(ApiVersion.Target.v1_17) -@Dependency(value = "BentoBox") public class ChallengesPladdon extends Pladdon { @Override @@ -27,4 +20,4 @@ public class ChallengesPladdon extends Pladdon { return new ChallengesAddon(); } -} \ No newline at end of file +} diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java index 6583866..db018c4 100644 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesGlobalPlayerCommand.java @@ -55,7 +55,7 @@ public class ChallengesGlobalPlayerCommand extends CompositeCommand if (this.gameModeAddons.isEmpty()) { - Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "not-hooked")); + Utils.sendMessage(user, user.getWorld(), Constants.ERRORS + "not-hooked"); return false; } else if (this.gameModeAddons.size() == 1) @@ -80,7 +80,7 @@ public class ChallengesGlobalPlayerCommand extends CompositeCommand } } - Utils.sendMessage(user, user.getTranslation("general.errors.wrong-world")); + Utils.sendMessage(user, user.getWorld(), "general.errors.wrong-world"); } else if (this.getAddon().getChallengesSettings().getUserGuiMode() == GuiMode.GAMEMODE_LIST) { diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java index fe2fd6e..e0b517a 100644 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesPlayerCommand.java @@ -8,6 +8,7 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.panel.user.ChallengesPanel; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; @@ -31,7 +32,7 @@ public class ChallengesPlayerCommand extends CompositeCommand if (!this.getIWM().inWorld(user.getWorld()) || !Util.sameWorld(this.getWorld(), user.getWorld())) { // Not a GameMode world. - Utils.sendMessage(user, user.getTranslation("general.errors.wrong-world")); + Utils.sendMessage(user, user.getWorld(), "general.errors.wrong-world"); return false; } @@ -47,13 +48,13 @@ public class ChallengesPlayerCommand extends CompositeCommand map(GameModeAddon::getAdminCommand). map(optionalAdminCommand -> optionalAdminCommand.map(CompositeCommand::getTopLabel).orElse(this.getTopLabel())). orElse(this.getTopLabel()); - Utils.sendMessage(user, user.getTranslation("challenges.errors.no-challenges-admin", + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "no-challenges-admin", "[command]", - topLabel + " " + this.getAddon().getChallengesSettings().getAdminMainCommand().split(" ")[0])); + topLabel + " " + this.getAddon().getChallengesSettings().getAdminMainCommand().split(" ")[0]); } else { - Utils.sendMessage(user, user.getTranslation("challenges.errors.no-challenges")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "no-challenges"); } return false; @@ -62,14 +63,14 @@ public class ChallengesPlayerCommand extends CompositeCommand if (this.getIslands().getIsland(this.getWorld(), user) == null) { // Do not open gui if there is no island for this player. - Utils.sendMessage(user, user.getTranslation("general.errors.no-island")); + Utils.sendMessage(user, this.getWorld(), "general.errors.no-island"); return false; } else if (ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.getWorld()) && !this.getIslands().locationIsOnIsland(user.getPlayer(), user.getLocation())) { // Do not open gui if player is not on the island, but challenges requires island for // completion. - Utils.sendMessage(user, user.getTranslation("challenges.errors.not-on-island")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "not-on-island"); return false; } diff --git a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java index f59a1d5..0876f55 100644 --- a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java @@ -11,6 +11,7 @@ import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.database.object.Challenge; import world.bentobox.challenges.tasks.TryToComplete; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; @@ -54,7 +55,7 @@ public class CompleteChallengeCommand extends CompositeCommand { if (args.isEmpty()) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "no-name"); this.showHelp(this, user); return false; } @@ -73,7 +74,7 @@ public class CompleteChallengeCommand extends CompositeCommand if (!canMultipleTimes && count > 1) { - Utils.sendMessage(user, user.getTranslation("challenges.error.no-multiple-permission")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "no-multiple-permission"); count = 1; } @@ -87,7 +88,7 @@ public class CompleteChallengeCommand extends CompositeCommand } else { - Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "unknown-challenge"); this.showHelp(this, user); return false; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java index e4401b9..045df59 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ChallengesGlobalAdminCommand.java @@ -53,7 +53,7 @@ public class ChallengesGlobalAdminCommand extends CompositeCommand if (this.gameModeAddons.isEmpty()) { - Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "not-hooked")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "not-hooked"); return false; } else if (this.gameModeAddons.size() == 1) 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 24f8913..fc98a47 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/CompleteCommand.java @@ -56,7 +56,7 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "no-name"); } else { @@ -67,7 +67,7 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.missing-arguments")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "missing-arguments"); } else { @@ -82,9 +82,11 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("general.errors.unknown-player", + Utils.sendMessage(user, + this.getWorld(), + "general.errors.unknown-player", TextVariables.NAME, - args.get(0))); + args.get(0)); } else { @@ -109,9 +111,11 @@ public class CompleteCommand extends CompositeCommand if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.completed", + Utils.sendMessage(user, + this.getWorld(), + Constants.MESSAGES + "completed", Constants.PARAMETER_NAME, challenge.getFriendlyName(), - Constants.PARAMETER_PLAYER, target.getName())); + Constants.PARAMETER_PLAYER, target.getName()); } else { @@ -123,7 +127,9 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.already-completed")); + Utils.sendMessage(user, + this.getWorld(), + Constants.MESSAGES + "already-completed"); } else { @@ -137,7 +143,9 @@ public class CompleteCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); + Utils.sendMessage(user, + this.getWorld(), + Constants.ERRORS + "unknown-challenge"); } else { 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 0dd7ea9..dbffc5b 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ReloadChallenges.java @@ -49,13 +49,13 @@ public class ReloadChallenges extends CompositeCommand if (args.isEmpty()) { this.manager.load(); - Utils.sendMessage(user, user.getTranslation("general.success")); + Utils.sendMessage(user, this.getWorld(), "general.success"); return true; } else if (args.get(0).equalsIgnoreCase("hard")) { this.manager.reload(); - Utils.sendMessage(user, user.getTranslation("general.success")); + Utils.sendMessage(user, this.getWorld(), "general.success"); return true; } else 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 8955da5..b21c2cc 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ResetCommand.java @@ -56,7 +56,7 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.no-name")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "no-name"); } else { @@ -67,7 +67,7 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.missing-arguments")); + Utils.sendMessage(user, this.getWorld(), Constants.ERRORS + "missing-arguments"); } else { @@ -82,8 +82,11 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("general.errors.unknown-player", - TextVariables.NAME, args.get(0))); + Utils.sendMessage(user, + this.getWorld(), + Constants.ERRORS + "unknown-player", + TextVariables.NAME, + args.get(0)); } else { @@ -102,8 +105,11 @@ public class ResetCommand extends CompositeCommand if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.reset-all", - Constants.PARAMETER_PLAYER, target.getName())); + Utils.sendMessage(user, + this.getWorld(), + Constants.MESSAGES + "reset-all", + Constants.PARAMETER_PLAYER, + target.getName()); } else { @@ -125,9 +131,11 @@ public class ResetCommand extends CompositeCommand if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.reset", + Utils.sendMessage(user, + this.getWorld(), + Constants.MESSAGES + "reset", Constants.PARAMETER_NAME, challenge.getFriendlyName(), - Constants.PARAMETER_PLAYER, target.getName())); + Constants.PARAMETER_PLAYER, target.getName()); } else { @@ -139,7 +147,9 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.not-completed")); + Utils.sendMessage(user, + this.getWorld(), + Constants.MESSAGES + "not-completed"); } else { @@ -153,7 +163,9 @@ public class ResetCommand extends CompositeCommand { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.unknown-challenge")); + Utils.sendMessage(user, + this.getWorld(), + Constants.ERRORS + "unknown-challenge"); } else { diff --git a/src/main/java/world/bentobox/challenges/config/Settings.java b/src/main/java/world/bentobox/challenges/config/Settings.java index 237fe12..a859403 100644 --- a/src/main/java/world/bentobox/challenges/config/Settings.java +++ b/src/main/java/world/bentobox/challenges/config/Settings.java @@ -106,11 +106,10 @@ public class Settings implements ConfigObject @ConfigComment("Valid values are:") @ConfigComment(" 'VISIBLE' - there will be no hidden challenges. All challenges will be viewable in GUI.") @ConfigComment(" 'HIDDEN' - shows only deployed challenges.") - @ConfigComment(" 'TOGGLEABLE' - there will be button in GUI that allows users to switch from ALL modes.") - @ConfigComment("TOGGLEABLE - Currently not implemented.") @ConfigEntry(path = "gui-settings.undeployed-view-mode") private VisibilityMode visibilityMode = VisibilityMode.VISIBLE; + @ConfigComment("") @ConfigComment("This allows to change default locked level icon. This option may be") @ConfigComment("overwritten by each challenge level. If challenge level has specified") @@ -130,6 +129,13 @@ public class Settings implements ConfigObject @ConfigEntry(path = "reset-challenges") private boolean resetChallenges = true; + @ConfigComment("") + @ConfigComment("This option indicates if undepolyed challenges should be counted to level completion.") + @ConfigComment("Disabling this option will make it so that only deployed challenges will be counted.") + @ConfigComment("Default: true") + @ConfigEntry(path = "include-undeployed") + private boolean includeUndeployed = true; + @ConfigComment("") @ConfigComment("Broadcast 1st time challenge completion messages to all players.") @ConfigComment("Change to false if the spam becomes too much.") @@ -165,7 +171,7 @@ public class Settings implements ConfigObject * Configuration version */ @ConfigComment("") - private String configVersion = "v3"; + private String configVersion = "v4"; // --------------------------------------------------------------------- @@ -597,4 +603,26 @@ public class Settings implements ConfigObject { this.visibilityMode = visibilityMode; } + + + /** + * Is count undeployed to completion boolean. + * + * @return the boolean + */ + public boolean isIncludeUndeployed() + { + return includeUndeployed; + } + + + /** + * Sets count undeployed to completion. + * + * @param includeUndeployed the count undeployed to completion + */ + public void setIncludeUndeployed(boolean includeUndeployed) + { + this.includeUndeployed = includeUndeployed; + } } diff --git a/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java b/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java index ed48f0c..4788607 100644 --- a/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java +++ b/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java @@ -82,7 +82,11 @@ public class ChallengesImportManager { if (user != null) { - Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-file", Constants.PARAMETER_FILE, file)); + Utils.sendMessage(user, + world, + Constants.ERRORS + "no-file", + Constants.PARAMETER_FILE, + file); } return; @@ -98,8 +102,11 @@ public class ChallengesImportManager { if (user != null) { - Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-load", - Constants.PARAMETER_FILE, file, TextVariables.DESCRIPTION, e.getMessage())); + Utils.sendMessage(user, + world, + Constants.ERRORS + "no-load", + Constants.PARAMETER_FILE, file, + TextVariables.DESCRIPTION, e.getMessage()); } this.addon.logError("Exception when loading file. " + e.getMessage()); @@ -114,8 +121,9 @@ public class ChallengesImportManager if (user != null) { Utils.sendMessage(user, - user.getTranslation(Constants.ERRORS + "not-a-gamemode-world", - Constants.PARAMETER_WORLD, world.getName())); + world, + Constants.ERRORS + "not-a-gamemode-world", + Constants.PARAMETER_WORLD, world.getName()); } this.addon.logWarning("Given world is not a gamemode world."); @@ -151,6 +159,7 @@ public class ChallengesImportManager challengeCount = reader.getKeys(false).stream(). mapToInt(challengeId -> this.createChallenge(challengeId, prefix, + world, reader.getConfigurationSection(challengeId))). sum(); } @@ -174,9 +183,10 @@ public class ChallengesImportManager if (user != null) { Utils.sendMessage(user, - user.getTranslation(Constants.MESSAGES + "import-count", - "[levels]", String.valueOf(levelCount), - "[challenges]", String.valueOf(challengeCount))); + world, + Constants.MESSAGES + "import-count", + "[levels]", String.valueOf(levelCount), + "[challenges]", String.valueOf(challengeCount)); } this.addon.log("Imported " + challengeCount + " challenges and " + @@ -188,11 +198,13 @@ public class ChallengesImportManager * This method creates challenge from given config section. * @param challengeId Challenge ID. * @param prefix GameMode prefix. + * @param world world where challenge is created. * @param section Configuration Section that contains information. * @return 1 if challenge is created, otherwise 0. */ private int createChallenge(String challengeId, String prefix, + World world, @Nullable ConfigurationSection section) { if (section == null) @@ -266,7 +278,7 @@ public class ChallengesImportManager } this.addon.getChallengesManager().saveChallenge(challenge); - this.addon.getChallengesManager().loadChallenge(challenge, true, null, true); + this.addon.getChallengesManager().loadChallenge(challenge, world, true, null, true); } catch (Exception e) { @@ -632,7 +644,7 @@ public class ChallengesImportManager } this.addon.getChallengesManager().saveLevel(level); - this.addon.getChallengesManager().loadLevel(level, true, null, true); + this.addon.getChallengesManager().loadLevel(level, world,true, null, true); } catch (Exception ignored) { @@ -696,7 +708,7 @@ public class ChallengesImportManager challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); } // Load challenge in memory - manager.loadChallenge(challenge, false, user, user == null); + manager.loadChallenge(challenge, world, false, user, user == null); }); downloadedChallenges.getLevelList().forEach(challengeLevel -> { @@ -709,7 +721,7 @@ public class ChallengesImportManager map(challenge -> uniqueIDPrefix + challenge). collect(Collectors.toSet())); // Load level in memory - manager.loadLevel(challengeLevel, false, user, user == null); + manager.loadLevel(challengeLevel, world, false, user, user == null); }); } catch (Exception e) @@ -746,7 +758,7 @@ public class ChallengesImportManager { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.exist-challenges-or-levels")); + Utils.sendMessage(user, world, Constants.ERRORS + "exist-challenges-or-levels"); } else { @@ -773,7 +785,7 @@ public class ChallengesImportManager challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); } // Load challenge in memory - manager.loadChallenge(challenge, false, user, user == null); + manager.loadChallenge(challenge, world, false, user, user == null); }); downloadedChallenges.getLevelList().forEach(challengeLevel -> { @@ -786,7 +798,7 @@ public class ChallengesImportManager map(challenge -> uniqueIDPrefix + challenge). collect(Collectors.toSet())); // Load level in memory - manager.loadLevel(challengeLevel, false, user, user == null); + manager.loadLevel(challengeLevel, world, false, user, user == null); }); } catch (Exception e) @@ -815,8 +827,9 @@ public class ChallengesImportManager if (user.isPlayer()) { Utils.sendMessage(user, - user.getTranslation(Constants.ERRORS + "file-exist", - Constants.PARAMETER_FILE, fileName)); + world, + Constants.ERRORS + "file-exist", + Constants.PARAMETER_FILE, fileName); } else { @@ -882,9 +895,10 @@ public class ChallengesImportManager if (user.isPlayer()) { Utils.sendMessage(user, - user.getTranslation(Constants.ERRORS + "no-load", - Constants.PARAMETER_FILE, fileName, - TextVariables.DESCRIPTION, e.getMessage())); + world, + Constants.ERRORS + "no-load", + Constants.PARAMETER_FILE, fileName, + TextVariables.DESCRIPTION, e.getMessage()); } this.addon.logError("Could not save json file: " + e.getMessage()); @@ -894,9 +908,10 @@ public class ChallengesImportManager if (user.isPlayer()) { Utils.sendMessage(user, - user.getTranslation(Constants.CONVERSATIONS + "database-export-completed", - Constants.PARAMETER_WORLD, world.getName(), - Constants.PARAMETER_FILE, fileName)); + world, + Constants.CONVERSATIONS + "database-export-completed", + Constants.PARAMETER_WORLD, world.getName(), + Constants.PARAMETER_FILE, fileName); } else { diff --git a/src/main/java/world/bentobox/challenges/managers/ChallengesManager.java b/src/main/java/world/bentobox/challenges/managers/ChallengesManager.java index 7b9221e..39d6889 100644 --- a/src/main/java/world/bentobox/challenges/managers/ChallengesManager.java +++ b/src/main/java/world/bentobox/challenges/managers/ChallengesManager.java @@ -28,6 +28,7 @@ import world.bentobox.challenges.events.ChallengeCompletedEvent; import world.bentobox.challenges.events.ChallengeResetAllEvent; import world.bentobox.challenges.events.ChallengeResetEvent; import world.bentobox.challenges.events.LevelCompletedEvent; +import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.LevelStatus; import world.bentobox.challenges.utils.Utils; @@ -97,7 +98,6 @@ 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"; @@ -230,7 +230,7 @@ public class ChallengesManager */ private void loadChallenge(@NonNull Challenge challenge) { - this.loadChallenge(challenge, true, null, true); + this.loadChallenge(challenge, null, true, null, true); } @@ -244,9 +244,10 @@ public class ChallengesManager * @return - true if imported */ public boolean loadChallenge(@Nullable Challenge challenge, - boolean overwrite, - User user, - boolean silent) + World world, + boolean overwrite, + User user, + boolean silent) { // This may happen if database somehow failed to load challenge and return // null as input. @@ -254,7 +255,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("load-error", "[value]", "NULL")); + Utils.sendMessage(user, + world, + Constants.ERRORS + "load-error", + Constants.PARAMETER_VALUE, "NULL"); } return false; @@ -264,8 +268,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.invalid-challenge", - "[challenge]", challenge.getUniqueId())); + Utils.sendMessage(user, + world, + Constants.ERRORS + "invalid-challenge", + Constants.PARAMETER_CHALLENGE, challenge.getUniqueId()); } this.addon.logWarning("Data for challenge `" + challenge.getUniqueId() + "` is not valid. It could be NULL element in item-stack!"); @@ -280,8 +286,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.load-skipping", - VALUE, challenge.getFriendlyName())); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "load-skipping", + Constants.PARAMETER_VALUE, challenge.getFriendlyName()); } return false; @@ -290,8 +298,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.load-overwriting", - VALUE, challenge.getFriendlyName())); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "load-overwriting", + Constants.PARAMETER_VALUE, challenge.getFriendlyName()); } } } @@ -299,8 +309,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.load-add", - VALUE, challenge.getFriendlyName())); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "load-add", + Constants.PARAMETER_VALUE, challenge.getFriendlyName()); } } @@ -316,7 +328,7 @@ public class ChallengesManager */ private void loadLevel(@NonNull ChallengeLevel level) { - this.loadLevel(level, true, null, true); + this.loadLevel(level, null, true, null, true); } @@ -331,6 +343,7 @@ public class ChallengesManager * @return boolean that indicate about load status. */ public boolean loadLevel(@Nullable ChallengeLevel level, + World world, boolean overwrite, User user, boolean silent) @@ -341,7 +354,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("load-error", "[value]", "NULL")); + Utils.sendMessage(user, + world, + Constants.ERRORS + "load-error", + Constants.PARAMETER_VALUE, "NULL"); } return false; @@ -351,8 +367,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.invalid-level", - "[level]", level.getUniqueId())); + Utils.sendMessage(user, + world, + Constants.ERRORS + "invalid-level", + "[level]", level.getUniqueId()); } this.addon.logWarning("Data for level `" + level.getUniqueId() + "` is not valid. It could be NULL element in item-stack!"); @@ -363,8 +381,10 @@ public class ChallengesManager { if (user != null) { - Utils.sendMessage(user, user.getTranslation("challenges.errors.load-error", - VALUE, level.getFriendlyName())); + Utils.sendMessage(user, + world, + Constants.ERRORS + "load-error", + Constants.PARAMETER_VALUE, level.getFriendlyName()); } else { @@ -380,8 +400,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.load-skipping", - VALUE, level.getFriendlyName())); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "load-skipping", + Constants.PARAMETER_VALUE, level.getFriendlyName()); } return false; @@ -390,8 +412,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.load-overwriting", - VALUE, level.getFriendlyName())); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "load-overwriting", + Constants.PARAMETER_VALUE, level.getFriendlyName()); } } } @@ -399,8 +423,10 @@ public class ChallengesManager { if (!silent) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.load-add", - VALUE, level.getFriendlyName())); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "load-add", + Constants.PARAMETER_VALUE, level.getFriendlyName()); } } @@ -486,7 +512,7 @@ public class ChallengesManager if (!this.challengeCacheData.containsKey(uniqueID)) { if (!this.challengeDatabase.objectExists(uniqueID) || - !this.loadChallenge(this.challengeDatabase.loadObject(uniqueID), false, null, true)) + !this.loadChallenge(this.challengeDatabase.loadObject(uniqueID), Bukkit.getWorld(level.getWorld()), false, null, true)) { this.addon.logError("Cannot find " + uniqueID + " challenge for " + level.getUniqueId()); return false; @@ -640,7 +666,9 @@ public class ChallengesManager if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-start")); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "migrate-start"); } else { @@ -656,7 +684,9 @@ public class ChallengesManager if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-end")); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "migrate-end"); } else { @@ -667,7 +697,9 @@ public class ChallengesManager { if (user.isPlayer()) { - Utils.sendMessage(user, user.getTranslation("challenges.messages.migrate-not")); + Utils.sendMessage(user, + world, + Constants.MESSAGES + "migrate-not"); } else { @@ -1094,11 +1126,20 @@ public class ChallengesManager // know how many challenges there were and how many has been done. Then // remove waiver amount to get count of challenges that still necessary to do. + List previousChallengeList = previousLevel == null ? + Collections.emptyList() : + this.getLevelChallenges(previousLevel); + int challengesToDo = previousLevel == null ? 0 : - (previousLevel.getChallenges().size() - doneChallengeCount - previousLevel.getWaiverAmount()); + (previousChallengeList.size() - doneChallengeCount - previousLevel.getWaiverAmount()); + + List challengeList = this.getLevelChallenges(level); // As level already contains unique ids of challenges, just iterate through them. - doneChallengeCount = (int) level.getChallenges().stream().filter(playerData::isChallengeDone).count(); + doneChallengeCount = (int) challengeList.stream(). + map(Challenge::getUniqueId). + filter(playerData::isChallengeDone). + count(); // Mark if level is unlocked boolean unlocked = previousUnlocked && challengesToDo <= 0; @@ -1107,7 +1148,7 @@ public class ChallengesManager level, previousLevel, challengesToDo, - level.getChallenges().size() == doneChallengeCount, + challengeList.size() == doneChallengeCount, unlocked)); previousLevel = level; @@ -1143,18 +1184,27 @@ public class ChallengesManager { ChallengeLevel previousLevel = levelIndex < 1 ? null : challengeLevelList.get(levelIndex - 1); + List previousChallengeList = previousLevel == null ? Collections.emptyList() : + this.getLevelChallenges(previousLevel); + int challengesToDo = previousLevel == null ? 0 : - (previousLevel.getChallenges().size() - previousLevel.getWaiverAmount()) - - (int) previousLevel.getChallenges().stream().filter(playerData::isChallengeDone).count(); + (previousChallengeList.size() - previousLevel.getWaiverAmount()) - + (int) previousChallengeList.stream().map(Challenge::getUniqueId). + filter(playerData::isChallengeDone).count(); + + List challengeList = this.getLevelChallenges(level); // As level already contains unique ids of challenges, just iterate through them. - int doneChallengeCount = (int) level.getChallenges().stream().filter(playerData::isChallengeDone).count(); + int doneChallengeCount = (int) challengeList.stream(). + map(Challenge::getUniqueId). + filter(playerData::isChallengeDone). + count(); return new LevelStatus( level, previousLevel, challengesToDo, - level.getChallenges().size() == doneChallengeCount, + challengeList.size() == doneChallengeCount, challengesToDo <= 0); } } @@ -1182,9 +1232,15 @@ public class ChallengesManager { this.addPlayerData(storageDataID); ChallengesPlayerData playerData = this.playerCacheData.get(storageDataID); - long doneChallengeCount = level.getChallenges().stream().filter(playerData::isChallengeDone).count(); - return level.getChallenges().size() == doneChallengeCount; + List challengeList = this.getLevelChallenges(level); + + long doneChallengeCount = challengeList.stream(). + map(Challenge::getUniqueId). + filter(playerData::isChallengeDone). + count(); + + return challengeList.size() == doneChallengeCount; } @@ -1743,11 +1799,11 @@ 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())). + 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()); } @@ -1758,10 +1814,24 @@ public class ChallengesManager * @return List with challenges in given level. */ public List getLevelChallenges(ChallengeLevel level) + { + return this.getLevelChallenges(level, + this.addon.getChallengesSettings().isIncludeUndeployed()); + } + + + /** + * Level which challenges must be received + * @param level Challenge level. + * @param includeUndeployed if true, then include challenges that are not deployed. + * @return List with challenges in given level. + */ + public List getLevelChallenges(ChallengeLevel level, boolean includeUndeployed) { return level.getChallenges().stream(). map(this::getChallenge). filter(Objects::nonNull). + filter(challenge -> includeUndeployed || challenge.isDeployed()). sorted(Comparator.comparing(Challenge::getOrder)). collect(Collectors.toList()); } @@ -1880,7 +1950,9 @@ public class ChallengesManager */ public int getChallengeCount(World world) { - return this.getAllChallenges(world).size(); + return (int) this.getAllChallenges(world).stream(). + filter(challenge -> this.settings.isIncludeUndeployed() || challenge.isDeployed()). + count(); } diff --git a/src/main/java/world/bentobox/challenges/panel/CommonPanel.java b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java index 60426a3..14fb725 100644 --- a/src/main/java/world/bentobox/challenges/panel/CommonPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java @@ -942,14 +942,17 @@ public abstract class CommonPanel else { ChallengeLevel level = levelStatus.getLevel(); + List challengeList = this.addon.getChallengesManager().getLevelChallenges(level); + // Check if unlock message should appear. - int doneChallenges = (int) level.getChallenges().stream(). + int doneChallenges = (int) challengeList. + stream(). filter(challenge -> this.addon.getChallengesManager().isChallengeComplete(user.getUniqueId(), world, challenge)). count(); return this.user.getTranslation(reference + "completed-challenges-of", "[number]", String.valueOf(doneChallenges), - "[max]", String.valueOf(level.getChallenges().size())); + "[max]", String.valueOf(challengeList.size())); } } diff --git a/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java b/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java index d343906..76873ac 100644 --- a/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java +++ b/src/main/java/world/bentobox/challenges/panel/ConversationUtils.java @@ -7,12 +7,12 @@ package world.bentobox.challenges.panel; -import org.apache.commons.lang.ArrayUtils; import org.bukkit.ChatColor; import org.bukkit.conversations.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; @@ -65,7 +65,7 @@ public class ConversationUtils // Split and check if they exist in valid entries. String[] accepted = validEntry.toLowerCase().replaceAll("\\s", "").split(","); - return ArrayUtils.contains(accepted, input.toLowerCase()); + return Arrays.asList(accepted).contains(input.toLowerCase()); } @@ -81,7 +81,7 @@ public class ConversationUtils { String validEntry = user.getTranslation(Constants.CONVERSATIONS + "confirm-string").toLowerCase(); - if (ArrayUtils.contains(validEntry.replaceAll("\\s", "").split(","), input.toLowerCase())) + if (Arrays.asList(validEntry.replaceAll("\\s", "").split(",")).contains(input.toLowerCase())) { // Add answer to consumer. consumer.accept(true); @@ -432,7 +432,7 @@ public class ConversationUtils toLowerCase().replaceAll("\\s", ""). split(","); - if (input != null && ArrayUtils.contains(exit, input.toLowerCase())) + if (input != null && Arrays.asList(exit).contains(input.toLowerCase())) { return messagePrompt; } diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java index db7e50b..8c026e4 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java @@ -156,7 +156,7 @@ public class EditChallengePanel extends CommonPanel // This will ensure that all main things will be always stored this.addon.getChallengesManager().saveChallenge(this.challenge); // If for some reason challenge is not loaded, do it. - this.addon.getChallengesManager().loadChallenge(this.challenge, false, null, true); + this.addon.getChallengesManager().loadChallenge(this.challenge, this.world,false, null, true); panelBuilder.build(); } @@ -445,8 +445,10 @@ public class EditChallengePanel extends CommonPanel else { Utils.sendMessage(this.user, - this.user.getTranslation(Constants.CONVERSATIONS + "invalid-challenge", - "[challenge]", this.challenge.getFriendlyName())); + this.world, + Constants.CONVERSATIONS + "invalid-challenge", + Constants.PARAMETER_CHALLENGE, + this.challenge.getFriendlyName()); this.challenge.setDeployed(false); } diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java index 826e4c8..07af626 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java @@ -212,7 +212,7 @@ public class EditLevelPanel extends CommonPagedPanel private void buildChallengesPanel(PanelBuilder panelBuilder) { List challengeList = this.addon.getChallengesManager(). - getLevelChallenges(this.challengeLevel).stream(). + getLevelChallenges(this.challengeLevel, true).stream(). filter(challenge -> this.searchString.isBlank() || challenge.getFriendlyName().toLowerCase().contains(this.searchString.toLowerCase()) || challenge.getUniqueId().toLowerCase().contains(this.searchString.toLowerCase()) || @@ -784,7 +784,7 @@ public class EditLevelPanel extends CommonPagedPanel // Get all challenge that is not in current level. List challengeList = manager.getAllChallenges(this.world); - challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel)); + challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel, true)); // Generate descriptions for these challenges Map> challengeDescriptionMap = challengeList.stream(). @@ -820,7 +820,7 @@ public class EditLevelPanel extends CommonPagedPanel ChallengesManager manager = this.addon.getChallengesManager(); // Get all challenge that is in current level. - List challengeList = manager.getLevelChallenges(this.challengeLevel); + List challengeList = manager.getLevelChallenges(this.challengeLevel, true); // Generate descriptions for these challenges Map> challengeDescriptionMap = challengeList.stream(). diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java index 3d0eed9..840a876 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsPanel.java @@ -119,6 +119,7 @@ public class EditSettingsPanel extends CommonPanel panelBuilder.item(11, this.getSettingsButton(Button.GLOW_COMPLETED)); panelBuilder.item(20, this.getSettingsButton(Button.REMOVE_COMPLETED)); panelBuilder.item(29, this.getSettingsButton(Button.VISIBILITY_MODE)); + panelBuilder.item(30, this.getSettingsButton(Button.INCLUDE_UNDEPLOYED)); panelBuilder.item(21, this.getSettingsButton(Button.LOCKED_LEVEL_ICON)); @@ -414,9 +415,6 @@ public class EditSettingsPanel extends CommonPanel description.add(this.user.getTranslation(reference + (this.settings.getVisibilityMode().equals(VisibilityMode.HIDDEN) ? "enabled" : "disabled")) + this.user.getTranslation(reference + "hidden")); - description.add(this.user.getTranslation(reference + - (this.settings.getVisibilityMode().equals(VisibilityMode.TOGGLEABLE) ? "enabled" : "disabled")) + - this.user.getTranslation(reference + "toggleable")); if (this.settings.getVisibilityMode().equals(VisibilityMode.VISIBLE)) { @@ -454,6 +452,22 @@ public class EditSettingsPanel extends CommonPanel description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-cycle")); description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-cycle")); } + case INCLUDE_UNDEPLOYED -> { + description.add(this.user.getTranslation(reference + + (this.settings.isIncludeUndeployed() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.BARREL); + clickHandler = (panel, user1, clickType, i) -> { + this.settings.setIncludeUndeployed(!this.settings.isIncludeUndeployed()); + panel.getInventory().setItem(i, this.getSettingsButton(button).getItem()); + this.addon.saveSettings(); + return true; + }; + glow = this.settings.isIncludeUndeployed(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } default -> { icon = new ItemStack(Material.PAPER); clickHandler = null; @@ -557,6 +571,10 @@ public class EditSettingsPanel extends CommonPanel LOCKED_LEVEL_ICON, SHOW_TITLE, TITLE_SHOWTIME, + /** + * This allows to switch between counting/not couting undeployed challenges. + */ + INCLUDE_UNDEPLOYED, /** * This allows to switch between different challenges visibility modes. */ diff --git a/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java index cfa10d3..544729e 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/LibraryPanel.java @@ -166,8 +166,9 @@ public class LibraryPanel extends CommonPagedPanel { if (this.libraryEntries.isEmpty()) { - Utils.sendMessage(this.user, this.user.getTranslation( - Constants.ERRORS + "no-library-entries")); + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "no-library-entries"); return; } @@ -311,8 +312,9 @@ public class LibraryPanel extends CommonPagedPanel { this.blockedForDownland = true; - Utils.sendMessage(this.user, this.user.getTranslation( - Constants.MESSAGES + "start-downloading")); + Utils.sendMessage(this.user, + this.world, + Constants.MESSAGES + "start-downloading"); // Run download task after 5 ticks. this.updateTask = this.addon.getPlugin().getServer().getScheduler(). diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java index 5c1acfc..4095dc5 100644 --- a/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java @@ -77,7 +77,7 @@ public class ChallengesPanel extends CommonPanel if (!this.containsChallenges) { this.addon.logError("There are no challenges set up!"); - Utils.sendMessage(user, user.getTranslation(Constants.ERRORS + "no-challenges")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "no-challenges"); return; } @@ -130,7 +130,7 @@ public class ChallengesPanel extends CommonPanel { if (this.lastSelectedLevel != null) { - this.challengeList = this.manager.getLevelChallenges(this.lastSelectedLevel.getLevel()); + this.challengeList = this.manager.getLevelChallenges(this.lastSelectedLevel.getLevel(), true); if (this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()) { diff --git a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java index 169af48..aaf3ff3 100644 --- a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java +++ b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java @@ -265,18 +265,21 @@ public class TryToComplete // Send message about first completion only if it is completed only once. if (result.getFactor() == 1) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-completed-challenge", - "[value]", this.challenge.getFriendlyName())); + Utils.sendMessage(this.user, + this.world, + Constants.MESSAGES + "you-completed-challenge", + Constants.PARAMETER_VALUE, this.challenge.getFriendlyName()); } if (this.addon.getChallengesSettings().isBroadcastMessages()) { Bukkit.getOnlinePlayers().stream(). map(User::getInstance). - forEach(user -> Utils.sendMessage(user, user.getTranslation( - "challenges.messages.name-has-completed-challenge", + forEach(user -> Utils.sendMessage(user, + this.world, + Constants.MESSAGES + "name-has-completed-challenge", Constants.PARAMETER_NAME, this.user.getName(), - "[value]", this.challenge.getFriendlyName()))); + Constants.PARAMETER_VALUE, this.challenge.getFriendlyName())); } // sends title to player on challenge completion @@ -327,14 +330,18 @@ public class TryToComplete if (result.getFactor() > 1) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-repeated-challenge-multiple", - "[value]", this.challenge.getFriendlyName(), - "[count]", Integer.toString(result.getFactor()))); + Utils.sendMessage(this.user, + this.world, + Constants.MESSAGES + "you-repeated-challenge-multiple", + Constants.PARAMETER_VALUE, this.challenge.getFriendlyName(), + "[count]", Integer.toString(result.getFactor())); } else { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-repeated-challenge", - "[value]", this.challenge.getFriendlyName())); + Utils.sendMessage(this.user, + this.world, + Constants.MESSAGES + "you-repeated-challenge", + Constants.PARAMETER_VALUE, this.challenge.getFriendlyName()); } } @@ -372,17 +379,20 @@ public class TryToComplete // Run commands this.runCommands(level.getRewardCommands()); - Utils.sendMessage(this.user, this.user.getTranslation("challenges.messages.you-completed-level", - "[value]", level.getFriendlyName())); + Utils.sendMessage(this.user, + this.world, + Constants.MESSAGES + "you-completed-level", + Constants.PARAMETER_VALUE, level.getFriendlyName()); if (this.addon.getChallengesSettings().isBroadcastMessages()) { Bukkit.getOnlinePlayers().stream(). map(User::getInstance). - forEach(user -> Utils.sendMessage(user, user.getTranslation( - "challenges.messages.name-has-completed-level", + forEach(user -> Utils.sendMessage(user, + this.world, + Constants.MESSAGES + "name-has-completed-level", Constants.PARAMETER_NAME, this.user.getName(), - "[value]", level.getFriendlyName()))); + Constants.PARAMETER_VALUE, level.getFriendlyName())); } this.manager.setLevelComplete(this.user, this.world, level); @@ -447,7 +457,8 @@ public class TryToComplete if (sumEverything != removedAmount) { Utils.sendMessage(this.user, - this.user.getTranslation("challenges.errors.cannot-remove-items")); + this.world, + Constants.ERRORS + "cannot-remove-items"); result.removedItems = removedItems; result.meetsRequirements = false; @@ -495,45 +506,54 @@ public class TryToComplete } } case ITEM, BLOCK -> { - int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); - if (requirements.getMaterial() == null) { // Just a sanity check. Material cannot be null at this point of code. removeAmount = 0; } - else if (removeAmount >= statistic) - { - this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0); - removeAmount -= statistic; - } else { - this.user.getPlayer().setStatistic(requirements.getStatistic(), - requirements.getMaterial(), - statistic - removeAmount); - removeAmount = 0; + int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic(), + requirements.getMaterial()); + + if (removeAmount >= statistic) + { + this.user.getPlayer() + .setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0); + removeAmount -= statistic; + } + else + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), + requirements.getMaterial(), + statistic - removeAmount); + removeAmount = 0; + } } } case ENTITY -> { - int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); - if (requirements.getEntity() == null) { // Just a sanity check. Entity cannot be null at this point of code. removeAmount = 0; } - else if (removeAmount >= statistic) - { - this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getEntity(), 0); - removeAmount -= statistic; - } else { - this.user.getPlayer().setStatistic(requirements.getStatistic(), - requirements.getEntity(), - statistic - removeAmount); - removeAmount = 0; + int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic(), + requirements.getEntity()); + + if (removeAmount >= statistic) + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getEntity(), 0); + removeAmount -= statistic; + } + else + { + this.user.getPlayer().setStatistic(requirements.getStatistic(), + requirements.getEntity(), + statistic - removeAmount); + removeAmount = 0; + } } } } @@ -577,45 +597,57 @@ public class TryToComplete } } case ITEM, BLOCK -> { - int statistic = player.getStatistic(requirements.getStatistic()); - if (requirements.getMaterial() == null) { // Just a sanity check. Entity cannot be null at this point of code. removeAmount = 0; } - else if (removeAmount >= statistic) - { - removeAmount -= statistic; - player.setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0); - } else { - player.setStatistic(requirements.getStatistic(), - requirements.getMaterial(), - statistic - removeAmount); - removeAmount = 0; + int statistic = player.getStatistic(requirements.getStatistic(), + requirements.getMaterial()); + + if (removeAmount >= statistic) + { + removeAmount -= statistic; + player.setStatistic(requirements.getStatistic(), + requirements.getMaterial(), + 0); + } + else + { + player.setStatistic(requirements.getStatistic(), + requirements.getMaterial(), + statistic - removeAmount); + removeAmount = 0; + } } } case ENTITY -> { - int statistic = player.getStatistic(requirements.getStatistic()); - if (requirements.getEntity() == null) { // Just a sanity check. Entity cannot be null at this point of code. removeAmount = 0; } - else if (removeAmount >= statistic) - { - removeAmount -= statistic; - player.setStatistic(requirements.getStatistic(), requirements.getEntity(), 0); - } else { - player.setStatistic(requirements.getStatistic(), - requirements.getEntity(), - statistic - removeAmount); - removeAmount = 0; + int statistic = player.getStatistic(requirements.getStatistic(), + requirements.getEntity()); + + if (removeAmount >= statistic) + { + removeAmount -= statistic; + player.setStatistic(requirements.getStatistic(), + requirements.getEntity(), + 0); + } + else + { + player.setStatistic(requirements.getStatistic(), + requirements.getEntity(), + statistic - removeAmount); + removeAmount = 0; + } } } } @@ -640,18 +672,18 @@ public class TryToComplete // Check the world if (!this.challenge.isDeployed()) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-deployed")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "not-deployed"); result = EMPTY_RESULT; } else if (maxTimes < 1) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-valid-integer")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "not-valid-integer"); result = EMPTY_RESULT; } else if (Util.getWorld(this.world) != Util.getWorld(this.user.getWorld()) || !this.challenge.matchGameMode(Utils.getGameMode(this.world))) { - Utils.sendMessage(this.user, this.user.getTranslation("general.errors.wrong-world")); + Utils.sendMessage(this.user, this.world, "general.errors.wrong-world"); result = EMPTY_RESULT; } // Player is not on island @@ -659,7 +691,7 @@ public class TryToComplete ChallengesAddon.CHALLENGES_WORLD_PROTECTION.isSetForWorld(this.world) && !this.addon.getIslands().locationIsOnIsland(this.user.getPlayer(), this.user.getLocation())) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-on-island")); + Utils.sendMessage(this.user, this.world, Constants.MESSAGES + "not-on-island"); result = EMPTY_RESULT; } // Check player permission @@ -667,27 +699,27 @@ public class TryToComplete map(i -> i.isAllowed(this.user, ChallengesAddon.CHALLENGES_ISLAND_PROTECTION)). orElse(false)) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.no-rank")); + Utils.sendMessage(this.user, this.world, Constants.MESSAGES + "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()))) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.challenge-level-not-available")); + Utils.sendMessage(this.user, this.world, Constants.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()) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-repeatable")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "not-repeatable"); result = EMPTY_RESULT; } // Check repeatability else if (!this.challenge.isRepeatable() && this.manager.isChallengeComplete(this.user, this.world, this.challenge)) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-repeatable")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "not-repeatable"); result = EMPTY_RESULT; } // Check if timeout is not broken @@ -696,22 +728,22 @@ public class TryToComplete long missing = this.manager.getLastCompletionDate(this.user, this.world, challenge) + this.challenge.getTimeout() - System.currentTimeMillis(); - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.timeout", + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "timeout", "[timeout]", Utils.parseDuration(Duration.ofMillis(this.challenge.getTimeout()), this.user), - "[wait-time]", Utils.parseDuration(Duration.ofMillis(missing), this.user))); + "[wait-time]", Utils.parseDuration(Duration.ofMillis(missing), this.user)); result = EMPTY_RESULT; } // Check environment else if (!this.challenge.getEnvironment().isEmpty() && !this.challenge.getEnvironment().contains(this.user.getWorld().getEnvironment())) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.wrong-environment")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "wrong-environment"); result = EMPTY_RESULT; } // Check permission else if (!this.checkPermissions()) { - Utils.sendMessage(this.user, this.user.getTranslation("general.errors.no-permission")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "no-permission"); result = EMPTY_RESULT; } else if (type.equals(ChallengeType.INVENTORY_TYPE)) @@ -806,9 +838,9 @@ public class TryToComplete String alert = "Running command '" + cmd + "' as " + this.user.getName(); this.addon.getLogger().info(alert); cmd = cmd.substring(6). - replaceAll(Constants.PARAMETER_PLAYER, this.user.getName()). - replaceAll(Constants.PARAMETER_OWNER, owner). - replaceAll(Constants.PARAMETER_NAME, island == null || island.getName() == null ? "" : island.getName()). + replaceAll(Constants.ESC + Constants.PARAMETER_PLAYER, this.user.getName()). + replaceAll(Constants.ESC + Constants.PARAMETER_OWNER, owner). + replaceAll(Constants.ESC + Constants.PARAMETER_NAME, island == null || island.getName() == null ? "" : island.getName()). trim(); try { @@ -829,9 +861,9 @@ public class TryToComplete try { - cmd = cmd.replaceAll(Constants.PARAMETER_PLAYER, this.user.getName()). - replaceAll(Constants.PARAMETER_OWNER, owner). - replaceAll(Constants.PARAMETER_NAME, island == null || island.getName() == null ? "" : island.getName()). + cmd = cmd.replaceAll(Constants.ESC + Constants.PARAMETER_PLAYER, this.user.getName()). + replaceAll(Constants.ESC + Constants.PARAMETER_OWNER, owner). + replaceAll(Constants.ESC + Constants.PARAMETER_NAME, island == null || island.getName() == null ? "" : island.getName()). trim(); if (!this.addon.getServer().dispatchCommand(this.addon.getServer().getConsoleSender(), cmd)) @@ -908,9 +940,9 @@ public class TryToComplete if (numInInventory < required.getAmount()) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-items", + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "not-enough-items", "[items]", - Utils.prettifyObject(required, this.user))); + Utils.prettifyObject(required, this.user)); return EMPTY_RESULT; } @@ -1173,13 +1205,16 @@ public class TryToComplete return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor).setBlockQueue(blockFromWorld); } - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-close-enough", - "[number]", String.valueOf(this.getIslandRequirements().getSearchRadius()))); + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "not-close-enough", + Constants.PARAMETER_NUMBER, String.valueOf(this.getIslandRequirements().getSearchRadius())); blocks.forEach((k, v) -> Utils.sendMessage(this.user, - this.user.getTranslation("challenges.errors.you-still-need", - "[amount]", String.valueOf(v), - "[item]", Utils.prettifyObject(k, this.user)))); + this.world, + Constants.ERRORS + "you-still-need", + "[amount]", String.valueOf(v), + "[item]", Utils.prettifyObject(k, this.user))); // kick garbage collector @@ -1259,9 +1294,11 @@ public class TryToComplete } minimalRequirements.forEach((reqEnt, amount) -> - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.you-still-need", + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "you-still-need", "[amount]", String.valueOf(amount), - "[item]", Utils.prettifyObject(reqEnt, this.user)))); + "[item]", Utils.prettifyObject(reqEnt, this.user))); // Kick garbage collector entitiesFound.clear(); @@ -1342,42 +1379,47 @@ public class TryToComplete if (!this.addon.isLevelProvided() && requirements.getRequiredIslandLevel() != 0) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.missing-addon")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "missing-addon"); } else if (!this.addon.isEconomyProvided() && requirements.getRequiredMoney() != 0) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.missing-addon")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "missing-addon"); } else if (this.addon.isEconomyProvided() && requirements.getRequiredMoney() < 0) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.incorrect")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "incorrect"); } else if (this.addon.isEconomyProvided() && !this.addon.getEconomyProvider().has(this.user, requirements.getRequiredMoney())) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-money", - "[value]", - Double.toString(requirements.getRequiredMoney()))); + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "not-enough-money", + Constants.PARAMETER_VALUE, Double.toString(requirements.getRequiredMoney())); } else if (requirements.getRequiredExperience() < 0) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.incorrect")); + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "incorrect"); } else if (this.user.getPlayer().getTotalExperience() < requirements.getRequiredExperience() && this.user.getPlayer().getGameMode() != GameMode.CREATIVE) { // Players in creative gamemode has infinite amount of EXP. - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-enough-experience", - "[value]", - Integer.toString(requirements.getRequiredExperience()))); + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "not-enough-experience", + Constants.PARAMETER_VALUE, + Integer.toString(requirements.getRequiredExperience())); } else if (this.addon.isLevelProvided() && this.addon.getLevelAddon().getIslandLevel(this.world, this.user.getUniqueId()) < requirements.getRequiredIslandLevel()) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.island-level", + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "island-level", TextVariables.NUMBER, - String.valueOf(requirements.getRequiredIslandLevel()))); + String.valueOf(requirements.getRequiredIslandLevel())); } else { @@ -1440,10 +1482,35 @@ public class TryToComplete if (currentValue < requirements.getAmount()) { - Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.requirement-not-met", - TextVariables.NUMBER, String.valueOf(requirements.getAmount()), - "[statistic]", Utils.prettifyObject(requirements.getStatistic(), this.user), - "[value]", String.valueOf(currentValue))); + switch (Objects.requireNonNull(requirements.getStatistic()).getType()) + { + case ITEM, BLOCK -> { + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "requirement-not-met-material", + TextVariables.NUMBER, String.valueOf(requirements.getAmount()), + "[statistic]", Utils.prettifyObject(requirements.getStatistic(), this.user), + "[material]", Utils.prettifyObject(requirements.getMaterial(), this.user), + Constants.PARAMETER_VALUE, String.valueOf(currentValue)); + } + case ENTITY -> { + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "requirement-not-met-entity", + TextVariables.NUMBER, String.valueOf(requirements.getAmount()), + "[statistic]", Utils.prettifyObject(requirements.getStatistic(), this.user), + "[entity]", Utils.prettifyObject(requirements.getEntity(), this.user), + Constants.PARAMETER_VALUE, String.valueOf(currentValue)); + } + default -> { + Utils.sendMessage(this.user, + this.world, + Constants.ERRORS + "requirement-not-met", + TextVariables.NUMBER, String.valueOf(requirements.getAmount()), + "[statistic]", Utils.prettifyObject(requirements.getStatistic(), this.user), + Constants.PARAMETER_VALUE, String.valueOf(currentValue)); + } + } } else { diff --git a/src/main/java/world/bentobox/challenges/utils/Constants.java b/src/main/java/world/bentobox/challenges/utils/Constants.java index bfe490a..3d8f28c 100644 --- a/src/main/java/world/bentobox/challenges/utils/Constants.java +++ b/src/main/java/world/bentobox/challenges/utils/Constants.java @@ -223,4 +223,9 @@ public class Constants * Reference string to challenge parameter in translations. */ public static final String PARAMETER_CHALLENGE = "[challenge]"; + + /** + * Regex escape chars. + */ + public static final String ESC = "\\"; } diff --git a/src/main/java/world/bentobox/challenges/utils/Utils.java b/src/main/java/world/bentobox/challenges/utils/Utils.java index dee956d..5b7e2df 100644 --- a/src/main/java/world/bentobox/challenges/utils/Utils.java +++ b/src/main/java/world/bentobox/challenges/utils/Utils.java @@ -186,11 +186,14 @@ public class Utils * Send given message to user and add prefix to the start of the message. * * @param user User who need to receive message. - * @param message String of message that must be send. + * @param world Reference to world where message must be send. + * @param translation String of message that must be send. + * @param parameters Parameters that must be added to translation. */ - public static void sendMessage(User user, String message) + public static void sendMessage(User user, World world, String translation, String... parameters) { - user.sendMessage(user.getTranslation(Constants.CONVERSATIONS + "prefix") + message); + user.sendMessage(user.getTranslation(world, Constants.CONVERSATIONS + "prefix") + + user.getTranslation(world, translation, parameters)); } diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index f80e638..8d34415 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -683,6 +683,15 @@ challenges: visible: "Show visible challenges" hidden: "Show all challenges" toggleable: "Allow toggling" + include_undeployed: + name: "&f&l Include Undeployed Challenges" + description: |- + &7 Indicates if undeployed + &7 challenges should be + &7 counted towards level + &7 completion. + enabled: "&2 Enabled" + disabled: "&c Disabled" download: name: "&f&l Download Libraries" description: |- @@ -1204,6 +1213,8 @@ challenges: not-hooked: "&c Challenges Addon could not find any GameMode." timeout: "&c This challenge requires to wait [timeout] between completions. You must wait [wait-time] till complete it again." requirement-not-met: "&c This challenge requires [statistic] to have [number]. You have only [value]. " + requirement-not-met-entity: "&c This challenge requires [statistic] [entity] to have [number]. You have only [value]. " + requirement-not-met-material: "&c This challenge requires [statistic] [material] to have [number]. You have only [value]. " # # Showcase for manual material translation # materials: # # Names should be lowercase. diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index 8a1be9f..81fd879 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -2,6 +2,9 @@ meta: authors: - BONNe + # Attention for Chinese translation + # 中文翻译须知,在本人2023年1月19日接手前,上一位翻译者疑似用机翻软件翻译了全文包括变量在内的所有文字。 + # 感谢原作者BONNe进行了修改,同时笔者对管理员部分的翻译和玩家全部的翻译进行了订正。时间仓促,请君斧正。 challenges: commands: admin: @@ -33,14 +36,14 @@ challenges: gamemode-gui: "&0&l 选择游戏模式" multiple-gui: "&0&l 多少次?" admin-gui: "&0&l 挑战管理菜单" - edit-challenge: "&0&l 编辑 [挑战]" - edit-level: "&0&l 编辑 [等级]" + edit-challenge: "&0&l 编辑 [challenge]" + edit-level: "&0&l 编辑 [level]" settings: "&0&l 设置" choose-challenge: "&0&l 选择挑战" choose-level: "&0&l 选择级别" - choose-player: "&0&l 选择播放器" + choose-player: "&0&l 选择玩家" library: "&0&l 库" - manage-blocks: "&0&l 管理块" + manage-blocks: "&0&l 管理方块" manage-entities: "&0&l 管理实体" type-selector: "&0&l 挑战类型选择器" item-selector: "&0&l 项目选择器" @@ -51,10 +54,10 @@ challenges: environment-selector: "&0&l 环境选择器" buttons: free-challenges: - name: "&f&l 免费挑战" + name: "&f&l 自由挑战" description: |- &7 显示列表 - &7 个免费挑战 + &7 个自由挑战 return: name: "&f&l 返回" description: |- @@ -62,20 +65,20 @@ challenges: &7 或退出 GUI previous: name: "&f&l 上一页" - description: "&7 切换到 &e [数字] &7 页面" + description: "&7 切换到 &e [number] &7 页" next: name: "&f&l 下一页" - description: "&7 切换到 &e [数字] &7 页面" + description: "&7 切换到 &e [number] &7 页" reduce: name: "&f&l 减少" - description: "&7 减少 &e [数字]" + description: "&7 减少 &e [number]" increase: name: "&f&l 增加" - description: "&7 增加 &e [数字]" + description: "&7 增加 &e [number]" accept: name: "&f&l 完成" description: |- - &7 完成挑战 &e [数字] + &7 完成挑战 &e [number] &7次(-s) quit: name: "&f&l 退出" @@ -97,10 +100,10 @@ challenges: &7 启动一个进程 &7 创造了一个新的挑战。 add_level: - name: "&f&l 创建关卡" + name: "&f&l 创建级别" description: |- &7 启动一个进程 - &7 创造了一个新的水平。 + &7 创造了一个新的级别。 edit_challenge: name: "&f&l 编辑挑战" description: |- @@ -117,7 +120,7 @@ challenges: &7 允许选择和删除 &7 挑战。 delete_level: - name: "&f&l 删除层级" + name: "&f&l 删除级别" description: |- &7 允许选择和删除 &7 一个级别。 @@ -146,7 +149,7 @@ challenges: name: "&f&l 库" description: |- &7 打开公共 - &7 挑战图书馆。 + &7 挑战库。 import_database: name: "&f&l 导入数据库" description: |- @@ -158,7 +161,7 @@ challenges: &7 允许导入模板 &7 文件有挑战。 export_challenges: - name: "&f&l 出口挑战" + name: "&f&l 导出挑战" description: |- &7 允许导出数据库 &7 到本地文件。 @@ -184,7 +187,7 @@ challenges: description: |- &7 允许更改 &7 显示名称。 - value: "&7 当前:&r [名称]" + value: "&7 当前:&r [name]" remove_on_complete: name: "&f&l 完成后隐藏" description: |- @@ -209,14 +212,14 @@ challenges: enabled: "&2" disabled: "&C" order: - name: "&f&l 订单" + name: "&f&l 顺序" description: |- &7 允许改变顺序 &7 个对象。 &7 相同数量的对象 &7 将由他们订购 &7 唯一 ID 名称。 - value: "&7 当前订单:&e [数字]" + value: "&7 当前顺序:&e [number]" icon: name: "&f&l 图标" description: |- @@ -234,82 +237,83 @@ challenges: &7 权限 &7 挑战是可完成的。 title: "&7 权限:" - permission: " &8 - [权限]" + permission: " &8 - [permission]" none: "&7 权限未设置。" remove_entities: name: "&f&l 删除实体" description: |- &7 允许切换 - &7 所需实体将 - &7 从世界中移除 - &7 完成后 - &7 挑战。 + &7 所需要的 + &7 在完成挑战 + &7 后, + &7 从世界中移除的实体 enabled: "&2 已启用" disabled: "&c 已禁用" required_entities: name: "&f&l 必需的实体" description: |- - &7 允许根据需要进行更改 - &7 实体为此 - &7 挑战是可完成的。 + &7 允许对 + &7 为完成这个挑战。 + &7 所需要的实体进行更改 title: "&7 实体:" - list: " &8 - [数字] x [实体]" + list: " &8 - [number] x [entity]" none: "&7 不添加实体。" remove_blocks: name: "&f&l 移除方块" description: |- &7 允许切换 - &7 所需的块将 - &7 从世界中移除 - &7 完成后 - &7 挑战。 + &7 所需要的 + &7 在完成挑战 + &7 后, + &7 从世界中移除的方块 enabled: "&2 已启用" disabled: "&c 已禁用" required_blocks: - name: "&f&l 所需块" + name: "&f&l 所需方块" description: |- - &7 允许根据需要进行更改 - &7 块为此 - &7 挑战是可完成的。 + &7 允许更改 + &7 为完成挑战所需要的 + &7 方块 title: "&7 块:" - list: " &8 - [数量] x [块]" + list: " &8 - [number] x [block]" none: "&7 块不被添加。" search_radius: name: "&f&l 搜索半径" description: |- - &7 允许改变半径 - &7 周围的玩家 - &7 块和/或实体是 - &7 检测到。 - value: "&7 当前距离:&e [数字]" + &7 允许更改任务所需的方块 + &7 或实体的检测半径。 + &7 (部分任务需要玩家周围 + &7 有一定数量的方块/实体) + value: "&7 当前距离:&e [number]" remove_items: - name: "&f&l 删除项目" + name: "&f&l 删除道具" description: |- &7 允许切换 - &7 项必填项 - &7 从库存中移除 - &7 完成后 - &7 挑战。 + &7 挑战所需道具 + &7 在挑战完成后 + &7 是否从背包中 + &7 移除。 enabled: "&2 已启用" disabled: "&c 已禁用" required_items: - name: "&f&l 必填项目" + name: "&f&l 需求道具" description: |- &7 允许根据需要进行更改 &7 项为此 &7 挑战是可完成的。 title: "&7 项:" - list: " &8 - [数量] x [项目]" + list: " &8 - [number] x [item]" none: "&7 项目未添加。" add_ignored_meta: name: "&f&l 添加忽略元数据" + #翻译到这了,下面的都是没有人工翻译的,cirno看了也无语 description: |- &7 允许添加哪个 &7 项应忽略 &7 任何元数据 &7 分配给他们。 title: "&7 项:" - list: " &8 - [数量] x [项目]" + list: " &8 - [number] x [item]" none: "&7 项目未添加。" remove_ignored_meta: name: "&f&l 删除忽略元数据" @@ -323,7 +327,7 @@ challenges: description: |- &7 允许切换 &7 所需经验将 - &7 从播放器中移除 + &7 从玩家中移除 &7 完成后 &7 挑战。 enabled: "&2 已启用" @@ -333,21 +337,21 @@ challenges: description: |- &7 允许更改 &7 所需经验 - &7 播放器。 - value: "&7 当前经验:&e [数字]" + &7 玩家。 + value: "&7 当前经验:&e [number]" required_level: name: "&f&l 所需岛屿等级" description: |- &7 允许更改 &7 所需岛屿等级 &7 挑战。 - value: "&7 当前级别:&e [数字]" + value: "&7 当前级别:&e [number]" remove_money: name: "&f&l 移除金钱" description: |- &7 允许切换 &7 所需资金将 - &7 从播放器中移除 + &7 从玩家中移除 &7 帐号完成后 &7 挑战。 enabled: "&2 已启用" @@ -358,21 +362,21 @@ challenges: &7 允许更改 &7 玩家需要的钱 &7 说明了挑战。 - value: "&7 当前值:&e [数字]" + value: "&7 当前值:&e [number]" statistic: name: "&f&l 统计" description: |- &7 允许更改 &7 统计类型是 &7 签入了这个挑战。 - value: "&7 当前值:&e [统计]" + value: "&7 当前值:&e [statistic]" statistic_amount: name: "&f&l 目标值" description: |- &7 允许更改 &7 统计目标值 &7 必须满足。 - value: "&7 当前值:&e [数字]" + value: "&7 当前值:&e [number]" remove_statistic: name: "&f&l 减少统计量" description: |- @@ -387,19 +391,19 @@ challenges: description: |- &7 允许更改 &7 统计目标块。 - value: "&7 当前块:&e [块]" + value: "&7 当前块:&e [block]" statistic_items: name: "&f&l 目标项目" description: |- &7 允许更改 &7 统计目标项。 - value: "&7 当前项目:&e [项目]" + value: "&7 当前项目:&e [item]" statistic_entities: name: "&f&l 目标实体" description: |- &7 允许更改 &7 统计目标实体。 - value: "&7 当前实体:&e [实体]" + value: "&7 当前实体:&e [entity]" reward_text: name: "&f&l 奖励文本" description: |- @@ -420,7 +424,7 @@ challenges: &7 允许更改奖励 &7 项。 title: "&7 项:" - list: " &8 - [数量] x [项目]" + list: " &8 - [number] x [item]" none: "&7 项目未添加。" repeat_reward_items: name: "&f&l 重复奖励项目" @@ -429,35 +433,35 @@ challenges: &7 奖励物品 &7 挑战。 title: "&7 项:" - list: " &8 - [数量] x [项目]" + list: " &8 - [number] x [item]" none: "&7 项目未添加。" reward_experience: name: "&f&l 奖励体验" description: |- &7 允许更改 &7 奖励经验 - &7 播放器。 - value: "&7 奖励经验:&e [数字]" + &7 玩家。 + value: "&7 奖励经验:&e [number]" repeat_reward_experience: name: "&f&l 重复奖励体验" description: |- &7 允许更改 &7 重复奖励经验 - &7 为播放器。 - value: "&7 奖励经验:&e [数字]" + &7 为玩家。 + value: "&7 奖励经验:&e [number]" reward_money: name: "&f&l 奖励金" description: |- &7 允许更改 &7 奖励金钱。 - value: "&7 当前值:&e [数字]" + value: "&7 当前值:&e [number]" repeat_reward_money: name: "&f&l 重复奖励金" description: |- &7 允许更改 &7 重复奖励金 &7 挑战。 - value: "&7 当前值:&e [数字]" + value: "&7 当前值:&e [number]" reward_commands: name: "&f&l 奖励命令" description: |- @@ -509,7 +513,7 @@ challenges: &7 允许更改 &7 重复次数 &7 挑战。 - value: "&7 当前值:&e [数字]" + value: "&7 当前值:&e [number]" cool_down: name: "&f&l 冷却" description: |- @@ -518,7 +522,7 @@ challenges: &7 之间必须等待 &7 可重复挑战 &7 完成。 - value: "&7 当前值:&e [时间]" + value: "&7 当前值:&e [time]" challenges: name: "&f&l 挑战" description: |- @@ -531,7 +535,7 @@ challenges: &7 的挑战 &7 未完成 &7 解锁下一个级别。 - value: "&7 当前值:&e [数字]" + value: "&7 当前值:&e [number]" add_challenges: name: "&f&l 添加挑战(-s)" description: |- @@ -626,13 +630,13 @@ challenges: &7 在用户数据中。 &7 0 表示数据将 &7 不会被删除。 - value: "&7 当前值:&e [数字]" + value: "&7 当前值:&e [number]" title_showtime: name: "&f&l 标题放映时间" description: |- &7 该标题的刻度数 &7 将显示给玩家。 - value: "&7 当前值:&e [数字]" + value: "&7 当前值:&e [number]" active_world_list: name: "&f&l 仅显示活跃世界" description: |- @@ -658,16 +662,16 @@ challenges: name: "&f&l 下载库" description: |- &7 可手动更新 - &7 挑战图书馆。 + &7 挑战库。 enabled: "&2 清除缓存" disabled: "&c 不清除缓存" player: - name: "&f&l [名称]" - description: "&7 岛主:[所有者]" + name: "&f&l [name]" + description: "&7 岛主:[owner]" members: "&7 岛成员:" - member: "&8 - [名称]" + member: "&8 - [name]" no-island: |- - &c 播放器没有 + &c 玩家没有 &c 一个岛屿。 player_list: name: "&f&l 选择用户列表" @@ -691,9 +695,9 @@ challenges: &7 个选定的块 &7 来自列表。 title: "&7 精选材料:" - material: "&8 - [材质]" + material: "&8 - [material]" material: - name: "&f&l [材质]" + name: "&f&l [material]" description: "&7 材质 ID:[id]" selected: "&2 已选择" add_entity: @@ -705,7 +709,7 @@ challenges: name: "&f&l 换鸡蛋" description: |- &7 允许从 - &7 鸡蛋给暴徒头。 + &7 鸡蛋给怪物头。 remove_entity: name: "&f&l 删除实体" description: |- @@ -713,9 +717,9 @@ challenges: &7 个选定的实体 &7 来自列表。 title: "&7 选定实体:" - entity: "&8 - [实体]" + entity: "&8 - [entity]" entity: - name: "&f&l [实体]" + name: "&f&l [entity]" description: "&7 实体 ID:[id]" selected: "&2 已选择" inventory_type: @@ -728,7 +732,7 @@ challenges: description: |- &7 检查的挑战 &7 周围的方块或实体 - &7 播放器。 + &7 玩家。 other_type: name: "&f&l 其他类型" description: |- @@ -756,40 +760,41 @@ challenges: &7 返回选中的元素 &7 并打开以前的 GUI。 title: "&7 已选择:" - element: "&8 - [元素]" + element: "&8 - [element]" statistic_element: - name: "&f&l [统计]" - description: "[描述]" + name: "&f&l [statistic]" + description: "[description]" environment_element: - name: "&f&l [环境]" - description: "[描述]" + name: "&f&l [environment]" + description: "[description]" search: name: "&f&l 搜索" description: |- &7 允许搜索 &7 元素与输入 &7 文本值。 - search: "&b 值:[值]" + search: "&b 值:[value]" + #上面的都是没有翻译的,cirno看了直摇头 tips: click-to-select: "&e 单击 &7 进行选择。" click-to-choose: "&e 单击 &7 进行选择。" - click-to-complete: "&e 点击 &7 完成。" - right-click-multiple-open: "&e 右击 &7 选择完成计数。" - shift-left-click-to-complete-all: "&e Shift 单击 &7 完成所有操作。" - left-click-to-accept: "&e 左键单击 &7 完成。" + click-to-complete: "&e 单击 &7 完成。" + right-click-multiple-open: "&e 右击 &7 选择完成挑战的次数。" + shift-left-click-to-complete-all: "&e 按住Shift并左击 &7 一键完成(根据背包中所需道具数量)。" + left-click-to-accept: "&e 左击 &7 接受。" right-click-to-write: "&e 右击 &7 写入。" click-to-reduce: "&e 点击 &7 减少。" click-to-increase: "&e 点击 &7 增加。" click-to-return: "&e 单击 &7 返回。" click-to-quit: "&e 点击 &7 退出。" - click-to-wipe: "&e 单击 &7 擦除。" - left-click-to-wipe: "&e 左键单击 &7 擦除。" + click-to-wipe: "&e 单击 &7 清除。" + left-click-to-wipe: "&e 左键单击 &7 清除。" right-click-to-switch: "&e 右键 &7 切换。" click-to-open: "&e 点击 &7 打开。" click-to-export: "&e 点击 &7 导出。" click-to-create: "&e 点击 &7 创建。" left-click-to-open: "&e 左键单击 &7 打开。" - right-click-to-reset-all: "&e 右键单击 &7 擦除所有内容。" + right-click-to-reset-all: "&e 右键单击 &7 重置所有内容。" click-to-toggle: "&e 单击 &7 进行切换。" click-to-change: "&e 单击 &7 进行更改。" shift-click-to-reset: "&e Shift 单击 &7 进行重置。" @@ -818,110 +823,111 @@ challenges: descriptions: challenge: lore: |- - [描述] - [地位] - [冷却] - [要求] - [奖励] + [description] + [status] + [cooldown] + [requirements] + [rewards] status: completed: "&2&l 已完成" - completed-times: "&2 完成 &7&l [数字] &r&2 时间(-s)" - completed-times-of: "&2 已完成 &7&l [次数] &r&2 共 &7&l [最多] &r&2 次" - completed-times-reached: "&2&l 全部完成 &7 [最多] &2 次" + #原来的time(-s)意思是如果完成复数次就是times,不是时间里秒的含义 + completed-times: "&2 完成 &7&l [number] &r&2 次" + completed-times-of: "&2 已完成 &7&l [number] &r&2 共 &7&l [max] &r&2 次" + completed-times-reached: "&2&l 全部完成 &7 [max] &2 次" cooldown: lore: |- - [超时] - [等待时间] - timeout: "&7&l 冷却时间:&r&7 [时间]" - wait-time: "&c&l 可用时间:&r&c [时间]" - in-days: "[数量] d" - in-hours: "[数量]小时" - in-minutes: "[数量] 分钟" - in-seconds: "[数字] s" + [timeout] + [wait-time] + timeout: "&7&l 冷却时间:&r&7 [time]" + wait-time: "&c&l 等待时间:&r&c [time]" + in-days: "[number] d" + in-hours: "[number]小时" + in-minutes: "[number] 分钟" + in-seconds: "[number] s" requirements: lore: |- - [环境] - [类型要求] - [权限] - environment-single: "&7 限于 [环境]" + [environment] + [type-requirement] + [permission] + environment-single: "&7 限于 [environment]" environment-title: "&7 限于:" - environment-list: " &7 - &e [环境]" + environment-list: " &7 - &e [environment]" permission-single: "&c 需要 [permissions] 权限" permissions-title: "&c 需要权限:" - permissions-list: " &c - [权限]" + permissions-list: " &c - [permission]" island: lore: |- - [块] - [实体] - [搜索半径] - [警告块] - [警告实体] - blocks-title: "&7&l 所需块:" - block-value: " &7 - &e [材质]" - blocks-value: " &7 - &e [数量] x [材料]" + [blocks] + [entities] + [search-radius] + [warning-block] + [warning-entity] + blocks-title: "&7&l 所需方块:" + block-value: " &7 - &e [material]" + blocks-value: " &7 - &e [number] x [material]" entities-title: "&7&l 所需实体:" - entity-value: " &7 - &e [实体]" - entities-value: " &7 - &e [数字] x [实体]" - search-radius: "&7 不超过 &e [数字] &7 米" - warning-block: "&e 块将被 &c 删除" + entity-value: " &7 - &e [entity]" + entities-value: " &7 - &e [number] x [entity]" + search-radius: "&7 不超过 &e [number] &7 米" + warning-block: "&e 方块将被 &c 删除" warning-entity: "&e 实体将被 &c 删除" inventory: lore: |- - [项目] - [警告] + [items] + [warning] item-title: "&7&l 必填项目:" - item-value: " &7 - &e [项目]" - items-value: " &7 - &e [数字] x [项目]" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" warning: "&e 项目(-s)将被 &c 删除" other: lore: |- - [经验] - [经验警告] - [钱] - [金钱警告] - [等级] - experience: "&7&l 所需经验:&r&e [数量]" + [experience] + [experience-warning] + [money] + [money-warning] + [level] + experience: "&7&l 所需经验:&r&e [number]" experience-warning: "&e 经验将被 &c 移除" - money: "&7&l 所需资金:&r&e [数字]" + money: "&7&l 所需资金:&r&e [number]" money-warning: "&e 钱将被 &c 移除" - level: "&7&l 所需岛屿等级:&r&e [数字]" + level: "&7&l 所需岛屿等级:&r&e [number]" statistic: lore: |- - [统计] - [警告] - multiple-target: "&7&l [统计]: &r&e [数字] x [目标]" - single-target: "&7&l [统计]: &r&e [目标]" - statistic: "&7&l [统计] &r&e [数字]" + [statistic] + [warning] + multiple-target: "&7&l [statistic]: &r&e [number] x [target]" + single-target: "&7&l [statistic]: &r&e [target]" + statistic: "&7&l [statistic] &r&e [number]" warning: "&e 统计数据将 &c 减少" rewards: lore: |- &7&l 奖励: - [文本] - [项目] - [经验] - [钱] - [命令] + [text] + [items] + [experience] + [money] + [commands] item-title: "&7 项:" - item-value: " &7 - &e [项目]" - items-value: " &7 - &e [数字] x [项目]" - experience: "&7 经验:&r&e [数字]" - money: "&7 金钱:&r&e [数字]" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + experience: "&7 经验:&r&e [number]" + money: "&7 金钱:&r&e [number]" commands-title: "&7 命令:" - command: " &7 - &e [命令]" + command: " &7 - &e [command]" level: lore: |- - [文本] - [地位] - [放弃] - [奖励] + [text] + [status] + [waiver] + [rewards] status: completed: "&2&l 已完成" completed-challenges-of: |- - &2 已完成 &7&l [数量] &r&2 出 + &2 已完成 &7&l [number] &r&2 出 &7&l [max] &r&2 挑战。 locked: "&c&l 锁定" missing-challenges: |- - &7 [数字] 更多的挑战必须是 + &7 [number] 更多的挑战必须是 &7 完成以解锁此级别。 waiver: |- &7&l [number] 挑战(-s) &r&7 可以 @@ -929,25 +935,26 @@ challenges: rewards: lore: |- &7&l 奖励: - [文本] - [项目] - [经验] - [钱] - [命令] + [text] + [items] + [experience] + [money] + [commands] item-title: "&7 项:" - item-value: " &7 - &e [项目]" - items-value: " &7 - &e [数字] x [项目]" - experience: "&7 经验:&r&e [数字]" - money: "&7 金钱:&r&e [数字]" + item-value: " &7 - &e [item]" + items-value: " &7 - &e [number] x [item]" + experience: "&7 经验:&r&e [number]" + money: "&7 金钱:&r&e [number]" commands-title: "&7 命令:" - command: " &7 - &e [命令]" + command: " &7 - &e [command]" library: - author: "&7 作者 &e [作者]" - version: "&7 Made with Challenges &e [版本]" + author: "&7 作者 &e [author]" + version: "&7 Made with Challenges &e [version]" lang: "&7 语言:&e [lang]" - gamemode: "&7 主要用于 &e [游戏模式]" + gamemode: "&7 主要用于 &e [gamemode]" + #后面没人工翻译了 conversations: - prefix: "&l&6 [便当盒]: &r" + prefix: "&l&6 [BentoBox]: &r" confirm-string: true, on, yes, 确认, y, 有效, 正确 deny-string: 假,关闭,否,拒绝,n,无效,不正确 cancel-string: 取消 @@ -956,17 +963,17 @@ challenges: input-number: "&e 请在聊天中输入一个号码。" input-seconds: "&e 请在聊天中输入秒。" numeric-only: "&c 给定的 [value] 不是数字!" - not-valid-value: "&c 给定的数字 [值] 无效。它必须大于 [min] 并且小于 [max]!" - user-data-removed: "&a [游戏模式] 的所有用户数据都从数据库中清除。" - confirm-user-data-deletion: "&e 请确认您要清除 [游戏模式] 的用户数据库。" - challenge-data-removed: "&a [游戏模式] 的所有挑战数据都从数据库中清除。" - confirm-challenge-data-deletion: "&e 请确认您要清除 [游戏模式] 的挑战数据库。" - all-data-removed: "&a [游戏模式] 的所有插件数据都从数据库中清除。" - confirm-all-data-deletion: "&e 请确认您要清除 [游戏模式] 的插件数据。" - write-name: "&e 请在聊天中写一个名字。" + not-valid-value: "&c 给定的数字 [value] 无效。它必须大于 [min] 并且小于 [max]!" + user-data-removed: "&a [gamemode] 的所有用户数据都从数据库中清除。" + confirm-user-data-deletion: "&e 请确认您要清除 [gamemode] 的用户数据库。" + challenge-data-removed: "&a [gamemode] 的所有挑战数据都从数据库中清除。" + confirm-challenge-data-deletion: "&e 请确认您要清除 [gamemode] 的挑战数据库。" + all-data-removed: "&a [gamemode] 的所有插件数据都从数据库中清除。" + confirm-all-data-deletion: "&e 请确认您要清除 [gamemode] 的插件数据。" + write-name: "&e 请在聊天中输入一个名字。" new-object-created: "&a 为 [gamemode] 创建了一个新对象。" object-already-exists: "&c 对象 &7 [id] &c 已经存在。选择不同的名称。" - invalid-challenge: "&c 挑战 [挑战] 包含无效数据。无法部署!" + invalid-challenge: "&c 挑战 [challenge] 包含无效数据。无法部署!" name-changed: "&a 成功,名称已更新。" write-description: "&e 请在聊天中输入新的描述,然后在一行中自行“退出”以完成。" description-changed: "&a 成功,说明已更新。" @@ -987,7 +994,7 @@ challenges: start-downloading: "&a 开始下载和导入挑战库。" written-text: "&a 输入文本:" confirm-data-replacement: "&e 请确认您想用新的挑战替换当前的挑战。" - new-challenges-imported: "&a 成功,[游戏模式] 的新挑战已导入。" + new-challenges-imported: "&a 成功,[gamemode] 的新挑战已导入。" exported-file-name: "&e 请输入导出的数据库文件的文件名。 (写“取消”退出)" database-export-completed: "&a 成功,[world] 的数据库导出完成。文件[文件]生成。" file-name-exist: "&c 名称为“[id]”的文件存在。无法覆盖。" @@ -998,11 +1005,12 @@ challenges: challenge-subtitle: "[friendlyName]" level-title: "&a已完成" level-subtitle: "[friendlyName]" + #上面没翻译了 messages: - completed: "&2 你为[玩家]完成了挑战[名字]!" + completed: "&2 你为[player]完成了挑战[name]!" already-completed: "&2 这个挑战已经完成了!" - reset: "&2 你为 [玩家] 重置挑战 [名称]!" - reset-all: "&2 所有[玩家]挑战均已重置!" + reset: "&2 你为 [player] 重置挑战 [name]!" + reset-all: "&2 所有[player]挑战均已重置!" not-completed: "&2 此挑战尚未完成!" migrate-start: "&2 开始迁移挑战插件数据。" migrate-end: "&2 挑战插件数据更新为新格式。" @@ -1031,7 +1039,7 @@ challenges: you-still-need: "&c你还差 &f[amount] &c个 &f[item] &c才能完成挑战。" missing-addon: "&c无法完成挑战:缺少必需的组件或插件。" incorrect: "&c无法完成挑战:必要条件设定错误。" - not-enough-money: "&c你必须有 &f[value] &c游戏币才能完成任务。" + not-enough-money: "&c你必须有 &f[value] &c金钱才能完成任务。" not-enough-experience: "&c你必须有 &f[value] &c经验值才能完成任务。" island-level: "&c你的岛屿等级必须达到 &flv[number] &c才能完成任务!" no-load: "&c错误: 无法载入 &fchallenges.yml&c. [message]" @@ -1047,18 +1055,106 @@ challenges: invalid-challenge: "&c挑战项 [challenge] &c包含错误,它不会从数据库中加载!" no-library-entries: "&c 找不到任何库条目。没什么可显示的。" not-hooked: "&c Challenges Addon 找不到任何游戏模式。" - timeout: "&c 此挑战需要在完成之间等待 [超时]。您必须等待 [wait-time] 才能再次完成。" + timeout: "&c 此挑战需要在完成之间等待 [timeout]。您必须等待 [wait-time] 才能再次完成。" + requirement-not-met: "&c This challenge requires [statistic] to have [number]. You have only [value]. " + # # Showcase for manual material translation +# materials: +# # Names should be lowercase. +# cobblestone: "Cobblestone" +# # Also supports descriptions. +# stone: +# name: "Stone" +# description: "" +# item-stacks: +# # Non-specific item meta translations. +# # TYPE is the item type +# # META is a content of item meta. +# generic: "[type] [meta]" +# # Non-specific meta translations. Will replace [meta] +# meta: +# upgraded: "Upgraded" +# extended: "Extended" +# potion-meta: "&e [type] [upgraded] [extended]" +# # Be aware, enchants are always listed below item in separate line. +# enchant-meta: " &7 - &e [type] [level]" +# skull-meta: ": &e [player-name]" +# book-meta: "&e [title] [author]" +# # Custom Enchantment Translation. +# enchant: +# menting: "Mending" +# unbreaking: "Unbreaking" +# # Custom Potion Translation. +# potion-type: +# water_breathing: "Water Breathing" +# # You can also create specific item translations +# # Like translate all potions. +# potion: +# # This will overwrite generic translation. +# name: "[type] [upgraded] [extended]" +# # Type is either specific translation or potion effect. +# uncraftable: "Uncraftable" +# water: "Water" +# mundane: "Mundane" +# thick: "Thick" +# awkward: "Awkward" +# night_vision: "Potion of Night Vision" +# invisibility: "Potion of Invisibility" +# jump: "Potion of Leaping" +# fire_resistance: "Potion of Fire Resistance" +# speed: "Potion of Swiftness" +# slowness: "Potion of Slowness" +# water_breathing: "Potion of Water Breathing" +# instant_heal: "Potion of Healing" +# instant_damage: "Potion of Harming" +# poison: "Potion of Poison" +# regen: "Potion of Regeneration" +# strength: "Potion of Strength" +# weakness: "Potion of Weakness" +# luck: "Potion of Luck" +# turtle_master: "Potion of Turtle Master" +# slow_falling: "Potion of Slow Falling" +# stone_shovel: +# # This will mean that only stone shovels will not show +# # meta information. +# name: "[type]" +# +# # Showcase how to support multi-linguistic challenges +# challenges: +# # Database ID name. +# example_challenge_id: +# name: "&2 Translated Name" +# description: |- +# &7 Translated Custom +# &7 description +# reward-text: |- +# &7 Translated Reward +# &7 text +# repeat-reward-text: |- +# &7 Translated Repeat +# &7 Reward text +# levels: +# # Database ID name. +# example_level_id: +# name: "&2 Translated Name" +# description: |- +# &7 Translated Custom +# &7 description +# reward-text: |- +# &7 Translated Reward +# &7 text protection: flags: CHALLENGES_ISLAND_PROTECTION: - description: 允许/禁止 在岛屿上完成挑战 - name: 挑战权限 + description: |- + &5 &o 切换谁可以 + &5 &o 完成挑战 + name: 挑战保护 CHALLENGES_WORLD_PROTECTION: description: |- - &7允许/禁止 限制玩家只能在自己岛 - &7上才能完成挑战。 - &c允许时,玩家只能在自己岛上进行 - &c和完成挑战。 - name: 挑战岛屿限制 - hint: "&c已被禁止在岛屿范围外进行挑战" + &5 &o 启用 / 禁用 + &5 &o 玩家们在自己 + &5 &o 岛屿中完成挑 + &5 &o 战的需求。 + name: 岛屿挑战限制 + hint: 岛屿外无挑战 version: 11 diff --git a/src/main/resources/locales/zh-HK.yml b/src/main/resources/locales/zh-HK.yml new file mode 100644 index 0000000..0bea736 --- /dev/null +++ b/src/main/resources/locales/zh-HK.yml @@ -0,0 +1,1014 @@ +# ########################################################################################## +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +# ########################################################################################## + +# This locale is updated to version 1.1.0 by JamesMCL44 + +meta: + authors: + - JamesMCL44 + +challenges: + commands: + admin: + main: + parameters: '' + description: '打開管理員菜單' + reload: + description: '重載挑戰組件' + parameters: '[hard]' + show: + description: '在聊天中列出當前世界的所有挑戰' + parameters: '' + complete: + description: '通過指令讓某個玩家完成指定挑戰' + parameters: + reset: + description: '重設玩家的指定挑戰紀錄. 如果 使用"all" 則重設全部挑戰紀錄' + parameters: + migrate: + description: '將目前挑戰紀錄數據格式 遷移至 版本0.8.0的儲存格式' + parameters: '' + user: + main: + description: '打開空島挑戰菜單' + parameters: '' + complete: + description: '通過指令完成挑戰' + parameters: [count] + gui: + titles: + # 這是主要菜單GUI的標題 + player-gui: '&0&l島嶼挑戰菜單' + # 這是主要菜單GUI的標題 + gamemode-gui: '&0&l選擇空島模式' + # The title for the Multiple Completion GUI + multiple-gui: '&0&l可重覆完成多少次?' + # GUI titles below is visible just for Admins. + admin-gui: '&0&l挑戰管理菜單' + edit-challenge: '&0&l編輯[challenge]' + edit-level: '&0&l編輯[level]' + settings: '&0&l設定' + choose-challenge: '&0&l選擇挑戰' + choose-level: '&0&l選擇挑戰等級' + choose-player: '&0&l選擇玩家' + library: '&0&l圖書館' + manage-blocks: '&0&l管理方塊' + manage-entities: '&0&l管理實體' + type-selector: '&0&l選擇挑戰類型' + item-selector: '&0&l選擇物品' + block-selector: '&0&l選擇方塊' + entity-selector: '&0&l選擇實體' + challenge-selector: '&0&l選擇挑戰' + statistic-selector: '&0&l選擇統計數據' + environment-selector: '&0&l選擇環境' + buttons: + free-challenges: + name: '&f&l自由挑戰' + description: '&7 列出所有自由挑戰' + return: + name: '&f&l返回上一級' + description: |- + &7 返回上一層&r或 + &7 退出並關閉菜單 + previous: + name: '&f&l上一頁' + description: '&7 轉到第&e[number]&7頁' + next: + name: '&f&l下一頁' + description: '&7轉到第&e[number]&7頁' + reduce: + name: '&f&l减少' + description: '&7减少&e[number]' + increase: + name: '&f&l增加' + description: '&7 增加&e[number]' + accept: + name: '&f&l完成' + description: '&7 完成挑戰共&e[number]次' + quit: + name: '&f&l退出' + description: '&7 退出並關閉菜單' + complete_user_challenges: + name: '&f&l強制玩家完成某個挑戰' + description: |- + &7 強制讓指定玩家完成某個挑戰 + &7 玩家無法獲得完成獎勵 + reset_user_challenges: + name: '&f&l重置玩家挑戰進度' + description: '&7 重置指定玩家的挑戰進度' + add_challenge: + name: '&f&l添加 新挑戰' + description: '&7 啟動添加 新挑戰 的程序' + add_level: + name: '&f&l添加 新挑戰等級' + description: '&7 啟動添加 新挑戰等級 的程序' + edit_challenge: + name: '&f&l編輯 挑戰設定' + description: '&7 對 某個挑戰的設定 進行修改調整' + edit_level: + name: '&f&l編輯 挑戰等級' + description: '&7 對 某個挑戰等級的設定 進行修改調整' + delete_challenge: + name: '&f&l删除 挑戰' + description: '&7 删除 指定挑戰' + delete_level: + name: '&f&l删除 挑戰等級' + description: '&7 删除 指定挑戰等級' + edit_settings: + name: '&f&l修改設定' + description: '&7 查看及修改附加插件設定' + complete_wipe: + name: '&f&l清空數據庫' + description: |- + &7 清空數據庫中的所有挑戰, + &7 包括玩家的挑戰數據 + challenge_wipe: + name: '&f&l清空挑戰' + description: |- + '&7 清空數據庫中的' + '&7 所有挑戰及挑戰等級' + user_wipe: + name: '&f&l清空玩家' + description: '&7 清空玩家的挑戰數據' + library: + name: '&f&l文庫' + description: '&7 打開公開的挑戰文庫' + import_database: + name: '&f&l導入挑戰數據庫' + description: '&7 導入挑戰數據庫' + import_template: + name: '&f&l導入模板' + description: '&7 導入挑戰模板' + export_challenges: + name: '&f&l導出挑戰' + description: '&7 導出挑戰數據庫' + properties: + name: '&f&l屬性' + description: '&7 查看所有主要屬性' + requirements: + name: '&f&l挑戰要求' + description: '&7 查看挑戰要求' + rewards: + name: '&f&l挑戰獎勵' + description: '&7 查看挑戰獎勵' + deployed: + name: '&f&l切換挑戰開放狀態' + description: '&7 切換 挑戰是否可被玩家完成' + enabled: "&2 已啟用" + disabled: "&c 已禁用" + name: + name: '&f&l挑戰名稱' + description: '&7 修改挑戰名稱' + value: '&7目前: &r[name]' + remove_on_complete: + name: '&f&l完成後隱藏挑戰' + description: |- + '&7 切換是否於於玩家完成後' + '&7 將該挑戰隱藏' + enabled: "&2 已啟用" + disabled: "&c 已禁用" + description: + name: '&f&l挑戰介紹' + description: '&7 修改挑戰介紹 可使用顏色編碼' + value: '&7目前介紹:' + environment: + name: '&f&l世界環境' + description: |- + &7 修改挑戰的世界限定: + &7 限制挑戰只能在 + &7 指定世界內完成 + enabled: '&2' + disabled: '&c' + order: + name: '&f&l順序' + description: |- + &7 修改挑戰順序 + &7 相同順序的物品會 + &7 依據相對應的物品ID排序 + value: '&7目前順序: &e[number]' + icon: + name: '&f&l圖標' + description: '&7 修改挑戰圖標' + locked_icon: + name: '&f&l圖標-未解鎖' + description: '&7 修改未解鎖挑戰的圖標' + required_permissions: + name: '&f&l權限要求' + description: |- + &7 修改完成挑戰時 + &7 需要具有的權限 + title: '&7 權限: ' + permission: ' &8 - [permission]' + none: '&7 尚未設置權限' + remove_entities: + name: '&f&l 移除實體' + description: |- + &7切換所要求的實體 + &7會否在完成挑戰後被移除 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + required_entities: + name: '&f&l 實體要求' + description: |- + &7 修改完成挑戰時 + &7 需要具有的實體 + title: '&7 實體: ' + list: ' &8 - [number] x [entity]' + none: '&7 未有加入實體' + remove_blocks: + name: '&f&l 移除方塊' + description: |- + &7 切換所要求的方塊 + &7 會否在完成挑戰後被移除 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + required_blocks: + name: '&f&l 方塊要求' + description: |- + &7 修改完成挑戰時 + &7 需要具有的方塊 + title: '&7 方塊: ' + list: ' &8 - [number] x [block]' + none: '&7 未有加入方塊' + search_radius: + name: '&f&l 搜索半徑' + description: |- + &7 玩家完成挑戰時 + &7 檢測實體/方塊的範圍(半徑) + value: '&7 目前距離: &e [number]' + remove_items: + name: '&f&l 移除物品' + description: |- + &7 切換所要求的物品 + &7 會否在完成挑戰後被移除 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + required_items: + name: '&f&l 物品要求' + description: |- + &7 修改完成挑戰時 + &7 需要具有的物品 + title: '&7 物品: ' + list: ' &8 - [number] x [item]' + none: '&7 未有加入物品' + add_ignored_meta: + name: '&f&l 加入Metadata忽略清單' + description: |- + &7 把物品加入至 + &7 Metadata忽略清單中 + title: '&7 物品: ' + list: ' &8 - [number] x [item]' + none: '&7 未有加入物品' + remove_ignored_meta: + name: '&f&l 移除Metadata忽略名單' + description: |- + &7 把物品移除自 + &7 Metadata忽略清單中 + remove_experience: + name: '&f&l 扣除經驗值' + description: |- + &7 切換所要求的經驗值 + &7 會否在完成挑戰後被扣除 + enabled: "&2已啟用" + disabled: "&c已禁用" + required_experience: + name: '&f&l 經驗值要求' + description: |- + &7 修改完成挑戰時 + &7 需要具有的經驗值 + value: '&7 目前經驗值要求: &e [number]等' + required_level: + name: '&f&l 島嶼等級要求' + description: |- + &7 修改完成挑戰時 + &7 需要具有的島嶼等級 + value: '&7 目前島嶼等級要求: &e [number]' + remove_money: + name: '&f&l 扣除遊戲幣' + description: |- + &7 切換所要求的遊戲幣 + &7 會否在完成挑戰後被扣除 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + required_money: + name: '&f&l 遊戲幣要求' + description: |- + &7 修改完成挑戰時 + &7 需要具有的遊戲幣 + value: '&7 目前遊戲幣要求: &e [number]' + statistic: + name: '&f&l 統計' + description: |- + &7 修改完成挑戰時 + &7 需要具有的統計數據類型 + value: '&7 目前類型: &e [statistic]' + statistic_amount: + name: '&f&l 目標數量' + description: |- + &7 修改完成挑戰時 + &7 需要具有的指定統計 + &7 數據類型的目標數量 + value: '&7 目前數量: &e[number]' + remove_statistic: + name: "&f&l 扣除統計數據" + description: |- + &7 切換所要求的統計數據 + &7 會否在完成挑戰後被扣除 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + statistic_blocks: + name: "&f&l 目標方塊類型" + description: |- + &7 修改挑戰所要求獲得 + &7 統計數據的方塊類型 + &7 (如: 紅石塊, 木材等)。 + value: "&7 目前方塊類型: &e[block]" + statistic_items: + name: "&f&l 目標物品類型" + description: |- + &7 修改挑戰所要求獲得 + &7 統計數據的物品類型 + &7 (如: 終界珍珠, 紅石火把等)。 + value: "&7 目前物品類型: &e[item]" + statistic_entities: + name: "&f&l 目標實體" + description: |- + &7 修改挑戰所要求獲得 + &7 統計數據的實體類型 + &7 (如: 羊隻, 村民等)。 + value: "&7 目前實體:&e [entity]" + reward_text: + name: "&f&l獎勵文本" + description: |- + &7 挑戰獎勵的文字 + &7 必須使用顏色代碼 + value: "&7 目前文本:" + repeat_reward_text: + name: "&f&l 重覆獎勵文本" + description: |- + &7 挑戰重覆獎勵的文字 + &7 必須使用顏色代碼 + value: "&7 目前文本:" + reward_items: + name: "&f&l 獎勵物品" + description: '&7 修改挑戰的獎勵物品' + title: "&7 物品: " + list: " &8 - [number] x [item]" + none: "&7 未添加物品" + repeat_reward_items: + name: "&f&l 重覆挑戰的獎勵物品" + description: '&7 修改重覆挑戰的獎勵物品' + title: "&7 物品: " + list: " &8 - [number] x [item]" + none: "&7 未添加物品" + reward_experience: + name: "&f&l 獎勵經驗瓶" + description: '&7 修改挑戰獎勵的經驗瓶' + value: "&7 目前經驗瓶數量: &e[number]" + repeat_reward_experience: + name: "&f&l 重覆挑戰的獎勵經驗瓶" + description: '&7 修改重覆挑戰的獎勵經驗瓶' + value: "&7 目前經驗瓶數量: &e [number]" + reward_money: + name: "&f&l 獎勵遊戲幣" + description: '&7 修改挑戰獎勵的遊戲幣' + value: "&7 目前數量: &e [number]" + repeat_reward_money: + name: "&f&l 獎勵遊戲幣" + description: '&7 修改重覆挑戰的獎勵遊戲幣' + value: "&7 目前數量: &e [number]" + reward_commands: + name: '&f&l 獎勵指令' + description: |- + &7 獎勵指令 + &8 提示: + &8 此指令默認設置: + &8 - 所有指令在開首加入 `/` + &8 - 所有指令由伺服器執行 + &8 + &8 如果想讓玩家執行指令 + &8 可以在開首加入`[SELF]` + &8 + &8 指令支援一個placeholder + &8 `[player]` 可用於取代 + &8 完成挑戰的玩家的名字 + value: '&7 目前指令:' + repeat_reward_commands: + name: '&f&l 重覆挑戰獎勵指令' + description: |- + &7 重覆挑戰獎勵指令 + &8 提示: + &8 此指令默認設置: + &8 - 所有指令在開首加入 `/` + &8 - 所有指令由伺服器執行 + &8 + &8 如果想讓玩家執行指令 + &8 可以在開首加入`[SELF]` + &8 + &8 指令支援一個placeholder + &8 `[player]` 可用於取代 + &8 完成挑戰的玩家的名字 + value: '&7 目前指令:' + repeatable: + name: '&f&l 可重覆挑戰' + description: '&7 切換 是否容許重覆完成挑戰' + enabled: "&2 已啟用" + disabled: "&c 已禁用" + repeat_count: + name: '&f&l 重覆完成次數' + description: |- + &7 修改 挑戰可重覆完成 + &7 的最大次數 + value: '&7 目前數: &e[number]次' + cool_down: + name: '&f&l 冷卻時間' + description: |- + &7 修改 每次重覆完成挑戰 + &7 相距的冷卻時間(以秒計) + value: '&7 目前數: &e[time]秒' + challenges: + name: '&f&l 挑戰' + description: |- + &7 查看各島嶼等級 + &7 需要完成的相應挑戰 + waiver_amount: + name: '&f&l 豁免挑戰' + description: |- + &7 可設定在解鎖下一等級時 + &7 有多少挑戰可被豁免完成 + &7 (如: 共30個挑戰,只需要 + &7 完成20個就可以解鎖, + &7 相等於&f10個被豁免完成&7) + value: '&7 目前數: &e[number]個' + add_challenges: + name: '&f&l 添加挑戰' + description: |- + &7 將挑戰加入到 + &7 指定島嶼等級的要求中 + remove_challenges: + name: '&f&l 刪除挑戰' + description: |- + &7 將挑戰從島嶼等級的 + &7 要求中刪除 + reset_on_new: + name: '&f&l 挑戰紀錄與島嶼一起重刷' + description: |- + &7 切換 是否當玩家重刷島嶼時 + &7 挑戰紀錄與島嶼清空 及 + &7 離開島嶼(即轉讓所有權)時 + &7 挑戰紀錄自動清空 + enabled: "&2已啟用" + disabled: "&c已禁用" + broadcast: + name: '&f&l 廣播' + description: |- + &7 當玩家首次完成項目挑戰時 + &7 會否在伺服器內公開廣播 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + remove_completed: + name: '&f&l 隱藏已完成項目' + description: |- + &7 將所有已完成的挑戰從菜單中隱藏 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + glow_completed: + name: '&f&l 已完成項目發光顯示' + description: |- + &7 將所有已完成的挑戰 + &7 加入發光顯示效果 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + store_history: + name: '&f&l 保存記錄' + description: |- + &7 保存所有已完成挑戰的相關紀錄 + &7 目前只能從後台的數據庫重新查看 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + data_per_island: + name: '&f&l 保存記錄(以島嶼為單位)' + description: |- + &7 獨立保存各個島嶼上所有 + &7 已完成挑戰的相關紀錄 + &7 完成的進度會在同一隊伍 + &7 中的隊員之間共享 + enabled: "&2 已啟用" + disabled: "&c 已禁用" + show_title: + name: '&f&l 顯示標題' + description: |- + &7 當玩家完成挑戰或提升等級時 + &7 在畫面中央顯示標題(Title) + enabled: "&2 已啟用" + disabled: "&c 已禁用" + gamemode_gui: + name: '&f&l 空島模式選擇界面' + description: |- + &7 啟用 可以透過`/challenges`指令 + &7 獲得可選擇的指定界面 + &7 + &c **需要重新啟動套用更改** + enabled: "&2 已啟用" + disabled: "&c 已禁用" + locked_level_icon: + name: '&f&l 預設未解鎖等級的圖標' + description: |- + &7 預設未解鎖等級的圖標 + &7 每個等級均可單獨設置圖標 + purge_history: + name: '&f&l 記錄保存時限' + description: |- + &7 玩家數據被保留的最大時長(天) + &7 0 代表數據不會被刪除 + value: '&7 目前: &e [number] 天' + title_showtime: + name: '&f&l 標題顯示時長' + description: |- + &7 當玩家完成挑戰或提升等級時 + &7 標題(Title)顯示的時長(tick) + &7 (20 tick = 1秒) + value: '&7 目前: &e [number] tick' + active_world_list: + name: '&f&l 只顯示身處世界' + description: |- + &7 (這設置只適用於空島模式 + &7 選擇器已開放給玩家使用) + &7 + &7 切換 在目前世界使用選擇器時 + &7 會否顯示該空島模式或挑戰 + &7 + &c **需要重新啟動套用更改** + enabled: "&2已啟用" + disabled: "&c已禁用" + visibility_mode: + name: '&f&l 可見性模式' + description: |- + &7 可見性模式會顯示所有挑戰 + &7 包括己隱藏挑戰 + enabled: '&2' + disabled: '&c' + visible: '顯示非隱藏挑戰' + hidden: '顯示所有挑戰' + toggleable: '可切換' + download: + name: '&f&l 從網上挑戰庫下載更新' + description: |- + &7 手動從網上挑戰庫 + &7 下載挑戰更新 + enabled: '&2 下載同時清除緩存' + disabled: '&c 只下載但不清除緩存' + player: + name: '&f&l [name]' + description: '&7 島主: [owner]' + members: '&7 島成員:' + member: '&8 - [name]' + no-island: '&c 這玩家沒有島嶼' + player_list: + name: '&f&l 選擇玩家清單' + description: '&7 選擇指定玩家清單' + enabled: '&2' + disabled: '&c' + online: '在線玩家' + with_island: '有島嶼玩家' + in_world: '當前世界內的玩家' + add_block: + name: '&f&l 添加方塊' + description: '&7 添加方塊至清單中' + remove_block: + name: '&f&l 移除方塊' + description: '&7 將方塊從清單中移除' + title: '&7 已選材料:' + material: '&8 - [material]' + material: + name: '&f&l [material]' + description: '&7 材料ID: [id]' + selected: '&2 已選' + add_entity: + name: '&f&l 添加實體' + description: '&7 添加實體至清單中' + switch_entity: + name: '&f&l 轉變生怪蛋' + description: '&7 把生怪蛋轉變成頭顱' + remove_entity: + name: '&f&l 移除實體' + description: '&7 將實體從清單中移除' + title: '&7 已選實體:' + entity: '&8 - [entity]' + entity: + name: '&f&l [entity]' + description: '&7 實體ID: [id]' + selected: '&2 已選' + inventory_type: + name: '&f&l 類型: 背包' + description: '&7 挑戰需要檢驗玩家背包內的物品' + island_type: + name: '&f&l 類型: 島嶼' + description: '&7 挑戰需要檢驗玩家附近的方塊或實體' + other_type: + name: '&f&l 類型: 其他' + description: |- + &7 挑戰需要依據外帶插件進行檢驗 + &7 如: 個人經驗等級或遊戲幣等 + statistic_type: + name: '&f&l 類型: 統計數據' + description: '&7 挑戰需要檢驗玩家的個人統計數據' + description: |- + &7 挑戰需要檢驗玩家的個人統計數據 + &7 如: 走了多遠 殺了多少怪等 + save: + name: '&f&l 保存' + description: '&7 保存更改並返回' + cancel: + name: '&f&l 取消' + description: '&7 放棄更改並返回' + accept_selected: + name: '&f&l 確定已選' + description: '&7 確定已選定並返回上一菜單' + title: '&7 已選定: ' + element: '&8 - [element]' + statistic_element: + name: '&f&l [statistic]' + description: '[description]' + environment_element: + name: '&f&l [environment]' + description: '[description]' + search: + name: '&f&l 搜索' + description: '&7 搜索就搜索 沒他了' + search: '&b 字條: [value]' + tips: + click-to-select: "&e 點擊&7選擇" + click-to-choose: "&e 點擊&7選擇" + click-to-complete: "&e 點擊&7完成" + right-click-multiple-open: "&7 右&e擊 &7選擇完成挑戰的次數" + shift-left-click-to-complete-all: |- + &e 按住Shift並左擊&7一鍵完成 + &e (根據背包中所需道具數量) + left-click-to-accept: "&7 左&e擊&7接受" + right-click-to-write: "&7 右&e擊&7寫入" + click-to-reduce: "&e 點擊&7減少" + click-to-increase: "&e 點擊&7增加" + click-to-return: "&e 點擊&7返回" + click-to-quit: "&e 點擊&7退出" + click-to-wipe: "&e 點擊&7清空" + left-click-to-wipe: "&7 左&e擊&7清除" + right-click-to-switch: "&7 右&e擊&7切換" + click-to-open: "&e 點擊&7打開" + click-to-export: "&e 點擊&7導出" + click-to-create: "&e 點擊&7創建" + left-click-to-open: "&7 左&e擊&7打開" + right-click-to-reset-all: "&7 右&e擊&7重置所有內容" + click-to-toggle: "&e 點擊&7進行切換" + click-to-change: "&e 點擊&7進行更改" + shift-click-to-reset: "&e 按住Shift並點擊&7進行重置" + click-to-add: "&e 點擊&7添加" + click-to-remove: "&e 點擊&7刪除" + left-click-to-cycle: "&7 左&e擊&7向下循環" + right-click-to-cycle: "&7 右&e擊&7向上循環" + click-to-edit: "&e 點擊&7進行編輯" + left-click-to-download: "&7 左&e擊&7下載" + right-click-to-toggle: "&7 右&e擊&7進行切換" + click-to-install: "&e 點擊&7安裝" + click-to-reset-all: "&e 點擊&7全部重置" + right-click-to-select: "&7 右&e擊&7選擇" + right-click-to-deselect: "&7 右&e擊&7取消" + left-click-to-choose: "&7 左&e擊&7選擇" + click-to-cancel: "&e 點擊&7取消" + click-to-save: "&e 點擊&7保存" + click-to-deselect: "&e 點擊&7取消選擇" + click-on-item: "&7 從你的背包中&e點選&7物品" + left-click-to-edit: "&7 左&e擊&7進行編輯。" + right-click-to-clear: "&7 右&e擊&7清除。" + click-to-previous: "&e 點擊 &7 查看上一頁。" + click-to-next: "&e 點擊 &7 查看下一頁。" + descriptions: + challenge: + lore: |- + [description] + [status] + [cooldown] + [requirements] + [rewards] + status: + completed: '&2&l 已完成' + completed-times: '&2 已完成&7&l[number]&r&2次' + completed-times-of: '&2 已完成&7&l[number]&r&2次 (最大&7&l[max]&r&2次) ' + completed-times-reached: '&2&l 已完成最大&7&l[max]&r&2次' + cooldown: + lore: |- + [timeout] + [wait-time] + timeout: '&7&l 冷卻時間: &r&7 [time]' + wait-time: '&c&l 冷卻倒計時: &r&c [time]' + in-days: '[number]天' + in-hours: '[number]小時' + in-minutes: '[number]分鐘' + in-seconds: '[number]秒' + requirements: + lore: |- + [environment] + [type-requirement] + [permissions] + environment-single: '&7 限於[environment]' + environment-title: '&7 限於: ' + environment-list: ' &7 - &e [environment]' + permission-single: '&c 需要 [permissions] 權限' + permissions-title: '&c 需要多個權限: ' + permissions-list: ' &c - [permission]' + island: + lore: |- + [blocks] + [entities] + [search-radius] + [warning-block] + [warning-entity] + blocks-title: '&7&l 所需方塊:' + block-value: ' &7 - &e [material]' + blocks-value: ' &7 - &e [number] x [material]' + entities-title: '&7&l 所需實體:' + entity-value: ' &7 - &e [entity]' + entities-value: ' &7 - &e [number] x [entity]' + search-radius: '&7 不可超過&e[number]&7米' + warning-block: '&e 方塊會被收取不退還' + warning-entity: '&e 實體會被收取不退還' + inventory: + lore: |- + [items] + [warning] + item-title: '&7&l 所需物品:' + item-value: ' &7 - &e [item]' + items-value: ' &7 - &e [number] x [item]' + warning: '&e 物品會被收取不退還' + other: + lore: |- + [experience] + [experience-warning] + [money] + [money-warning] + [level] + experience: '&7&l 所需經驗值: &r&e [number]' + experience-warning: '&e 經驗值會被收取不退還' + money: '&7&l 所需遊戲幣: &r&e [number]' + money-warning: '&e 遊戲幣會被收取不退還' + level: '&7&l 需要的島嶼等級: &r&e [number]' + statistic: + lore: |- + [statistic] + [warning] + multiple-target: '&7&l [statistic]: &r&e [number] x [target]' + single-target: '&7&l [statistic]: &r&e [target]' + statistic: '&7&l [statistic] &r&e [number]' + warning: '&e 該統計記錄會&c被扣除' + rewards: + lore: |- + &7&l 完成獎勵: + [text] + [items] + [experience] + [money] + [commands] + item-title: '&7 物品:' + item-value: ' &7 - &e [item]' + items-value: ' &7 - &e [number] x [item]' + experience: '&7 經驗值: &r&e [number]' + money: '&7 遊戲幣: &r&e [number]' + commands-title: '&7 指令:' + command: ' &7 - &e [command]' + level: + lore: |- + [text] + [status] + [waiver] + [rewards] + status: + completed: '&2&l 已完成' + completed-challenges-of: |- + &2 已完成&7&l[max]個&r&2挑戰中的 + &7&l [number]個 + locked: '&c&l未解鎖' + missing-challenges: |- + &7 需要先完成 [number] 挑戰 + &7 才可解鎖這等級。 + waiver: |- + &7 目前等級容許豁免完成 &l[number]個挑戰 + &7 以提升到下一等級 + rewards: + lore: |- + &7&l 完成獎勵: + [text] + [items] + [experience] + [money] + [commands] + item-title: '&7 物品:' + item-value: ' &7 - &e[item]' + items-value: ' &7 - &e[number] x [item]' + experience: '&7 經驗值: &r&e[number]' + money: '&7 遊戲幣: &r&e[number]' + commands-title: '&7 指令:' + command: ' &7 - &e[command]' + library: + author: '&7 作者: &e[author]' + version: '&7 依據挑戰附加-版本: &e[version]' + lang: '&7 語言: &e [lang]' + gamemode: '&7 主要模式: &e [gamemode]' + conversations: + prefix: '&l&6 [BentoBox]: &r' + confirm-string: true, on, yes, confirm, y, valid, correct + deny-string: false, off, no, deny, n, invalid, incorrect + cancel-string: cancel + exit-string: cancel, exit, quit + cancelled: '&c 對話取消!' + input-number: '&e 請在聊天中輸入 一個數字' + input-seconds: '&e 請在聊天中輸入 一個秒數' + numeric-only: '&c 輸入的[value]不是 有效數字!' + not-valid-value: '&c 輸入的[value]無效. 數字必須於[min]及[max]之間!' + user-data-removed: '&a 空島模式[gamemode]中的&4所有玩家數據&a已被清除' + confirm-user-data-deletion: |- + &e 請再次 &4確認 &e要把空島模式[gamemode]中的 + &4所有&e玩家數據 &a從數據庫中刪除 + challenge-data-removed: '&a 空島模式[gamemode]中的&4所有挑戰紀錄&a已被清除' + confirm-challenge-data-deletion: |- + &e 請再次 &4確認 &e要把空島模式[gamemode]中的 + &4所有&e挑戰紀錄 &a從數據庫中刪除 + all-data-removed: '&a 空島模式[gamemode]中的&4所有附加插件(addon)的紀錄&a已被清除' + confirm-all-data-deletion: |- + &e 請再次 &4確認 &e要把空島模式[gamemode]中的 + &4所有&e附加插件(addon)的紀錄 &a從數據庫中刪除 + write-name: '&e 請在聊天中輸入 一個名字' + new-object-created: '&a 為空島模式[gamemode]創造了新物品' + object-already-exists: '&c 物品&7[id] &c已經存在。 請選擇新名字。' + invalid-challenge: '&c 挑戰[challenge]包含無效數據。 無法啟用!' + name-changed: '&a 動作完成,名稱已被更新。' + write-description: '&e 請在聊天中輸入 介紹 及 在單獨一行中輸入''quit''完成。' + description-changed: '&a 動作完成,介紹已被更新。' + write-permissions: '&e 請在聊天中輸入所要求的權限,每個權限以單獨一行輸入 及 在單獨一行中輸入''quit''完成。' + permissions-changed: '&a 動作完成,挑戰所要求的權限已被更新。' + write-reward-text: '&e 請在聊天中輸入(首次)完成挑戰後顯示的字句 及 在單獨一行中輸入''quit''完成。' + reward-text-changed: '&a 動作完成,(首次)完成後顯示的字句已被更新。' + write-repeat-reward-text: '&e 請在聊天中輸入(重覆)完成挑戰後顯示的字句 及 在單獨一行中輸入''quit''完成。' + repeat-reward-text-changed: '&a 動作完成,(重覆)完成後顯示的字句已被更新。' + write-reward-commands: '&e 請在聊天中輸入(首次)完成挑戰後執行的指令 及 在單獨一行中輸入''quit''完成。' + reward-commands-changed: '&a 動作完成,(首次)完成後執行的指令已被更新。' + write-repeat-reward-commands: '&e 請在聊天中輸入(重覆)完成挑戰後執行的指令 及 在單獨一行中輸入''quit''完成。' + repeat-reward-commands-changed: '&a 動作完成,(重覆)完成後執行的指令已被更新。' + challenge-removed: '&a 空島模式[gamemode]中的挑戰[challenge] 已被清除。' + confirm-challenge-deletion: '&e 請 再次確認 要清除空島模式[gamemode]中的挑戰[challenge]' + level-removed: '&a 空島模式[gamemode]中的等級[level] 已被清除。' + confirm-level-deletion: '&e 請 再次確認 要清除空島模式[gamemode]中的等級[level]' + start-downloading: '&a 開始下載及導入挑戰庫' + written-text: '&a 已輸入文本:' + confirm-data-replacement: '&e 請 再次確認 要以新挑戰替代現存挑戰。' + new-challenges-imported: '&a 動作完成,空島模式[gamemode]中新的挑戰已被導入。' + exported-file-name: '&e 輸入已導出數據庫的檔案名稱。 (輸入''cancel''退出)' + database-export-completed: '&a 動作完成,世界[world]數據已完成導出,檔案[file]已生成。' + file-name-exist: '&c 檔案''[id]''已存在。 無法覆蓋。' + write-search: '&e 請輸入搜索字條 (輸入''cancel''退出)' + search-updated: '&a 搜索完成' + titles: + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the challenge object. + # [friendlyName] will be replaced with challenge friendly name. + # [level] will be replaced with level friendly name. + # [rewardText] will be replaced with the challenge reward text. + challenge-title: '挑戰完成' + challenge-subtitle: '[friendlyName]' + # Title and subtitle may contain variables in [] that will be replaced with a proper message from the level object. + # [friendlyName] will be replaced with level friendly name. + # [rewardText] will be replaced with the level reward text. + level-title: '等級完成' + level-subtitle: '[friendlyName]' + messages: + completed: '&2 你已為[player]完成挑戰[name]!' + already-completed: '&2 這個挑戰已經完成了!' + reset: '&2 你已重設玩家[player]的[name]挑戰紀錄!' + reset-all: '&2 玩家[player]的所有挑戰紀錄已被重設!' + not-completed: '&2 這個挑戰還未完成啊!' + migrate-start: '&2 開始遷移挑戰附加插件的數據。' + migrate-end: '&2 挑戰附加插件的數據已遷移至新格式。' + migrate-not: '&2 數據已確認有效。' + start-downloading: '&5 開始下載及導入挑戰。' + you-completed-challenge: '&2 你已完成了 [value]&2挑戰!' + you-repeated-challenge: '&2 你已重覆完成了 [value]&2挑戰!' + you-repeated-challenge-multiple: '&2 你已重覆完成了 [value]&2挑戰 [count]次!' + you-completed-level: '&2 你已完成了 [value]&2等級!' + name-has-completed-challenge: '&5 [name]已完成了 [value]&2挑戰!' + name-has-completed-level: '&5 [name]已完成了 [value]&2等級!' + load-skipping: '"[value]"已存在 - 忽略中' + load-overwriting: 覆蓋 "[value]" + load-add: '加入新物品: [value]' + errors: + no-name: '&c 欠缺挑戰名稱' + unknown-challenge: '&c 不明挑戰' + not-valid-integer: |- + &c 輸入的數字"[value]"無效! + &c 數字必須在[min]和[max]之間。 + 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]經驗值以完成挑戰。' + island-level: '&c 你的島嶼必須到達最少[number]等級以完成挑戰!' + no-load: '&c 錯誤: 無法載入檔案。 [message]' + load-error: '&c 錯誤: 無法載入 [value].' + no-rank: '&c 你還未達到足夠的頭銜等級以完成挑戰。' + cannot-remove-items: '&c 部分物品無法從你的背包內移除!' + exist-challenges-or-levels: '&c 你的世界內已經有了這挑戰。 無法進行!' + no-challenges: '&c 這個世界尚未開放挑戰功能!' + no-challenges-admin: '&c 這個世界尚未開放挑戰功能! 使用&5 /[command] &c添加挑戰!' + missing-arguments: '&c 指令不完整。' + no-multiple-permission: '&c 你沒有權限同時重覆完成這挑戰。' + invalid-level: '&c 等級[level]中存在錯誤數據。 無法從數據庫中加載!' + invalid-challenge: '&c 挑戰[challenge]中存在錯誤數據。 無法從數據庫中加載!' + no-library-entries: '&c 無法找到任何可用的資料。 沒有可以顯示的。' + not-hooked: '&c 挑戰附加插件沒有發現任何遊戲模式。' + timeout: '&c 每次完成這挑戰要求最少等待[timeout]秒。 你必須等待[wait-time]秒才可以再次完成。' + requirement-not-met: '&c 這挑戰要求[statistic]達到[number]。 你只有[value]。' +# # Showcase for manual material translation +# materials: +# # Names should be lowercase. +# cobblestone: "Cobblestone" +# # Also supports descriptions. +# stone: +# name: "Stone" +# description: "" +# item-stacks: +# # Non-specific item meta translations. +# # TYPE is the item type +# # META is a content of item meta. +# generic: "[type] [meta]" +# # Non-specific meta translations. Will replace [meta] +# meta: +# upgraded: "Upgraded" +# extended: "Extended" +# potion-meta: "&e [type] [upgraded] [extended]" +# # Be aware, enchants are always listed below item in separate line. +# enchant-meta: " &7 - &e [type] [level]" +# skull-meta: ": &e [player-name]" +# book-meta: "&e [title] [author]" +# # Custom Enchantment Translation. +# enchant: +# menting: "Mending" +# unbreaking: "Unbreaking" +# # Custom Potion Translation. +# potion-type: +# water_breathing: "Water Breathing" +# # You can also create specific item translations +# # Like translate all potions. +# potion: +# # This will overwrite generic translation. +# name: "[type] [upgraded] [extended]" +# # Type is either specific translation or potion effect. +# water_breathing: "Potion of Water Breathing" +# stone_shovel: +# # This will mean that only stone shovels will not show +# # meta information. +# name: "[type]" +# +# # Showcase how to support multi-linguistic challenges +# challenges: +# # Database ID name. +# example_challenge_id: +# name: "&2 Translated Name" +# description: |- +# &7 Translated Custom +# &7 description +# reward-text: |- +# &7 Translated Reward +# &7 text +# repeat-reward-text: |- +# &7 Translated Repeat +# &7 Reward text +# levels: +# # Database ID name. +# example_level_id: +# name: "&2 Translated Name" +# description: |- +# &7 Translated Custom +# &7 description +# reward-text: |- +# &7 Translated Reward +# &7 text +protection: + flags: + CHALLENGES_ISLAND_PROTECTION: + description: '&5&o 切換 任何玩家可以完成挑戰' + name: 挑戰保護 + CHALLENGES_WORLD_PROTECTION: + description: |- + &5&o爲玩家啓用/禁用 + &5&o要求他們在他們的島嶼上 + &5&o才能完成挑戰. + name: 挑戰島嶼限制 + hint: 請在自己的島嶼完成挑戰! + +version: 12 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..3c1152d --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,9 @@ +name: BentoBox-Challenges +main: world.bentobox.challenges.ChallengesPladdon +version: ${project.version}${build.number} +api-version: "1.17" + +authors: [tastybento, BONNe] +contributors: ["The BentoBoxWorld Community"] +website: https://bentobox.world +description: ${project.description} diff --git a/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java index 06891d4..4e8fca2 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java @@ -245,96 +245,96 @@ public class ChallengesManagerTest { } /** - * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, World, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeNoOverwriteSilent() { // load once - assertTrue(cm.loadChallenge(challenge, false, user, true)); + assertTrue(cm.loadChallenge(challenge, world, false, user, true)); // load twice - no overwrite - assertFalse(cm.loadChallenge(challenge, false, user, true)); + assertFalse(cm.loadChallenge(challenge, world, false, user, true)); } /** - * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, World, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeNoOverwriteNotSilent() { // load once - assertTrue(cm.loadChallenge(challenge, false, user, true)); + assertTrue(cm.loadChallenge(challenge, world, false, user, true)); // load twice - no overwrite, not silent - assertFalse(cm.loadChallenge(challenge, false, user, false)); + assertFalse(cm.loadChallenge(challenge, world, false, user, false)); verify(user).getTranslation("challenges.messages.load-skipping", "[value]", "name"); } /** - * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, World, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeOverwriteSilent() { // load once - assertTrue(cm.loadChallenge(challenge, false, user, true)); + assertTrue(cm.loadChallenge(challenge, world, false, user, true)); // overwrite - assertTrue(cm.loadChallenge(challenge, true, user, true)); + assertTrue(cm.loadChallenge(challenge, world, true, user, true)); verify(user, never()).getTranslation(anyString(), anyString(), anyString()); } /** - * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, World, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadChallengeOverwriteNotSilent() { // load once - assertTrue(cm.loadChallenge(challenge, false, user, true)); + assertTrue(cm.loadChallenge(challenge, world, false, user, true)); // overwrite not silent - assertTrue(cm.loadChallenge(challenge, true, user, false)); + assertTrue(cm.loadChallenge(challenge, world, true, user, false)); verify(user).getTranslation("challenges.messages.load-overwriting", "[value]", "name"); } /** - * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, World, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelNoOverwriteSilent() { // load once - assertTrue(cm.loadLevel(level, false, user, true)); + assertTrue(cm.loadLevel(level, world, false, user, true)); // load twice - no overwrite - assertFalse(cm.loadLevel(level, false, user, true)); + assertFalse(cm.loadLevel(level, world, false, user, true)); } /** - * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, World, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelNoOverwriteNotSilent() { // load once - assertTrue(cm.loadLevel(level, false, user, true)); + assertTrue(cm.loadLevel(level, world, false, user, true)); // load twice - no overwrite, not silent - assertFalse(cm.loadLevel(level, false, user, false)); + assertFalse(cm.loadLevel(level, world, false, user, false)); verify(user).getTranslation("challenges.messages.load-skipping", "[value]", "Novice"); } /** - * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, World, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelOverwriteSilent() { // load once - assertTrue(cm.loadLevel(level, false, user, true)); + assertTrue(cm.loadLevel(level, world, false, user, true)); // overwrite - assertTrue(cm.loadLevel(level, true, user, true)); + assertTrue(cm.loadLevel(level, world, true, user, true)); verify(user, never()).getTranslation(anyString(), anyString(), anyString()); } /** - * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + * Test method for {@link ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, World, boolean, world.bentobox.bentobox.api.user.User, boolean)}. */ @Test public void testLoadLevelOverwriteNotSilent() { // load once - assertTrue(cm.loadLevel(level, false, user, true)); + assertTrue(cm.loadLevel(level, world, false, user, true)); // overwrite not silent - assertTrue(cm.loadLevel(level, true, user, false)); + assertTrue(cm.loadLevel(level, world, true, user, false)); verify(user).getTranslation("challenges.messages.load-overwriting", "[value]", "Novice"); } @@ -660,7 +660,7 @@ public class ChallengesManagerTest { public void testGetAllChallengesNames() { assertTrue(cm.getAllChallengesNames(world).isEmpty()); cm.saveChallenge(challenge); - cm.loadChallenge(challenge, false, user, true); + cm.loadChallenge(challenge, world, false, user, true); List list = cm.getAllChallengesNames(world); assertFalse(list.isEmpty()); assertEquals(cName, list.get(0)); @@ -673,7 +673,7 @@ public class ChallengesManagerTest { public void testGetAllChallenges() { assertTrue(cm.getAllChallenges(world).isEmpty()); cm.saveChallenge(challenge); - cm.loadChallenge(challenge, false, user, true); + cm.loadChallenge(challenge, world, false, user, true); List list = cm.getAllChallenges(world); assertFalse(list.isEmpty()); assertEquals(challenge, list.get(0)); @@ -688,12 +688,12 @@ public class ChallengesManagerTest { assertTrue(cm.getFreeChallenges(world).isEmpty()); // One normal cm.saveChallenge(challenge); - cm.loadChallenge(challenge, false, user, true); + cm.loadChallenge(challenge, world, false, user, true); assertTrue(cm.getFreeChallenges(world).isEmpty()); // One free challenge.setLevel(""); cm.saveChallenge(challenge); - cm.loadChallenge(challenge, false, user, true); + cm.loadChallenge(challenge, world, false, user, true); List list = cm.getFreeChallenges(world); assertFalse(list.isEmpty()); assertEquals(challenge, list.get(0)); @@ -792,7 +792,7 @@ public class ChallengesManagerTest { public void testGetLevelString() { assertNull(cm.getLevel("dss")); cm.saveLevel(level); - cm.loadLevel(level, false, user, true); + cm.loadLevel(level, world, false, user, true); assertEquals(level, cm.getLevel(levelName)); }