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