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 <apachezy@hotmail.com>

* 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 <patrick.wassmuth@gmx.de>
Co-authored-by: Michael F <unhappyangel83@googlemail.com>
Co-authored-by: DAge030 <dage030@web.de>

* 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 <tastybento@wasteofplastic.com>
Co-authored-by: apachezy <50116371+apachezy@users.noreply.github.com>
Co-authored-by: zhangYi <apachezy@hotmail.com>
Co-authored-by: Qumoo <76853697+Qumoo@users.noreply.github.com>
Co-authored-by: tastybento <tastybento@users.noreply.github.com>
Co-authored-by: gitlocalize-app[bot] <55277160+gitlocalize-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick <patrick.wassmuth@gmx.de>
Co-authored-by: Michael F <unhappyangel83@googlemail.com>
Co-authored-by: DAge030 <dage030@web.de>
This commit is contained in:
BONNe 2022-05-06 19:51:54 +03:00 committed by GitHub
parent 24fa0fe2d5
commit 7ac1ab4a61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
119 changed files with 22142 additions and 16426 deletions

View File

@ -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:

198
pom.xml
View File

@ -32,17 +32,19 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<powermock.version>2.0.4</powermock.version>
<java.version>16</java.version>
<powermock.version>2.0.9</powermock.version>
<!-- More visible way how to change dependency versions -->
<spigot.version>1.15.2-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>1.15.4</bentobox.version>
<level.version>2.5.0</level.version>
<spigot.version>1.17.1-R0.1-SNAPSHOT</spigot.version>
<spigot-annotations.version>1.2.3-SNAPSHOT</spigot-annotations.version>
<bentobox.version>1.20.0</bentobox.version>
<level.version>2.6.3</level.version>
<vault.version>1.7</vault.version>
<panelutils.version>1.0.0</panelutils.version>
<!-- Revision variable removes warning about dynamic version -->
<revision>${build.version}-SNAPSHOT</revision>
<!-- This allows to change between versions and snapshots. -->
<build.version>0.8.4</build.version>
<build.version>1.0.0</build.version>
<build.number>-LOCAL</build.number>
<!-- Sonar Cloud -->
<sonar.projectKey>BentoBoxWorld_Challenges</sonar.projectKey>
@ -92,35 +94,33 @@
</repository>
</distributionManagement>
<pluginRepositories>
<pluginRepository>
<id>apache.snapshots</id>
<url>https://repository.apache.org/snapshots/</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<!-- Spigot Repo -->
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
</repository>
<repository>
<id>spigotmc-public</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<!-- CodeMC Repo for BentoBox -->
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
<url>https://repo.codemc.io/repository/maven-public</url>
</repository>
<!-- Minecraft Lib Repo -->
<repository>
<id>codemc-nms</id>
<url>https://repo.codemc.org/repository/nms/</url>
</repository>
<!--Vault Repo is down. -->
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<id>minecraft-repo</id>
<url>https://libraries.minecraft.net/</url>
</repository>
</repositories>
<dependencies>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
@ -129,15 +129,49 @@
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigot.version}</version>
<artifactId>plugin-annotations</artifactId>
<version>1.2.3-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Vault API -->
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>VaultAPI</artifactId>
<version>${vault.version}</version>
<scope>provided</scope>
</dependency>
<!-- BentoBox dependencies. -->
<dependency>
<groupId>world.bentobox</groupId>
<artifactId>bentobox</artifactId>
<version>${bentobox.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>world.bentobox</groupId>
<artifactId>level</artifactId>
<version>${level.version}</version>
<scope>provided</scope>
</dependency>
<!-- PanelUtils -->
<dependency>
<groupId>lv.id.bonne</groupId>
<artifactId>panelutils</artifactId>
<version>${panelutils.version}</version>
<scope>compile</scope>
</dependency>
<!-- Annotations -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>22.0.0</version>
<scope>provided</scope>
</dependency>
<!-- Mockito (Unit testing) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.1.0</version>
<version>3.11.2</version>
<scope>test</scope>
</dependency>
<dependency>
@ -152,22 +186,11 @@
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!-- Mojang Auth -->
<dependency>
<groupId>world.bentobox</groupId>
<artifactId>bentobox</artifactId>
<version>${bentobox.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>world.bentobox</groupId>
<artifactId>level</artifactId>
<version>${level.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>VaultAPI</artifactId>
<version>${vault.version}</version>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>1.5.21</version>
<scope>provided</scope>
</dependency>
</dependencies>
@ -213,16 +236,49 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>3.7.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<release>${java.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<version>3.0.0-M5</version>
<configuration>
<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
--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
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -234,12 +290,10 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<source>8</source>
<show>public</show>
<failOnError>false</failOnError>
<additionalJOption>-Xdoclint:none</additionalJOption>
<!-- To compile with Java 11, this tag may be required -->
<!-- <javadocExecutable>${java.home}/bin/javadoc</javadocExecutable> -->
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
</configuration>
<executions>
<execution>
@ -263,28 +317,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
<pattern>io.github.TheBusyBiscuit.GitHubWebAPI4Java</pattern>
<shadedPattern>world.bentobox.bentobox.api.github</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
@ -322,6 +354,38 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.1-SNAPSHOT</version>
<configuration>
<minimizeJar>true</minimizeJar>
<artifactSet>
<includes>
<include>lv.id.bonne:panelutils:*</include>
</includes>
</artifactSet>
<transformers>
<!-- Add a transformer to exclude any other manifest files (possibly from dependencies). -->
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
<resource>MANIFEST.MF</resource>
</transformer>
<!-- Add a transformer to include your custom manifest file. -->
<transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
<resource>META-INF/MANIFEST.MF</resource>
<file>src/main/resources/META-INF/MANIFEST.MF</file>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

1
src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/.DS_Store

View File

@ -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<GameModeAddon> 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<GameModeAddon> 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<Addon> 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<VaultHook> 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
// ---------------------------------------------------------------------

View File

@ -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 <code>true</code> if everything was successful, otherwise <code>false</code>.
*/
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 <code>true</code> if everything was successful, otherwise <code>false</code>.
*/
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 <code>true</code> if everything was successful, otherwise <code>false</code>
*/
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<Challenge> 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<ChallengeLevel> 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<Challenge> getChallengeList()
{
return challengeList;
}
/**
* This method sets given list as default challenge list.
* @param challengeList new default challenge list.
*/
void setChallengeList(List<Challenge> challengeList)
{
this.challengeList = challengeList;
}
/**
* This method returns list of default challenge levels.
* @return List that contains default challenge levels.
*/
List<ChallengeLevel> getLevelList()
{
return challengeLevelList;
}
/**
* This method sets given list as default challenge level list.
* @param levelList new default challenge level list.
*/
void setLevelList(List<ChallengeLevel> 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<Challenge> challengeList;
/**
* Holds a list with default levels.
*/
@Expose
private List<ChallengeLevel> challengeLevelList;
/**
* Holds a variable that stores in which addon version file was made.
*/
@Expose
private String version;
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
private ChallengesAddon addon;
}

View File

@ -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();
}
}

View File

@ -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<String> 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<String> 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);
}
}

View File

@ -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<GameModeAddon> gameModeAddons)
public ChallengesGlobalPlayerCommand(ChallengesAddon addon, List<GameModeAddon> 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.<ChallengesAddon>getAddon().getChallengesSettings().getPlayerMainCommand().split(" ")[0]));
return true;
}
else if (this.addon.getChallengesSettings().getUserGuiMode() == GuiMode.CURRENT_WORLD)
else if (this.<ChallengesAddon>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.<ChallengesAddon>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.<ChallengesAddon>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<GameModeAddon> gameModeAddons;
/**
* Challenges addon for easier operations.
*/
private ChallengesAddon addon;
private final List<GameModeAddon> gameModeAddons;
}

View File

@ -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<String> 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.<ChallengesAddon>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<String> 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);
}
}

View File

@ -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.<ChallengesAddon>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.<ChallengesAddon>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;
}

View File

@ -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<String> 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;
}
}

View File

@ -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<GameModeAddon> 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<String> args)
{
// For single game mode just open correct gui.
@Override
public boolean execute(User user, String label, List<String> 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<GameModeAddon> gameModeAddons;
return true;
}
return false;
}
}

View File

@ -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<GameModeAddon> 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<String> 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.<ChallengesAddon>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<GameModeAddon> gameModeAddons;
}

View File

@ -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;
}

View File

@ -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<String> 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<String> 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<String> args)
{
return DefaultsCommand.this.addon.getImportManager().generateDefaultChallengeFile(
user,
this.getWorld(),
!args.isEmpty() && args.get(0).equalsIgnoreCase("overwrite"));
}
/**
* {@inheritDoc}
*/
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> 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;
}

View File

@ -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<String> args) {
((ChallengesAddon)getAddon()).getChallengesManager().migrateDatabase(user, getWorld());
public boolean execute(User user, String label, List<String> 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");

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<String> args) {
public boolean execute(User user, String label, List<String> args)
{
if (user.isPlayer())
{
((ChallengesAddon) getAddon()).getChallengesManager().
@ -40,7 +45,5 @@ public class ShowChallenges extends CompositeCommand {
}
return true;
}
}

View File

@ -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<ChallengeLore> 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<LevelLore> 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<ChallengeLore> 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<LevelLore> 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<ChallengeLore> challengeLoreMessage)
{
this.challengeLoreMessage = challengeLoreMessage;
}
/**
* This method sets the levelLoreMessage object value.
* @param levelLoreMessage the levelLoreMessage object new value.
*/
public void setLevelLoreMessage(List<LevelLore> 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;
}

View File

@ -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<String> requiredPermissions = new HashSet<>();
@Deprecated
@Expose
private Map<Material, Integer> requiredBlocks = new EnumMap<>(Material.class);
@Deprecated
@Expose
private boolean removeBlocks;
@Deprecated
@Expose
@JsonAdapter(EntityCompatibilityAdapter.class)
private Map<EntityType, Integer> requiredEntities = new EnumMap<>(EntityType.class);
@Deprecated
@Expose
private boolean removeEntities;
@Deprecated
@Expose
private List<ItemStack> 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<String> rewardCommands = new ArrayList<>();
/**
* Set of item stacks that should ignore metadata.
*/
@Expose
private Set<Material> 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<String> getRequiredPermissions()
{
return requiredPermissions;
}
/**
* @return the requiredBlocks
*/
@Deprecated
public Map<Material, Integer> getRequiredBlocks()
{
return requiredBlocks;
}
/**
* @return the removeBlocks
*/
@Deprecated
public boolean isRemoveBlocks()
{
return removeBlocks;
}
/**
* @return the requiredEntities
*/
@Deprecated
public Map<EntityType, Integer> getRequiredEntities()
{
return requiredEntities;
}
/**
* @return the removeEntities
*/
@Deprecated
public boolean isRemoveEntities()
{
return removeEntities;
}
/**
* @return the requiredItems
*/
@Deprecated
public List<ItemStack> 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<Material> 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<String> requiredPermissions)
{
this.requiredPermissions = requiredPermissions;
}
/**
* This method sets the requiredBlocks value.
* @param requiredBlocks the requiredBlocks new value.
*
*/
@Deprecated
public void setRequiredBlocks(Map<Material, Integer> 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<EntityType, Integer> 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<ItemStack> 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<Material> 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)
{

View File

@ -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<ChallengeLevel>
@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<ChallengeLevel>
@Expose
private Set<String> challenges = new HashSet<>();
@ConfigComment("")
@ConfigComment("Set of materials which metadata can be ignored.")
@Expose
private Set<Material> ignoreRewardMetaData = new HashSet<>();
// ---------------------------------------------------------------------
// Section: Getters
@ -236,7 +241,7 @@ public class ChallengeLevel implements DataObject, Comparable<ChallengeLevel>
* 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<ChallengeLevel>
}
/**
* Gets ignore reward meta data.
*
* @return the ignore reward meta data
*/
public Set<Material> getIgnoreRewardMetaData()
{
return ignoreRewardMetaData;
}
// ---------------------------------------------------------------------
// Section: Setters
// ---------------------------------------------------------------------
@ -395,7 +411,7 @@ public class ChallengeLevel implements DataObject, Comparable<ChallengeLevel>
* @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<ChallengeLevel>
}
/**
* Sets ignore reward meta data.
*
* @param ignoreRewardMetaData the ignore reward meta data
*/
public void setIgnoreRewardMetaData(Set<Material> ignoreRewardMetaData)
{
this.ignoreRewardMetaData = ignoreRewardMetaData;
}
// ---------------------------------------------------------------------
// Section: Other methods
// ---------------------------------------------------------------------
@ -449,7 +476,7 @@ public class ChallengeLevel implements DataObject, Comparable<ChallengeLevel>
* {@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<ChallengeLevel>
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<ChallengeLevel>
* 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<ChallengeLevel>
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)
{

View File

@ -65,7 +65,7 @@ public class ChallengesPlayerData implements DataObject
private Map<String, Integer> 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;

View File

@ -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<ChallengeLore>, List<String>>
{
@SuppressWarnings("unchecked")
@Override
public List<ChallengeLore> deserialize(Object from)
{
List<ChallengeLore> result;
if (from instanceof List)
{
result = ((List<String>) from).stream().
map(ChallengeLore::valueOf).
collect(Collectors.toCollection(ArrayList::new));
}
else
{
result = new ArrayList<>(0);
}
return result;
}
@SuppressWarnings("unchecked")
@Override
public List<String> serialize(Object to)
{
List<String> result;
if (to instanceof List)
{
result = ((List<ChallengeLore>) to).stream().
map(ChallengeLore::name).
collect(Collectors.toCollection(ArrayList::new));
}
else
{
result = new ArrayList<>(0);
}
return result;
}
}

View File

@ -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<EntityType, Integer> deserialize(JsonElement json,

View File

@ -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<LevelLore>, List<String>>
{
@SuppressWarnings("unchecked")
@Override
public List<LevelLore> deserialize(Object from)
{
List<LevelLore> result;
if (from instanceof List)
{
result = ((List<String>) from).stream().
map(LevelLore::valueOf).
collect(Collectors.toCollection(ArrayList::new));
}
else
{
result = new ArrayList<>(0);
}
return result;
}
@SuppressWarnings("unchecked")
@Override
public List<String> serialize(Object to)
{
List<String> result;
if (to instanceof List)
{
result = ((List<LevelLore>) to).stream().
map(LevelLore::name).
collect(Collectors.toCollection(ArrayList::new));
}
else
{
result = new ArrayList<>(0);
}
return result;
}
}

View File

@ -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<Challenge.ChallengeType>, JsonDeserializer<Challenge.ChallengeType>
{
/**
* 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;
};
}
}

View File

@ -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<Material> 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<Material> 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<ItemStack> requiredItems = new ArrayList<>();
/**
* Set of item stacks that should ignore metadata.
*/
@Expose
private Set<Material> ignoreMetaData = new HashSet<>();
/**
* Boolean that indicate if challenge completion should remove items from inventory.
*/

View File

@ -39,7 +39,7 @@ public class IslandRequirements extends Requirements
/**
* Method IslandRequirements#getRequiredBlocks returns the requiredBlocks of this object.
*
* @return the requiredBlocks (type Map<Material, Integer>) of this object.
* @return the requiredBlocks (type {@code Map<Material, Integer>}) of this object.
*/
public Map<Material, Integer> getRequiredBlocks()
{
@ -83,7 +83,7 @@ public class IslandRequirements extends Requirements
/**
* Method IslandRequirements#getRequiredEntities returns the requiredEntities of this object.
*
* @return the requiredEntities (type Map<EntityType, Integer>) of this object.
* @return the requiredEntities (type {@code Map<EntityType, Integer>}) of this object.
*/
public Map<EntityType, Integer> 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()));

View File

@ -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()));

View File

@ -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();
// ---------------------------------------------------------------------

View File

@ -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;
}

View File

@ -101,5 +101,5 @@ public class ChallengeDataRequestHandler extends AddonRequestHandler
/**
* Variable stores challenges addon.
*/
private ChallengesAddon addon;
private final ChallengesAddon addon;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -94,5 +94,5 @@ public class LevelDataRequestHandler extends AddonRequestHandler
/**
* Variable stores challenges addon.
*/
private ChallengesAddon addon;
private final ChallengesAddon addon;
}

View File

@ -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;
}

View File

@ -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());
}
}
}

View File

@ -50,5 +50,5 @@ public class SaveListener implements Listener
// ---------------------------------------------------------------------
private ChallengesAddon addon;
private final ChallengesAddon addon;
}

File diff suppressed because it is too large Load Diff

View File

@ -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<Challenge> challengeDatabase;
private final Database<Challenge> challengeDatabase;
/**
* This config object stores structures for challenge level objects.
*/
private Database<ChallengeLevel> levelDatabase;
private final Database<ChallengeLevel> levelDatabase;
/**
* This database allows to access player challenge data.
* This database allows accessing player challenge data.
*/
private Database<ChallengesPlayerData> playersDatabase;
private final Database<ChallengesPlayerData> playersDatabase;
/**
* This is local cache that links challenge unique id with challenge object.
*/
private Map<String, Challenge> challengeCacheData;
private final Map<String, Challenge> challengeCacheData;
/**
* This is local cache that links level unique id with level object.
*/
private Map<String, ChallengeLevel> levelCacheData;
private final Map<String, ChallengeLevel> levelCacheData;
/**
* This is local cache that links UUID with corresponding player challenge data.
*/
private Map<String, ChallengesPlayerData> playerCacheData;
private final Map<String, ChallengesPlayerData> 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<ChallengeLevel> 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<Challenge> 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<ChallengesPlayerData> playerDataList = this.playersDatabase.loadObjects();
playerDataList.forEach(playerData -> this.playersDatabase.deleteID(playerData.getUniqueId()));
this.playerCacheData.clear();
List<ChallengesPlayerData> 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<ChallengeLevel> 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);

File diff suppressed because it is too large Load Diff

View File

@ -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<T> 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<T> 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<String> 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<String> 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 = "";
}

File diff suppressed because it is too large Load Diff

View File

@ -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<Boolean> 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<String> consumer,
Function<String, Boolean> 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<Number> 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<List<String>> 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<String>) 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<String>) 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<String> desc = new ArrayList<>();
if (context.getSessionData(SESSION_CONSTANT) instanceof List list)
{
desc = (List<String>) 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<String> 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"));
}
};
}
}

View File

@ -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<GameModeAddon> 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<CompositeCommand> 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<GameModeAddon> gameModeAddons;
/**
* Stores if current GUI is in Admin Mode or not.
*/
private boolean adminMode;
}

View File

@ -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<String> consumer,
Function<String, Boolean> 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();
}
}

View File

@ -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<String> 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<String> 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<String, Boolean> 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<String> 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<String, Boolean> 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<String> 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<String, Boolean> 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<Boolean> 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<Boolean> 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<Boolean> 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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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<Challenge> 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<String> 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<Challenge> challengeList = manager.getAllChallenges(this.world);
challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel));
// Generate descriptions for these challenges
Map<Challenge, List<String>> 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<Challenge> challengeList = manager.getLevelChallenges(this.challengeLevel);
// Generate descriptions for these challenges
Map<Challenge, List<String>> 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;
}

View File

@ -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<Challenge>
{
// ---------------------------------------------------------------------
// 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<Challenge> 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<String> 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<String> 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<List<String>> 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<Number> 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<Number> 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<List<String>> 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<Material> 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<Material> 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<String> 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<List<String>> 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<Number> 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<Number> 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<Challenge> challengeList = manager.getAllChallenges(this.world);
challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel));
// Generate descriptions for these challenges
Map<Challenge, List<String>> 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<Challenge> challengeList = manager.getLevelChallenges(this.challengeLevel);
// Generate descriptions for these challenges
Map<Challenge, List<String>> 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;
}

View File

@ -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<String> 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<ChallengeLore> lore = this.activeValues.stream().
map(ChallengeLore::valueOf).
collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size())));
this.addon.getChallengesSettings().setChallengeLoreMessage(lore);
break;
}
case LEVELS:
{
List<LevelLore> 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<String> 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<String> 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<String> 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<String> 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<String, Integer> 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<String> 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<String> activeValues;
// ---------------------------------------------------------------------
// Section: Constants
// ---------------------------------------------------------------------
private final static String REFERENCE_DESCRIPTION = "challenges.gui.descriptions.admin.";
}

View File

@ -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<String> 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<String> 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;
}

View File

@ -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<String> 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<Number> 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<Number> 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;
}

View File

@ -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<LibraryEntry>
{
// ---------------------------------------------------------------------
// 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<LibraryEntry> 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<LibraryEntry> 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<String> 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<Boolean> 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<String> generateEntryDescription(LibraryEntry entry)
{
final String reference = Constants.DESCRIPTIONS + "library.";
List<String> 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<LibraryEntry> libraryEntries;
/**
* Stores filtered items.
*/
private List<LibraryEntry> filterElements;
}

View File

@ -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<Challenge> 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;
}

View File

@ -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<Challenge>
{
// ---------------------------------------------------------------------
// 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<Challenge> 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<Boolean> 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;
}

View File

@ -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<ChallengeLevel> 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;
}

View File

@ -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<ChallengeLevel>
{
// ---------------------------------------------------------------------
// 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<ChallengeLevel> 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<Boolean> 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;
}

View File

@ -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<LibraryEntry> 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<String> 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<String> generateEntryDescription(LibraryEntry entry)
{
List<String> 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.";
}

View File

@ -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<Player> 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<Challenge, List<String>> 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<Player> 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<String> 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();
}
}

View File

@ -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<Player>
{
// ---------------------------------------------------------------------
// 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<String> 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<Challenge> challengeList = this.manager.getAllChallenges(this.world);
// Generate descriptions for these challenges
Map<Challenge, List<String>> 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<Challenge> challengeList = this.manager.getAllChallenges(this.world);
// Generate descriptions for these challenges
Map<Challenge, List<String>> 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<Player> 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<String> 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<Player> onlineUsers;
/**
* List with players that should be in GUI.
*/
private List<Player> filterElements;
/**
* Current operation mode.
*/
private final Mode operationMode;
/**
* Current index of view mode
*/
private ViewMode mode = ViewMode.ONLINE;
/**
* Stores clicked player.
*/
private Player selectedPlayer;
}

View File

@ -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<Material, Integer> 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<Material> selectedMaterials;
/**
* List of materials to avoid order issues.
*/
private List<Material> materialList;
/**
* List of required materials.
*/
private Map<Material, Integer> materialMap;
}

View File

@ -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<Material>
{
private ManageBlocksPanel(CommonPanel parentGUI, Map<Material, Integer> 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<Material, Integer> 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<String> 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<String> 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<Number> 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<Material> selectedMaterials;
/**
* List of materials to avoid order issues.
*/
private final List<Material> materialList;
/**
* List of required materials.
*/
private final Map<Material, Integer> materialMap;
/**
* Stores filtered items.
*/
private List<Material> filterElements;
}

View File

@ -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<EntityType, Integer> 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<EntityType> entityList;
/**
* Set with entities that are selected.
*/
private Set<EntityType> selectedEntities;
/**
* Map that contains all entities and their cound.
*/
private Map<EntityType, Integer> requiredEntities;
/**
* Boolean indicate if entities should be displayed as eggs or mob heads.
*/
private boolean asEggs;
}

View File

@ -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<EntityType>
{
private ManageEntitiesPanel(CommonPanel parentGUI, Map<EntityType, Integer> 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<EntityType, Integer> 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<String> 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<String> 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<Number> 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<EntityType> entityList;
/**
* Set with entities that are selected.
*/
private final Set<EntityType> selectedEntities;
/**
* Map that contains all entities and their cound.
*/
private final Map<EntityType, Integer> requiredEntities;
/**
* Boolean indicate if entities should be displayed as eggs or mob heads.
*/
private boolean asEggs;
/**
* Stores filtered items.
*/
private List<EntityType> filterElements;
}

View File

@ -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<Challenge> 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<Challenge> 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<String> 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<LevelStatus> 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;
}

View File

@ -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<LevelStatus> 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<LevelStatus> 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<String> 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<String> 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<String> 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<String> 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<String> 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<Challenge> 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<LevelStatus> levelList;
/**
* This will be used if free challenges are more than 18.
*/
private int challengeIndex;
/**
* This list contains challenges in current Panel.
*/
private List<Challenge> challengeList;
/**
* This indicates last selected level.
*/
private LevelStatus lastSelectedLevel;
}

View File

@ -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<GameModeAddon> 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<GameModeAddon> 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<String> 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<String> 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<String> 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<GameModeAddon> addonList;
/**
* Indicate if gui is for players or admins.
*/
private final boolean adminMode;
}

View File

@ -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<Integer> 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<Integer> 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;
}

View File

@ -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<Integer> action)
{
this.addon = addon;
this.user = user;
this.action = action;
}
public static void open(ChallengesAddon addon, User user, Consumer<Integer> 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<String> 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<String> 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<Number> 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<String> 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<Integer> action;
/**
* Variable stores Challenges addon.
*/
protected final ChallengesAddon addon;
/**
* Local storing of selected value.
*/
private int completionValue = 1;
}

View File

@ -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<Challenge>
{
private ChallengeSelector(User user, Material border, Map<Challenge, List<String>> challengesDescriptionMap, BiConsumer<Boolean, Set<Challenge>> 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<Challenge, List<String>> challengesDescriptionMap, BiConsumer<Boolean, Set<Challenge>> 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<String> 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<String> 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<Boolean, Set<Challenge>> consumer;
/**
* Current value.
*/
private final List<Challenge> elements;
/**
* Selected challenges that will be returned to consumer.
*/
private final Set<Challenge> selectedElements;
/**
* Map that contains all challenge descriptions
*/
private final Map<Challenge, List<String>> challengesDescriptionMap;
/**
* Border Material.
*/
private final Material border;
/**
* Current value.
*/
private List<Challenge> filterElements;
}

View File

@ -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<Challenge.ChallengeType, Requirements> 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<Challenge.ChallengeType, Requirements> 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<String> 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<Challenge.ChallengeType, Requirements> consumer;
}

View File

@ -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<Challenge.ChallengeType, Requirements> 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<Challenge.ChallengeType, Requirements> 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<String> 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();
}
}

View File

@ -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<Boolean> 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<Boolean> consumer;
}

View File

@ -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<World.Environment> values, BiConsumer<Boolean, Set<World.Environment>> 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<World.Environment> values, BiConsumer<Boolean, Set<World.Environment>> 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<String> 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<String> 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
}
}

View File

@ -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<ItemStack> itemStacks, BiConsumer<Boolean, List<ItemStack>> consumer)
{
public ItemSwitchGUI(User user, List<ItemStack> itemStacks, int lineLength, BiConsumer<Boolean, List<ItemStack>> 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<ItemStack> itemStacks, BiConsumer<Boolean, List<ItemStack>> 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<String> description = new ArrayList<>(3);
description.add(this.user.getTranslation(reference + "description"));
ItemStack icon;
String name;
List<String> 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<ItemStack> 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<ItemStack> itemStacks;
/**
* Consumer that returns item stacks on save action.
*/
private BiConsumer<Boolean, List<ItemStack>> consumer;
/**
* This variable stores how large line can be, before warp it.
*/
private int lineLength;
}

View File

@ -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<Material>
{
private MultiBlockSelector(User user, Mode mode, Set<Material> excluded, BiConsumer<Boolean, Collection<Material>> 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<Material> excluded, BiConsumer<Boolean, Collection<Material>> 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<Boolean, Collection<Material>> 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<String> 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<String> 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<Material> elements;
/**
* Set that contains selected materials.
*/
private final Set<Material> selectedElements;
/**
* This variable stores consumer.
*/
private final BiConsumer<Boolean, Collection<Material>> consumer;
/**
* Stores filtered items.
*/
private List<Material> filterElements;
}

View File

@ -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<EntityType>
{
private MultiEntitySelector(User user, boolean asEgg, Mode mode, Set<EntityType> excluded, BiConsumer<Boolean, Collection<EntityType>> 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<EntityType> excluded, BiConsumer<Boolean, Collection<EntityType>> 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<Boolean, Collection<EntityType>> 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<String> 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<String> 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<EntityType> elements;
/**
* Set that contains selected materials.
*/
private final Set<EntityType> selectedElements;
/**
* This variable stores consumer.
*/
private final BiConsumer<Boolean, Collection<EntityType>> consumer;
/**
* Indicates that entity must be displayed as egg.
*/
private final boolean asEgg;
/**
* Stores filtered items.
*/
private List<EntityType> filterElements;
}

View File

@ -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<Boolean, Integer> consumer)
{
this(user, value, Integer.MIN_VALUE, Integer.MAX_VALUE, lineLength, consumer);
}
public NumberGUI(User user, int value, int minValue, int lineLength, BiConsumer<Boolean, Integer> consumer)
{
this(user, value, minValue, Integer.MAX_VALUE, lineLength, consumer);
}
public NumberGUI(User user, int value, int minValue, int maxValue, int lineLength, BiConsumer<Boolean, Integer> 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<Number> 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<Boolean, Integer> 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;
}

View File

@ -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<T>
{
/**
* 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<T> 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<String> 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<String> 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;
}

View File

@ -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<Boolean, Set<Material>> consumer)
{
this(user, false, new HashSet<>(), consumer);
}
public SelectBlocksGUI(User user, boolean singleSelect, BiConsumer<Boolean, Set<Material>> consumer)
{
this.consumer = consumer;
this.user = user;
this.singleSelect = singleSelect;
// Current GUI cannot display air blocks. It crashes with null-pointer
Set<Material> 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<Material> excludedMaterial, BiConsumer<Boolean, Set<Material>> 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<String> 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<Material> elements;
/**
* Set that contains selected materials.
*/
private Set<Material> selectedMaterials;
/**
* This variable stores consumer.
*/
private BiConsumer<Boolean, Set<Material>> consumer;
/**
* User who runs GUI.
*/
private User user;
/**
* This indicate that return set must contain only single item.
*/
private boolean singleSelect;
}

View File

@ -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<Challenge, List<String>> challengesDescriptionMap, int lineLength, BiConsumer<Boolean, Set<Challenge>> 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<String> 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<Boolean, Set<Challenge>> consumer;
/**
* User who runs GUI.
*/
private User user;
/**
* Current value.
*/
private List<Challenge> challengesList;
/**
* Selected challenges that will be returned to consumer.
*/
private Set<Challenge> selectedChallenges;
/**
* Map that contains all challenge descriptions
*/
private Map<Challenge, List<String>> challengesDescriptionMap;
/**
* This variable stores how large line can be, before warp it.
*/
private int lineLength;
}

View File

@ -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<Boolean, Set<EntityType>> consumer)
{
this(user, Collections.emptySet(), true, consumer);
}
public SelectEntityGUI(User user, Set<EntityType> excludedEntities, boolean asEggs, BiConsumer<Boolean, Set<EntityType>> 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<String> 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<Boolean, Set<EntityType>> consumer;
/**
* Set that contains selected entities.
*/
private Set<EntityType> 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<EntityType> entities;
}

View File

@ -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<World.Environment> values, BiConsumer<Boolean, Set<World.Environment>> 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<World.Environment> values;
/**
* Stores current Consumer
*/
private BiConsumer<Boolean, Set<World.Environment>> consumer;
}

View File

@ -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<Material>
{
private SingleBlockSelector(User user, Mode mode, Set<Material> excluded, BiConsumer<Boolean, Material> 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<Material> excluded, BiConsumer<Boolean, Material> 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<Boolean, Material> 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<Boolean, Material> 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<String> 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<String> 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<Material> elements;
/**
* This variable stores consumer.
*/
private final BiConsumer<Boolean, Material> consumer;
/**
* Stores filtered items.
*/
private List<Material> filterElements;
}

View File

@ -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<EntityType>
{
/**
* 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<EntityType> excluded, BiConsumer<Boolean, EntityType> 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<EntityType> excluded, BiConsumer<Boolean, EntityType> 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<Boolean, EntityType> 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<Boolean, EntityType> 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<String> 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<String> 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<EntityType> elements;
/**
* Indicates if entities are displayed as eggs or heads.
*/
private final boolean asEggs;
/**
* This variable stores consumer.
*/
private final BiConsumer<Boolean, EntityType> consumer;
/**
* Stores filtered items.
*/
private List<EntityType> filterElements;
}

View File

@ -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<Statistic>
{
/**
* Instantiates a new Statistic selector.
*
* @param user the user
* @param consumer the consumer
*/
private StatisticSelector(User user, BiConsumer<Boolean, Statistic> 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<Boolean, Statistic> 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<String> 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<String> 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<Statistic> elements;
/**
* This variable stores consumer.
*/
private final BiConsumer<Boolean, Statistic> consumer;
/**
* Stores filtered items.
*/
private List<Statistic> filterElements;
}

View File

@ -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<Boolean, List<String>> consumer)
{
this(user, Collections.singleton(value), lineLength, consumer);
}
public StringListGUI(User user, Collection<String> value, int lineLength, BiConsumer<Boolean, List<String>> consumer)
{
this(user, new ArrayList<>(value), lineLength, consumer);
}
public StringListGUI(User user, List<String> value, int lineLength, BiConsumer<Boolean, List<String>> 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<String> 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<String> 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<String> 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<Boolean, List<String>> consumer;
/**
* User who runs GUI.
*/
private User user;
/**
* Current value.
*/
private List<String> value;
/**
* This variable stores how large line can be, before warp it.
*/
private int lineLength;
}

View File

@ -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<ItemStack, Integer> removedItems =
Map<ItemStack, Integer> 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<UUID> 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<ItemStack> 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<ItemStack> 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<Block> 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<Entity> 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.

View File

@ -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]";
}

View File

@ -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<String> 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<String> 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<String> stringSplit(List<String> stringList, int warpLength)
{
if (stringList.isEmpty())
{
return stringList;
}
List<String> 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("-", "_");
}
}

View File

@ -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 <http://creativecommons.org/publicdomain/zero/1.0/>.
*
* @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<String, HeadLib> 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 "&amp;" color codes
* @param loreLines optional lore lines. Supports "&amp;" 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 "&amp;" color codes
* @param loreLines optional lore lines. Supports "&amp;" 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<String> 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);
}
}
}

View File

@ -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();

View File

@ -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<ItemStack> groupEqualItems(List<ItemStack> requiredItems)
public static List<ItemStack> groupEqualItems(List<ItemStack> requiredItems, Set<Material> ignoreMetaData)
{
List<ItemStack> 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 <T> Instance of given object.
* @return Next value after currentValue in values array.
*/
public static <T extends Object> T getNextValue(T[] values, T currentValue)
public static <T> T getNextValue(T[] values, T currentValue)
{
for (int i = 0; i < values.length; i++)
{
@ -133,7 +146,7 @@ public class Utils
* @param <T> Instance of given object.
* @return Previous value before currentValue in values array.
*/
public static <T extends Object> T getPreviousValue(T[] values, T currentValue)
public static <T> 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;
}
}

View File

@ -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<LibraryEntry> getLibraryEntries()
{
List<LibraryEntry> 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<LibraryEntry> library;
private final List<LibraryEntry> library;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More