diff --git a/sponge/api7/build.gradle b/sponge/api7/build.gradle new file mode 100644 index 00000000..ee48a116 --- /dev/null +++ b/sponge/api7/build.gradle @@ -0,0 +1,159 @@ +buildscript { + repositories { + maven { + name = 'forge' + url = 'https://files.minecraftforge.net/maven' + } + maven { + url = 'https://plugins.gradle.org/m2/' + } + } + + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:5.0.9' + classpath 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0' + } +} + +plugins { + id 'com.github.johnrengelman.shadow' version '7.0.0' + id 'java' + id 'maven-publish' +} + +apply plugin: 'net.minecraftforge.gradle' + +compileJava.options.encoding = 'UTF-8' + +group = 'dev.triumphteam' + + + +// Environment variables for the build set by the build server +ext.buildNumber = System.env.BUILD_NUMBER ?: '0' + +defaultTasks 'clean', 'build' + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' + +archivesBaseName = 'triumph-gui-sponge' + +project.ext.getGitHash = { + def command = Runtime.getRuntime().exec("git rev-parse --short HEAD") + def result = command.waitFor() + return (result == 0) ? command.inputStream.text.trim() : "nogit" +} + +repositories { + mavenLocal() + maven { + name = 'sponge' + url = 'https://repo.spongepowered.org/maven/' + } + maven { + name = 'sonatype_releases' + url = 'https://oss.sonatype.org/content/repositories/releases' + } + maven { + name = 'sonatype_snapshots' + url = 'https://oss.sonatype.org/content/repositories/snapshots' + } +} + +minecraft { + // The mappings can be changed at any time, and must be in the following format. + // snapshot_YYYYMMDD Snapshot are built nightly. + // stable_# Stables are built at the discretion of the MCP team. + // Use non-default mappings at your own risk. they may not always work. + // Simply re-run your setup task after changing the mappings to update your workspace. + //mappings channel: '@MAPPING_CHANNEL@', version: '@MAPPING_VERSION@' + mappings channel: 'snapshot', version: '20180808-1.12' + accessTransformer = file('src/main/resources/META-INF/triumph_at.cfg') +} + +sourceSets { + api +} + +dependencies { + annotationProcessor 'org.spongepowered:spongeapi:7.3.0' + minecraft 'net.minecraft:joined:1.12.2' + compileOnly fileTree(dir: 'libs', include: ['*.jar']) + compileOnly ("org.spongepowered:spongecommon:1.12.2-7.4.4:dev") { + exclude module: 'testplugins' + exclude group: 'co.aikar' + } + + compileOnly "org.apache.commons:commons-lang3:3.9" + compileOnly "org.checkerframework:checker:2.8.2" + compileOnly "net.kyori:adventure-api:4.9.3" + compileOnly "net.kyori:adventure-text-serializer-gson:4.9.3" + compileOnly "net.kyori:adventure-text-serializer-gson-legacy-impl:4.9.3" + compileOnly "net.kyori:adventure-text-serializer-legacy:4.9.3" + compileOnly "net.kyori:adventure-text-serializer-plain:4.9.3" + implementation "net.md-5:bungeecord-chat:1.16-R0.4" +} + +jar { + //manifest.attributes('FMLAT': 'triumph_at.cfg') + manifest.attributes('Implementation-Title': 'TriumphGUI') + manifest.attributes('Implementation-Version': "$version") + manifest.attributes('Git-Hash': project.ext.getGitHash()) + classifier = 'SNAPSHOT' + baseName = 'triumph-gui-sponge' +} + +if (JavaVersion.current().isJava8Compatible()) { + tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + } +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +tasks.assemble { + dependsOn(tasks.shadowJar) +} + +artifacts { + archives shadowJar +} + +tasks { + shadowJar { + dependencies { + include dependency("net.md-5:bungeecord-chat:1.16-R0.4") + } + exclude 'META-INF/versions/**', 'META-INF/maven/**' + classifier = '' + exclude "dummyThing" + } + reobf { + create("shadowJar") + } +} + +publishing { + repositories { + maven { + credentials { + username = 'bloodshot' + password = 'o8#&ymYtH7X5B#g' + } + url = 'https://repo.glaremasters.me/repository/bloodshot' + } + } + publications { + maven(MavenPublication) { + groupId = 'dev.triumphteam' + artifactId = 'gui' + version = '3.1.0-SNAPSHOT' + + from components.java + } + } +} \ No newline at end of file diff --git a/sponge/api7/gradle/wrapper/gradle-wrapper.jar b/sponge/api7/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/sponge/api7/gradle/wrapper/gradle-wrapper.jar differ diff --git a/sponge/api7/gradle/wrapper/gradle-wrapper.properties b/sponge/api7/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..2e6e5897 --- /dev/null +++ b/sponge/api7/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/sponge/api7/gradlew b/sponge/api7/gradlew new file mode 100644 index 00000000..fbd7c515 --- /dev/null +++ b/sponge/api7/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/sponge/api7/gradlew.bat b/sponge/api7/gradlew.bat new file mode 100644 index 00000000..5093609d --- /dev/null +++ b/sponge/api7/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/sponge/api7/settings.gradle b/sponge/api7/settings.gradle new file mode 100644 index 00000000..feb85144 --- /dev/null +++ b/sponge/api7/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "triumph-gui" \ No newline at end of file diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/TriumphPlugin.java b/sponge/api7/src/main/java/dev/triumphteam/gui/TriumphPlugin.java new file mode 100644 index 00000000..ff34ea22 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/TriumphPlugin.java @@ -0,0 +1,61 @@ +/** + * MIT License + *

+ * Copyright (c) 2021 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui; + +import com.google.inject.Inject; +import dev.triumphteam.gui.guis.GuiListener; +import dev.triumphteam.gui.guis.InteractionModifierListener; +import org.slf4j.Logger; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.config.ConfigDir; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.game.state.GameConstructionEvent; +import org.spongepowered.api.plugin.Plugin; +import org.spongepowered.api.plugin.PluginContainer; + +import java.nio.file.Path; + +@Plugin(id = "triump-gui", name = "Triumph GUI", version = "3.1.0", description = "Designed to simplify the creation of inventory GUIs.", +authors = {"bloodmc, ipsk"}) +public class TriumphPlugin { + + // The plugin instance for registering the event and for the close delay. + @Inject public PluginContainer pluginContainer; + @Inject private Logger logger; + @Inject @ConfigDir(sharedRoot = false) + private Path configPath; + private static TriumphPlugin instance; + + @Listener(order = Order.FIRST) + public void onConstruct(GameConstructionEvent event) { + instance = this; + Sponge.getEventManager().registerListeners(this.pluginContainer, new GuiListener()); + Sponge.getEventManager().registerListeners(this.pluginContainer, new InteractionModifierListener()); + } + + public static TriumphPlugin getInstance() { + return instance; + } +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/BaseGuiBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/BaseGuiBuilder.java new file mode 100644 index 00000000..2c3872d4 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/BaseGuiBuilder.java @@ -0,0 +1,315 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.gui; + +import dev.triumphteam.gui.components.InteractionModifier; +import dev.triumphteam.gui.components.exception.GuiException; +import dev.triumphteam.gui.guis.BaseGui; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumSet; +import java.util.Set; +import java.util.function.Consumer; + +/** + * The base for all the GUI builders this is due to some limitations + * where some builders will have unique features based on the GUI type + * + * @param The Type of {@link BaseGui} + */ +@SuppressWarnings("unchecked") +public abstract class BaseGuiBuilder> { + + private Component title = null; + private int rows = 1; + private final EnumSet interactionModifiers = EnumSet.noneOf(InteractionModifier.class); + + private Consumer consumer; + + /** + * Sets the rows for the GUI + * This will only work on CHEST {@link dev.triumphteam.gui.components.GuiType} + * + * @param rows The amount of rows + * @return The builder + */ + @NotNull + @Contract("_ -> this") + public B rows(final int rows) { + this.rows = rows; + return (B) this; + } + + /** + * Sets the title for the GUI + * This will be either a Component or a String + * + * @param title The GUI title + * @return The builder + */ + @NotNull + @Contract("_ -> this") + public B title(@NotNull final Component title) { + this.title = title; + return (B) this; + } + + /** + * Disable item placement inside the GUI + * + * @return The builder + * @since 3.0.0 + * @author SecretX + */ + @NotNull + @Contract(" -> this") + public B disableItemPlace() { + interactionModifiers.add(InteractionModifier.PREVENT_ITEM_PLACE); + return (B) this; + } + + /** + * Disable item retrieval inside the GUI + * + * @return The builder + * @since 3.0.0 + * @author SecretX + */ + @NotNull + @Contract(" -> this") + public B disableItemTake() { + interactionModifiers.add(InteractionModifier.PREVENT_ITEM_TAKE); + return (B) this; + } + + /** + * Disable item swap inside the GUI + * + * @return The builder + * @since 3.0.0 + * @author SecretX + */ + @NotNull + @Contract(" -> this") + public B disableItemSwap() { + interactionModifiers.add(InteractionModifier.PREVENT_ITEM_SWAP); + return (B) this; + } + + /** + * Disable item drop inside the GUI + * + * @return The builder + * @since 3.0.3 + */ + @NotNull + @Contract(" -> this") + public B disableItemDrop() { + interactionModifiers.add(InteractionModifier.PREVENT_ITEM_DROP); + return (B) this; + } + + /** + * Disable other GUI actions + * This option pretty much disables creating a clone stack of the item + * + * @return The builder + * @since 3.0.4 + */ + @NotNull + @Contract(" -> this") + public B disableOtherActions() { + interactionModifiers.add(InteractionModifier.PREVENT_OTHER_ACTIONS); + return (B) this; + } + + /** + * Disable all the modifications of the GUI, making it immutable by player interaction + * + * @return The builder + * @since 3.0.0 + * @author SecretX + */ + @NotNull + @Contract(" -> this") + public B disableAllInteractions() { + interactionModifiers.addAll(InteractionModifier.VALUES); + return (B) this; + } + + /** + * Allows item placement inside the GUI + * + * @return The builder + * @since 3.0.0 + * @author SecretX + */ + @NotNull + @Contract(" -> this") + public B enableItemPlace() { + interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_PLACE); + return (B) this; + } + + /** + * Allow items to be taken from the GUI + * + * @return The builder + * @since 3.0.0 + * @author SecretX + */ + @NotNull + @Contract(" -> this") + public B enableItemTake() { + interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_TAKE); + return (B) this; + } + + /** + * Allows item swap inside the GUI + * + * @return The builder + * @since 3.0.0 + * @author SecretX + */ + @NotNull + @Contract(" -> this") + public B enableItemSwap() { + interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_SWAP); + return (B) this; + } + + /** + * Allows item drop inside the GUI + * + * @return The builder + * @since 3.0.3 + */ + @NotNull + @Contract(" -> this") + public B enableItemDrop() { + interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_DROP); + return (B) this; + } + + /** + * Enable other GUI actions + * This option pretty much enables creating a clone stack of the item + * + * @return The builder + * @since 3.0.4 + */ + @NotNull + @Contract(" -> this") + public B enableOtherActions() { + interactionModifiers.remove(InteractionModifier.PREVENT_OTHER_ACTIONS); + return (B) this; + } + + /** + * Enable all modifications of the GUI, making it completely mutable by player interaction + * + * @return The builder + * @since 3.0.0 + * @author SecretX + */ + @NotNull + @Contract(" -> this") + public B enableAllInteractions() { + interactionModifiers.clear(); + return (B) this; + } + + /** + * Applies anything to the GUI once it's created + * Can be pretty useful for setting up small things like default actions + * + * @param consumer A {@link Consumer} that passes the built GUI + * @return The builder + */ + @NotNull + @Contract("_ -> this") + public B apply(@NotNull final Consumer consumer) { + this.consumer = consumer; + return (B) this; + } + + /** + * Creates the given GuiBase + * Has to be abstract because each GUI are different + * + * @return The new {@link BaseGui} + */ + @NotNull + @Contract(" -> new") + public abstract G create(); + + /** + * Getter for the title + * + * @return The current title + */ + @NotNull + protected Component getTitle() { + if (title == null) { + throw new GuiException("GUI title is missing!"); + } + + return title; + } + + /** + * Getter for the rows + * + * @return The amount of rows + */ + protected int getRows() { + return rows; + } + + /** + * Getter for the consumer + * + * @return The consumer + */ + @Nullable + protected Consumer getConsumer() { + return consumer; + } + + + /** + * Getter for the set of interaction modifiers + * @return The set of {@link InteractionModifier} + * @since 3.0.0 + * @author SecretX + */ + @NotNull + protected Set getModifiers() { + return interactionModifiers; + } +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/PaginatedBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/PaginatedBuilder.java new file mode 100644 index 00000000..deea0f6d --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/PaginatedBuilder.java @@ -0,0 +1,70 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.gui; + +import dev.triumphteam.gui.components.util.Legacy; +import dev.triumphteam.gui.guis.PaginatedGui; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +/** + * GUI builder for creating a {@link PaginatedGui} + */ +public class PaginatedBuilder extends BaseGuiBuilder { + + private int pageSize = 0; + + /** + * Sets the desirable page size, most of the time this isn't needed + * + * @param pageSize The amount of free slots that page items should occupy + * @return The current builder + */ + @NotNull + @Contract("_ -> this") + public PaginatedBuilder pageSize(final int pageSize) { + this.pageSize = pageSize; + return this; + } + + /** + * Creates a new {@link PaginatedGui} + * + * @return A new {@link PaginatedGui} + */ + @NotNull + @Override + @Contract(" -> new") + public PaginatedGui create() { + final PaginatedGui gui = new PaginatedGui(getRows(), pageSize, Legacy.SERIALIZER.serialize(getTitle()), getModifiers()); + + final Consumer consumer = getConsumer(); + if (consumer != null) consumer.accept(gui); + + return gui; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/ScrollingBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/ScrollingBuilder.java new file mode 100644 index 00000000..175bd1b1 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/ScrollingBuilder.java @@ -0,0 +1,96 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.gui; + +import dev.triumphteam.gui.components.ScrollType; +import dev.triumphteam.gui.components.util.Legacy; +import dev.triumphteam.gui.guis.ScrollingGui; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +/** + * The simple GUI builder is used for creating a {@link ScrollingGui} that uses {@link Component} for title + * TODO This class needs more work to remove the redundant pageSize since it's the same as the paginated builder + */ +public final class ScrollingBuilder extends BaseGuiBuilder { + + private ScrollType scrollType; + private int pageSize = -1; + + /** + * Main constructor + * + * @param scrollType The {@link ScrollType} to default to + */ + public ScrollingBuilder(@NotNull final ScrollType scrollType) { + this.scrollType = scrollType; + } + + /** + * Sets the {@link ScrollType} to be used + * + * @param scrollType Either horizontal or vertical scrolling + * @return The current builder + */ + @NotNull + @Contract("_ -> this") + public ScrollingBuilder scrollType(@NotNull final ScrollType scrollType) { + this.scrollType = scrollType; + return this; + } + + /** + * Sets the desirable page size, most of the times this isn't needed + * + * @param pageSize The amount of free slots that page items should occupy + * @return The current builder + */ + @NotNull + @Contract("_ -> this") + public ScrollingBuilder pageSize(final int pageSize) { + this.pageSize = pageSize; + return this; + } + + /** + * Creates a new {@link ScrollingGui} + * + * @return A new {@link ScrollingGui} + */ + @NotNull + @Override + @Contract(" -> new") + public ScrollingGui create() { + final ScrollingGui gui = new ScrollingGui(getRows(), pageSize, Legacy.SERIALIZER.serialize(getTitle()), scrollType, getModifiers()); + + final Consumer consumer = getConsumer(); + if (consumer != null) consumer.accept(gui); + + return gui; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/SimpleBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/SimpleBuilder.java new file mode 100644 index 00000000..8dc98b67 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/SimpleBuilder.java @@ -0,0 +1,87 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.gui; + +import dev.triumphteam.gui.components.GuiType; +import dev.triumphteam.gui.components.util.Legacy; +import dev.triumphteam.gui.guis.Gui; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +/** + * The simple GUI builder is used for creating a {@link Gui} + */ +public final class SimpleBuilder extends BaseGuiBuilder { + + private GuiType guiType; + + /** + * Main constructor + * + * @param guiType The {@link GuiType} to default to + */ + public SimpleBuilder(@NotNull final GuiType guiType) { + this.guiType = guiType; + } + + /** + * Sets the {@link GuiType} to use on the GUI + * This method is unique to the simple GUI + * + * @param guiType The {@link GuiType} + * @return The current builder + */ + @NotNull + @Contract("_ -> this") + public SimpleBuilder type(@NotNull final GuiType guiType) { + this.guiType = guiType; + return this; + } + + /** + * Creates a new {@link Gui} + * + * @return A new {@link Gui} + */ + @NotNull + @Override + @Contract(" -> new") + public Gui create() { + final Gui gui; + final String title = Legacy.SERIALIZER.serialize(getTitle()); + if (guiType == null || guiType == GuiType.CHEST) { + gui = new Gui(getRows(), title, getModifiers()); + } else { + gui = new Gui(guiType, title, getModifiers()); + } + + final Consumer consumer = getConsumer(); + if (consumer != null) consumer.accept(gui); + + return gui; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/StorageBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/StorageBuilder.java new file mode 100644 index 00000000..f160514a --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/gui/StorageBuilder.java @@ -0,0 +1,55 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.gui; + +import dev.triumphteam.gui.components.util.Legacy; +import dev.triumphteam.gui.guis.StorageGui; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +/** + * The simple GUI builder is used for creating a {@link StorageGui} + */ +public final class StorageBuilder extends BaseGuiBuilder { + + /** + * Creates a new {@link StorageGui} + * + * @return A new {@link StorageGui} + */ + @NotNull + @Override + @Contract(" -> new") + public StorageGui create() { + final StorageGui gui = new StorageGui(getRows(), Legacy.SERIALIZER.serialize(getTitle()), getModifiers()); + + final Consumer consumer = getConsumer(); + if (consumer != null) consumer.accept(gui); + + return gui; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BannerBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BannerBuilder.java new file mode 100644 index 00000000..ddc44096 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BannerBuilder.java @@ -0,0 +1,184 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.item; + +import dev.triumphteam.gui.components.exception.GuiException; +import dev.triumphteam.gui.components.util.VersionHelper; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.data.meta.PatternLayer; +import org.spongepowered.api.data.type.BannerPatternShape; +import org.spongepowered.api.data.type.DyeColor; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Item builder for banners only + * + * @author GabyTM https://github.com/iGabyTM + * @since 3.0.1 + */ +@SuppressWarnings("unused") +public final class BannerBuilder extends BaseItemBuilder { + + private static final ItemType DEFAULT_BANNER; + private static final Set BANNERS; + + static { + if (VersionHelper.IS_ITEM_LEGACY) { + DEFAULT_BANNER = ItemTypes.BANNER; + BANNERS = new HashSet<>(Arrays.asList(ItemTypes.BANNER)); + } else { + DEFAULT_BANNER = ItemTypes.BANNER; + BANNERS = new HashSet<>(Arrays.asList(ItemTypes.BANNER)); + } + } + + BannerBuilder() { + super(ItemStack.builder().itemType(DEFAULT_BANNER).build()); + } + + BannerBuilder(@NotNull ItemStack itemStack) { + super(itemStack); + if (!BANNERS.contains(itemStack.getType())) { + throw new GuiException("BannerBuilder requires the material to be a banner!"); + } + } + + /** + * Sets the base color for this banner + * + * @param color the base color + * @return {@link BannerBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BannerBuilder baseColor(@NotNull final DyeColor color) { + this.getMeta().add(Keys.DYE_COLOR, color); + return this; + } + + /** + * Adds a new pattern on top of the existing patterns + * + * @param color the pattern color + * @param pattern the pattern type + * @return {@link BannerBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_, _ -> this") + public BannerBuilder pattern(@NotNull final DyeColor color, @NotNull final PatternLayer pattern) { + final List patterns = new ArrayList<>(); + patterns.add(PatternLayer.of(pattern.getShape(), color)); + this.getMeta().add(Keys.BANNER_PATTERNS, patterns); + return this; + } + + /** + * Adds new patterns on top of the existing patterns + * + * @param pattern the patterns + * @return {@link BannerBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BannerBuilder pattern(@NotNull final PatternLayer... pattern) { + return pattern(Arrays.asList(pattern)); + } + + /** + * Adds new patterns on top of the existing patterns + * + * @param patterns the patterns + * @return {@link BannerBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BannerBuilder pattern(@NotNull final List patterns) { + this.getMeta().add(Keys.BANNER_PATTERNS, patterns); + return this; + } + + /** + * Sets the pattern at the specified index + * + * @param index the index + * @param color the pattern color + * @param pattern the pattern type + * @return {@link BannerBuilder} + * @throws IndexOutOfBoundsException when index is not in [0, {@link BannerMeta#numberOfPatterns()}) range + * @since 3.0.1 + */ + @NotNull + @Contract("_, _, _ -> this") + public BannerBuilder pattern(final int index, @NotNull final DyeColor color, @NotNull final BannerPatternShape pattern) { + return pattern(index, PatternLayer.of(pattern, color)); + } + + /** + * Sets the pattern at the specified index + * + * @param index the index + * @param pattern the new pattern + * @return {@link BannerBuilder} + * @throws IndexOutOfBoundsException when index is not in [0, {@link BannerMeta#numberOfPatterns()}) range + * @since 3.0.1 + */ + @NotNull + @Contract("_, _ -> this") + public BannerBuilder pattern(final int index, @NotNull final PatternLayer pattern) { + this.getMeta().add(Keys.BANNER_PATTERNS, new ArrayList<>(Arrays.asList(pattern))); + return this; + } + + /** + * Sets the patterns used on this banner + * + * @param patterns the new list of patterns + * @return {@link BannerBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BannerBuilder setPatterns(@NotNull List<@NotNull PatternLayer> patterns) { + this.getMeta().add(Keys.BANNER_PATTERNS, patterns); + return this; + } + + // TODO add shield() + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BaseItemBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BaseItemBuilder.java new file mode 100644 index 00000000..e028ee37 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BaseItemBuilder.java @@ -0,0 +1,470 @@ +/** + * MIT License + *

+ * Copyright (c) 2021 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.item; + +import dev.triumphteam.gui.components.GuiAction; +import dev.triumphteam.gui.components.util.ItemNbt; +import dev.triumphteam.gui.components.util.Legacy; +import dev.triumphteam.gui.guis.GuiItem; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; +import org.apache.commons.lang3.Validate; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.event.item.inventory.ClickInventoryEvent; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.enchantment.Enchantment; +import org.spongepowered.api.item.enchantment.EnchantmentTypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.text.Text; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Contains all the common methods for the future ItemBuilders + * + * @param The ItemBuilder type so the methods can cast to the subtype + */ +@SuppressWarnings("unchecked") +public abstract class BaseItemBuilder> { + + private static final Set LEATHER_ARMOR = new HashSet<>(Arrays.asList( + ItemTypes.LEATHER_HELMET, ItemTypes.LEATHER_CHESTPLATE, ItemTypes.LEATHER_LEGGINGS, ItemTypes.LEATHER_BOOTS + )); + + private ItemStack itemStack; + private ItemStack.Builder meta; + + protected BaseItemBuilder(@NotNull final ItemStack itemStack) { + Validate.notNull(itemStack, "Item can't be null!"); + + this.itemStack = itemStack; + meta = ItemStack.builder().from(itemStack); + } + + /** + * Sets the display name of the item using {@link Component} + * + * @param name The {@link Component} name + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B name(@NotNull final Component name) { + if (meta == null) return (B) this; + + meta.add(Keys.DISPLAY_NAME, Legacy.toText(name)); + return (B) this; + } + + /** + * Sets the amount of items + * + * @param amount the amount of items + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B amount(final int amount) { + meta.quantity(amount); + return (B) this; + } + + /** + * Set the lore lines of an item + * + * @param lore Lore lines as varargs + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B lore(@Nullable final Component @NotNull ... lore) { + return lore(Arrays.asList(lore)); + } + + /** + * Set the lore lines of an item + * + * @param lore A {@link List} with the lore lines + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B lore(@NotNull final List<@Nullable Component> lore) { + if (meta == null) return (B) this; + + List textList = new ArrayList<>(); + for (Component component : lore) { + textList.add(Legacy.toText(component)); + } + meta.add(Keys.ITEM_LORE, textList); + return (B) this; + } + + /** + * Consumer for freely adding to the lore + * + * @param lore A {@link Consumer} with the {@link List} of lore {@link Component} + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B lore(@NotNull final Consumer> lore) { + if (meta == null) return (B) this; + + /* TODO + List components; + if (VersionHelper.IS_ITEM_LEGACY) { + final List stringLore = meta.getLore(); + if (stringLore == null) return (B) this; + components = stringLore.stream().map(Legacy.SERIALIZER::deserialize).collect(Collectors.toList()); + } else { + try { + final List jsonLore = (List) LORE_FIELD.get(meta); + components = jsonLore.stream().map(GSON::deserialize).collect(Collectors.toList()); + } catch (IllegalAccessException exception) { + components = new ArrayList<>(); + exception.printStackTrace(); + } + } + + lore.accept(components); + return lore(components);*/ + return (B) this; + } + + /** + * Enchants the {@link ItemStack} + * + * @param enchantment The {@link Enchantment} to add + * @param level The level of the {@link Enchantment} + * @param ignoreLevelRestriction If should or not ignore it + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_, _, _ -> this") + public B enchant(@NotNull final Enchantment enchantment, final int level, final boolean ignoreLevelRestriction) { + meta.add(Keys.ITEM_ENCHANTMENTS, Arrays.asList(enchantment)); + return (B) this; + } + + /** + * Enchants the {@link ItemStack} + * + * @param enchantment The {@link Enchantment} to add + * @param level The level of the {@link Enchantment} + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_, _ -> this") + public B enchant(@NotNull final Enchantment enchantment, final int level) { + return enchant(enchantment, level, true); + } + + /** + * Enchants the {@link ItemStack} + * + * @param enchantment The {@link Enchantment} to add + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B enchant(@NotNull final Enchantment enchantment) { + return enchant(enchantment, 1, true); + } + + /** + * Disenchants a certain {@link Enchantment} from the {@link ItemStack} + * + * @param enchantment The {@link Enchantment} to remove + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B disenchant(@NotNull final Enchantment enchantment) { + /* TODO + meta.add(Keys.ITEM_ENCHANTMENTS).get().remove(enchantment);*/ + return (B) this; + } + + /** + * Add an {@link ItemFlag} to the item + * + * @param flags The {@link ItemFlag} to add + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B flags(@NotNull final ItemFlag... flags) { + for (ItemFlag itemFlag : flags) { + if (itemFlag == ItemFlag.HIDE_ATTRIBUTES) { + meta.add(Keys.HIDE_ATTRIBUTES, true); + } else if (itemFlag == ItemFlag.HIDE_DESTROYS) { + meta.add(Keys.HIDE_CAN_DESTROY, true); + } else if (itemFlag == ItemFlag.HIDE_ENCHANTS) { + meta.add(Keys.HIDE_ENCHANTMENTS, true); + } else if (itemFlag == ItemFlag.HIDE_UNBREAKABLE) { + meta.add(Keys.HIDE_UNBREAKABLE, true); + } else if (itemFlag == ItemFlag.HIDE_PLACED_ON) { + meta.add(Keys.HIDE_CAN_PLACE, true); + } else { + meta.add(Keys.HIDE_MISCELLANEOUS, true); + } + } + return (B) this; + } + + /** + * Makes the {@link ItemStack} unbreakable + * + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract(" -> this") + public B unbreakable() { + return unbreakable(true); + } + + /** + * Sets the item as unbreakable + * + * @param unbreakable If should or not be unbreakable + * @return {@link ItemBuilder} + */ + @NotNull + @Contract("_ -> this") + public B unbreakable(boolean unbreakable) { + meta.add(Keys.UNBREAKABLE, unbreakable); + return (B) this; + } + + /** + * Makes the {@link ItemStack} glow + * + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract(" -> this") + public B glow() { + return glow(true); + } + + /** + * Adds or removes the {@link ItemStack} glow + * + * @param glow Should the item glow + * @return {@link ItemBuilder} + */ + @NotNull + @Contract("_ -> this") + public B glow(boolean glow) { + if (glow) { + final List enchants = new ArrayList<>(); + enchants.add(Enchantment.builder().type(EnchantmentTypes.LURE).level(1).build()); + meta.add(Keys.ITEM_ENCHANTMENTS, enchants); + meta.add(Keys.HIDE_ENCHANTMENTS, true); + return (B) this; + } + + return (B) this; + } + + /** + * Sets the custom model data of the item + * Added in 1.13 + * + * @param modelData The custom model data from the resource pack + * @return {@link ItemBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> this") + public B model(final int modelData) { + // Not available on legacy + // Keep to maintain common builder code across platforms + /*if (VersionHelper.IS_CUSTOM_MODEL_DATA) { + meta.setCustomModelData(modelData); + }*/ + + return (B) this; + } + + /** + * Color an {@link org.bukkit.inventory.ItemStack} + * + * @param color color + * @return {@link B} + * @see org.bukkit.inventory.meta.LeatherArmorMeta#setColor(Color) + * @see org.bukkit.inventory.meta.MapMeta#setColor(Color) + * @since 3.0.3 + */ + @NotNull + @Contract("_ -> this") + public B color(@NotNull final TextColor color) { + /* TODO + if (LEATHER_ARMOR.contains(itemStack.getType())) { + final LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) getMeta(); + + leatherArmorMeta.setColor(color); + setMeta(leatherArmorMeta); + }*/ + + return (B) this; + } + + /** + * Sets NBT tag to the {@link ItemStack} + * + * @param key The NBT key + * @param value The NBT value + * @return {@link ItemBuilder} + */ + @NotNull + @Contract("_, _ -> this") + public B setNbt(@NotNull final String key, @Nullable final String value) { + itemStack = ItemNbt.setString(itemStack, key, value); + return (B) this; + } + + /** + * Sets NBT tag to the {@link ItemStack} + * + * @param key The NBT key + * @param value The NBT value + * @return {@link ItemBuilder} + */ + @NotNull + @Contract("_, _ -> this") + public B setNbt(@NotNull final String key, final boolean value) { + itemStack = ItemNbt.setBoolean(itemStack, key, value); + return (B) this; + } + + /** + * Removes NBT tag from the {@link ItemStack} + * + * @param key The NBT key + * @return {@link ItemBuilder} + */ + @NotNull + @Contract("_ -> this") + public B removeNbt(@NotNull final String key) { + itemStack = ItemNbt.removeTag(itemStack, key); + return (B) this; + } + + /** + * Builds the item into {@link ItemStack} + * + * @return The fully built {@link ItemStack} + */ + @NotNull + public ItemStack build() { + this.itemStack = meta.build(); + return itemStack; + } + + /** + * Creates a {@link GuiItem} instead of an {@link ItemStack} + * + * @return A {@link GuiItem} with no {@link GuiAction} + */ + @NotNull + @Contract(" -> new") + public GuiItem asGuiItem() { + return new GuiItem(build()); + } + + /** + * Creates a {@link GuiItem} instead of an {@link ItemStack} + * + * @param action The {@link GuiAction} to apply to the item + * @return A {@link GuiItem} with {@link GuiAction} + */ + @NotNull + @Contract("_ -> new") + public GuiItem asGuiItem(@NotNull final GuiAction action) { + return new GuiItem(build(), action); + } + + /** + * Package private getter for extended builders + * + * @return The ItemStack + */ + @NotNull + protected ItemStack getItemStack() { + return itemStack; + } + + /** + * Package private setter for the extended builders + * + * @param itemStack The ItemStack + */ + protected void setItemStack(@NotNull final ItemStack itemStack) { + this.itemStack = itemStack; + } + + /** + * Package private getter for extended builders + * + * @return The ItemMeta + */ + @NotNull + protected ItemStack.Builder getMeta() { + return meta; + } + + /** + * Package private setter for the extended builders + * + * @param meta The ItemMeta + */ + protected void setMeta(@NotNull final ItemStack.Builder meta) { + this.meta = meta; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BookBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BookBuilder.java new file mode 100644 index 00000000..ffc2a7f7 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/BookBuilder.java @@ -0,0 +1,188 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.item; + +import dev.triumphteam.gui.components.exception.GuiException; +import dev.triumphteam.gui.components.util.Legacy; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.text.Text; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Item builder for {@link Material#WRITTEN_BOOK} and {@link Material#WRITTEN_BOOK} only + * + * @author GabyTM https://github.com/iGabyTM + * @since 3.0.1 + */ +public class BookBuilder extends BaseItemBuilder { + + private static final Set BOOKS = new HashSet<>(Arrays.asList(ItemTypes.WRITABLE_BOOK, ItemTypes.WRITTEN_BOOK)); + + BookBuilder(@NotNull ItemStack itemStack) { + super(itemStack); + if (!BOOKS.contains(itemStack.getType())) { + throw new GuiException("BookBuilder requires the material to be a WRITABLE_BOOK/WRITTEN_BOOK!"); + } + } + + /** + * Sets the author of the book. Removes author when given null. + * + * @param author the author to set + * @return {@link BookBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BookBuilder author(@Nullable final Component author) { + this.getItemStack().offer(Keys.BOOK_AUTHOR, Legacy.toText(author)); + return this; + } + + /** + * Sets the generation of the book. Removes generation when given null. + * + * @param generation the generation to set + * @return {@link BookBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BookBuilder generation(@Nullable final int generation) { + this.getItemStack().offer(Keys.GENERATION, generation); + return this; + } + + /** + * Adds new pages to the end of the book. Up to a maximum of 50 pages with + * 256 characters per page. + * + * @param pages list of pages + * @return {@link BookBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BookBuilder page(@NotNull final Component... pages) { + return page(Arrays.asList(pages)); + } + + /** + * Adds new pages to the end of the book. Up to a maximum of 50 pages with + * 256 characters per page. + * + * @param pages list of pages + * @return {@link BookBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BookBuilder page(@NotNull final List pages) { + final List textPages = new ArrayList<>(); + for (final Component page : pages) { + textPages.add(Legacy.toText(page)); + } + this.getItemStack().offer(Keys.BOOK_PAGES, textPages); + return this; + } + + /** + * Sets the specified page in the book. Pages of the book must be + * contiguous. + *

+ * The data can be up to 256 characters in length, additional characters + * are truncated. + *

+ * Pages are 1-indexed. + * + * @param page the page number to set, in range [1, {@link BookMeta#getPageCount()}] + * @param data the data to set for that page + * @return {@link BookBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_, _ -> this") + public BookBuilder page(final int page, @NotNull final Component data) { + List textPages = this.getItemStack().get(Keys.BOOK_PAGES).orElse(null); + if (textPages == null || textPages.isEmpty()) { + return this; + } + + if (textPages.size() < (page - 1)) { + return this; + } + + final List modifiedPages = new ArrayList<>(); + int count = 1; + for (Text bookPage : textPages) { + if (count == page) { + modifiedPages.add(Legacy.toText(data)); + } else { + modifiedPages.add(bookPage); + } + count++; + } + this.getItemStack().offer(Keys.BOOK_PAGES, modifiedPages); + return this; + } + + /** + * Sets the title of the book. + *

+ * Limited to 32 characters. Removes title when given null. + * + * @param title the title to set + * @return {@link BookBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public BookBuilder title(@Nullable Component title) { + /* TODO + final BookMeta bookMeta = (BookMeta) getMeta(); + + if (title == null) { + bookMeta.setTitle(null); + setMeta(bookMeta); + return this; + } + + bookMeta.setTitle(Legacy.SERIALIZER.serialize(title)); + setMeta(bookMeta);*/ + return this; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/FireworkBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/FireworkBuilder.java new file mode 100644 index 00000000..59dfd520 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/FireworkBuilder.java @@ -0,0 +1,125 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.item; + +import dev.triumphteam.gui.components.exception.GuiException; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.item.FireworkEffect; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; + +import java.util.Arrays; +import java.util.List; + +/** + * Item builder for {@link Material#FIREWORK_ROCKET} and {@link Material#FIREWORK_ROCKET} only + * + * @author GabyTM https://github.com/iGabyTM + * @since 3.0.1 + */ +public class FireworkBuilder extends BaseItemBuilder { + + private static final ItemType STAR = ItemTypes.FIREWORK_CHARGE; + private static final ItemType ROCKET = ItemTypes.FIREWORKS; + + FireworkBuilder(@NotNull final ItemStack itemStack) { + super(itemStack); + if (itemStack.getType() != STAR && itemStack.getType() != ROCKET) { + throw new GuiException("FireworkBuilder requires the material to be a FIREWORK_STAR/FIREWORK_ROCKET!"); + } + } + + /** + * Add several firework effects to this firework. + * + * @param effects effects to add + * @return {@link FireworkBuilder} + * @throws IllegalArgumentException If effects is null + * @throws IllegalArgumentException If any effect is null (may be thrown after changes have occurred) + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public FireworkBuilder effect(@NotNull final FireworkEffect... effects) { + return effect(Arrays.asList(effects)); + } + + /** + * Add several firework effects to this firework. + * + * @param effects effects to add + * @return {@link FireworkBuilder} + * @throws IllegalArgumentException If effects is null + * @throws IllegalArgumentException If any effect is null (may be thrown after changes have occurred) + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public FireworkBuilder effect(@NotNull final List effects) { + if (effects.isEmpty()) { + return this; + } + + /* TODO + if (getItemStack().getType() == STAR) { + final FireworkEffectMeta effectMeta = (FireworkEffectMeta) getMeta(); + + effectMeta.setEffect(effects.get(0)); + setMeta(effectMeta); + return this; + } + + final FireworkMeta fireworkMeta = (FireworkMeta) getMeta(); + + fireworkMeta.addEffects(effects); + setMeta(fireworkMeta);*/ + return this; + } + + /** + * Sets the approximate power of the firework. Each level of power is half + * a second of flight time. + * + * @param power the power of the firework, from 0-128 + * @return {@link FireworkBuilder} + * @throws IllegalArgumentException if {@literal height<0 or height>128} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> this") + public FireworkBuilder power(final int power) { + /* TODO + if (getItemStack().getType() == ROCKET) { + final FireworkMeta fireworkMeta = (FireworkMeta) getMeta(); + + fireworkMeta.setPower(power); + setMeta(fireworkMeta); + }*/ + + return this; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/ItemBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/ItemBuilder.java new file mode 100644 index 00000000..0162ecdd --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/ItemBuilder.java @@ -0,0 +1,274 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.item; + +import dev.triumphteam.gui.components.util.SkullUtil; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.User; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.enchantment.Enchantment; +import org.spongepowered.api.item.enchantment.EnchantmentType; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.serializer.TextSerializers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +/** + * Main ItemBuilder + */ +public class ItemBuilder extends BaseItemBuilder { + + /** + * Constructor of the item builder + * + * @param itemStack The {@link ItemStack} of the item + */ + ItemBuilder(@NotNull final ItemStack itemStack) { + super(itemStack); + } + + /** + * Main method to create {@link ItemBuilder} + * + * @param itemStack The {@link ItemStack} you want to edit + * @return A new {@link ItemBuilder} + */ + @NotNull + @Contract("_ -> new") + public static ItemBuilder from(@NotNull final ItemStack itemStack) { + return new ItemBuilder(itemStack); + } + + + /** + * Alternative method to create {@link ItemBuilder} + * + * @param material The {@link Material} you want to create an item from + * @return A new {@link ItemBuilder} + */ + @NotNull + @Contract("_ -> new") + public static ItemBuilder from(@NotNull final ItemType material) { + return new ItemBuilder(ItemStack.of(material)); + } + + /** + * Method for creating a {@link BannerBuilder} which will have BANNER specific methods + * + * @return A new {@link BannerBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract(" -> new") + public static BannerBuilder banner() { + return new BannerBuilder(); + } + + /** + * Method for creating a {@link BannerBuilder} which will have BANNER specific methods + * + * @param itemStack An existing BANNER {@link ItemStack} + * @return A new {@link BannerBuilder} + * @throws dev.triumphteam.gui.components.exception.GuiException if the item is not a BANNER + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> new") + public static BannerBuilder banner(@NotNull final ItemStack itemStack) { + return new BannerBuilder(itemStack); + } + + /** + * Method for creating a {@link BookBuilder} which will have {@link Material#WRITABLE_BOOK} / + * {@link Material#WRITTEN_BOOK} specific methods + * + * @param itemStack an existing {@link Material#WRITABLE_BOOK} / {@link Material#WRITTEN_BOOK} {@link ItemStack} + * @return A new {@link FireworkBuilder} + * @throws dev.triumphteam.gui.components.exception.GuiException if the item type is not {@link Material#WRITABLE_BOOK} + * or {@link Material#WRITTEN_BOOK} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> new") + public static BookBuilder book(@NotNull final ItemStack itemStack) { + return new BookBuilder(itemStack); + } + + /** + * Method for creating a {@link FireworkBuilder} which will have {@link Material#FIREWORK_ROCKET} specific methods + * + * @return A new {@link FireworkBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract(" -> new") + public static FireworkBuilder firework() { + return new FireworkBuilder(ItemStack.of(ItemTypes.FIREWORKS)); + } + + /** + * Method for creating a {@link FireworkBuilder} which will have {@link Material#FIREWORK_ROCKET} specific methods + * + * @param itemStack an existing {@link Material#FIREWORK_ROCKET} {@link ItemStack} + * @return A new {@link FireworkBuilder} + * @throws dev.triumphteam.gui.components.exception.GuiException if the item type is not {@link Material#FIREWORK_ROCKET} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> new") + public static FireworkBuilder firework(@NotNull final ItemStack itemStack) { + return new FireworkBuilder(itemStack); + } + + /** + * Method for creating a {@link MapBuilder} which will have {@link Material#MAP} specific methods + * + * @return A new {@link MapBuilder} + * @since 3.0.1 + */ + /* TODO + @NotNull + @Contract(" -> new") + public static MapBuilder map() { + return new MapBuilder(); + }*/ + + /** + * Method for creating a {@link MapBuilder} which will have @link Material#MAP} specific methods + * + * @param itemStack An existing {@link Material#MAP} {@link ItemStack} + * @return A new {@link MapBuilder} + * @throws dev.triumphteam.gui.components.exception.GuiException if the item type is not {@link Material#MAP} + * @since 3.0.1 + */ + /* TODO + @NotNull + @Contract("_ -> new") + public static MapBuilder map(@NotNull final ItemStack itemStack) { + return new MapBuilder(itemStack); + }*/ + + /** + * Method for creating a {@link SkullBuilder} which will have PLAYER_HEAD specific methods + * + * @return A new {@link SkullBuilder} + */ + @NotNull + @Contract(" -> new") + public static SkullBuilder skull() { + return new SkullBuilder(); + } + + /** + * Method for creating a {@link SkullBuilder} which will have PLAYER_HEAD specific methods + * + * @param itemStack An existing PLAYER_HEAD {@link ItemStack} + * @return A new {@link SkullBuilder} + * @throws dev.triumphteam.gui.components.exception.GuiException if the item is not a player head + */ + @NotNull + @Contract("_ -> new") + public static SkullBuilder skull(@NotNull final ItemStack itemStack) { + return new SkullBuilder(itemStack); + } + + /** + * Method for creating a {@link FireworkBuilder} which will have {@link Material#FIREWORK_STAR} specific methods + * + * @return A new {@link FireworkBuilder} + * @since 3.0.1 + */ + @NotNull + @Contract(" -> new") + public static FireworkBuilder star() { + return new FireworkBuilder(ItemStack.of(ItemTypes.FIREWORK_CHARGE)); + } + + /** + * Method for creating a {@link FireworkBuilder} which will have {@link Material#FIREWORK_STAR} specific methods + * + * @param itemStack an existing {@link Material#FIREWORK_STAR} {@link ItemStack} + * @return A new {@link FireworkBuilder} + * @throws dev.triumphteam.gui.components.exception.GuiException if the item type is not {@link Material#FIREWORK_STAR} + * @since 3.0.1 + */ + @NotNull + @Contract("_ -> new") + public static FireworkBuilder star(@NotNull final ItemStack itemStack) { + return new FireworkBuilder(itemStack); + } + + /** + * Sets the skull texture + * + * @param texture The base64 texture + * @return {@link ItemBuilder} + * @deprecated In favor of {@link SkullBuilder#texture(String)}, nothing changed just the name, will be removed in 3.0.1 + */ + @Deprecated + public ItemBuilder setSkullTexture(@NotNull final String texture) { + if (getItemStack().getType() != SkullUtil.SKULL) return this; + + /* TODO + final SkullMeta skullMeta = (SkullMeta) getMeta(); + final GameProfile profile = new GameProfile(UUID.randomUUID(), null); + profile.getProperties().put("textures", new Property("textures", texture)); + final Field profileField; + + try { + profileField = skullMeta.getClass().getDeclaredField("profile"); + profileField.setAccessible(true); + profileField.set(skullMeta, profile); + } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) { + ex.printStackTrace(); + } + + setMeta(skullMeta);*/ + return this; + } + + /** + * Sets skull owner via bukkit methods + * + * @param player {@link OfflinePlayer} to set skull of + * @return {@link ItemBuilder} + * @deprecated In favor of {@link SkullBuilder#owner(OfflinePlayer)}, nothing changed just the name, will be removed in 3.0.1 + */ + @Deprecated + public ItemBuilder setSkullOwner(@NotNull final User player) { + if (getItemStack().getType() != SkullUtil.SKULL) return this; + + this.getMeta().add(Keys.SKIN_UNIQUE_ID, player.getUniqueId()); + return this; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/ItemFlag.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/ItemFlag.java new file mode 100644 index 00000000..652d7ed4 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/ItemFlag.java @@ -0,0 +1,55 @@ +/** + * MIT License + *

+ * Copyright (c) 2021 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.item; + +/** + * Used to hide item attributes + */ +public enum ItemFlag { + + /** + * Used to show/hide enchants + */ + HIDE_ENCHANTS, + /** + * Used to show/hide attributes + */ + HIDE_ATTRIBUTES, + /** + * Used to show/hide the unbreakable state + */ + HIDE_UNBREAKABLE, + /** + * Used to show/hide what the ItemStack can break or destroy + */ + HIDE_DESTROYS, + /** + * Used to show/hide where this ItemStack can be built or placed on + */ + HIDE_PLACED_ON, + /** + * Used to show/hide potion effects on this ItemStack + */ + HIDE_POTION_EFFECTS +} \ No newline at end of file diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/SkullBuilder.java b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/SkullBuilder.java new file mode 100644 index 00000000..8ac11ae7 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/builder/item/SkullBuilder.java @@ -0,0 +1,129 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.builder.item; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import dev.triumphteam.gui.components.exception.GuiException; +import dev.triumphteam.gui.components.util.SkullUtil; +import dev.triumphteam.gui.components.util.VersionHelper; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.User; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; + +import java.lang.reflect.Field; +import java.util.UUID; + +/** + * New builder for skull only, created to separate the specific features for skulls + * Soon I'll add more useful features to this builder + */ +public final class SkullBuilder extends BaseItemBuilder { + + private static final int V1_12 = 1120; + //private static final Field PROFILE_FIELD; + + static { + Field field; + + /*try { + final SkullMeta skullMeta = (SkullMeta) new ItemStack(SkullUtil.SKULL).getItemMeta(); + field = skullMeta.getClass().getDeclaredField("profile"); + field.setAccessible(true); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + field = null; + }*/ + + //PROFILE_FIELD = field; + } + + SkullBuilder() { + super(ItemStack.of(SkullUtil.SKULL)); + } + + SkullBuilder(final @NotNull ItemStack itemStack) { + super(itemStack); + if (itemStack.getType() != SkullUtil.SKULL) { + throw new GuiException("SkullBuilder requires the material to be a PLAYER_HEAD/SKULL_ITEM!"); + } + } + + /** + * Sets the skull texture using a BASE64 string + * + * @param texture The base64 texture + * @return {@link SkullBuilder} + */ + @NotNull + @Contract("_ -> this") + public SkullBuilder texture(@NotNull final String texture) { + if (getItemStack().getType() != SkullUtil.SKULL) return this; + + //if (PROFILE_FIELD == null) { + // return this; + //} + + /*ItemStack.of(ItemTypes.SKULL).offer(Keys.SKULL_TYPE, ) + final SkullMeta skullMeta = (SkullMeta) getMeta(); + final GameProfile profile = new GameProfile(UUID.randomUUID(), null); + profile.getProperties().put("textures", new Property("textures", texture)); + + try { + PROFILE_FIELD.set(skullMeta, profile); + } catch (IllegalArgumentException | IllegalAccessException ex) { + ex.printStackTrace(); + } + + setMeta(skullMeta);*/ + return this; + } + + /** + * Sets skull owner via bukkit methods + * + * @param player {@link OfflinePlayer} to set skull of + * @return {@link SkullBuilder} + */ + @NotNull + @Contract("_ -> this") + public SkullBuilder owner(@NotNull final User player) { + if (getItemStack().getType() != SkullUtil.SKULL) return this; + + /*final SkullMeta skullMeta = (SkullMeta) getMeta(); + + if (VersionHelper.IS_SKULL_OWNER_LEGACY) { + skullMeta.setOwner(player.getName()); + } else { + skullMeta.setOwningPlayer(player); + } + + setMeta(skullMeta);*/ + return this; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiAction.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiAction.java new file mode 100644 index 00000000..94daf8d0 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiAction.java @@ -0,0 +1,38 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components; + +import org.spongepowered.api.event.Event; + +@FunctionalInterface +public interface GuiAction { + + /** + * Executes the event passed to it + * + * @param event Inventory action + */ + void execute(final T event); + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiPage.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiPage.java new file mode 100644 index 00000000..05b54a41 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiPage.java @@ -0,0 +1,27 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components; + +public final class GuiPage { +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiType.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiType.java new file mode 100644 index 00000000..6b8afe09 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/GuiType.java @@ -0,0 +1,80 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.item.inventory.InventoryArchetype; +import org.spongepowered.api.item.inventory.InventoryArchetypes; +import org.spongepowered.api.item.inventory.property.InventoryCapacity; +import org.spongepowered.common.registry.type.item.InventoryArchetypeRegistryModule; + +// TODO COMMENTS +public enum GuiType { + + CHEST(Archetypes.GUI_CHEST, 9), + WORKBENCH(Archetypes.GUI_WORKBENCH, 9), + HOPPER(Archetypes.GUI_HOPPER, 5), + DISPENSER(Archetypes.GUI_DISPENSER, 8), + BREWING(Archetypes.GUI_BREWING, 4); + + @NotNull + private final InventoryArchetype inventoryType; + private final int limit; + + GuiType(@NotNull final InventoryArchetype inventoryType, final int limit) { + this.inventoryType = inventoryType; + this.limit = limit; + } + + @NotNull + public InventoryArchetype getInventoryType() { + return inventoryType; + } + + public int getLimit() { + return this.limit; + } + + public static class Archetypes { + + public static final InventoryArchetype GUI_CHEST; + public static final InventoryArchetype GUI_WORKBENCH; + public static final InventoryArchetype GUI_HOPPER; + public static final InventoryArchetype GUI_DISPENSER; + public static final InventoryArchetype GUI_BREWING; + + static { + GUI_CHEST = InventoryArchetype.builder().from(InventoryArchetypes.CHEST).property(InventoryCapacity.of(9)).build("triumph:chest", "chest"); + GUI_WORKBENCH = InventoryArchetype.builder().from(InventoryArchetypes.WORKBENCH).property(InventoryCapacity.of(9)).build("triumph:workbench", "workbench"); + GUI_HOPPER = InventoryArchetype.builder().from(InventoryArchetypes.HOPPER).property(InventoryCapacity.of(5)).build("triumph:hopper", "hopper"); + GUI_DISPENSER = InventoryArchetype.builder().from(InventoryArchetypes.DISPENSER).property(InventoryCapacity.of(8)).build("triumph:dispenser", "dispenser"); + GUI_BREWING = InventoryArchetype.builder().from(InventoryArchetypes.BREWING_STAND).property(InventoryCapacity.of(4)).build("triumph:brewing", "brewing"); + InventoryArchetypeRegistryModule.getInstance().registerAdditionalCatalog(GUI_CHEST); + InventoryArchetypeRegistryModule.getInstance().registerAdditionalCatalog(GUI_WORKBENCH); + InventoryArchetypeRegistryModule.getInstance().registerAdditionalCatalog(GUI_HOPPER); + InventoryArchetypeRegistryModule.getInstance().registerAdditionalCatalog(GUI_DISPENSER); + InventoryArchetypeRegistryModule.getInstance().registerAdditionalCatalog(GUI_BREWING); + } + } +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/InteractionModifier.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/InteractionModifier.java new file mode 100644 index 00000000..2a1597cc --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/InteractionModifier.java @@ -0,0 +1,44 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +/** + * Used to control what kind of interaction can happen inside a GUI + * + * @since 3.0.0 + * @author SecretX + */ +public enum InteractionModifier { + PREVENT_ITEM_PLACE, + PREVENT_ITEM_TAKE, + PREVENT_ITEM_SWAP, + PREVENT_ITEM_DROP, + PREVENT_OTHER_ACTIONS; + + public static final Set VALUES = Collections.unmodifiableSet(EnumSet.allOf(InteractionModifier.class)); +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/ScrollType.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/ScrollType.java new file mode 100644 index 00000000..6ffa597d --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/ScrollType.java @@ -0,0 +1,36 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components; + +import dev.triumphteam.gui.guis.ScrollingGui; + +/** + * Possible Scroll types for the {@link ScrollingGui} + */ +public enum ScrollType { + + HORIZONTAL, + VERTICAL + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/Serializable.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/Serializable.java new file mode 100644 index 00000000..dc3516b0 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/Serializable.java @@ -0,0 +1,36 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface Serializable { + + List encodeGui(); + + void decodeGui(@NotNull final List gui); + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/exception/GuiException.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/exception/GuiException.java new file mode 100644 index 00000000..dbf38b93 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/exception/GuiException.java @@ -0,0 +1,30 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components.exception; + +public final class GuiException extends RuntimeException { + public GuiException(String message) { + super(message); + } +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/nbt/LegacyNbt.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/nbt/LegacyNbt.java new file mode 100644 index 00000000..15532987 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/nbt/LegacyNbt.java @@ -0,0 +1,208 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components.nbt; + +import net.minecraft.nbt.NBTTagCompound; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.api.item.inventory.ItemStack; + +/** + * Class to set / get NBT tags from items. + * I hate this class. + */ +public final class LegacyNbt implements NbtWrapper { + + /** + * Sets an NBT tag to the an {@link ItemStack}. + * + * @param itemStack The current {@link ItemStack} to be set. + * @param key The NBT key to use. + * @param value The tag value to set. + * @return An {@link ItemStack} that has NBT set. + */ + @Override + public ItemStack setString(@NotNull final ItemStack itemStack, final String key, final String value) { + if (itemStack.isEmpty()) return itemStack; + + Object nmsItemStack = asNMSCopy(itemStack); + Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); + + setString(itemCompound, key, value); + setTag(nmsItemStack, itemCompound); + + return asBukkitCopy(nmsItemStack); + } + + /** + * Removes a tag from an {@link ItemStack}. + * + * @param itemStack The current {@link ItemStack} to be remove. + * @param key The NBT key to remove. + * @return An {@link ItemStack} that has the tag removed. + */ + @Override + public ItemStack removeTag(@NotNull final ItemStack itemStack, final String key) { + if (itemStack.isEmpty()) return itemStack; + + Object nmsItemStack = asNMSCopy(itemStack); + Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); + + remove(itemCompound, key); + setTag(nmsItemStack, itemCompound); + + return asBukkitCopy(nmsItemStack); + } + + /** + * Sets a boolean to the {@link ItemStack}. + * Mainly used for setting an item to be unbreakable on older versions. + * + * @param itemStack The {@link ItemStack} to set the boolean to. + * @param key The key to use. + * @param value The boolean value. + * @return An {@link ItemStack} with a boolean value set. + */ + @Override + public ItemStack setBoolean(@NotNull final ItemStack itemStack, final String key, final boolean value) { + if (itemStack.isEmpty()) return itemStack; + + Object nmsItemStack = asNMSCopy(itemStack); + Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); + + setBoolean(itemCompound, key, value); + setTag(nmsItemStack, itemCompound); + + return asBukkitCopy(nmsItemStack); + } + + /** + * Gets the NBT tag based on a given key. + * + * @param itemStack The {@link ItemStack} to get from. + * @param key The key to look for. + * @return The tag that was stored in the {@link ItemStack}. + */ + @Nullable + @Override + public String getString(@NotNull final ItemStack itemStack, final String key) { + if (itemStack.isEmpty()) return null; + + Object nmsItemStack = asNMSCopy(itemStack); + Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); + + return getString(itemCompound, key); + } + + /** + * Mimics the itemCompound#setString method. + * + * @param itemCompound The ItemCompound. + * @param key The key to add. + * @param value The value to add. + */ + private static void setString(final Object itemCompound, final String key, final String value) { + ((NBTTagCompound) itemCompound).setString(key, value); + } + + private static void setBoolean(final Object itemCompound, final String key, final boolean value) { + ((NBTTagCompound) itemCompound).setBoolean(key, value); + } + + /** + * Mimics the itemCompound#remove method. + * + * @param itemCompound The ItemCompound. + * @param key The key to remove. + */ + private static void remove(final Object itemCompound, final String key) { + ((NBTTagCompound) itemCompound).removeTag(key); + } + + /** + * Mimics the itemCompound#getString method. + * + * @param itemCompound The ItemCompound. + * @param key The key to get from. + * @return A string with the value from the key. + */ + private static String getString(final Object itemCompound, final String key) { + return ((NBTTagCompound) itemCompound).getString(key); + } + + /** + * Mimics the nmsItemStack#hasTag method. + * + * @param nmsItemStack the NMS ItemStack to check from. + * @return True or false depending if it has tag or not. + */ + private static boolean hasTag(final Object nmsItemStack) { + return ((net.minecraft.item.ItemStack) nmsItemStack).hasTagCompound(); + } + + /** + * Mimics the nmsItemStack#getTag method. + * + * @param nmsItemStack The NMS ItemStack to get from. + * @return The tag compound. + */ + public static Object getTag(final Object nmsItemStack) { + return ((net.minecraft.item.ItemStack) nmsItemStack).getTagCompound(); + } + + /** + * Mimics the nmsItemStack#setTag method. + * + * @param nmsItemStack the NMS ItemStack to set the tag to. + * @param itemCompound The item compound to set. + */ + private static void setTag(final Object nmsItemStack, final Object itemCompound) { + ((net.minecraft.item.ItemStack) nmsItemStack).setTagCompound((NBTTagCompound) itemCompound); + } + + /** + * Mimics the new NBTTagCompound instantiation. + * + * @return The new NBTTagCompound. + */ + private static Object newNBTTagCompound() { + return new NBTTagCompound(); + } + + public static ItemStack asBukkitCopy(Object nmsItemStack) { + return ItemStack.builder().from((ItemStack) nmsItemStack).build(); + } + + /** + * Mimics the CraftItemStack#asNMSCopy method. + * + * @param itemStack The ItemStack to make NMS copy. + * @return An NMS copy of the ItemStack. + */ + public static Object asNMSCopy(final ItemStack itemStack) { + net.minecraft.item.ItemStack stack = ((net.minecraft.item.ItemStack)(Object) itemStack).copy(); + stack.setCount(itemStack.getQuantity()); + return stack; + } +} \ No newline at end of file diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/nbt/NbtWrapper.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/nbt/NbtWrapper.java new file mode 100644 index 00000000..561c3211 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/nbt/NbtWrapper.java @@ -0,0 +1,72 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components.nbt; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.api.item.inventory.ItemStack; + +public interface NbtWrapper { + + /** + * Sets an String NBT tag to the an {@link ItemStack}. + * + * @param itemStack The current {@link ItemStack} to be set. + * @param key The NBT key to use. + * @param value The tag value to set. + * @return An {@link ItemStack} that has NBT set. + */ + ItemStack setString(@NotNull final ItemStack itemStack, final String key, final String value); + + /** + * Removes a tag from an {@link ItemStack}. + * + * @param itemStack The current {@link ItemStack} to be remove. + * @param key The NBT key to remove. + * @return An {@link ItemStack} that has the tag removed. + */ + ItemStack removeTag(@NotNull final ItemStack itemStack, final String key); + + /** + * Sets a boolean to the {@link ItemStack}. + * Mainly used for setting an item to be unbreakable on older versions. + * + * @param itemStack The {@link ItemStack} to set the boolean to. + * @param key The key to use. + * @param value The boolean value. + * @return An {@link ItemStack} with a boolean value set. + */ + ItemStack setBoolean(@NotNull final ItemStack itemStack, final String key, final boolean value); + + /** + * Gets the NBT tag based on a given key. + * + * @param itemStack The {@link ItemStack} to get from. + * @param key The key to look for. + * @return The tag that was stored in the {@link ItemStack}. + */ + @Nullable + String getString(@NotNull final ItemStack itemStack, final String key); + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/GuiFiller.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/GuiFiller.java new file mode 100644 index 00000000..842cc9d4 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/GuiFiller.java @@ -0,0 +1,229 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components.util; + +import dev.triumphteam.gui.components.GuiType; +import dev.triumphteam.gui.components.exception.GuiException; +import dev.triumphteam.gui.guis.BaseGui; +import dev.triumphteam.gui.guis.GuiItem; +import dev.triumphteam.gui.guis.PaginatedGui; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * TODO fix comments + */ +public final class GuiFiller { + + private final BaseGui gui; + + public GuiFiller(final BaseGui gui) { + this.gui = gui; + } + + /** + * Fills top portion of the GUI + * + * @param guiItem GuiItem + */ + public void fillTop(@NotNull final GuiItem guiItem) { + fillTop(Collections.singletonList(guiItem)); + } + + /** + * Fills top portion of the GUI with alternation + * + * @param guiItems List of GuiItems + */ + public void fillTop(@NotNull final List guiItems) { + final List items = repeatList(guiItems, gui.getRows() * 9); + for (int i = 0; i < 9; i++) { + if (!gui.getGuiItems().containsKey(i)) gui.setItem(i, items.get(i)); + } + } + + /** + * Fills bottom portion of the GUI + * + * @param guiItem GuiItem + */ + public void fillBottom(@NotNull final GuiItem guiItem) { + fillBottom(Collections.singletonList(guiItem)); + } + + /** + * Fills bottom portion of the GUI with alternation + * + * @param guiItems GuiItem + */ + public void fillBottom(@NotNull final List guiItems) { + final int rows = gui.getRows(); + final List items = repeatList(guiItems, rows * 9); + for (int i = 9; i > 0; i--) { + if (gui.getGuiItems().get((rows * 9) - i) == null) { + gui.setItem((rows * 9) - i, items.get(i)); + } + } + } + + /** + * Fills the outside section of the GUI with a GuiItem + * + * @param guiItem GuiItem + */ + public void fillBorder(@NotNull final GuiItem guiItem) { + fillBorder(Collections.singletonList(guiItem)); + } + + /** + * Fill empty slots with Multiple GuiItems, goes through list and starts again + * + * @param guiItems GuiItem + */ + public void fillBorder(@NotNull final List guiItems) { + final int rows = gui.getRows(); + if (rows <= 2) return; + + final List items = repeatList(guiItems, rows * 9); + + for (int i = 0; i < rows * 9; i++) { + if ((i <= 8) || (i >= (rows * 9) - 9) + || i == 9 || i == 18 + || i == 27 || i == 36 + || i == 17 || i == 26 + || i == 35 || i == 44) + gui.setItem(i, items.get(i)); + + } + } + + /** + * Fills rectangle from points within the GUI + * + * @param rowFrom Row point 1 + * @param colFrom Col point 1 + * @param rowTo Row point 2 + * @param colTo Col point 2 + * @param guiItem Item to fill with + * @author Harolds + */ + public void fillBetweenPoints(final int rowFrom, final int colFrom, final int rowTo, final int colTo, @NotNull final GuiItem guiItem) { + fillBetweenPoints(rowFrom, colFrom, rowTo, colTo, Collections.singletonList(guiItem)); + } + + /** + * Fills rectangle from points within the GUI + * + * @param rowFrom Row point 1 + * @param colFrom Col point 1 + * @param rowTo Row point 2 + * @param colTo Col point 2 + * @param guiItems Item to fill with + * @author Harolds + */ + public void fillBetweenPoints(final int rowFrom, final int colFrom, final int rowTo, final int colTo, @NotNull final List guiItems) { + final int minRow = Math.min(rowFrom, rowTo); + final int maxRow = Math.max(rowFrom, rowTo); + final int minCol = Math.min(colFrom, colTo); + final int maxCol = Math.max(colFrom, colTo); + + final int rows = gui.getRows(); + final List items = repeatList(guiItems, rows * 9); + + for (int row = 1; row <= rows; row++) { + for (int col = 1; col <= 9; col++) { + int slot = getSlotFromRowCol(row, col); + if (!((row >= minRow && row <= maxRow) && (col >= minCol && col <= maxCol))) + continue; + + gui.setItem(slot, items.get(slot)); + } + } + } + + /** + * Sets an GuiItem to fill up the entire inventory where there is no other item + * + * @param guiItem The item to use as fill + */ + public void fill(@NotNull final GuiItem guiItem) { + fill(Collections.singletonList(guiItem)); + } + + /** + * Fill empty slots with Multiple GuiItems, goes through list and starts again + * + * @param guiItems GuiItem + */ + public void fill(@NotNull final List guiItems) { + if (gui instanceof PaginatedGui) { + throw new GuiException("Full filling a GUI is not supported in a Paginated GUI!"); + } + + final GuiType type = gui.guiType(); + + final int fill; + if (type == GuiType.CHEST) { + fill = gui.getRows() * type.getLimit(); + } else { + fill = type.getLimit(); + } + + final int rows = gui.getRows(); + final List items = repeatList(guiItems, rows * 9); + for (int i = 0; i < fill; i++) { + if (gui.getGuiItems().get(i) == null) gui.setItem(i, items.get(i)); + } + } + + /** + * Repeats any type of Array. Allows for alternating items + * Stores references to existing Objects -> Does not create new objects + * + * @param guiItems Array Type + * @param newLength Length of array + * @return New array + */ + private List repeatList(@NotNull final List guiItems, final int newLength) { + final List repeated = new ArrayList<>(); + Collections.nCopies(gui.getRows() * 9, guiItems).forEach(repeated::addAll); + return repeated; + } + + + /** + * Gets the slot from the row and col passed + * + * @param row The row + * @param col The col + * @return The new slot + */ + private int getSlotFromRowCol(final int row, final int col) { + return (col + (row - 1) * 9) - 1; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/ItemNbt.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/ItemNbt.java new file mode 100644 index 00000000..c65af04e --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/ItemNbt.java @@ -0,0 +1,95 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components.util; + +import dev.triumphteam.gui.components.nbt.LegacyNbt; +import dev.triumphteam.gui.components.nbt.NbtWrapper; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.item.inventory.ItemStack; + +/** + * Ideally this wouldn't need to be an util, but because of the {@link LegacyNbt} it makes it easier. Legacy.. + * Will use the PDC wrapper if version is higher than 1.14 + */ +public final class ItemNbt { + + private static final NbtWrapper nbt = selectNbt(); + + /** + * Sets an NBT tag to the an {@link ItemStack}. + * + * @param itemStack The current {@link ItemStack} to be set. + * @param key The NBT key to use. + * @param value The tag value to set. + * @return An {@link ItemStack} that has NBT set. + */ + public static ItemStack setString(@NotNull final ItemStack itemStack, @NotNull final String key, @NotNull final String value) { + return nbt.setString(itemStack, key, value); + } + + /** + * Gets the NBT tag based on a given key. + * + * @param itemStack The {@link ItemStack} to get from. + * @param key The key to look for. + * @return The tag that was stored in the {@link ItemStack}. + */ + public static String getString(@NotNull final ItemStack itemStack, @NotNull final String key) { + return nbt.getString(itemStack, key); + } + + /** + * Sets a boolean to the {@link ItemStack}. + * Mainly used for setting an item to be unbreakable on older versions. + * + * @param itemStack The {@link ItemStack} to set the boolean to. + * @param key The key to use. + * @param value The boolean value. + * @return An {@link ItemStack} with a boolean value set. + */ + public static ItemStack setBoolean(@NotNull final ItemStack itemStack, @NotNull final String key, final boolean value) { + return nbt.setBoolean(itemStack, key, value); + } + + /** + * Removes a tag from an {@link ItemStack}. + * + * @param itemStack The current {@link ItemStack} to be remove. + * @param key The NBT key to remove. + * @return An {@link ItemStack} that has the tag removed. + */ + public static ItemStack removeTag(@NotNull final ItemStack itemStack, @NotNull final String key) { + return nbt.removeTag(itemStack, key); + } + + /** + * Selects which {@link NbtWrapper} to use based on server version. + * + * @return A {@link NbtWrapper} implementation, {@link Pdc} if version is higher than 1.14 and {@link LegacyNbt} if not. + */ + private static NbtWrapper selectNbt() { + return new LegacyNbt(); + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/Legacy.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/Legacy.java new file mode 100644 index 00000000..9fd81fc4 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/Legacy.java @@ -0,0 +1,48 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components.util; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.serializer.TextSerializers; + +public final class Legacy { + + public static final LegacyComponentSerializer SERIALIZER = LegacyComponentSerializer.builder() + .extractUrls() + .character(LegacyComponentSerializer.AMPERSAND_CHAR) + .hexColors() + .useUnusualXRepeatedCharacterHexFormat() + .build(); + + public static Text toText(Component component) { + return TextSerializers.FORMATTING_CODE.deserialize(SERIALIZER.serialize(component)); + } + + private Legacy() { + throw new UnsupportedOperationException("Class should not be instantiated!"); + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/SkullUtil.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/SkullUtil.java new file mode 100644 index 00000000..f727c35a --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/SkullUtil.java @@ -0,0 +1,49 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components.util; + +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; + +public final class SkullUtil { + + /** + * The main SKULL material for the version + */ + public static final ItemType SKULL = getSkullMaterial(); + + /** + * Gets the correct {@link Material} for the version + * + * @return The correct SKULL {@link Material} + */ + private static ItemType getSkullMaterial() { + if (VersionHelper.IS_ITEM_LEGACY) { + return ItemTypes.SKULL; + } + + return ItemTypes.SKULL; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/VersionHelper.java b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/VersionHelper.java new file mode 100644 index 00000000..77d56b1a --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/components/util/VersionHelper.java @@ -0,0 +1,85 @@ +/** + * MIT License + *

+ * Copyright (c) 2021 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.components.util; + +/** + * Class for detecting server version for legacy support :( + */ +public final class VersionHelper { + + // SkullMeta#setOwningPlayer was added + private static final int V1_12 = 1120; + + private static final int CURRENT_VERSION = getCurrentVersion(); + + /** + * Checks if the version supports Components or not + * Paper versions above 1.16.5 would be true + * Spigot always false + */ + public static final boolean IS_COMPONENT_LEGACY = true; + + /** + * Checks if the version is lower than 1.13 due to the item changes + */ + public static final boolean IS_ITEM_LEGACY = true; + + /** + * Checks if the version supports the {@link org.bukkit.inventory.meta.ItemMeta#setUnbreakable(boolean)} method + */ + public static final boolean IS_UNBREAKABLE_LEGACY = false; + + /** + * Checks if the version doesn't have {@link org.bukkit.inventory.meta.SkullMeta#setOwningPlayer(OfflinePlayer)} and + * {@link org.bukkit.inventory.meta.SkullMeta#setOwner(String)} should be used instead + */ + public static final boolean IS_SKULL_OWNER_LEGACY = false; + + /** + * Gets the current server version + * + * @return A protocol like number representing the version, for example 1.16.5 - 1165 + */ + private static int getCurrentVersion() { + // No need to cache since will only run once + /*final Matcher matcher = Pattern.compile("(?\\d+\\.\\d+)(?\\.\\d+)?").matcher(Bukkit.getBukkitVersion()); + + final StringBuilder stringBuilder = new StringBuilder(); + if (matcher.find()) { + stringBuilder.append(matcher.group("version").replace(".", "")); + final String patch = matcher.group("patch"); + if (patch == null) stringBuilder.append("0"); + else stringBuilder.append(patch.replace(".", "")); + } + + Integer version = Ints.tryParse(stringBuilder.toString()); + + // Should never fail + if (version == null) throw new GuiException("Could not retrieve server version!"); + + return version;*/ + return 1120; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/guis/BaseGui.java b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/BaseGui.java new file mode 100644 index 00000000..b9b62c60 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/BaseGui.java @@ -0,0 +1,1013 @@ +/** + * MIT License + *

+ * Copyright (c) 2021 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.guis; + +import dev.triumphteam.gui.TriumphPlugin; +import dev.triumphteam.gui.components.GuiAction; +import dev.triumphteam.gui.components.GuiType; +import dev.triumphteam.gui.components.InteractionModifier; +import dev.triumphteam.gui.components.exception.GuiException; +import dev.triumphteam.gui.components.util.GuiFiller; +import dev.triumphteam.gui.components.util.Legacy; +import net.kyori.adventure.text.Component; +import net.minecraft.entity.player.EntityPlayerMP; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.item.inventory.ClickInventoryEvent; +import org.spongepowered.api.event.item.inventory.InteractInventoryEvent; +import org.spongepowered.api.item.inventory.Container; +import org.spongepowered.api.item.inventory.Inventory; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.property.AbstractInventoryProperty; +import org.spongepowered.api.item.inventory.property.InventoryCapacity; +import org.spongepowered.api.item.inventory.property.InventoryDimension; +import org.spongepowered.api.item.inventory.property.InventoryTitle; +import org.spongepowered.api.item.inventory.property.SlotIndex; +import org.spongepowered.api.text.Text; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + + +/** + * Base class that every GUI extends. + * Contains all the basics for the GUI to work. + * Main and simplest implementation of this is {@link Gui}. + */ +public abstract class BaseGui extends AbstractInventoryProperty { + + // Main inventory. + private Inventory inventory; + + // title + private String title; + + // Gui filler. + private final GuiFiller filler = new GuiFiller(this); + + private int rows = 1; + + // Gui type, defaults to chest. + private GuiType guiType = GuiType.CHEST; + + // Contains all items the GUI will have. + private final Map guiItems; + + // Actions for specific slots. + private final Map> slotActions; + // Interaction modifiers. + private final Set interactionModifiers; + // Action to execute when clicking on any item. + private GuiAction defaultClickAction; + // Action to execute when clicking on the top part of the GUI only. + private GuiAction defaultTopClickAction; + // Action to execute when clicking on the player Inventory. + private GuiAction playerInventoryAction; + // Action to execute when dragging the item on the GUI. + private GuiAction dragAction; + // Action to execute when GUI closes. + private GuiAction closeGuiAction; + // Action to execute when GUI opens. + private GuiAction openGuiAction; + // Action to execute when clicked outside the GUI. + private GuiAction outsideClickAction; + + // Whether the GUI is updating. + private boolean updating; + + // Whether should run the actions from the close and open methods. + private boolean runCloseAction = true; + private boolean runOpenAction = true; + + /** + * The main constructor, using {@link String}. + * + * @param rows The amount of rows to use. + * @param title The GUI title using {@link String}. + * @param interactionModifiers Modifiers to select which interactions are allowed. + * @since 3.0.0. + */ + public BaseGui(final int rows, @NotNull final String title, @NotNull final Set interactionModifiers) { + int finalRows = rows; + if (!(rows >= 1 && rows <= 6)) finalRows = 1; + this.rows = finalRows; + this.interactionModifiers = safeCopyOf(interactionModifiers); + this.title = title; + int inventorySize = this.rows * 9; + this.inventory = Inventory.builder().property("triumph", this).of(GuiType.CHEST.getInventoryType()).property(InventoryDimension.of(9, this.rows)).property(InventoryTitle.of(Text.of(title))).build(TriumphPlugin.getInstance()); + this.slotActions = new LinkedHashMap<>(inventorySize); + this.guiItems = new LinkedHashMap<>(inventorySize); + } + + /** + * Alternative constructor that takes {@link GuiType} instead of rows number. + * + * @param guiType The {@link GuiType} to use. + * @param title The GUI title using {@link String}. + * @param interactionModifiers Modifiers to select which interactions are allowed. + * @since 3.0.0 + */ + public BaseGui(@NotNull final GuiType guiType, @NotNull final String title, @NotNull final Set interactionModifiers) { + this.guiType = guiType; + this.interactionModifiers = safeCopyOf(interactionModifiers); + this.title = title; + int inventorySize = this.rows * 9; + this.inventory = Inventory.builder().property("triumph", this).of(GuiType.CHEST.getInventoryType()).property(InventoryDimension.of(9, this.rows)).property(InventoryTitle.of(Text.of(title))).build(TriumphPlugin.getInstance()); + this.slotActions = new LinkedHashMap<>(inventorySize); + this.guiItems = new LinkedHashMap<>(inventorySize); + } + + /** + * Copy a set into an EnumSet, required because {@link EnumSet#copyOf(EnumSet)} throws an exception if the collection passed as argument is empty. + * + * @param set The set to be copied. + * @return An EnumSet with the provided elements from the original set. + */ + @NotNull + private EnumSet safeCopyOf(@NotNull final Set set) { + if (set.isEmpty()) return EnumSet.noneOf(InteractionModifier.class); + else return EnumSet.copyOf(set); + } + + /** + * Legacy constructor that takes rows and title. + * + * @param rows The amount of rows the GUI should have. + * @param title The GUI title. + * @deprecated In favor of {@link BaseGui#BaseGui(int, String, Set)}. + */ + @Deprecated + public BaseGui(final int rows, @NotNull final String title) { + int finalRows = rows; + if (!(rows >= 1 && rows <= 6)) finalRows = 1; + this.rows = finalRows; + this.interactionModifiers = EnumSet.noneOf(InteractionModifier.class); + this.title = title; + + inventory = Inventory.builder().property("triumph", this).of(GuiType.CHEST.getInventoryType()).property(InventoryDimension.of(9, this.rows)).property(InventoryTitle.of(Text.of(title))).build(TriumphPlugin.getInstance()); + slotActions = new LinkedHashMap<>(); + guiItems = new LinkedHashMap<>(); + } + + /** + * Alternative constructor that takes {@link GuiType} instead of rows number. + * + * @param guiType The {@link GuiType} to use. + * @param title The GUI title. + * @deprecated In favor of {@link BaseGui#BaseGui(GuiType, String, Set)}. + */ + @Deprecated + public BaseGui(@NotNull final GuiType guiType, @NotNull final String title) { + this.guiType = guiType; + this.interactionModifiers = EnumSet.noneOf(InteractionModifier.class); + this.title = title; + + inventory = Inventory.builder().property("triumph", this).of(this.guiType.getInventoryType()).property(InventoryDimension.of(this.guiType.getLimit(), this.rows)).property(InventoryTitle.of(Text.of(title))).build(TriumphPlugin.getInstance()); + slotActions = new LinkedHashMap<>(); + guiItems = new LinkedHashMap<>(); + } + + /** + * Gets the GUI's title as string. + * + * @return The GUI's title. + */ + @NotNull + @Deprecated + public String getTitle() { + return title; + } + + /** + * Gets the GUI title as a {@link Component}. + * + * @return The GUI title {@link Component}. + */ + @NotNull + public Component title() { + return Legacy.SERIALIZER.deserialize(title); + } + + /** + * Sets the {@link GuiItem} to a specific slot on the GUI. + * + * @param slot The GUI slot. + * @param guiItem The {@link GuiItem} to add to the slot. + */ + public void setItem(final int slot, @NotNull final GuiItem guiItem) { + validateSlot(slot); + guiItems.put(slot, guiItem); + } + + /** + * Removes the given {@link GuiItem} from the GUI. + * + * @param item The item to remove. + */ + public void removeItem(@NotNull final GuiItem item) { + final Optional> entry = guiItems.entrySet() + .stream() + .filter(it -> it.getValue().equals(item)) + .findFirst(); + + entry.ifPresent(it -> { + guiItems.remove(it.getKey()); + }); + } + + /** + * Removes the given {@link ItemStack} from the GUI. + * + * @param item The item to remove. + */ + public void removeItem(@NotNull final ItemStack item) { + final Optional> entry = guiItems.entrySet() + .stream() + .filter(it -> it.getValue().getItemStack().equals(item)) + .findFirst(); + + entry.ifPresent(it -> { + guiItems.remove(it.getKey()); + }); + } + + /** + * Removes the {@link GuiItem} in the specific slot. + * + * @param slot The GUI slot. + */ + public void removeItem(final int slot) { + validateSlot(slot); + guiItems.remove(slot); + } + + /** + * Alternative {@link #removeItem(int)} with cols and rows. + * + * @param row The row. + * @param col The column. + */ + public void removeItem(final int row, final int col) { + removeItem(getSlotFromRowCol(row, col)); + } + + /** + * Alternative {@link #setItem(int, GuiItem)} to set item that takes a {@link List} of slots instead. + * + * @param slots The slots in which the item should go. + * @param guiItem The {@link GuiItem} to add to the slots. + */ + public void setItem(@NotNull final List slots, @NotNull final GuiItem guiItem) { + for (final int slot : slots) { + setItem(slot, guiItem); + } + } + + /** + * Alternative {@link #setItem(int, GuiItem)} to set item that uses ROWS and COLUMNS instead of slots. + * + * @param row The GUI row number. + * @param col The GUI column number. + * @param guiItem The {@link GuiItem} to add to the slot. + */ + public void setItem(final int row, final int col, @NotNull final GuiItem guiItem) { + setItem(getSlotFromRowCol(row, col), guiItem); + } + + /** + * Adds {@link GuiItem}s to the GUI without specific slot. + * It'll set the item to the next empty slot available. + * + * @param items Varargs for specifying the {@link GuiItem}s. + */ + public void addItem(@NotNull final GuiItem... items) { + this.addItem(false, items); + } + + /** + * Adds {@link GuiItem}s to the GUI without specific slot. + * It'll set the item to the next empty slot available. + * + * @param items Varargs for specifying the {@link GuiItem}s. + * @param expandIfFull If true, expands the gui if it is full + * and there are more items to be added + */ + public void addItem(final boolean expandIfFull, @NotNull final GuiItem... items) { + final List notAddedItems = new ArrayList<>(); + + for (final GuiItem guiItem : items) { + for (int slot = 0; slot < rows * 9; slot++) { + if (guiItems.get(slot) != null) { + if (slot == rows * 9 - 1) { + notAddedItems.add(guiItem); + } + continue; + } + + guiItems.put(slot, guiItem); + break; + } + } + + if (!expandIfFull || this.rows >= 6 || + notAddedItems.isEmpty() || + (this.guiType != null && this.guiType != GuiType.CHEST)) { + return; + } + + this.rows++; + this.inventory = Inventory.builder().of(this.guiType.getInventoryType()).property(InventoryCapacity.of(this.rows * 9)).property(InventoryTitle.of(Text.of(title))).build(TriumphPlugin.getInstance()); + this.update(); + this.addItem(true, notAddedItems.toArray(new GuiItem[0])); + } + + /** + * Sets the {@link GuiAction} of a default click on any item. + * See {@link ClickInventoryEvent}. + * + * @param defaultClickAction {@link GuiAction} to resolve when any item is clicked. + */ + public void setDefaultClickAction(@Nullable final GuiAction<@NotNull ClickInventoryEvent> defaultClickAction) { + this.defaultClickAction = defaultClickAction; + } + + /** + * Sets the {@link GuiAction} of a default click on any item on the top part of the GUI. + * Top inventory being for example chests etc, instead of the {@link Player} inventory. + * See {@link ClickInventoryEvent}. + * + * @param defaultTopClickAction {@link GuiAction} to resolve when clicking on the top inventory. + */ + public void setDefaultTopClickAction(@Nullable final GuiAction<@NotNull ClickInventoryEvent> defaultTopClickAction) { + this.defaultTopClickAction = defaultTopClickAction; + } + + public void setPlayerInventoryAction(@Nullable final GuiAction<@NotNull ClickInventoryEvent> playerInventoryAction) { + this.playerInventoryAction = playerInventoryAction; + } + + /** + * Sets the {@link GuiAction} to run when clicking on the outside of the inventory. + * See {@link ClickInventoryEvent}. + * + * @param outsideClickAction {@link GuiAction} to resolve when clicking outside of the inventory. + */ + public void setOutsideClickAction(@Nullable final GuiAction outsideClickAction) { + this.outsideClickAction = outsideClickAction; + } + + /** + * Sets the {@link GuiAction} of a default drag action. + * See {@link ClickInventoryEvent.Drag}. + * + * @param dragAction {@link GuiAction} to resolve. + */ + public void setDragAction(@Nullable final GuiAction dragAction) { + this.dragAction = dragAction; + } + + /** + * Sets the {@link GuiAction} to run once the inventory is closed. + * See {@link InteractInventoryEvent.Close}. + * + * @param closeGuiAction {@link GuiAction} to resolve when the inventory is closed. + */ + public void setCloseGuiAction(@Nullable final GuiAction closeGuiAction) { + this.closeGuiAction = closeGuiAction; + } + + /** + * Sets the {@link GuiAction} to run when the GUI opens. + * See {@link InteractInventoryEvent.Open}. + * + * @param openGuiAction {@link GuiAction} to resolve when opening the inventory. + */ + public void setOpenGuiAction(@Nullable final GuiAction openGuiAction) { + this.openGuiAction = openGuiAction; + } + + /** + * Adds a {@link GuiAction} for when clicking on a specific slot. + * See {@link ClickInventoryEvent}. + * + * @param slot The slot that will trigger the {@link GuiAction}. + * @param slotAction {@link GuiAction} to resolve when clicking on specific slots. + */ + public void addSlotAction(final int slot, @Nullable final GuiAction slotAction) { + validateSlot(slot); + slotActions.put(slot, slotAction); + } + + /** + * Alternative method for {@link #addSlotAction(int, GuiAction)} to add a {@link GuiAction} to a specific slot using ROWS and COLUMNS instead of slots. + * See {@link ClickInventoryEvent}. + * + * @param row The row of the slot. + * @param col The column of the slot. + * @param slotAction {@link GuiAction} to resolve when clicking on the slot. + */ + public void addSlotAction(final int row, final int col, @Nullable final GuiAction slotAction) { + addSlotAction(getSlotFromRowCol(row, col), slotAction); + } + + /** + * Gets a specific {@link GuiItem} on the slot. + * + * @param slot The slot of the item. + * @return The {@link GuiItem} on the introduced slot or {@code null} if doesn't exist. + */ + @Nullable + public GuiItem getGuiItem(final int slot) { + return guiItems.get(slot); + } + + /** + * Checks whether or not the GUI is updating. + * + * @return Whether the GUI is updating or not. + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean isUpdating() { + return updating; + } + + /** + * Sets the updating status of the GUI. + * + * @param updating Sets the GUI to the updating status. + */ + public void setUpdating(final boolean updating) { + this.updating = updating; + } + + /** + * Opens the GUI for a {@link Player}. + * + * @param player The {@link Player} to open the GUI to. + */ + public void open(@NotNull final Player player) { + if (player.get(Keys.IS_SLEEPING).isPresent() && player.get(Keys.IS_SLEEPING).get()) return; + + inventory.clear(); + populateGui(); + Sponge.getScheduler().createTaskBuilder().delayTicks(1).execute(() -> { + player.openInventory(inventory); + }).submit(TriumphPlugin.getInstance()); + } + + /** + * Closes the GUI with a {@code 2 tick} delay (to prevent items from being taken from the {@link Inventory}). + * + * @param player The {@link Player} to close the GUI to. + */ + public void close(@NotNull final Player player) { + close(player, true); + } + + /** + * Closes the GUI with a {@code 2 tick} delay (to prevent items from being taken from the {@link Inventory}). + * + * @param player The {@link Player} to close the GUI to. + * @param runCloseAction If should or not run the close action. + */ + public void close(@NotNull final Player player, final boolean runCloseAction) { + Sponge.getScheduler().createTaskBuilder().delayTicks(2).execute(() -> { + this.runCloseAction = runCloseAction; + player.closeInventory(); + this.runCloseAction = true; + }).submit(TriumphPlugin.getInstance()); + } + + /** + * Updates the GUI for all the {@link Inventory} views. + */ + public void update() { + inventory.clear(); + populateGui(); + for (Player viewer : new ArrayList<>(inventory.query(Container.class).getViewers())) { + ((EntityPlayerMP) viewer).sendContainerToPlayer(((EntityPlayerMP) viewer).openContainer); + } + } + + /** + * Updates the title of the GUI. + * This method may cause LAG if used on a loop. + * + * @param title The title to set. + * @return The GUI for easier use when declaring, works like a builder. + */ + @Contract("_ -> this") + @NotNull + public BaseGui updateTitle(@NotNull final String title) { + updating = true; + + final List viewers = new ArrayList<>(inventory.query(Container.class).getViewers()); + + this.inventory = Inventory.builder().of(this.guiType.getInventoryType()).property(InventoryDimension.of(this.guiType.getLimit(), this.rows)).property(InventoryTitle.of(Text.of(title))).build(TriumphPlugin.getInstance()); + + for (final Player player : viewers) { + open(player); + } + + updating = false; + this.title = title; + return this; + } + + /** + * Updates the specified item in the GUI at runtime, without creating a new {@link GuiItem}. + * + * @param slot The slot of the item to update. + * @param itemStack The {@link ItemStack} to replace in the original one in the {@link GuiItem}. + */ + public void updateItem(final int slot, @NotNull final ItemStack itemStack) { + if (!guiItems.containsKey(slot)) return; + final GuiItem guiItem = guiItems.get(slot); + guiItem.setItemStack(itemStack); + inventory.query(SlotIndex.of(slot)).set(guiItem.getItemStack()); + } + + /** + * Alternative {@link #updateItem(int, ItemStack)} that takes ROWS and COLUMNS instead of slots. + * + * @param row The row of the slot. + * @param col The columns of the slot. + * @param itemStack The {@link ItemStack} to replace in the original one in the {@link GuiItem}. + */ + public void updateItem(final int row, final int col, @NotNull final ItemStack itemStack) { + updateItem(getSlotFromRowCol(row, col), itemStack); + } + + /** + * Alternative {@link #updateItem(int, ItemStack)} but creates a new {@link GuiItem}. + * + * @param slot The slot of the item to update. + * @param item The {@link GuiItem} to replace in the original. + */ + public void updateItem(final int slot, @NotNull final GuiItem item) { + if (!guiItems.containsKey(slot)) return; + guiItems.put(slot, item); + inventory.query(SlotIndex.of(slot)).set(item.getItemStack()); + } + + /** + * Alternative {@link #updateItem(int, GuiItem)} that takes ROWS and COLUMNS instead of slots. + * + * @param row The row of the slot. + * @param col The columns of the slot. + * @param item The {@link GuiItem} to replace in the original. + */ + public void updateItem(final int row, final int col, @NotNull final GuiItem item) { + updateItem(getSlotFromRowCol(row, col), item); + } + + /** + * Disable item placement inside the GUI. + * + * @return The BaseGui. + * @author SecretX. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> this") + public BaseGui disableItemPlace() { + interactionModifiers.add(InteractionModifier.PREVENT_ITEM_PLACE); + return this; + } + + /** + * Disable item retrieval inside the GUI. + * + * @return The BaseGui. + * @author SecretX. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> this") + public BaseGui disableItemTake() { + interactionModifiers.add(InteractionModifier.PREVENT_ITEM_TAKE); + return this; + } + + /** + * Disable item swap inside the GUI. + * + * @return The BaseGui. + * @author SecretX. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> this") + public BaseGui disableItemSwap() { + interactionModifiers.add(InteractionModifier.PREVENT_ITEM_SWAP); + return this; + } + + /** + * Disable item drop inside the GUI + * + * @return The BaseGui + * @since 3.0.3. + */ + @NotNull + @Contract(" -> this") + public BaseGui disableItemDrop() { + interactionModifiers.add(InteractionModifier.PREVENT_ITEM_DROP); + return this; + } + + /** + * Disable other GUI actions + * This option pretty much disables creating a clone stack of the item + * + * @return The BaseGui + * @since 3.0.4 + */ + @NotNull + @Contract(" -> this") + public BaseGui disableOtherActions() { + interactionModifiers.add(InteractionModifier.PREVENT_OTHER_ACTIONS); + return this; + } + + /** + * Disable all the modifications of the GUI, making it immutable by player interaction. + * + * @return The BaseGui. + * @author SecretX. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> this") + public BaseGui disableAllInteractions() { + interactionModifiers.addAll(InteractionModifier.VALUES); + return this; + } + + /** + * Allows item placement inside the GUI. + * + * @return The BaseGui. + * @author SecretX. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> this") + public BaseGui enableItemPlace() { + interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_PLACE); + return this; + } + + /** + * Allow items to be taken from the GUI. + * + * @return The BaseGui. + * @author SecretX. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> this") + public BaseGui enableItemTake() { + interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_TAKE); + return this; + } + + /** + * Allows item swap inside the GUI. + * + * @return The BaseGui. + * @author SecretX. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> this") + public BaseGui enableItemSwap() { + interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_SWAP); + return this; + } + + /** + * Allows item drop inside the GUI + * + * @return The BaseGui + * @since 3.0.3 + */ + @NotNull + @Contract(" -> this") + public BaseGui enableItemDrop() { + interactionModifiers.remove(InteractionModifier.PREVENT_ITEM_DROP); + return this; + } + + /** + * Enable other GUI actions + * This option pretty much enables creating a clone stack of the item + * + * @return The BaseGui + * @since 3.0.4 + */ + @NotNull + @Contract(" -> this") + public BaseGui enableOtherActions() { + interactionModifiers.remove(InteractionModifier.PREVENT_OTHER_ACTIONS); + return this; + } + + /** + * Enable all modifications of the GUI, making it completely mutable by player interaction. + * + * @return The BaseGui. + * @author SecretX. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> this") + public BaseGui enableAllInteractions() { + interactionModifiers.clear(); + return this; + } + + /** + * Check if item placement is allowed inside this GUI. + * + * @return True if item placement is allowed for this GUI. + * @author SecretX. + * @since 3.0.0. + */ + public boolean canPlaceItems() { + return !interactionModifiers.contains(InteractionModifier.PREVENT_ITEM_PLACE); + } + + /** + * Check if item retrieval is allowed inside this GUI. + * + * @return True if item retrieval is allowed inside this GUI. + * @author SecretX. + * @since 3.0.0. + */ + public boolean canTakeItems() { + return !interactionModifiers.contains(InteractionModifier.PREVENT_ITEM_TAKE); + } + + /** + * Check if item swap is allowed inside this GUI. + * + * @return True if item swap is allowed for this GUI. + * @author SecretX. + * @since 3.0.0. + */ + public boolean canSwapItems() { + return !interactionModifiers.contains(InteractionModifier.PREVENT_ITEM_SWAP); + } + + /** + * Check if item drop is allowed inside this GUI + * + * @return True if item drop is allowed for this GUI + * @since 3.0.3 + */ + public boolean canDropItems() { + return !interactionModifiers.contains(InteractionModifier.PREVENT_ITEM_DROP); + } + + /** + * Check if any other actions are allowed in this GUI + * + * @return True if other actions are allowed + * @since 3.0.4 + */ + public boolean allowsOtherActions() { + return !interactionModifiers.contains(InteractionModifier.PREVENT_OTHER_ACTIONS); + } + + /** + * Gets the {@link GuiFiller} that it's used for filling up the GUI in specific ways. + * + * @return The {@link GuiFiller}. + */ + @NotNull + public GuiFiller getFiller() { + return filler; + } + + /** + * Gets an immutable {@link Map} with all the GUI items. + * + * @return The {@link Map} with all the {@link #guiItems}. + */ + @NotNull + public Map<@NotNull Integer, @NotNull GuiItem> getGuiItems() { + return guiItems; + } + + /** + * Gets the main {@link Inventory} of this GUI. + * + * @return Gets the {@link Inventory} from the holder. + */ + @NotNull + public Inventory getInventory() { + return inventory; + } + + /** + * Gets the amount of {@link #rows}. + * + * @return The {@link #rows} of the GUI. + */ + public int getRows() { + return rows; + } + + /** + * Gets the {@link GuiType} in use. + * + * @return The {@link GuiType}. + */ + @NotNull + public GuiType guiType() { + return guiType; + } + + /** + * Gets the default click resolver. + */ + @Nullable + GuiAction getDefaultClickAction() { + return defaultClickAction; + } + + /** + * Gets the default top click resolver. + */ + @Nullable + GuiAction getDefaultTopClickAction() { + return defaultTopClickAction; + } + + /** + * Gets the player inventory action. + */ + @Nullable + GuiAction getPlayerInventoryAction() { + return playerInventoryAction; + } + + /** + * Gets the default drag resolver. + */ + @Nullable + GuiAction getDragAction() { + return dragAction; + } + + /** + * Gets the close gui resolver. + */ + @Nullable + GuiAction getCloseGuiAction() { + return closeGuiAction; + } + + /** + * Gets the open gui resolver. + */ + @Nullable + GuiAction getOpenGuiAction() { + return openGuiAction; + } + + /** + * Gets the resolver for the outside click. + */ + @Nullable + GuiAction getOutsideClickAction() { + return outsideClickAction; + } + + /** + * Gets the action for the specified slot. + * + * @param slot The slot clicked. + */ + @Nullable + GuiAction getSlotAction(final int slot) { + return slotActions.get(slot); + } + + /** + * Populates the GUI with it's items. + */ + void populateGui() { + for (final Map.Entry entry : guiItems.entrySet()) { + inventory.query(SlotIndex.of(entry.getKey())).set(entry.getValue().getItemStack()); + } + } + + boolean shouldRunCloseAction() { + return runCloseAction; + } + + boolean shouldRunOpenAction() { + return runOpenAction; + } + + /** + * Gets the slot from the row and column passed. + * + * @param row The row. + * @param col The column. + * @return The slot needed. + */ + int getSlotFromRowCol(final int row, final int col) { + return (col + (row - 1) * 9) - 1; + } + + /** + * Sets the new inventory of the GUI. + * + * @param inventory The new inventory. + */ + public void setInventory(@NotNull final Inventory inventory) { + this.inventory = inventory; + } + + /* + TODO fix this part, find a better solution for using Paper + protected Inventory createRowedInventory(@NotNull final Component title) { + if (VersionHelper.IS_COMPONENT_LEGACY) { + return Bukkit.createInventory(this, this.rows * 9, Legacy.SERIALIZER.serialize(title)); + } + + return inventory = Bukkit.createInventory(this, this.rows * 9, title); + } + + private Inventory createTypedInventory(@NotNull final Component title) { + final InventoryType inventoryType = guiType.getInventoryType(); + if (VersionHelper.IS_COMPONENT_LEGACY) { + return Bukkit.createInventory(this, inventoryType, Legacy.SERIALIZER.serialize(title)); + } + + return Bukkit.createInventory(this, inventoryType, title); + }*/ + + /** + * Checks if the slot introduces is a valid slot. + * + * @param slot The slot to check. + */ + private void validateSlot(final int slot) { + final int limit = guiType.getLimit(); + + if (guiType == GuiType.CHEST) { + if (slot < 0 || slot >= rows * limit) throwInvalidSlot(slot); + return; + } + + if (slot < 0 || slot > limit) throwInvalidSlot(slot); + } + + /** + * Throws an exception if the slot is invalid. + * + * @param slot The specific slot to display in the error message. + */ + private void throwInvalidSlot(final int slot) { + if (guiType == GuiType.CHEST) { + throw new GuiException("Slot " + slot + " is not valid for the gui type - " + guiType.name() + " and rows - " + rows + "!"); + } + + throw new GuiException("Slot " + slot + " is not valid for the gui type - " + guiType.name() + "!"); + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/guis/Gui.java b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/Gui.java new file mode 100644 index 00000000..31d8e884 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/Gui.java @@ -0,0 +1,183 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.guis; + +import dev.triumphteam.gui.builder.gui.PaginatedBuilder; +import dev.triumphteam.gui.builder.gui.ScrollingBuilder; +import dev.triumphteam.gui.builder.gui.SimpleBuilder; +import dev.triumphteam.gui.builder.gui.StorageBuilder; +import dev.triumphteam.gui.components.GuiType; +import dev.triumphteam.gui.components.InteractionModifier; +import dev.triumphteam.gui.components.ScrollType; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * Standard GUI implementation of {@link BaseGui} + */ +public class Gui extends BaseGui { + + /** + * Main constructor for the GUI + * + * @param rows The amount of rows the GUI should have + * @param title The GUI's title using {@link String} + * @param interactionModifiers A set containing the {@link InteractionModifier} this GUI should use + * @author SecretX + * @since 3.0.3 + */ + public Gui(final int rows, @NotNull final String title, @NotNull final Set interactionModifiers) { + super(rows, title, interactionModifiers); + } + + /** + * Alternative constructor that takes both a {@link GuiType} and a set of {@link InteractionModifier} + * + * @param guiType The {@link GuiType} to be used + * @param title The GUI's title using {@link String} + * @param interactionModifiers A set containing the {@link InteractionModifier} this GUI should use + * @author SecretX + * @since 3.0.3 + */ + public Gui(@NotNull final GuiType guiType, @NotNull final String title, @NotNull final Set interactionModifiers) { + super(guiType, title, interactionModifiers); + } + + /** + * Old main constructor for the GUI + * + * @param rows The amount of rows the GUI should have + * @param title The GUI's title + * @deprecated In favor of {@link Gui#Gui(int, String, Set)} + */ + @Deprecated + public Gui(final int rows, @NotNull final String title) { + super(rows, title); + } + + /** + * Alternative constructor that defaults to 1 row + * + * @param title The GUI's title + * @deprecated In favor of {@link Gui#Gui(int, String, Set)} + */ + @Deprecated + public Gui(@NotNull final String title) { + super(1, title); + } + + /** + * Main constructor that takes a {@link GuiType} instead of rows + * + * @param guiType The {@link GuiType} to be used + * @param title The GUI's title + * @deprecated In favor of {@link Gui#Gui(GuiType, String, Set)} + */ + @Deprecated + public Gui(@NotNull final GuiType guiType, @NotNull final String title) { + super(guiType, title); + } + + /** + * Creates a {@link SimpleBuilder} to build a {@link dev.triumphteam.gui.guis.Gui} + * + * @param type The {@link GuiType} to be used + * @return A {@link SimpleBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> new") + public static SimpleBuilder gui(@NotNull final GuiType type) { + return new SimpleBuilder(type); + } + + /** + * Creates a {@link SimpleBuilder} with CHEST as the {@link GuiType} + * + * @return A CHEST {@link SimpleBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract(" -> new") + public static SimpleBuilder gui() { + return gui(GuiType.CHEST); + } + + /** + * Creates a {@link StorageBuilder}. + * + * @return A CHEST {@link StorageBuilder}. + * @since 3.0.0. + */ + @NotNull + @Contract(" -> new") + public static StorageBuilder storage() { + return new StorageBuilder(); + } + + /** + * Creates a {@link PaginatedBuilder} to build a {@link dev.triumphteam.gui.guis.PaginatedGui} + * + * @return A {@link PaginatedBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract(" -> new") + public static PaginatedBuilder paginated() { + return new PaginatedBuilder(); + } + + /** + * Creates a {@link ScrollingBuilder} to build a {@link dev.triumphteam.gui.guis.ScrollingGui} + * + * @param scrollType The {@link ScrollType} to be used by the GUI + * @return A {@link ScrollingBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract("_ -> new") + public static ScrollingBuilder scrolling(@NotNull final ScrollType scrollType) { + return new ScrollingBuilder(scrollType); + } + + /** + * Creates a {@link ScrollingBuilder} with VERTICAL as the {@link ScrollType} + * + * @return A vertical {@link SimpleBuilder} + * @since 3.0.0 + */ + @NotNull + @Contract(" -> new") + public static ScrollingBuilder scrolling() { + return scrolling(ScrollType.VERTICAL); + } + + @Override + public int compareTo(Object o) { + return 0; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/guis/GuiItem.java b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/GuiItem.java new file mode 100644 index 00000000..3ee644a0 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/GuiItem.java @@ -0,0 +1,140 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.guis; + +import dev.triumphteam.gui.components.GuiAction; +import dev.triumphteam.gui.components.util.ItemNbt; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.api.event.item.inventory.ClickInventoryEvent; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.inventory.ItemStack; + +import java.util.UUID; + +/** + * GuiItem represents the {@link ItemStack} on the {@link Inventory} + */ +@SuppressWarnings("unused") +public class GuiItem { + + // Action to do when clicking on the item + private GuiAction action; + + // The ItemStack of the GuiItem + private ItemStack itemStack; + + // Random UUID to identify the item when clicking + private final UUID uuid = UUID.randomUUID(); + + /** + * Main constructor of the GuiItem + * + * @param itemStack The {@link ItemStack} to be used + * @param action The {@link GuiAction} to run when clicking on the Item + */ + public GuiItem(@NotNull final ItemStack itemStack, @Nullable final GuiAction<@NotNull ClickInventoryEvent> action) { + Validate.notNull(itemStack, "The ItemStack for the GUI Item cannot be null!"); + + this.action = action; + + // Sets the UUID to an NBT tag to be identifiable later + this.itemStack = ItemNbt.setString(itemStack, "mf-gui", uuid.toString()); + } + + /** + * Secondary constructor with no action + * + * @param itemStack The ItemStack to be used + */ + public GuiItem(@NotNull final ItemStack itemStack) { + this(itemStack, null); + } + + /** + * Alternate constructor that takes {@link Material} instead of an {@link ItemStack} but without a {@link GuiAction} + * + * @param material The {@link Material} to be used when invoking class + */ + public GuiItem(@NotNull final ItemType material) { + this(ItemStack.of(material), null); + } + + /** + * Alternate constructor that takes {@link Material} instead of an {@link ItemStack} + * + * @param material The {@code Material} to be used when invoking class + * @param action The {@link GuiAction} should be passed on {@link InventoryClickEvent} + */ + public GuiItem(@NotNull final ItemType material, @Nullable final GuiAction<@NotNull ClickInventoryEvent> action) { + this(ItemStack.of(material), action); + } + + /** + * Replaces the {@link ItemStack} of the GUI Item + * + * @param itemStack The new {@link ItemStack} + */ + public void setItemStack(@NotNull final ItemStack itemStack) { + Validate.notNull(itemStack, "The ItemStack for the GUI Item cannot be null!"); + this.itemStack = ItemNbt.setString(itemStack, "mf-gui", uuid.toString()); + } + + /** + * Replaces the {@link GuiAction} of the current GUI Item + * + * @param action The new {@link GuiAction} to set + */ + public void setAction(@Nullable final GuiAction<@NotNull ClickInventoryEvent> action) { + this.action = action; + } + + /** + * Gets the GuiItem's {@link ItemStack} + * + * @return The {@link ItemStack} + */ + @NotNull + public ItemStack getItemStack() { + return itemStack; + } + + /** + * Gets the random {@link UUID} that was generated when the GuiItem was made + */ + @NotNull + UUID getUuid() { + return uuid; + } + + /** + * Gets the {@link GuiAction} to do when the player clicks on it + */ + @Nullable + GuiAction getAction() { + return action; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/guis/GuiListener.java b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/GuiListener.java new file mode 100644 index 00000000..661bf5f7 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/GuiListener.java @@ -0,0 +1,208 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.guis; + +import dev.triumphteam.gui.components.GuiAction; +import dev.triumphteam.gui.components.util.ItemNbt; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.item.inventory.ClickInventoryEvent; +import org.spongepowered.api.event.item.inventory.ClickInventoryEvent.Drop.Outside; +import org.spongepowered.api.event.item.inventory.InteractInventoryEvent; +import org.spongepowered.api.item.inventory.InventoryArchetypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.Slot; +import org.spongepowered.api.item.inventory.property.SlotIndex; +import org.spongepowered.common.item.inventory.custom.CustomContainer; + +public final class GuiListener { + + /** + * Handles what happens when a player clicks on the GUI + * + * @param event The ClickInventoryEvent + */ + @Listener(order = Order.FIRST, beforeModifications = true) + public void onGuiClick(final ClickInventoryEvent event) { + if (!(event.getTargetInventory() instanceof CustomContainer) || !((CustomContainer) event.getTargetInventory()).inv.getArchetype().getId().contains("triumph:")) { + return; + } + + // Gui + final BaseGui gui = (BaseGui) ((CustomContainer) event.getTargetInventory()).inv.getProperties().get("triumph"); + if (gui == null) { + return; + } + + if (event instanceof ClickInventoryEvent.Drop.Outside) { + // Executes the outside click action + final GuiAction outsideClickAction = gui.getOutsideClickAction(); + if (outsideClickAction != null && event.getTargetInventory() == null) { + outsideClickAction.execute((Outside) event); + return; + } + return; + } + + // Default click action and checks weather or not there is a default action and executes it + final GuiAction defaultTopClick = gui.getDefaultTopClickAction(); + if (defaultTopClick != null && event.getTargetInventory().getArchetype() != InventoryArchetypes.PLAYER) { + defaultTopClick.execute(event); + } + + // Default click action and checks weather or not there is a default action and executes it + final GuiAction playerInventoryClick = gui.getPlayerInventoryAction(); + if (playerInventoryClick != null && event.getTargetInventory().getArchetype() == InventoryArchetypes.PLAYER) { + playerInventoryClick.execute(event); + } + + // Default click action and checks weather or not there is a default action and executes it + final GuiAction defaultClick = gui.getDefaultClickAction(); + if (defaultClick != null) { + defaultClick.execute(event); + } + + // Slot action and checks weather or not there is a slot action and executes it + final Slot slot = event.getSlot().orElse(null); + if (slot != null) { + final int slotIndex = slot.getInventoryProperty(SlotIndex.class).get().getValue(); + final GuiAction slotAction = gui.getSlotAction(slotIndex); + if (slotAction != null && event.getTargetInventory().getArchetype() != InventoryArchetypes.PLAYER) { + slotAction.execute(event); + } + + // Checks whether it's a paginated gui or not + GuiItem guiItem = null; + if (gui instanceof PaginatedGui) { + final PaginatedGui paginatedGui = (PaginatedGui) gui; + + // Gets the gui item from the added items or the page items + guiItem = paginatedGui.getGuiItem(slotIndex); + if (guiItem == null) guiItem = paginatedGui.getPageItem(slotIndex); + + } else { + // The clicked GUI Item + guiItem = gui.getGuiItem(slotIndex); + } + if (guiItem == null) { + return; + } + final ItemStack itemStack = guiItem.getItemStack(); + + if (itemStack == null) { + return; + } + + if (!isGuiItem(itemStack, guiItem)) return; + + // Executes the action of the item + final GuiAction itemAction = guiItem.getAction(); + if (itemAction != null) itemAction.execute(event); + } + } + + /** + * Handles what happens when a player clicks on the GUI + * + * @param event The ClickInventoryEvent + */ + @Listener(order = Order.FIRST, beforeModifications = true) + public void onGuiDrag(final ClickInventoryEvent.Drag event) { + if (!(event.getTargetInventory() instanceof CustomContainer) || !((CustomContainer) event.getTargetInventory()).inv.getArchetype().getId().contains("triumph:")) { + return; + } + // Gui + final BaseGui gui = (BaseGui) ((CustomContainer) event.getTargetInventory()).inv.getProperties().get("triumph"); + if (gui == null) { + return; + } + + // Default click action and checks weather or not there is a default action and executes it + final GuiAction dragAction = gui.getDragAction(); + if (dragAction != null) dragAction.execute(event); + } + + /** + * Handles what happens when the GUI is closed + * + * @param event The InteractInventoryEvent.Close + */ + @Listener(order = Order.FIRST, beforeModifications = true) + public void onGuiClose(final InteractInventoryEvent.Close event) { + if (!(event.getTargetInventory() instanceof CustomContainer) || !((CustomContainer) event.getTargetInventory()).inv.getArchetype().getId().contains("triumph:")) { + return; + } + // GUI + final BaseGui gui = (BaseGui) ((CustomContainer) event.getTargetInventory()).inv.getProperties().get("triumph"); + if (gui == null) { + return; + } + + // The GUI action for closing + final GuiAction closeAction = gui.getCloseGuiAction(); + + // Checks if there is or not an action set and executes it + if (closeAction != null && !gui.isUpdating() && gui.shouldRunCloseAction()) closeAction.execute(event); + } + + /** + * Handles what happens when the GUI is opened + * + * @param event The InteractInventoryEvent.Open + */ + @Listener(order = Order.FIRST, beforeModifications = true) + public void onGuiOpen(final InteractInventoryEvent.Open event) { + if (!(event.getTargetInventory() instanceof CustomContainer) || !((CustomContainer) event.getTargetInventory()).inv.getArchetype().getId().contains("triumph:")) { + return; + } + // GUI + final BaseGui gui = (BaseGui) ((CustomContainer) event.getTargetInventory()).inv.getProperties().get("triumph"); + if (gui == null) { + return; + } + + // The GUI action for opening + final GuiAction openAction = gui.getOpenGuiAction(); + + // Checks if there is or not an action set and executes it + if (openAction != null && !gui.isUpdating()) openAction.execute(event); + } + + /** + * Checks if the item is or not a GUI item + * + * @param currentItem The current item clicked + * @param guiItem The GUI item in the slot + * @return Whether it is or not a GUI item + */ + private boolean isGuiItem(@Nullable final ItemStack currentItem, @Nullable final GuiItem guiItem) { + if (currentItem == null || guiItem == null) return false; + // Checks whether the Item is truly a GUI Item + final String nbt = ItemNbt.getString(currentItem, "mf-gui"); + if (nbt == null) return false; + return nbt.equals(guiItem.getUuid().toString()); + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/guis/InteractionModifierListener.java b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/InteractionModifierListener.java new file mode 100644 index 00000000..7d6b32ad --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/InteractionModifierListener.java @@ -0,0 +1,221 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.guis; + +import com.google.common.base.Preconditions; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.item.inventory.ChangeInventoryEvent; +import org.spongepowered.api.event.item.inventory.ClickInventoryEvent; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.Inventory; +import org.spongepowered.api.item.inventory.InventoryArchetypes; +import org.spongepowered.api.item.inventory.property.AbstractInventoryProperty; +import org.spongepowered.api.item.inventory.transaction.SlotTransaction; + +/** + * Listener that apply default GUI {@link dev.triumphteam.gui.components.InteractionModifier InteractionModifier}s to all GUIs + * + * @author SecretX + * @since 3.0.0 + */ +public final class InteractionModifierListener { + + /** + * Handles any click on GUIs, applying all {@link dev.triumphteam.gui.components.InteractionModifier InteractionModifier} as required + * + * @param event The ClickInventoryEvent + * @author SecretX + * @since 3.0.0 + */ + @Listener(order = Order.FIRST, beforeModifications = true) + public void onGuiClick(final ClickInventoryEvent event) { + if (!(event.getTargetInventory().getArchetype().getId().contains("triumph:"))) return; + + // Gui + final BaseGui gui = (BaseGui) event.getTargetInventory().getInventoryProperty(AbstractInventoryProperty.class).orElse(null); + if (gui == null) { + return; + } + + // if player is trying to do a disabled action, cancel it + if ((!gui.canPlaceItems() && isPlaceItemEvent(event)) || (!gui.canTakeItems() && isTakeItemEvent(event)) || (!gui.canSwapItems() && isSwapItemEvent(event)) || (!gui.canDropItems() && isDropItemEvent(event)) || (!gui.allowsOtherActions() && isOtherEvent(event))) { + event.setCancelled(true); + } + } + + /** + * Handles any item drag on GUIs, applying all {@link dev.triumphteam.gui.components.InteractionModifier InteractionModifier} as required + * + * @param event The InventoryDragEvent + * @author SecretX + * @since 3.0.0 + */ + @Listener(order = Order.FIRST, beforeModifications = true) + public void onGuiDrag(final ClickInventoryEvent.Drag event) { + if (!(event.getTargetInventory().getArchetype().getId().contains("triumph:"))) return; + + // Gui + final BaseGui gui = (BaseGui) event.getTargetInventory().getInventoryProperty(AbstractInventoryProperty.class).orElse(null); + if (gui == null) { + return; + } + + // if players are allowed to place items on the GUI, or player is not dragging on GUI, return + if (gui.canPlaceItems() || !isDraggingOnGui(event)) return; + + // cancel the interaction + event.setCancelled(true); + } + + /** + * Checks if what is happening on the {@link ClickInventoryEvent} is take an item from the GUI + * + * @param event The ClickInventoryEvent + * @return True if the {@link ClickInventoryEvent} is for taking an item from the GUI + * @author SecretX + * @since 3.0.0 + */ + private boolean isTakeItemEvent(final ChangeInventoryEvent event) { + Preconditions.checkNotNull(event, "event cannot be null"); + + final Inventory inventory = event.getTargetInventory(); + final Inventory clickedInventory = event.getTargetInventory(); + + // magic logic, simplified version of https://paste.helpch.at/tizivomeco.cpp + if (clickedInventory != null && clickedInventory.getArchetype() == InventoryArchetypes.PLAYER || inventory.getArchetype() == InventoryArchetypes.PLAYER) { + return false; + } + + for (SlotTransaction slotTransaction : event.getTransactions()) { + if (slotTransaction.getOriginal().getType() != ItemTypes.NONE && slotTransaction.getFinal().getType() == ItemTypes.NONE) { + return true; + } + } + return false; + } + + /** + * Checks if what is happening on the {@link ClickInventoryEvent} is place an item on the GUI + * + * @param event The ClickInventoryEvent + * @return True if the {@link ClickInventoryEvent} is for placing an item from the GUI + * @author SecretX + * @since 3.0.0 + */ + private boolean isPlaceItemEvent(final ChangeInventoryEvent event) { + Preconditions.checkNotNull(event, "event cannot be null"); + + final Inventory inventory = event.getTargetInventory(); + final Inventory clickedInventory = event.getTargetInventory(); + + if (clickedInventory != null && clickedInventory.getArchetype() == InventoryArchetypes.PLAYER || inventory.getArchetype() == InventoryArchetypes.PLAYER) { + return false; + } + + // shift click on item in player inventory + if (event instanceof ChangeInventoryEvent.Transfer + && clickedInventory != null + && inventory.getArchetype() != clickedInventory.getArchetype()) { + return true; + } + + // normal click on gui empty slot with item on cursor + for (SlotTransaction slotTransaction : event.getTransactions()) { + if (slotTransaction.getOriginal().getType() == ItemTypes.NONE && slotTransaction.getFinal().getType() != ItemTypes.NONE) { + return true; + } + } + return false; + } + + /** + * Checks if what is happening on the {@link ClickInventoryEvent} is swap any item with an item from the GUI + * + * @param event The ClickInventoryEvent + * @return True if the {@link ClickInventoryEvent} is for swapping any item with an item from the GUI + * @author SecretX + * @since 3.0.0 + */ + private boolean isSwapItemEvent(final ClickInventoryEvent event) { + Preconditions.checkNotNull(event, "event cannot be null"); + + final Inventory inventory = event.getTargetInventory(); + final Inventory clickedInventory = event.getTargetInventory(); + + if (clickedInventory != null && clickedInventory.getArchetype() == InventoryArchetypes.PLAYER || inventory.getArchetype() == InventoryArchetypes.PLAYER) { + return false; + } + + for (SlotTransaction slotTransaction : event.getTransactions()) { + if (slotTransaction.getOriginal().getType() == ItemTypes.NONE && slotTransaction.getFinal().getType() != ItemTypes.NONE) { + return true; + } + } + return false; + } + + private boolean isDropItemEvent(final ClickInventoryEvent event) { + Preconditions.checkNotNull(event, "event cannot be null"); + + final Inventory inventory = event.getTargetInventory(); + final Inventory clickedInventory = event.getTargetInventory(); + + if (clickedInventory != null && clickedInventory.getArchetype() == InventoryArchetypes.PLAYER || inventory.getArchetype() == InventoryArchetypes.PLAYER) { + return false; + } + + return true; + } + + private boolean isOtherEvent(final ClickInventoryEvent event) { + Preconditions.checkNotNull(event, "event cannot be null"); + + final Inventory inventory = event.getTargetInventory(); + final Inventory clickedInventory = event.getTargetInventory(); + + if (clickedInventory != null && clickedInventory.getArchetype() == InventoryArchetypes.PLAYER || inventory.getArchetype() == InventoryArchetypes.PLAYER) { + return false; + } + + return true; + } + + /** + * Checks if any item is being dragged on the GUI + * + * @param event The InventoryDragEvent + * @return True if the {@link InventoryDragEvent} is for dragging an item inside the GUI + * @author SecretX + * @since 3.0.0 + */ + private boolean isDraggingOnGui(final ClickInventoryEvent.Drag event) { + Preconditions.checkNotNull(event, "event cannot be null"); + /* TODO + final int topSlots = event.getView().getTopInventory().getSize(); + // is dragging on any top inventory slot + return event.getRawSlots().stream().anyMatch(slot -> slot < topSlots);*/ + return false; + } +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/guis/PaginatedGui.java b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/PaginatedGui.java new file mode 100644 index 00000000..a9c8bc1d --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/PaginatedGui.java @@ -0,0 +1,514 @@ +/** + * MIT License + *

+ * Copyright (c) 2021 TriumphTeam + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.guis; + +import dev.triumphteam.gui.TriumphPlugin; +import dev.triumphteam.gui.components.GuiType; +import dev.triumphteam.gui.components.InteractionModifier; +import net.minecraft.entity.player.EntityPlayerMP; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.item.inventory.Container; +import org.spongepowered.api.item.inventory.Inventory; +import org.spongepowered.api.item.inventory.InventoryArchetypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.property.InventoryCapacity; +import org.spongepowered.api.item.inventory.property.InventoryTitle; +import org.spongepowered.api.item.inventory.property.SlotIndex; +import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult; +import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult.Type; +import org.spongepowered.api.text.Text; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * GUI that allows you to have multiple pages + */ +@SuppressWarnings("unused") +public class PaginatedGui extends BaseGui { + + // List with all the page items + private final List pageItems = new ArrayList<>(); + // Saves the current page items and it's slot + private final Map currentPage; + + private int pageSize; + private int pageNum = 1; + + /** + * Main constructor to provide a way to create PaginatedGui + * + * @param rows The amount of rows the GUI should have + * @param pageSize The page size. + * @param title The GUI's title using {@link String} + * @param interactionModifiers A set containing what {@link InteractionModifier} this GUI should have + * @author SecretX + * @since 3.0.3 + */ + public PaginatedGui(final int rows, final int pageSize, @NotNull final String title, @NotNull final Set interactionModifiers) { + super(rows, title, interactionModifiers); + this.pageSize = pageSize; + int inventorySize = rows * 9; + this.currentPage = new LinkedHashMap<>(inventorySize); + } + + /** + * Old main constructor of the PaginatedGui + * + * @param rows The rows the GUI should have + * @param pageSize The pageSize + * @param title The GUI's title + * @deprecated In favor of {@link PaginatedGui#PaginatedGui(int, int, String, Set)} + */ + @Deprecated + public PaginatedGui(final int rows, final int pageSize, @NotNull final String title) { + super(rows, title); + this.pageSize = pageSize; + int inventorySize = rows * 9; + this.currentPage = new LinkedHashMap<>(inventorySize); + } + + /** + * Alternative constructor that doesn't require the {@link #pageSize} to be defined + * + * @param rows The rows the GUI should have + * @param title The GUI's title + * @deprecated In favor of {@link PaginatedGui#PaginatedGui(int, int, String, Set)} + */ + @Deprecated + public PaginatedGui(final int rows, @NotNull final String title) { + this(rows, 0, title); + } + + /** + * Alternative constructor that only requires title + * + * @param title The GUI's title + * @deprecated In favor of {@link PaginatedGui#PaginatedGui(int, int, String, Set)} + */ + @Deprecated + public PaginatedGui(@NotNull final String title) { + this(2, title); + } + + /** + * Sets the page size + * + * @param pageSize The new page size + * @return The GUI for easier use when declaring, works like a builder + */ + public BaseGui setPageSize(final int pageSize) { + this.pageSize = pageSize; + return this; + } + + /** + * Adds an {@link GuiItem} to the next available slot in the page area + * + * @param item The {@link GuiItem} to add to the page + */ + public void addItem(@NotNull final GuiItem item) { + pageItems.add(item); + } + + /** + * Overridden {@link BaseGui#addItem(GuiItem...)} to add the items to the page instead + * + * @param items Varargs for specifying the {@link GuiItem}s + */ + @Override + public void addItem(@NotNull final GuiItem... items) { + pageItems.addAll(Arrays.asList(items)); + } + + /** + * Overridden {@link BaseGui#update()} to use the paginated open + */ + @Override + public void update() { + getInventory().clear(); + populateGui(); + + updatePage(); + } + + /** + * Updates the page {@link GuiItem} on the slot in the page + * Can get the slot from {@link InventoryClickEvent#getSlot()} + * + * @param slot The slot of the item to update + * @param itemStack The new {@link ItemStack} + */ + public void updatePageItem(final int slot, @NotNull final ItemStack itemStack) { + if (!currentPage.containsKey(slot)) return; + final GuiItem guiItem = currentPage.get(slot); + guiItem.setItemStack(itemStack); + getInventory().query(SlotIndex.of(slot)).set(guiItem.getItemStack()); + } + + /** + * Alternative {@link #updatePageItem(int, ItemStack)} that uses ROWS and COLUMNS instead + * + * @param row The row of the slot + * @param col The columns of the slot + * @param itemStack The new {@link ItemStack} + */ + public void updatePageItem(final int row, final int col, @NotNull final ItemStack itemStack) { + updateItem(getSlotFromRowCol(row, col), itemStack); + } + + /** + * Alternative {@link #updatePageItem(int, ItemStack)} that uses {@link GuiItem} instead + * + * @param slot The slot of the item to update + * @param item The new ItemStack + */ + public void updatePageItem(final int slot, @NotNull final GuiItem item) { + if (!currentPage.containsKey(slot)) return; + // Gets the old item and its index on the main items list + final GuiItem oldItem = currentPage.get(slot); + final int index = pageItems.indexOf(currentPage.get(slot)); + + // Updates both lists and inventory + currentPage.put(slot, item); + pageItems.set(index, item); + getInventory().query(SlotIndex.of(slot)).set(item.getItemStack()); + } + + /** + * Alternative {@link #updatePageItem(int, GuiItem)} that uses ROWS and COLUMNS instead + * + * @param row The row of the slot + * @param col The columns of the slot + * @param item The new {@link GuiItem} + */ + public void updatePageItem(final int row, final int col, @NotNull final GuiItem item) { + updateItem(getSlotFromRowCol(row, col), item); + } + + /** + * Removes a given {@link GuiItem} from the page. + * + * @param item The {@link GuiItem} to remove. + */ + public void removePageItem(@NotNull final GuiItem item) { + pageItems.remove(item); + updatePage(); + } + + /** + * Removes a given {@link ItemStack} from the page. + * + * @param item The {@link ItemStack} to remove. + */ + public void removePageItem(@NotNull final ItemStack item) { + final Optional guiItem = pageItems.stream().filter(it -> it.getItemStack().equals(item)).findFirst(); + guiItem.ifPresent(this::removePageItem); + } + + /** + * Overrides {@link BaseGui#open(HumanEntity)} to use the paginated populator instead + * + * @param player The {@link HumanEntity} to open the GUI to + */ + @Override + public void open(@NotNull final Player player) { + open(player, 1); + } + + /** + * Specific open method for the Paginated GUI + * Uses {@link #populatePage()} + * + * @param player The {@link HumanEntity} to open it to + * @param openPage The specific page to open at + */ + public void open(@NotNull final Player player, final int openPage) { + if (player.get(Keys.IS_SLEEPING).isPresent() && player.get(Keys.IS_SLEEPING).get()) return; + if (openPage <= getPagesNum() || openPage > 0) pageNum = openPage; + + getInventory().clear(); + currentPage.clear(); + + populateGui(); + + if (pageSize == 0) pageSize = calculatePageSize(); + + populatePage(); + + Sponge.getScheduler().createTaskBuilder().delayTicks(1).execute(() -> { + player.openInventory(getInventory()); + }).submit(TriumphPlugin.getInstance()); + } + + /** + * Overrides {@link BaseGui#updateTitle(String)} to use the paginated populator instead + * Updates the title of the GUI + * This method may cause LAG if used on a loop + * + * @param title The title to set + * @return The GUI for easier use when declaring, works like a builder + */ + @Override + @NotNull + public BaseGui updateTitle(@NotNull final String title) { + setUpdating(true); + + final List viewers = new ArrayList<>(getInventory().query(Container.class).getViewers()); + + final Inventory inventory = Inventory.builder().of(GuiType.CHEST.getInventoryType()).property(InventoryCapacity.of(getInventory().capacity())).property(InventoryTitle.of(Text.of(title))).build(TriumphPlugin.getInstance()); + setInventory(inventory); + + for (final Player player : viewers) { + open(player, getPageNum()); + } + + setUpdating(false); + + return this; + } + + /** + * Gets an immutable {@link Map} with all the current pages items + * + * @return The {@link Map} with all the {@link #currentPage} + */ + @NotNull + public Map<@NotNull Integer, @NotNull GuiItem> getCurrentPageItems() { + return Collections.unmodifiableMap(currentPage); + } + + /** + * Gets an immutable {@link List} with all the page items added to the GUI + * + * @return The {@link List} with all the {@link #pageItems} + */ + @NotNull + public List<@NotNull GuiItem> getPageItems() { + return Collections.unmodifiableList(pageItems); + } + + + /** + * Gets the current page number + * + * @return The current page number + */ + public int getCurrentPageNum() { + return pageNum; + } + + /** + * Gets the next page number + * + * @return The next page number or {@link #pageNum} if no next is present + */ + public int getNextPageNum() { + if (pageNum + 1 > getPagesNum()) return pageNum; + return pageNum + 1; + } + + /** + * Gets the previous page number + * + * @return The previous page number or {@link #pageNum} if no previous is present + */ + public int getPrevPageNum() { + if (pageNum - 1 == 0) return pageNum; + return pageNum - 1; + } + + /** + * Goes to the next page + * + * @return False if there is no next page. + */ + public boolean next() { + if (pageNum + 1 > getPagesNum()) return false; + + pageNum++; + updatePage(); + return true; + } + + /** + * Goes to the previous page if possible + * + * @return False if there is no previous page. + */ + public boolean previous() { + if (pageNum - 1 == 0) return false; + + pageNum--; + updatePage(); + return true; + } + + /** + * Gets the page item for the GUI listener + * + * @param slot The slot to get + * @return The GuiItem on that slot + */ + GuiItem getPageItem(final int slot) { + return currentPage.get(slot); + } + + /** + * Gets the items in the page + * + * @param givenPage The page to get + * @return A list with all the page items + */ + private List getPageNum(final int givenPage) { + final int page = givenPage - 1; + + final List guiPage = new ArrayList<>(); + + int max = ((page * pageSize) + pageSize); + if (max > pageItems.size()) max = pageItems.size(); + + for (int i = page * pageSize; i < max; i++) { + guiPage.add(pageItems.get(i)); + } + + return guiPage; + } + + /** + * Gets the number of pages the GUI has + * + * @return The pages number + */ + public int getPagesNum() { + return (int) Math.ceil((double) pageItems.size() / pageSize); + } + + /** + * Populates the inventory with the page items + */ + private void populatePage() { + // Adds the paginated items to the page + final List guiItems = getPageNum(pageNum); + for (int slot = 0; slot < getRows() * 9; slot++) { + if (guiItems.size() < slot + 1) { + break; + } + final GuiItem guiItem = guiItems.get(slot); + currentPage.put(slot, guiItem); + getInventory().query(SlotIndex.of(slot)).set(guiItem.getItemStack()); + } + } + + /** + * Gets the current page items to be used on other gui types + * + * @return The {@link Map} with all the {@link #currentPage} + */ + Map getMutableCurrentPageItems() { + return currentPage; + } + + /** + * Clears the page content + */ + void clearPage() { + for (Map.Entry entry : currentPage.entrySet()) { + getInventory().query(SlotIndex.of(entry.getKey())).set(ItemStack.empty()); + } + } + + /** + * Clears all previously added page items + */ + public void clearPageItems(final boolean update) { + pageItems.clear(); + if (update) update(); + } + + public void clearPageItems() { + clearPageItems(false); + } + + + /** + * Gets the page size + * + * @return The page size + */ + int getPageSize() { + return pageSize; + } + + /** + * Gets the page number + * + * @return The current page number + */ + int getPageNum() { + return pageNum; + } + + /** + * Sets the page number + * + * @param pageNum Sets the current page to be the specified number + */ + void setPageNum(final int pageNum) { + this.pageNum = pageNum; + } + + /** + * Updates the page content + */ + void updatePage() { + clearPage(); + populatePage(); + } + + /** + * Calculates the size of the give page + * + * @return The page size + */ + int calculatePageSize() { + return getInventory().capacity(); + } + + @Override + public int compareTo(Object o) { + // TODO + return 0; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/guis/ScrollingGui.java b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/ScrollingGui.java new file mode 100644 index 00000000..ab8e3165 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/ScrollingGui.java @@ -0,0 +1,323 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.guis; + +import dev.triumphteam.gui.TriumphPlugin; +import dev.triumphteam.gui.components.InteractionModifier; +import dev.triumphteam.gui.components.ScrollType; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.item.inventory.property.SlotIndex; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * GUI that allows you to scroll through items + */ +@SuppressWarnings("unused") +public class ScrollingGui extends PaginatedGui { + + private final ScrollType scrollType; + private int scrollSize = 0; + + /** + * Main constructor of the Scrolling GUI + * + * @param rows The rows the GUI should have + * @param pageSize The Page size + * @param title The title using {@link String} + * @param scrollType The {@link ScrollType} + * @param interactionModifiers A set containing the {@link InteractionModifier} this GUI should use + * @since 3.0.3 + * @author SecretX + */ + public ScrollingGui(final int rows, final int pageSize, @NotNull final String title, @NotNull final ScrollType scrollType, @NotNull final Set interactionModifiers) { + super(rows, pageSize, title, interactionModifiers); + + this.scrollType = scrollType; + } + + /** + * Main constructor of the Scrolling GUI + * + * @param rows The rows the GUI should have + * @param pageSize The Page size + * @param title The GUI's title + * @param scrollType The {@link ScrollType} + * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} + */ + @Deprecated + public ScrollingGui(final int rows, final int pageSize, @NotNull final String title, @NotNull final ScrollType scrollType) { + super(rows, pageSize, title); + this.scrollType = scrollType; + } + + /** + * Alternative constructor that doesn't require the {@link ScrollType} + * + * @param rows The rows the GUI should have + * @param pageSize The Page size + * @param title The GUI's title + * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} + */ + @Deprecated + public ScrollingGui(final int rows, final int pageSize, @NotNull final String title) { + this(rows, pageSize, title, ScrollType.VERTICAL); + } + + /** + * Alternative constructor that doesn't require the {@link ScrollType} or page size + * + * @param rows The rows the GUI should have + * @param title The GUI's title + * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} + */ + @Deprecated + public ScrollingGui(final int rows, @NotNull final String title) { + this(rows, 0, title, ScrollType.VERTICAL); + } + + /** + * Alternative constructor that doesn't require the page size + * + * @param rows The rows the GUI should have + * @param title The GUI's title + * @param scrollType The {@link ScrollType} + * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} + */ + @Deprecated + public ScrollingGui(final int rows, @NotNull final String title, @NotNull final ScrollType scrollType) { + this(rows, 0, title, scrollType); + } + + /** + * Alternative constructor that only requires title + * + * @param title The GUI's title + * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} + */ + @Deprecated + public ScrollingGui(@NotNull final String title) { + this(2, title); + } + + /** + * Alternative constructor that doesn't require the rows or page size + * + * @param title The GUI's title + * @param scrollType The {@link ScrollType} + * @deprecated In favor of {@link ScrollingGui#ScrollingGui(int, int, String, ScrollType, Set)} + */ + @Deprecated + public ScrollingGui(@NotNull final String title, @NotNull final ScrollType scrollType) { + this(2, title, scrollType); + } + + /** + * Overrides {@link PaginatedGui#next()} to make it work with the specific scrolls + */ + @Override + public boolean next() { + if (getPageNum() * scrollSize + getPageSize() > getPageItems().size() + scrollSize) return false; + + setPageNum(getPageNum() + 1); + updatePage(); + return true; + } + + /** + * Overrides {@link PaginatedGui#previous()} to make it work with the specific scrolls + */ + @Override + public boolean previous() { + if (getPageNum() - 1 == 0) return false; + + setPageNum(getPageNum() - 1); + updatePage(); + return true; + } + + /** + * Overrides {@link PaginatedGui#open(HumanEntity)} to make it work with the specific scrolls + * + * @param player The {@link HumanEntity} to open the GUI to + */ + @Override + public void open(@NotNull final Player player) { + open(player, 1); + } + + /** + * Overrides {@link PaginatedGui#open(HumanEntity, int)} to make it work with the specific scrolls + * + * @param player The {@link HumanEntity} to open the GUI to + * @param openPage The page to open on + */ + @Override + public void open(@NotNull final Player player, final int openPage) { + if (player.get(Keys.IS_SLEEPING).get()) return; + getInventory().clear(); + getMutableCurrentPageItems().clear(); + + populateGui(); + + if (getPageSize() == 0) setPageSize(calculatePageSize()); + if (scrollSize == 0) scrollSize = calculateScrollSize(); + if (openPage > 0 && (openPage * scrollSize + getPageSize() <= getPageItems().size() + scrollSize)) { + setPageNum(openPage); + } + + populatePage(); + + Sponge.getScheduler().createTaskBuilder().delayTicks(1).execute(() -> { + player.openInventory(getInventory()); + }).submit(TriumphPlugin.getInstance()); + } + + /** + * Overrides {@link PaginatedGui#updatePage()} to make it work with the specific scrolls + */ + @Override + void updatePage() { + clearPage(); + populatePage(); + } + + /** + * Fills the page with the items + */ + private void populatePage() { + // Adds the paginated items to the page + for (final GuiItem guiItem : getPage(getPageNum())) { + if (scrollType == ScrollType.HORIZONTAL) { + putItemHorizontally(guiItem); + continue; + } + + putItemVertically(guiItem); + } + } + + /** + * Calculates the size of each scroll + * + * @return The size of he scroll + */ + private int calculateScrollSize() { + int counter = 0; + + if (scrollType == ScrollType.VERTICAL) { + boolean foundCol = false; + + for (int row = 1; row <= getRows(); row++) { + for (int col = 1; col <= 9; col++) { + final int slot = getSlotFromRowCol(row, col); + if (getInventory().query(SlotIndex.of(slot)) == null) { + if (!foundCol) foundCol = true; + counter++; + } + } + + if (foundCol) return counter; + } + + return counter; + } + + boolean foundRow = false; + + for (int col = 1; col <= 9; col++) { + for (int row = 1; row <= getRows(); row++) { + final int slot = getSlotFromRowCol(row, col); + if (getInventory().query(SlotIndex.of(slot)) == null) { + if (!foundRow) foundRow = true; + counter++; + } + } + + if (foundRow) return counter; + } + + return counter; + } + + /** + * Puts the item in the GUI for horizontal scrolling + * + * @param guiItem The gui item + */ + private void putItemVertically(final GuiItem guiItem) { + for (int slot = 0; slot < getRows() * 9; slot++) { + if (getInventory().query(SlotIndex.of(slot)) != null) continue; + getMutableCurrentPageItems().put(slot, guiItem); + getInventory().query(SlotIndex.of(slot)).set(guiItem.getItemStack()); + break; + } + } + + /** + * Puts item into the GUI for vertical scrolling + * + * @param guiItem The gui item + */ + private void putItemHorizontally(final GuiItem guiItem) { + for (int col = 1; col < 10; col++) { + for (int row = 1; row <= getRows(); row++) { + final int slot = getSlotFromRowCol(row, col); + if (getInventory().query(SlotIndex.of(slot)) != null) continue; + + getMutableCurrentPageItems().put(slot, guiItem); + getInventory().query(SlotIndex.of(slot)).set(guiItem.getItemStack()); + return; + } + } + } + + /** + * Gets the items from the current page + * + * @param givenPage The page number + * @return A list with all the items + */ + private List getPage(final int givenPage) { + final int page = givenPage - 1; + final int pageItemsSize = getPageItems().size(); + + final List guiPage = new ArrayList<>(); + + int max = page * scrollSize + getPageSize(); + if (max > pageItemsSize) max = pageItemsSize; + + for (int i = page * scrollSize; i < max; i++) { + guiPage.add(getPageItems().get(i)); + } + + return guiPage; + } + +} diff --git a/sponge/api7/src/main/java/dev/triumphteam/gui/guis/StorageGui.java b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/StorageGui.java new file mode 100644 index 00000000..9d166292 --- /dev/null +++ b/sponge/api7/src/main/java/dev/triumphteam/gui/guis/StorageGui.java @@ -0,0 +1,132 @@ +/** + * MIT License + * + * Copyright (c) 2021 TriumphTeam + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.triumphteam.gui.guis; + +import dev.triumphteam.gui.TriumphPlugin; +import dev.triumphteam.gui.components.InteractionModifier; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult; +import org.spongepowered.api.item.inventory.transaction.InventoryTransactionResult.Type; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * GUI that does not clear its items once it's closed + */ +@SuppressWarnings("unused") +public class StorageGui extends BaseGui { + + /** + * Main constructor for the StorageGui + * + * @param rows The amount of rows the GUI should have + * @param title The GUI's title using {@link String} + * @param interactionModifiers A set containing the {@link InteractionModifier} this GUI should use + * @since 3.0.3 + */ + public StorageGui(final int rows, @NotNull final String title, @NotNull final Set interactionModifiers) { + super(rows, title, interactionModifiers); + } + + /** + * Main constructor of the Persistent GUI + * + * @param rows The rows the GUI should have + * @param title The GUI's title + */ + @Deprecated + public StorageGui(final int rows, @NotNull final String title) { + super(rows, title); + } + + /** + * Alternative constructor that does not require rows + * + * @param title The GUI's title + */ + @Deprecated + public StorageGui(@NotNull final String title) { + super(1, title); + } + + /** + * Adds {@link ItemStack} to the inventory straight, not the GUI + * + * @param items Varargs with {@link ItemStack}s + * @return An immutable {@link Map} with the left overs + */ + @NotNull + public Map<@NotNull Integer, @NotNull ItemStack> addItem(@NotNull final ItemStack... items) { + for (ItemStack stack : items) { + final InventoryTransactionResult result = getInventory().offer(stack); + if (result.getType() == Type.FAILURE ) { + break; + } + } + + // TODO + //getInventory().slots(). + //return Collections.unmodifiableMap(getInventory().addItem(items)); + return new HashMap<>(); + } + + /** + * Adds {@link ItemStack} to the inventory straight, not the GUI + * + * @param items Varargs with {@link ItemStack}s + * @return An immutable {@link Map} with the left overs + */ + public Map<@NotNull Integer, @NotNull ItemStack> addItem(@NotNull final List items) { + return addItem(items.toArray(new ItemStack[0])); + } + + /** + * Overridden {@link BaseGui#open(HumanEntity)} to prevent + * + * @param player The {@link HumanEntity} to open the GUI to + */ + @Override + public void open(@NotNull final Player player) { + if (player.get(Keys.IS_SLEEPING).get()) return; + populateGui(); + Sponge.getScheduler().createTaskBuilder().delayTicks(1).execute(() -> { + player.openInventory(getInventory()); + }).submit(TriumphPlugin.getInstance()); + } + + @Override + public int compareTo(Object o) { + // TODO Auto-generated method stub + return 0; + } + +}