first commit
This commit is contained in:
commit
a2935ad53d
31 changed files with 3127 additions and 0 deletions
170
.gitignore
vendored
Normal file
170
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/intellij,java,gradle
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,java,gradle
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
# Azure Toolkit for IntelliJ plugin
|
||||
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
|
||||
.idea/**/azureSettings.xml
|
||||
|
||||
### Java ###
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
|
||||
### Gradle ###
|
||||
.gradle
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Avoid ignore Gradle wrappper properties
|
||||
!gradle-wrapper.properties
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# Eclipse Gradle plugin generated files
|
||||
# Eclipse Core
|
||||
.project
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
### Gradle Patch ###
|
||||
# Java heap dump
|
||||
*.hprof
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/intellij,java,gradle
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
6
.idea/compiler.xml
generated
Normal file
6
.idea/compiler.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
10
.idea/misc.xml
generated
Normal file
10
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/modules.xml
generated
Normal file
9
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/SnowCore.main.iml" filepath="$PROJECT_DIR$/.idea/modules/SnowCore.main.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/SnowCore.test.iml" filepath="$PROJECT_DIR$/.idea/modules/SnowCore.test.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
13
.idea/modules/SnowCore.main.iml
generated
Normal file
13
.idea/modules/SnowCore.main.iml
generated
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="minecraft" name="Minecraft">
|
||||
<configuration>
|
||||
<autoDetectTypes>
|
||||
<platformType>ADVENTURE</platformType>
|
||||
</autoDetectTypes>
|
||||
<projectReimportVersion>1</projectReimportVersion>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
13
.idea/modules/SnowCore.test.iml
generated
Normal file
13
.idea/modules/SnowCore.test.iml
generated
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="minecraft" name="Minecraft">
|
||||
<configuration>
|
||||
<autoDetectTypes>
|
||||
<platformType>ADVENTURE</platformType>
|
||||
</autoDetectTypes>
|
||||
<projectReimportVersion>1</projectReimportVersion>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
73
build.gradle
Normal file
73
build.gradle
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id("xyz.jpenilla.run-paper") version "2.3.1"
|
||||
id "io.github.goooler.shadow" version "8.1.8"
|
||||
}
|
||||
|
||||
group = 'ovh.sad'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "papermc-repo"
|
||||
url = "https://repo.papermc.io/repository/maven-public/"
|
||||
}
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven {
|
||||
url = 'https://repo.extendedclip.com/releases/'
|
||||
}
|
||||
}
|
||||
tasks.build {
|
||||
dependsOn shadowJar
|
||||
}
|
||||
tasks.shadowJar {
|
||||
archiveClassifier.set("shaded") // removes "-all"
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
relocate("dev.triumphteam.gui", "ovh.sad.snowcore.gui")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT")
|
||||
implementation "dev.triumphteam:triumph-gui:3.1.13"
|
||||
compileOnly("com.github.MilkBowl:VaultAPI:1.7") { exclude( group: 'org.bukkit', module: 'bukkit' ) }
|
||||
compileOnly 'me.clip:placeholderapi:2.12.2'
|
||||
}
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
// Configure the Minecraft version for our task.
|
||||
// This is the only required configuration besides applying the plugin.
|
||||
// Your plugin's jar (or shadowJar if present) will be used automatically.
|
||||
minecraftVersion("1.21")
|
||||
}
|
||||
}
|
||||
|
||||
def targetJavaVersion = 21
|
||||
java {
|
||||
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
|
||||
sourceCompatibility = javaVersion
|
||||
targetCompatibility = javaVersion
|
||||
if (JavaVersion.current() < javaVersion) {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8'
|
||||
|
||||
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
|
||||
options.release.set(targetJavaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
def props = [version: version]
|
||||
inputs.properties props
|
||||
filteringCharset 'UTF-8'
|
||||
filesMatching('plugin.yml') {
|
||||
expand props
|
||||
}
|
||||
}
|
||||
0
gradle.properties
Normal file
0
gradle.properties
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
249
gradlew
vendored
Executable file
249
gradlew
vendored
Executable file
|
|
@ -0,0 +1,249 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# 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 ;; #(
|
||||
MSYS* | 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
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
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
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# 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"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
92
gradlew.bat
vendored
Normal file
92
gradlew.bat
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
@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=.
|
||||
@rem This is normally unused
|
||||
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% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
: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 %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 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!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
settings.gradle
Normal file
1
settings.gradle
Normal file
|
|
@ -0,0 +1 @@
|
|||
rootProject.name = 'SnowCore'
|
||||
179
src/main/java/ovh/sad/snowcore/Area.java
Normal file
179
src/main/java/ovh/sad/snowcore/Area.java
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
package ovh.sad.snowcore;
|
||||
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.Ageable;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Waterlogged;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class Area {
|
||||
static class MaterialWithChance {
|
||||
public Material material;
|
||||
public float chance; // in precentage 1 to 100, 33.33 works aswell
|
||||
|
||||
public MaterialWithChance(Material material, float chance) {
|
||||
this.material = material;
|
||||
this.chance = chance;
|
||||
}
|
||||
}
|
||||
|
||||
private final Location positionOne;
|
||||
private final Location positionTwo;
|
||||
|
||||
public final String name;
|
||||
|
||||
private final MaterialWithChance[] materials;
|
||||
public final float airPercentage;
|
||||
|
||||
public Area(String name, MaterialWithChance[] materials,
|
||||
Location positionOne,
|
||||
Location positionTwo,
|
||||
float airPercentage
|
||||
) {
|
||||
this.name = name;
|
||||
this.materials = materials;
|
||||
this.positionOne = positionOne;
|
||||
this.positionTwo = positionTwo;
|
||||
this.airPercentage = airPercentage;
|
||||
}
|
||||
|
||||
public void regen(World world) {
|
||||
|
||||
int minX = Math.min(positionOne.getBlockX(), positionTwo.getBlockX());
|
||||
int minY = Math.min(positionOne.getBlockY(), positionTwo.getBlockY());
|
||||
int minZ = Math.min(positionOne.getBlockZ(), positionTwo.getBlockZ());
|
||||
|
||||
int maxX = Math.max(positionOne.getBlockX(), positionTwo.getBlockX());
|
||||
int maxZ = Math.max(positionOne.getBlockZ(), positionTwo.getBlockZ());
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
|
||||
Block base = world.getBlockAt(x, minY, z);
|
||||
if (!isReplaceable(base.getType())) continue;
|
||||
|
||||
if (airPercentage > 0 && random.nextInt(100) < airPercentage) {
|
||||
|
||||
for (int y = minY; y <= minY + 3; y++) {
|
||||
Block b = world.getBlockAt(x, y, z);
|
||||
if (isReplaceable(b.getType())) {
|
||||
b.setType(Material.AIR, false);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int totalWeight = 0;
|
||||
for (MaterialWithChance m : materials) {
|
||||
totalWeight += m.chance;
|
||||
}
|
||||
|
||||
int roll = random.nextInt(totalWeight);
|
||||
int current = 0;
|
||||
MaterialWithChance chosen = null;
|
||||
|
||||
for (MaterialWithChance m : materials) {
|
||||
current += m.chance;
|
||||
if (roll < current) {
|
||||
chosen = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (chosen == null) continue;
|
||||
|
||||
for (int y = minY; y <= minY + 3; y++) {
|
||||
Block b = world.getBlockAt(x, y, z);
|
||||
if (isReplaceable(b.getType())) {
|
||||
b.setType(Material.AIR, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDoubleHeight(chosen.material)) {
|
||||
placeBisected(world, x, minY, z, chosen.material);
|
||||
} else {
|
||||
base.setType(chosen.material, false);
|
||||
|
||||
BlockData data = base.getBlockData();
|
||||
|
||||
if(data instanceof Ageable ageable) {
|
||||
ageable.setAge(1+random.nextInt(ageable.getMaximumAge()));
|
||||
base.setBlockData(ageable, false);
|
||||
}
|
||||
|
||||
if (data instanceof Waterlogged waterlogged) {
|
||||
waterlogged.setWaterlogged(false);
|
||||
base.setBlockData(waterlogged, false);
|
||||
}
|
||||
|
||||
if (data instanceof org.bukkit.block.data.type.FlowerBed petals) {
|
||||
int max = petals.getMaximumFlowerAmount();
|
||||
petals.setFlowerAmount(random.nextInt(1, max + 1));
|
||||
base.setBlockData(petals, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private boolean isReplaceable(Material m) {
|
||||
if (m == Material.AIR) return true;
|
||||
|
||||
for (MaterialWithChance allowed : materials) {
|
||||
if (allowed.material == m) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private static void placeBisected(World world, int x, int y, int z, Material mat) {
|
||||
Block bottom = world.getBlockAt(x, y, z);
|
||||
Block top = world.getBlockAt(x, y + 1, z);
|
||||
|
||||
bottom.setType(mat, false);
|
||||
top.setType(mat, false);
|
||||
|
||||
Bisected lower = (Bisected) bottom.getBlockData();
|
||||
Bisected upper = (Bisected) top.getBlockData();
|
||||
|
||||
lower.setHalf(Bisected.Half.BOTTOM);
|
||||
upper.setHalf(Bisected.Half.TOP);
|
||||
|
||||
bottom.setBlockData(lower, false);
|
||||
top.setBlockData(upper, false);
|
||||
}
|
||||
|
||||
public float getAirPercentage(World world) {
|
||||
int minX = Math.min(positionOne.getBlockX(), positionTwo.getBlockX());
|
||||
int maxX = Math.max(positionOne.getBlockX(), positionTwo.getBlockX());
|
||||
|
||||
int minY = Math.min(positionOne.getBlockY(), positionTwo.getBlockY());
|
||||
|
||||
int minZ = Math.min(positionOne.getBlockZ(), positionTwo.getBlockZ());
|
||||
int maxZ = Math.max(positionOne.getBlockZ(), positionTwo.getBlockZ());
|
||||
|
||||
int total = 0;
|
||||
int airCount = 0;
|
||||
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
total++;
|
||||
Block block = world.getBlockAt(x, minY, z);
|
||||
if (block.getType() == Material.AIR) airCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (total == 0) return 0f;
|
||||
|
||||
return (airCount / (float) total) * 100f;
|
||||
}
|
||||
private static boolean isDoubleHeight(Material m) {
|
||||
return m.createBlockData() instanceof Bisected;
|
||||
}
|
||||
}
|
||||
115
src/main/java/ovh/sad/snowcore/Prices.java
Normal file
115
src/main/java/ovh/sad/snowcore/Prices.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package ovh.sad.snowcore;
|
||||
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import ovh.sad.snowcore.items.Hoe;
|
||||
import ovh.sad.snowcore.items.Shovel;
|
||||
import ovh.sad.snowcore.items.Tool;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Prices {
|
||||
|
||||
public static final Map<Material, Double> SELL_PRICES = new HashMap<>();
|
||||
private static final Map<Material, Integer> toolPrices = new HashMap<>();
|
||||
|
||||
static {
|
||||
|
||||
/* =========================
|
||||
Snow (very common / farmable)
|
||||
========================= */
|
||||
SELL_PRICES.put(Material.SNOWBALL, 0.5); // safe value
|
||||
|
||||
|
||||
/* =========================
|
||||
Cactus Area
|
||||
========================= */
|
||||
SELL_PRICES.put(Material.CACTUS_FLOWER, 4.0);
|
||||
SELL_PRICES.put(Material.PEONY, 4.0);
|
||||
SELL_PRICES.put(Material.PINK_TULIP, 4.0);
|
||||
SELL_PRICES.put(Material.CACTUS, 7.0);
|
||||
|
||||
|
||||
/* =========================
|
||||
Spruce Area
|
||||
========================= */
|
||||
SELL_PRICES.put(Material.WILDFLOWERS, 1.0);
|
||||
SELL_PRICES.put(Material.SPRUCE_SAPLING, 10.0);
|
||||
|
||||
|
||||
/* =========================
|
||||
Pink Area
|
||||
========================= */
|
||||
SELL_PRICES.put(Material.PINK_PETALS, 1.0);
|
||||
SELL_PRICES.put(Material.ALLIUM, 7.0);
|
||||
SELL_PRICES.put(Material.CHERRY_SAPLING, 20.0);
|
||||
|
||||
|
||||
/* =========================
|
||||
White Area
|
||||
========================= */
|
||||
SELL_PRICES.put(Material.OXEYE_DAISY, 2.0);
|
||||
SELL_PRICES.put(Material.LILY_OF_THE_VALLEY, 2.0);
|
||||
SELL_PRICES.put(Material.CLOSED_EYEBLOSSOM, 10.0);
|
||||
|
||||
|
||||
/* =========================
|
||||
Gray Area
|
||||
========================= */
|
||||
SELL_PRICES.put(Material.DEAD_FIRE_CORAL, 4.0);
|
||||
SELL_PRICES.put(Material.DEAD_TUBE_CORAL, 4.0);
|
||||
SELL_PRICES.put(Material.DEAD_HORN_CORAL, 4.0);
|
||||
SELL_PRICES.put(Material.DEAD_BRAIN_CORAL_FAN, 4.0);
|
||||
SELL_PRICES.put(Material.OPEN_EYEBLOSSOM, 20.0);
|
||||
|
||||
|
||||
/* =========================
|
||||
Red Area
|
||||
========================= */
|
||||
SELL_PRICES.put(Material.ROSE_BUSH, 3.0);
|
||||
SELL_PRICES.put(Material.POPPY, 3.0);
|
||||
SELL_PRICES.put(Material.RED_MUSHROOM, 3.0);
|
||||
SELL_PRICES.put(Material.CRIMSON_FUNGUS, 10.0);
|
||||
|
||||
|
||||
/* Farming areas */
|
||||
|
||||
SELL_PRICES.put(Material.BEETROOT_SEEDS, 0.5);
|
||||
SELL_PRICES.put(Material.POTATO, 1.0);
|
||||
SELL_PRICES.put(Material.CARROT, 1.0);
|
||||
SELL_PRICES.put(Material.WHEAT_SEEDS, 0.5);
|
||||
|
||||
SELL_PRICES.put(Material.POISONOUS_POTATO, 10.0 );
|
||||
SELL_PRICES.put(Material.WHEAT, 4.0);
|
||||
SELL_PRICES.put(Material.BEETROOT, 2.0);
|
||||
|
||||
|
||||
// Load hoe prices
|
||||
List<? extends Tool<?>> hoes = Hoe.HoeDefinitions.ORDERED;
|
||||
int[] hoePrices = {0, 1000, 5000, 10000, 40000, 200000}; // adjust if needed
|
||||
for (int i = 0; i < hoes.size() && i < hoePrices.length; i++) {
|
||||
toolPrices.put(hoes.get(i).material, hoePrices[i]);
|
||||
}
|
||||
|
||||
// Load shovel prices
|
||||
List<? extends Tool<?>> shovels = Shovel.ShovelDefinitions.ORDERED;
|
||||
int[] shovelPrices = {0, 1000, 5000, 10000, 40000, 200000}; // adjust if needed
|
||||
for (int i = 0; i < shovels.size() && i < shovelPrices.length; i++) {
|
||||
toolPrices.put(shovels.get(i).material, shovelPrices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer getToolBuyPrice(Tool<?> tool) {
|
||||
return toolPrices.get(tool.material);
|
||||
}
|
||||
public static boolean hasSellPrice(Material mat) {
|
||||
return SELL_PRICES.containsKey(mat);
|
||||
}
|
||||
|
||||
public static double getSellPrice(Material mat) {
|
||||
return SELL_PRICES.get(mat);
|
||||
}
|
||||
}
|
||||
318
src/main/java/ovh/sad/snowcore/Snowcore.java
Normal file
318
src/main/java/ovh/sad/snowcore/Snowcore.java
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
package ovh.sad.snowcore;
|
||||
|
||||
import io.papermc.paper.command.brigadier.BasicCommand;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.type.Snow;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import ovh.sad.snowcore.commands.RegenCommand;
|
||||
import ovh.sad.snowcore.commands.SellCommand;
|
||||
import ovh.sad.snowcore.commands.SnowifyCommand;
|
||||
import ovh.sad.snowcore.commands.ViewItemsCommand;
|
||||
import ovh.sad.snowcore.listeners.BreakListener;
|
||||
import ovh.sad.snowcore.listeners.InteractListener;
|
||||
import ovh.sad.snowcore.listeners.PlaceListener;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
|
||||
public final class Snowcore extends JavaPlugin {
|
||||
public static Economy econ = null;
|
||||
|
||||
public static Area[] areas = new Area[]{
|
||||
new Area(
|
||||
"Cactus Area",
|
||||
|
||||
new Area.MaterialWithChance[]{
|
||||
new Area.MaterialWithChance(Material.CACTUS_FLOWER, 28.57f),
|
||||
new Area.MaterialWithChance(Material.PEONY, 28.57f),
|
||||
new Area.MaterialWithChance(Material.PINK_TULIP, 28.57f),
|
||||
new Area.MaterialWithChance(Material.CACTUS, 14.29f)
|
||||
},
|
||||
new Location(null, 80, 129, 161),
|
||||
new Location(null, 73, 131, 184),
|
||||
15
|
||||
),
|
||||
new Area(
|
||||
"Spruce Area",
|
||||
new Area.MaterialWithChance[]{
|
||||
new Area.MaterialWithChance(Material.SPRUCE_SAPLING, 10),
|
||||
new Area.MaterialWithChance(Material.WILDFLOWERS, 80)
|
||||
},
|
||||
new Location(null, 64, 129, 161),
|
||||
new Location(null, 69, 129, 170),
|
||||
5
|
||||
),
|
||||
new Area(
|
||||
"Pink Area",
|
||||
new Area.MaterialWithChance[]{
|
||||
new Area.MaterialWithChance(Material.PINK_PETALS, 80),
|
||||
new Area.MaterialWithChance(Material.ALLIUM, 15),
|
||||
new Area.MaterialWithChance(Material.CHERRY_SAPLING, 5)
|
||||
},
|
||||
new Location(null, 64, 130, 174),
|
||||
new Location(null, 69, 129, 184),
|
||||
0
|
||||
),
|
||||
new Area(
|
||||
"White Area",
|
||||
new Area.MaterialWithChance[]{
|
||||
new Area.MaterialWithChance(Material.OXEYE_DAISY, 45),
|
||||
new Area.MaterialWithChance(Material.LILY_OF_THE_VALLEY, 45),
|
||||
new Area.MaterialWithChance(Material.CLOSED_EYEBLOSSOM, 10)
|
||||
|
||||
},
|
||||
new Location(null, 69, 129, 189),
|
||||
new Location(null, 64, 129, 198),
|
||||
10
|
||||
),
|
||||
new Area(
|
||||
"Gray Area",
|
||||
new Area.MaterialWithChance[]{
|
||||
new Area.MaterialWithChance(Material.DEAD_FIRE_CORAL, 23.75f),
|
||||
new Area.MaterialWithChance(Material.DEAD_TUBE_CORAL, 23.75f),
|
||||
new Area.MaterialWithChance(Material.DEAD_HORN_CORAL, 23.75f),
|
||||
new Area.MaterialWithChance(Material.DEAD_BRAIN_CORAL_FAN, 23.75f),
|
||||
new Area.MaterialWithChance(Material.OPEN_EYEBLOSSOM, 5f)
|
||||
},
|
||||
new Location(null, 69, 129, 202),
|
||||
new Location(null, 64, 129, 211),
|
||||
20
|
||||
),
|
||||
new Area(
|
||||
"Red Area",
|
||||
new Area.MaterialWithChance[]{
|
||||
new Area.MaterialWithChance(Material.ROSE_BUSH, 30),
|
||||
new Area.MaterialWithChance(Material.POPPY, 30),
|
||||
new Area.MaterialWithChance(Material.RED_MUSHROOM, 30),
|
||||
new Area.MaterialWithChance(Material.CRIMSON_FUNGUS, 10)
|
||||
|
||||
},
|
||||
new Location(null, 73, 129, 189),
|
||||
new Location(null, 80, 131, 211),
|
||||
30
|
||||
),
|
||||
new Area(
|
||||
"Wheat & Beetroot Area",
|
||||
new Area.MaterialWithChance[]{
|
||||
new Area.MaterialWithChance(Material.WHEAT, 50),
|
||||
new Area.MaterialWithChance(Material.BEETROOTS, 50)
|
||||
},
|
||||
new Location(null, 43, 129, 184),
|
||||
new Location(null, 59, 129, 161),
|
||||
35
|
||||
),
|
||||
new Area(
|
||||
"Carrot & Potato Area",
|
||||
new Area.MaterialWithChance[]{
|
||||
new Area.MaterialWithChance(Material.CARROTS, 50),
|
||||
new Area.MaterialWithChance(Material.POTATOES, 50)
|
||||
},
|
||||
new Location(null, 43, 129, 189),
|
||||
new Location(null, 59, 129, 211),
|
||||
35
|
||||
)
|
||||
};
|
||||
|
||||
public static void snowify(World world, Location pos1, Location pos2) {
|
||||
int minX = Math.min(pos1.getBlockX(), pos2.getBlockX());
|
||||
int maxX = Math.max(pos1.getBlockX(), pos2.getBlockX());
|
||||
int minY = Math.min(pos1.getBlockY(), pos2.getBlockY());
|
||||
int maxY = Math.max(pos1.getBlockY(), pos2.getBlockY());
|
||||
int minZ = Math.min(pos1.getBlockZ(), pos2.getBlockZ());
|
||||
int maxZ = Math.max(pos1.getBlockZ(), pos2.getBlockZ());
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
for (int y = maxY; y >= minY; y--) {
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
|
||||
// Only care about full snow blocks as the base
|
||||
if (block.getType() != Material.SNOW_BLOCK) continue;
|
||||
|
||||
Block above = world.getBlockAt(x, y + 1, z);
|
||||
Material aboveType = above.getType();
|
||||
|
||||
if (aboveType == Material.AIR) {
|
||||
if (random.nextInt(100) < 35) {
|
||||
above.setType(Material.SNOW, false);
|
||||
Snow snow = (Snow) above.getBlockData();
|
||||
|
||||
int layers = random.nextInt(1, 5); // 1–4
|
||||
snow.setLayers(Math.min(layers, snow.getMaximumLayers()));
|
||||
above.setBlockData(snow, false);
|
||||
}
|
||||
} else if (aboveType == Material.SNOW) {
|
||||
if (random.nextInt(100) < 35) {
|
||||
Snow snow = (Snow) above.getBlockData();
|
||||
|
||||
int add = random.nextInt(1, 5); // 1–4
|
||||
int max = snow.getMaximumLayers();
|
||||
|
||||
snow.setLayers(Math.min(snow.getLayers() + add, max));
|
||||
above.setBlockData(snow, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ComponentLogger LOGGER;
|
||||
public static JavaPlugin PLUGIN;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
LOGGER = this.getComponentLogger();
|
||||
PLUGIN = this;
|
||||
|
||||
BasicCommand regen = new RegenCommand();
|
||||
BasicCommand snowify = new SnowifyCommand();
|
||||
BasicCommand sell = new SellCommand();
|
||||
|
||||
BasicCommand viewitems = new ViewItemsCommand();
|
||||
RegisteredServiceProvider<Economy> rsp =
|
||||
getServer().getServicesManager().getRegistration(Economy.class);
|
||||
|
||||
if (rsp == null) {
|
||||
getLogger().severe("No Vault economy provider found!");
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
||||
new SnowcoreExpansion(this).register();
|
||||
}
|
||||
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
|
||||
econ = rsp.getProvider();
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
registerCommand("regen", regen);
|
||||
registerCommand("sell", sell);
|
||||
registerCommand("snowify", snowify);
|
||||
registerCommand("viewitems", viewitems);
|
||||
|
||||
getServer().getPluginManager().registerEvents(new BreakListener(), this);
|
||||
getServer().getPluginManager().registerEvents(new PlaceListener(), this);
|
||||
getServer().getPluginManager().registerEvents(new InteractListener(), this);
|
||||
} catch(Exception e) {
|
||||
LOGGER.error(Component.text("You are most likely restarting the plugin via /plm restart snowcore. Please don't do this, commands will not be registered."));
|
||||
}
|
||||
|
||||
LOGGER.info(Component.text("SNOWCORE - good morning!", Style.style(TextColor.color(0, 255, 0))));
|
||||
|
||||
Random random = new Random();
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
World world = Bukkit.getWorld("world");
|
||||
|
||||
for(Area area : areas) {
|
||||
if (area.getAirPercentage(world) > 65) {
|
||||
area.regen(world);
|
||||
|
||||
final Component areaReplenish = MiniMessage.miniMessage().deserialize(
|
||||
"<rainbow><bold>‧₊˚❀༉‧₊˚.</rainbow> <white>Seems like the <area> area has been replenished..</white> <rainbow><bold>‧₊˚❀༉‧₊˚.</rainbow>",
|
||||
Placeholder.unparsed("area", area.name)
|
||||
);
|
||||
|
||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
||||
player.sendActionBar(areaReplenish);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(this, 0L, 10L); // 0L delay, 200L = 10s
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (random.nextInt(100) < 15) {
|
||||
final Component snowing = MiniMessage.miniMessage().deserialize(
|
||||
"<blue><bold>❄❄❄</blue> <white>It seems to be snowing..</white>"
|
||||
);
|
||||
|
||||
World world = Bukkit.getWorld("world");
|
||||
Snowcore.snowify(
|
||||
world,
|
||||
new Location(world, 39, 127, 157),
|
||||
new Location(world, 8, 139, 215)
|
||||
);
|
||||
|
||||
Bukkit.broadcast(snowing);
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(this, 0L, 100L); // 0L delay, 200L = 10s
|
||||
}
|
||||
|
||||
public static int fortuneToDrops(int fortune, int drops) {
|
||||
if (fortune <= 0) return drops;
|
||||
|
||||
// Clamp fortune to 1-20
|
||||
fortune = Math.min(Math.max(fortune, 1), 20);
|
||||
|
||||
// Base chance multiplier
|
||||
double multiplier = 1.0 + Math.pow(fortune, 1.3) / 10.0;
|
||||
|
||||
// Apply multiplier to original drops
|
||||
int newDrops = (int) Math.round(drops * multiplier);
|
||||
|
||||
return Math.max(newDrops, drops); // never return less than original
|
||||
}
|
||||
public static int getLevelFromExp(int exp) {
|
||||
// EXP = 5 * L^2 -> L = sqrt(exp / 5)
|
||||
return (int) Math.floor(Math.sqrt(exp / 5.0));
|
||||
}
|
||||
|
||||
public static void sendActionBar(Player player, Component message) {
|
||||
Component component = MiniMessage.miniMessage().deserialize(
|
||||
"<red><bold>❌</bold> "
|
||||
).append(message);
|
||||
player.sendActionBar(component);
|
||||
}
|
||||
|
||||
public static void sellInventory(Player player) {
|
||||
var inv = player.getInventory();
|
||||
|
||||
double finalPrice = 0d;
|
||||
|
||||
for (int slot = 0; slot < 35; slot++) {
|
||||
ItemStack item = inv.getItem(slot);
|
||||
|
||||
if (item == null || item.getType().isAir()) continue;
|
||||
|
||||
boolean prices = Prices.hasSellPrice(item.getType());
|
||||
|
||||
if(prices) {
|
||||
double price = Prices.getSellPrice(item.getType()) * item.getAmount();
|
||||
|
||||
finalPrice += price;
|
||||
|
||||
inv.setItem(slot, null);
|
||||
}
|
||||
}
|
||||
|
||||
Snowcore.econ.depositPlayer(player, finalPrice);
|
||||
player.sendMessage(Component.text("Sold items for §a" + finalPrice + "§r!"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
LOGGER.info(Component.text("SNOWCORE - good night.", Style.style(TextColor.color(255, 0, 0))));
|
||||
}
|
||||
}
|
||||
79
src/main/java/ovh/sad/snowcore/SnowcoreExpansion.java
Normal file
79
src/main/java/ovh/sad/snowcore/SnowcoreExpansion.java
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package ovh.sad.snowcore;
|
||||
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import ovh.sad.snowcore.items.Hoe;
|
||||
import ovh.sad.snowcore.items.ToolUtils;
|
||||
import ovh.sad.snowcore.items.Shovel;
|
||||
|
||||
public class SnowcoreExpansion extends PlaceholderExpansion {
|
||||
private final Snowcore plugin; //
|
||||
|
||||
public SnowcoreExpansion(Snowcore plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getAuthor() {
|
||||
return String.join(", ", plugin.getPluginMeta().getAuthors());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getIdentifier() {
|
||||
return "snowcore";
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getVersion() {
|
||||
return plugin.getPluginMeta().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist() {
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
@Override
|
||||
public String onRequest(OfflinePlayer oplayer, @NotNull String params) {
|
||||
if (params.equalsIgnoreCase("tool_level")) {
|
||||
Player player = oplayer.getPlayer();
|
||||
if (player != null) {
|
||||
ItemStack item = player.getInventory().getItemInMainHand();
|
||||
Material type = item.getType();
|
||||
|
||||
if (Hoe.HoeDefinitions.ALL_HOES.containsKey(type)
|
||||
|| Shovel.ShovelDefinitions.ALL_SHOVELS.containsKey(type)) {
|
||||
|
||||
int level = ToolUtils.getLevel(item);
|
||||
|
||||
// Clamp between 1 and 80
|
||||
int clampedLevel = Math.max(1, Math.min(80, level));
|
||||
|
||||
// Interpolate hue (0° red → 270° violet)
|
||||
float hue = (clampedLevel - 1) / 79.0f * 270f;
|
||||
|
||||
java.awt.Color color = java.awt.Color.getHSBColor(hue / 360f, 1f, 1f);
|
||||
|
||||
String hex = String.format("#%02x%02x%02x",
|
||||
color.getRed(),
|
||||
color.getGreen(),
|
||||
color.getBlue());
|
||||
|
||||
return net.md_5.bungee.api.ChatColor.of(hex) + "❁ " + level;
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
25
src/main/java/ovh/sad/snowcore/commands/RegenCommand.java
Normal file
25
src/main/java/ovh/sad/snowcore/commands/RegenCommand.java
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package ovh.sad.snowcore.commands;
|
||||
|
||||
import io.papermc.paper.command.brigadier.BasicCommand;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import ovh.sad.snowcore.Area;
|
||||
import ovh.sad.snowcore.Snowcore;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@NullMarked
|
||||
public class RegenCommand implements BasicCommand {
|
||||
@Override
|
||||
public void execute(CommandSourceStack source, String[] args) {
|
||||
for(Area area: Snowcore.areas) {
|
||||
area.regen(Objects.requireNonNull(source.getExecutor()).getWorld());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String permission() {
|
||||
return "snowcore.regen_areas";
|
||||
}
|
||||
}
|
||||
123
src/main/java/ovh/sad/snowcore/commands/SellCommand.java
Normal file
123
src/main/java/ovh/sad/snowcore/commands/SellCommand.java
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
package ovh.sad.snowcore.commands;
|
||||
|
||||
import dev.triumphteam.gui.builder.item.ItemBuilder;
|
||||
import dev.triumphteam.gui.guis.Gui;
|
||||
import dev.triumphteam.gui.guis.GuiItem;
|
||||
import io.papermc.paper.command.brigadier.BasicCommand;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import ovh.sad.snowcore.Prices;
|
||||
import ovh.sad.snowcore.Snowcore;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@NullMarked
|
||||
public class SellCommand implements BasicCommand {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSourceStack source, String[] args) {
|
||||
AtomicBoolean sold = new AtomicBoolean(false);
|
||||
Gui gui = Gui.gui()
|
||||
.title(Component.text("Sell Items"))
|
||||
.rows(6)
|
||||
.create();
|
||||
|
||||
gui.setDefaultClickAction(event -> {
|
||||
if (event.getSlot() >= 45) event.setCancelled(true); // only block bottom row
|
||||
});
|
||||
|
||||
|
||||
GuiItem pane = ItemBuilder.from(Material.BLACK_STAINED_GLASS_PANE)
|
||||
.name(Component.text(" "))
|
||||
.asGuiItem();
|
||||
|
||||
// Fill entire bottom row
|
||||
for (int col = 1; col <= 9; col++) {
|
||||
gui.setItem(6, col, pane);
|
||||
}
|
||||
|
||||
GuiItem confirm = ItemBuilder.from(Material.GREEN_WOOL)
|
||||
.name(Component.text("§aConfirm Sell"))
|
||||
.asGuiItem(event -> {
|
||||
event.setCancelled(true);
|
||||
|
||||
Player player = (Player) event.getWhoClicked();
|
||||
|
||||
sellItemsFromInv(sold, player, event.getInventory());
|
||||
});
|
||||
|
||||
// Cancel (red wool)
|
||||
GuiItem cancel = ItemBuilder.from(Material.RED_WOOL)
|
||||
.name(Component.text("§cCancel"))
|
||||
.asGuiItem(event -> {
|
||||
Player player = (Player)event.getWhoClicked();
|
||||
|
||||
event.setCancelled(true);
|
||||
player.closeInventory();
|
||||
});
|
||||
|
||||
gui.setItem(6, 4, confirm);
|
||||
|
||||
gui.setItem(6, 6, cancel);
|
||||
|
||||
gui.setCloseGuiAction(event -> {
|
||||
Player player = (Player) event.getPlayer();
|
||||
UUID id = player.getUniqueId();
|
||||
|
||||
// If they sold, DON'T return items
|
||||
if (!sold.get()) {
|
||||
sellItemsFromInv(sold, player, event.getInventory());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
gui.open((Player) Objects.requireNonNull(source.getExecutor()));
|
||||
|
||||
}
|
||||
|
||||
private void sellItemsFromInv(AtomicBoolean sold, Player player, Inventory inventory) {
|
||||
sold.set(true); // mark as sold
|
||||
|
||||
var inv = inventory;
|
||||
|
||||
double finalPrice = 0d;
|
||||
|
||||
for (int slot = 0; slot < 45; slot++) {
|
||||
ItemStack item = inv.getItem(slot);
|
||||
|
||||
if (item == null || item.getType().isAir()) continue;
|
||||
|
||||
boolean prices = Prices.hasSellPrice(item.getType());
|
||||
|
||||
if(!prices) {
|
||||
player.getInventory().addItem(item);
|
||||
inv.setItem(slot, null);
|
||||
} else {
|
||||
double price = Prices.getSellPrice(item.getType()) * item.getAmount();
|
||||
|
||||
finalPrice += price;
|
||||
|
||||
inv.setItem(slot, null);
|
||||
}
|
||||
}
|
||||
|
||||
Snowcore.econ.depositPlayer(player, finalPrice);
|
||||
player.sendMessage(Component.text("Sold items for §a" + finalPrice + "§r!"));
|
||||
player.closeInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String permission() {
|
||||
return "snowcore.sell";
|
||||
}
|
||||
}
|
||||
30
src/main/java/ovh/sad/snowcore/commands/SnowifyCommand.java
Normal file
30
src/main/java/ovh/sad/snowcore/commands/SnowifyCommand.java
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package ovh.sad.snowcore.commands;
|
||||
|
||||
import io.papermc.paper.command.brigadier.BasicCommand;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import ovh.sad.snowcore.Snowcore;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@NullMarked
|
||||
public class SnowifyCommand implements BasicCommand {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSourceStack source, String[] args) {
|
||||
World world = Objects.requireNonNull(source.getExecutor()).getWorld();
|
||||
Snowcore.snowify(
|
||||
world,
|
||||
new Location(world, 39, 127, 157),
|
||||
new Location(world, 8, 139, 215)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String permission() {
|
||||
return "snowcore.snowify";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package ovh.sad.snowcore.commands;
|
||||
|
||||
import dev.triumphteam.gui.guis.Gui;
|
||||
import dev.triumphteam.gui.guis.GuiItem;
|
||||
import io.papermc.paper.command.brigadier.BasicCommand;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import ovh.sad.snowcore.items.Hoe;
|
||||
import ovh.sad.snowcore.items.Shovel;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ViewItemsCommand implements BasicCommand {
|
||||
@Override
|
||||
public void execute(@NotNull CommandSourceStack source, String @NotNull [] args) {
|
||||
Gui gui = Gui.gui()
|
||||
.title(Component.text("All obtainable items"))
|
||||
.rows(6)
|
||||
.create();
|
||||
|
||||
Hoe.HoeDefinitions.ALL_HOES.forEach((material, hoe) -> {
|
||||
gui.addItem(new GuiItem(hoe.renderItem(null)));
|
||||
});
|
||||
|
||||
Shovel.ShovelDefinitions.ALL_SHOVELS.forEach((material, shovel) -> {
|
||||
gui.addItem(new GuiItem(shovel.renderItem(null)));
|
||||
});
|
||||
|
||||
gui.open((Player) Objects.requireNonNull(source.getExecutor()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String permission() {
|
||||
return "snowcore.view_items";
|
||||
}
|
||||
}
|
||||
200
src/main/java/ovh/sad/snowcore/guis/UpgradeYourToolsGui.java
Normal file
200
src/main/java/ovh/sad/snowcore/guis/UpgradeYourToolsGui.java
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
package ovh.sad.snowcore.guis;
|
||||
|
||||
import dev.triumphteam.gui.builder.item.ItemBuilder;
|
||||
import dev.triumphteam.gui.guis.Gui;
|
||||
import dev.triumphteam.gui.guis.GuiItem;
|
||||
import dev.triumphteam.gui.guis.PaginatedGui;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import ovh.sad.snowcore.Prices;
|
||||
import ovh.sad.snowcore.Snowcore;
|
||||
import ovh.sad.snowcore.items.Hoe;
|
||||
import ovh.sad.snowcore.items.Shovel;
|
||||
import ovh.sad.snowcore.items.Tool;
|
||||
import ovh.sad.snowcore.items.ToolUtils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class UpgradeYourToolsGui {
|
||||
|
||||
public static void open(PlayerInteractEntityEvent event) {
|
||||
openHoes(event.getPlayer());
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// HOES
|
||||
// =========================================================
|
||||
private static void openHoes(Player player) {
|
||||
|
||||
PaginatedGui gui = Gui.paginated()
|
||||
.title(Component.text("Upgrade Your Tools - Hoes"))
|
||||
.rows(3)
|
||||
.pageSize(7) // only columns 2-8
|
||||
.create();
|
||||
|
||||
gui.setDefaultClickAction(e -> e.setCancelled(true));
|
||||
|
||||
fillLayout(gui);
|
||||
|
||||
// Add ordered hoes
|
||||
for (Hoe hoe : Hoe.HoeDefinitions.ORDERED) {
|
||||
gui.addItem(createToolGuiItem(hoe, player, gui));
|
||||
}
|
||||
|
||||
// Navigation (bottom right)
|
||||
gui.setItem(3, 9,
|
||||
ItemBuilder.from(Material.ARROW)
|
||||
.name(Component.text("Next → Shovels", NamedTextColor.YELLOW))
|
||||
.asGuiItem(e -> {
|
||||
e.setCancelled(true);
|
||||
openShovels(player);
|
||||
})
|
||||
);
|
||||
|
||||
gui.open(player);
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// SHOVELS
|
||||
// =========================================================
|
||||
private static void openShovels(Player player) {
|
||||
|
||||
PaginatedGui gui = Gui.paginated()
|
||||
.title(Component.text("Upgrade Your Tools - Shovels"))
|
||||
.rows(3)
|
||||
.pageSize(7)
|
||||
.create();
|
||||
|
||||
gui.setDefaultClickAction(e -> e.setCancelled(true));
|
||||
|
||||
fillLayout(gui);
|
||||
|
||||
for (Shovel shovel : Shovel.ShovelDefinitions.ORDERED) {
|
||||
gui.addItem(createToolGuiItem(shovel, player, gui));
|
||||
}
|
||||
|
||||
gui.setItem(3, 9,
|
||||
ItemBuilder.from(Material.ARROW)
|
||||
.name(Component.text("← Previous Hoes", NamedTextColor.YELLOW))
|
||||
.asGuiItem(e -> {
|
||||
e.setCancelled(true);
|
||||
openHoes(player);
|
||||
})
|
||||
);
|
||||
|
||||
gui.open(player);
|
||||
}
|
||||
|
||||
private static <T extends Tool<?>> T getPreviousTool(T tool) {
|
||||
int index;
|
||||
if (tool instanceof Shovel) {
|
||||
index = Shovel.ShovelDefinitions.ORDERED.indexOf(tool);
|
||||
if (index <= 0) return null;
|
||||
return (T) Shovel.ShovelDefinitions.ORDERED.get(index - 1);
|
||||
} else if (tool instanceof Hoe) {
|
||||
index = Hoe.HoeDefinitions.ORDERED.indexOf(tool);
|
||||
if (index <= 0) return null;
|
||||
return (T) Hoe.HoeDefinitions.ORDERED.get(index - 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <T extends Tool<?>> GuiItem createToolGuiItem(T tool, Player player, PaginatedGui gui) {
|
||||
return new GuiItem(
|
||||
tool.renderItem(null),
|
||||
e -> {
|
||||
e.setCancelled(true);
|
||||
|
||||
if (tool.material == Material.WOODEN_SHOVEL || tool.material == Material.WOODEN_HOE) {
|
||||
// First tool is free
|
||||
player.sendMessage(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
"<green>✔ You've been given a <name>! Enjoy Snowing.",
|
||||
Placeholder.component("name", tool.name())
|
||||
)
|
||||
);
|
||||
|
||||
player.getInventory().addItem(tool.renderItem(null));
|
||||
gui.close(player);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get previous tool
|
||||
Tool<?> previousTool = getPreviousTool(tool);
|
||||
if (previousTool == null) return; // safety
|
||||
|
||||
// Check requirement
|
||||
boolean hasPrevious = false;
|
||||
for (ItemStack item : player.getInventory()) {
|
||||
if (item != null && item.getType() == previousTool.material) {
|
||||
hasPrevious = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasPrevious) {
|
||||
player.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
"<red>❌ Missing a requirement:</red> <previousname>",
|
||||
Placeholder.component("previousname", previousTool.name())
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check economy
|
||||
double balance = Snowcore.econ.getBalance(player);
|
||||
Integer cost = Prices.getToolBuyPrice(tool);
|
||||
|
||||
if (balance >= cost) {
|
||||
Snowcore.econ.withdrawPlayer(player, cost);
|
||||
player.getInventory().remove(previousTool.material);
|
||||
player.getInventory().addItem(tool.renderItem(null));
|
||||
|
||||
player.sendMessage(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
"<green>✔ Unlocked <yellow><name></yellow> for <gold>$<cost></gold> and <previousname>",
|
||||
Placeholder.component("previousname", previousTool.name()),
|
||||
Placeholder.component("name", tool.name()),
|
||||
Placeholder.unparsed("cost", cost.toString())
|
||||
)
|
||||
);
|
||||
gui.close(player);
|
||||
} else {
|
||||
player.sendMessage(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
"<red>✘ Not enough money! Need <gold>$<cost></gold>, you have <yellow>$<balance></yellow>",
|
||||
Placeholder.unparsed("balance", String.valueOf(balance)),
|
||||
Placeholder.unparsed("cost", cost.toString())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private static void fillLayout(PaginatedGui gui) {
|
||||
GuiItem pane = ItemBuilder.from(Material.GRAY_STAINED_GLASS_PANE)
|
||||
.name(Component.empty())
|
||||
.asGuiItem(e -> e.setCancelled(true));
|
||||
|
||||
for (int col = 1; col <= 9; col++) {
|
||||
gui.setItem(1, col, pane);
|
||||
}
|
||||
|
||||
// Row 2 (side panes only)
|
||||
gui.setItem(2, 1, pane);
|
||||
gui.setItem(2, 9, pane);
|
||||
|
||||
// Row 3 (all panes)
|
||||
for (int col = 1; col <= 9; col++) {
|
||||
gui.setItem(3, col, pane);
|
||||
}
|
||||
}
|
||||
}
|
||||
140
src/main/java/ovh/sad/snowcore/items/Hoe.java
Normal file
140
src/main/java/ovh/sad/snowcore/items/Hoe.java
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package ovh.sad.snowcore.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Hoe extends Tool<Hoe> {
|
||||
private final List<Material> canBreak = new ArrayList<>();
|
||||
private final List<Hoe> dependsOn = new ArrayList<>();
|
||||
|
||||
public Hoe(Material material, String name) {
|
||||
super(material, name);
|
||||
}
|
||||
|
||||
public Hoe addCanBreaks(Collection<Material> m) {
|
||||
canBreak.addAll(m);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Hoe addDepend(Hoe h) {
|
||||
dependsOn.add(h);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class HoeDefinitions {
|
||||
public static final Hoe WOODEN_HOE = new Hoe(Material.WOODEN_HOE, "<#DCB190><b>Wooden Hoe</b></#DCB190>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.WIND, 5)
|
||||
.addCanBreaks(List.of( Material.SPRUCE_SAPLING, Material.WILDFLOWERS ));
|
||||
public static final Hoe STONE_HOE = new Hoe(Material.STONE_HOE, "<gray><b>Stone Hoe</b><gray>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.WIND, 15)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.DOUBLE_DROP, 5)
|
||||
.addCanBreaks(List.of( Material.PINK_PETALS, Material.ALLIUM, Material.CHERRY_SAPLING ))
|
||||
.addDepend(WOODEN_HOE);
|
||||
|
||||
public static final Hoe COPPER_HOE = new Hoe(Material.COPPER_HOE, "<gold><b>Copper Hoe</b><gold>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.WIND, 25)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.DOUBLE_DROP, 15)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.FORTUNE, 1)
|
||||
.limit(ToolUtils.EnchantmentType.FORTUNE, 5)
|
||||
.limit(ToolUtils.EnchantmentType.WIND, 40)
|
||||
.addCanBreaks(List.of(
|
||||
Material.OXEYE_DAISY, Material.LILY_OF_THE_VALLEY, Material.CLOSED_EYEBLOSSOM,
|
||||
Material.CARROTS, Material.POTATOES
|
||||
))
|
||||
.addDepend(STONE_HOE);
|
||||
|
||||
public static final Hoe IRON_HOE = new Hoe(Material.IRON_HOE, "<white><b>Iron Hoe</b><white>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.WIND, 30)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.DOUBLE_DROP, 20)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.FORTUNE, 2)
|
||||
.limit(ToolUtils.EnchantmentType.FORTUNE, 10)
|
||||
.limit(ToolUtils.EnchantmentType.WIND, 50)
|
||||
.addAbility(ToolUtils.AbilityType.JACKPOT, 3, 0, 0, true)
|
||||
.addAbility(ToolUtils.AbilityType.MULTIPLIER, 1.5, 5, 50_000, false)
|
||||
.addCanBreaks(
|
||||
List.of(
|
||||
Material.WHEAT,
|
||||
Material.BEETROOTS,
|
||||
Material.DEAD_FIRE_CORAL, Material.DEAD_TUBE_CORAL, Material.DEAD_HORN_CORAL, Material.DEAD_BRAIN_CORAL_FAN, Material.OPEN_EYEBLOSSOM
|
||||
))
|
||||
.addDepend(COPPER_HOE);
|
||||
|
||||
public static final Hoe DIAMOND_HOE = new Hoe(Material.DIAMOND_HOE, "<aqua><b>Diamond Hoe</b><aqua>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.WIND, 30)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.DOUBLE_DROP, 20)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.FORTUNE, 3)
|
||||
.limit(ToolUtils.EnchantmentType.FORTUNE, 20)
|
||||
.limit(ToolUtils.EnchantmentType.WIND, 60)
|
||||
.addAbility(ToolUtils.AbilityType.MULTIPLIER, 2.5, 5, 50_000, false)
|
||||
.addAbility(ToolUtils.AbilityType.JACKPOT, 5, 0, 0, true)
|
||||
.addAbility(ToolUtils.AbilityType.THREE_BY_THREE, 0, 0, 0, true)
|
||||
.addAbility(ToolUtils.AbilityType.AUTOSELL, 1, 15, 100_000, false)
|
||||
.addCanBreaks( List.of( Material.ROSE_BUSH, Material.POPPY, Material.RED_MUSHROOM, Material.CRIMSON_FUNGUS ) )
|
||||
.addDepend(IRON_HOE);
|
||||
|
||||
public static final Hoe NETHERITE_HOE = new Hoe(Material.NETHERITE_HOE, "<dark_purple><b>Netherite Hoe</b><dark_purple>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.WIND, 40)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.DOUBLE_DROP, 30)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.FORTUNE, 4)
|
||||
.limit(ToolUtils.EnchantmentType.FORTUNE, 30)
|
||||
.limit(ToolUtils.EnchantmentType.WIND, 70)
|
||||
.limit(ToolUtils.EnchantmentType.DOUBLE_DROP, 60)
|
||||
.addAbility(ToolUtils.AbilityType.MULTIPLIER, 5, 5, 75_000, false)
|
||||
.addAbility(ToolUtils.AbilityType.AUTOSELL, 1, 25, 150_000, false)
|
||||
.addAbility(ToolUtils.AbilityType.THREE_BY_THREE, 0, 0, 0, true)
|
||||
.addAbility(ToolUtils.AbilityType.JACKPOT, 15, 0, 15_000, true)
|
||||
.addCanBreaks( List.of( Material.CACTUS_FLOWER, Material.PEONY, Material.PINK_TULIP, Material.CACTUS ) )
|
||||
.addDepend(DIAMOND_HOE);
|
||||
|
||||
public static final HashMap<Material, Hoe> ALL_HOES = new HashMap<>(
|
||||
Map.of(WOODEN_HOE.material, WOODEN_HOE,
|
||||
STONE_HOE.material, STONE_HOE,
|
||||
COPPER_HOE.material, COPPER_HOE,
|
||||
IRON_HOE.material, IRON_HOE,
|
||||
DIAMOND_HOE.material, DIAMOND_HOE,
|
||||
NETHERITE_HOE.material, NETHERITE_HOE
|
||||
)
|
||||
);
|
||||
|
||||
public static final List<Hoe> ORDERED = List.of(
|
||||
WOODEN_HOE,
|
||||
STONE_HOE,
|
||||
COPPER_HOE,
|
||||
IRON_HOE,
|
||||
DIAMOND_HOE,
|
||||
NETHERITE_HOE
|
||||
);
|
||||
|
||||
public static @Nullable Hoe getRequiredHoe(Material material) {
|
||||
for (Hoe hoe : ORDERED) {
|
||||
if (getAllBlocks(hoe).contains(material)) {
|
||||
return hoe;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<Material> getAllBlocks(Hoe hoe) {
|
||||
Set<Material> result = new HashSet<>();
|
||||
Set<Hoe> visited = new HashSet<>();
|
||||
|
||||
collectBlocks(hoe, result, visited);
|
||||
|
||||
return new ArrayList<>(result);
|
||||
}
|
||||
|
||||
private static void collectBlocks(Hoe hoe, Set<Material> result, Set<Hoe> visited) {
|
||||
if (!visited.add(hoe)) return; // already processed
|
||||
|
||||
result.addAll(hoe.canBreak);
|
||||
|
||||
for (Hoe parent : hoe.dependsOn) {
|
||||
collectBlocks(parent, result, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
85
src/main/java/ovh/sad/snowcore/items/Shovel.java
Normal file
85
src/main/java/ovh/sad/snowcore/items/Shovel.java
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package ovh.sad.snowcore.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import java.util.*;
|
||||
|
||||
public class Shovel extends Tool<Shovel> {
|
||||
public Shovel(Material material, String name) {
|
||||
super(material, name);
|
||||
}
|
||||
|
||||
public static class ShovelDefinitions {
|
||||
public static final Shovel WOODEN_SHOVEL = new Shovel(Material.WOODEN_SHOVEL, "<#90bbdc><b>Wooden Shovel</b></#90bbdc>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.SWEEPER, 10)
|
||||
.limit(ToolUtils.EnchantmentType.SWEEPER, 15);
|
||||
public static final Shovel STONE_SHOVEL = new Shovel(Material.STONE_SHOVEL, "<gray><b>Stone Shovel</b><gray>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.SWEEPER, 20)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.LAYER_REMOVER, 10)
|
||||
.limit(ToolUtils.EnchantmentType.SWEEPER, 25)
|
||||
.limit(ToolUtils.EnchantmentType.LAYER_REMOVER, 12);
|
||||
|
||||
public static final Shovel COPPER_SHOVEL = new Shovel(Material.COPPER_SHOVEL, "<#6699ff><b>Copper Shovel</b><#6699ff>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.SWEEPER, 30)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.LAYER_REMOVER, 15)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.FORTUNE, 1)
|
||||
.limit(ToolUtils.EnchantmentType.FORTUNE, 5)
|
||||
.limit(ToolUtils.EnchantmentType.SWEEPER, 40)
|
||||
.limit(ToolUtils.EnchantmentType.LAYER_REMOVER, 20);
|
||||
|
||||
public static final Shovel IRON_SHOVEL = new Shovel(Material.IRON_SHOVEL, "<white><b>Iron Shovel</b><white>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.SWEEPER, 40)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.LAYER_REMOVER, 25)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.FORTUNE, 3)
|
||||
.limit(ToolUtils.EnchantmentType.FORTUNE, 10)
|
||||
.limit(ToolUtils.EnchantmentType.SWEEPER, 50)
|
||||
.limit(ToolUtils.EnchantmentType.LAYER_REMOVER, 30)
|
||||
.addAbility(ToolUtils.AbilityType.NUKE, 0, 0, 0, true)
|
||||
.addAbility(ToolUtils.AbilityType.MULTIPLIER, 2, 5, 40_000, false)
|
||||
.addAbility(ToolUtils.AbilityType.AUTOSELL, 1, 20, 15_000, false);
|
||||
|
||||
public static final Shovel DIAMOND_SHOVEL = new Shovel(Material.DIAMOND_SHOVEL, "<#ff5555><b>Diamond Shovel</b><#ff5555>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.SWEEPER, 50)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.LAYER_REMOVER, 35)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.FORTUNE, 4)
|
||||
.limit(ToolUtils.EnchantmentType.FORTUNE, 10)
|
||||
.limit(ToolUtils.EnchantmentType.SWEEPER, 60)
|
||||
.limit(ToolUtils.EnchantmentType.LAYER_REMOVER, 40)
|
||||
.addAbility(ToolUtils.AbilityType.NUKE, 0, 0, 0, true)
|
||||
.addAbility(ToolUtils.AbilityType.WIFI, 2, 0, 0, true)
|
||||
.addAbility(ToolUtils.AbilityType.MULTIPLIER, 3, 5, 50_000, false)
|
||||
.addAbility(ToolUtils.AbilityType.AUTOSELL, 1, 25, 40_000, false);
|
||||
|
||||
public static final Shovel NETHERITE_SHOVEL = new Shovel(Material.NETHERITE_SHOVEL, "<#4dc44d><b>Netherite Shovel</b><#4dc44d>")
|
||||
.addEnchantment(ToolUtils.EnchantmentType.SWEEPER, 50)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.LAYER_REMOVER, 40)
|
||||
.addEnchantment(ToolUtils.EnchantmentType.FORTUNE, 6)
|
||||
.limit(ToolUtils.EnchantmentType.FORTUNE, 30)
|
||||
.limit(ToolUtils.EnchantmentType.SWEEPER, 100)
|
||||
.limit(ToolUtils.EnchantmentType.LAYER_REMOVER, 100)
|
||||
.addAbility(ToolUtils.AbilityType.MULTIPLIER, 5, 5, 75_000, false)
|
||||
.addAbility(ToolUtils.AbilityType.AUTOSELL, 1, 30, 60_000, false)
|
||||
.addAbility(ToolUtils.AbilityType.NUKE, 0, 0, 0, true)
|
||||
.addAbility(ToolUtils.AbilityType.WIFI, 6, 0, 0 , true);
|
||||
|
||||
public static final HashMap<Material, Shovel> ALL_SHOVELS = new HashMap<>(
|
||||
Map.of(WOODEN_SHOVEL.material, WOODEN_SHOVEL,
|
||||
STONE_SHOVEL.material, STONE_SHOVEL,
|
||||
COPPER_SHOVEL.material, COPPER_SHOVEL,
|
||||
IRON_SHOVEL.material, IRON_SHOVEL,
|
||||
DIAMOND_SHOVEL.material, DIAMOND_SHOVEL,
|
||||
NETHERITE_SHOVEL.material, NETHERITE_SHOVEL
|
||||
)
|
||||
);
|
||||
|
||||
public static final List<Shovel> ORDERED = List.of(
|
||||
WOODEN_SHOVEL,
|
||||
STONE_SHOVEL,
|
||||
COPPER_SHOVEL,
|
||||
IRON_SHOVEL,
|
||||
DIAMOND_SHOVEL,
|
||||
NETHERITE_SHOVEL
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
184
src/main/java/ovh/sad/snowcore/items/Tool.java
Normal file
184
src/main/java/ovh/sad/snowcore/items/Tool.java
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
package ovh.sad.snowcore.items;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public abstract class Tool<T extends Tool<T>> {
|
||||
|
||||
public final Material material;
|
||||
protected final String name;
|
||||
|
||||
public final List<ToolUtils.Enchantment> definedEnchantments = new ArrayList<>();
|
||||
public final Map<ToolUtils.EnchantmentType, Integer> definedEnchantLimits = new HashMap<>();
|
||||
public final List<ToolUtils.Ability> definedAbilities = new ArrayList<>();
|
||||
|
||||
protected Tool(Material material, String name) {
|
||||
this.material = material;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public ItemStack renderItem(@Nullable ItemStack itemToRender) {
|
||||
if(itemToRender == null) {
|
||||
ItemStack item = new ItemStack(material);
|
||||
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
|
||||
if(meta == null) throw new Error("RenderItem errored, meta could not be found!");
|
||||
|
||||
Component name = MiniMessage.miniMessage().deserialize(
|
||||
"<shadow:black><gray>[</gray><green>✩ 1<gray>, <blue>❁ 1</blue>]</gray></shadow> " + this.name
|
||||
);
|
||||
|
||||
meta.setUnbreakable(true);
|
||||
meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE);
|
||||
meta.displayName(name);
|
||||
|
||||
List<Component> lore = new ArrayList<>();
|
||||
List<ToolUtils.Ability> abilities = definedAbilities.stream().filter(a -> a.byDefault).toList();
|
||||
|
||||
lore.add(MiniMessage.miniMessage().deserialize(
|
||||
"<gray><i>(Shift rightclick to upgrade!)"
|
||||
));
|
||||
|
||||
lore.add(Component.text(""));
|
||||
|
||||
definedEnchantments.forEach(z -> {
|
||||
if(z.type.usesLevel) {
|
||||
lore.add(MiniMessage.miniMessage().deserialize(
|
||||
"<yellow> • " + z.type.name + " " + z.amount
|
||||
));
|
||||
} else {
|
||||
lore.add(MiniMessage.miniMessage().deserialize(
|
||||
"<white> • " + z.type.name + " (" + z.amount + "%)"
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
abilities.forEach(z -> {
|
||||
lore.add(MiniMessage.miniMessage().deserialize(
|
||||
"<aqua> ⭐ <b>" + z.type.name + "</b>, " + z.type.description.replaceAll("%maxValue%", String.valueOf(z.maxValue))
|
||||
));
|
||||
});
|
||||
|
||||
meta.lore(lore);
|
||||
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
|
||||
pdc.set(ToolUtils.EXPERIENCE, PersistentDataType.INTEGER, 1);
|
||||
pdc.set(ToolUtils.TOTAL_EXPERIENCE, PersistentDataType.INTEGER, 1);
|
||||
|
||||
pdc.set(ToolUtils.ENCHANTMENTS,
|
||||
PersistentDataType.LIST.listTypeFrom(ToolUtils.Enchantment.EMPTY_INSTANCE),
|
||||
definedEnchantments);
|
||||
|
||||
pdc.set(ToolUtils.ABILITIES,
|
||||
PersistentDataType.LIST.listTypeFrom(ToolUtils.Ability.EMPTY_INSTANCE),
|
||||
definedAbilities.stream().filter(a -> a.byDefault).toList());
|
||||
|
||||
meta.setEnchantmentGlintOverride(true);
|
||||
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
} else {
|
||||
ItemStack item = itemToRender.clone();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
|
||||
if(meta == null) throw new Error("RenderItem errored, meta could not be found!");
|
||||
|
||||
Integer exp = ToolUtils.getExp(item);
|
||||
Integer level = ToolUtils.getLevel(item);
|
||||
|
||||
Component name = MiniMessage.miniMessage().deserialize(
|
||||
"<shadow:black><gray>[</gray><green>✩ " + exp + "<gray>, <blue>❁ " + level + "</blue>]</gray></shadow> " + this.name
|
||||
);
|
||||
meta.displayName(name);
|
||||
|
||||
List<Component> lore = new ArrayList<>();
|
||||
List<ToolUtils.Ability> abilities = ToolUtils.getAbilities(item);
|
||||
|
||||
lore.add(MiniMessage.miniMessage().deserialize(
|
||||
"<gray><i>(Shift rightclick to upgrade!)"
|
||||
));
|
||||
|
||||
lore.add(Component.text(""));
|
||||
ToolUtils.getEnchantments(item).forEach(z -> {
|
||||
if(z.type.usesLevel) {
|
||||
lore.add(MiniMessage.miniMessage().deserialize(
|
||||
"<yellow> • " + z.type.name + " " + z.amount
|
||||
));
|
||||
} else {
|
||||
lore.add(MiniMessage.miniMessage().deserialize(
|
||||
"<white> • " + z.type.name + " (" + z.amount + "%)"
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
abilities.forEach(z -> {
|
||||
lore.add(MiniMessage.miniMessage().deserialize(
|
||||
"<aqua> ⭐ <b>" + z.type.name + "</b>, " + z.type.description.replaceAll("%maxValue%", String.valueOf(z.maxValue))
|
||||
));
|
||||
});
|
||||
|
||||
meta.lore(lore);
|
||||
|
||||
meta.setEnchantmentGlintOverride(true);
|
||||
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public ItemStack increaseEXP(ItemStack item, int amount) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return item;
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
|
||||
int exp = pdc.get(ToolUtils.EXPERIENCE, PersistentDataType.INTEGER) + amount;
|
||||
int total = pdc.get(ToolUtils.TOTAL_EXPERIENCE, PersistentDataType.INTEGER) + amount;
|
||||
|
||||
pdc.set(ToolUtils.EXPERIENCE, PersistentDataType.INTEGER, exp);
|
||||
pdc.set(ToolUtils.TOTAL_EXPERIENCE, PersistentDataType.INTEGER, total);
|
||||
|
||||
item.setItemMeta(meta);
|
||||
return this.renderItem(item);
|
||||
}
|
||||
|
||||
public Component name() {
|
||||
return MiniMessage.miniMessage().deserialize(name);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
/* Builder API (shared) */
|
||||
/* ------------------------------------------------ */
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T addEnchantment(ToolUtils.EnchantmentType type, int amount) {
|
||||
definedEnchantments.add(new ToolUtils.Enchantment(type, amount));
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T limit(ToolUtils.EnchantmentType type, int max) {
|
||||
definedEnchantLimits.put(type, max);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T addAbility(ToolUtils.AbilityType type, double max, int unlock, double cost, boolean byDefault) {
|
||||
definedAbilities.add(new ToolUtils.Ability(type, max, unlock, cost, byDefault));
|
||||
return (T) this;
|
||||
}
|
||||
}
|
||||
267
src/main/java/ovh/sad/snowcore/items/ToolUtils.java
Normal file
267
src/main/java/ovh/sad/snowcore/items/ToolUtils.java
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
package ovh.sad.snowcore.items;
|
||||
|
||||
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import ovh.sad.snowcore.Snowcore;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
public class ToolUtils {
|
||||
static final NamespacedKey ABILITIES = new NamespacedKey(Snowcore.PLUGIN, "snowcore_abilities");
|
||||
static final NamespacedKey ENCHANTMENTS = new NamespacedKey(Snowcore.PLUGIN, "snowcore_enchants");
|
||||
static final NamespacedKey EXPERIENCE = new NamespacedKey(Snowcore.PLUGIN, "snowcore_exp");
|
||||
static final NamespacedKey TOTAL_EXPERIENCE = new NamespacedKey(Snowcore.PLUGIN, "snowcore_exp_total");
|
||||
|
||||
public enum EnchantmentType {
|
||||
|
||||
FORTUNE("Fortune", 200, true),
|
||||
WIND("Wind", 120, false),
|
||||
DOUBLE_DROP("Double Drop", 350, false),
|
||||
NONE("418 Teapot", 0, false),
|
||||
|
||||
SWEEPER("Sweeper", 100, false),
|
||||
LAYER_REMOVER("Layer Remover", 150, false);
|
||||
|
||||
public final String name;
|
||||
public final int baseExpCost;
|
||||
public final boolean usesLevel; // false = chance %
|
||||
|
||||
EnchantmentType(String name, int baseExpCost, boolean usesLevel) {
|
||||
this.baseExpCost = baseExpCost;
|
||||
this.usesLevel = usesLevel;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Enchantment implements PersistentDataType<byte[], Enchantment> {
|
||||
public static final Enchantment EMPTY_INSTANCE = new Enchantment(EnchantmentType.NONE, 0);
|
||||
|
||||
public final EnchantmentType type;
|
||||
public int amount;
|
||||
|
||||
public Enchantment(EnchantmentType type, int amount) {
|
||||
this.type = type;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public int getExpRequired(int level) {
|
||||
if(type.usesLevel) {
|
||||
return type.baseExpCost * level * level;
|
||||
} else {
|
||||
double l = level / 10.0;
|
||||
return (int)Math.ceil(type.baseExpCost * Math.pow(l, 1.7));
|
||||
}
|
||||
}
|
||||
|
||||
public Enchantment copy() {
|
||||
return new Enchantment(type, amount);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull Class<byte[]> getPrimitiveType() {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Class<Enchantment> getComplexType() {
|
||||
return Enchantment.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @NotNull [] toPrimitive(Enchantment complex, @NotNull PersistentDataAdapterContext context) {
|
||||
ByteBuffer bb = ByteBuffer.allocate(
|
||||
Integer.BYTES + // enum ordinal
|
||||
Integer.BYTES // amount
|
||||
);
|
||||
|
||||
bb.putInt(complex.type.ordinal());
|
||||
bb.putInt(complex.amount);
|
||||
|
||||
return bb.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Enchantment fromPrimitive(byte @NotNull [] primitive, @NotNull PersistentDataAdapterContext context) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(primitive);
|
||||
|
||||
EnchantmentType type = EnchantmentType.values()[bb.getInt()];
|
||||
int amount = bb.getInt();
|
||||
|
||||
return new Enchantment(type, amount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum AbilityType {
|
||||
// Hoe only
|
||||
JACKPOT("Jackpot", "you have a %maxValue%% chance to drop 5x items!"),
|
||||
THREE_BY_THREE("3x3", "you mine blocks in a 3x3 grid!"),
|
||||
|
||||
// Shovel only
|
||||
NUKE("Nuke", "you have a 10% chance to remove 108 layers of snow in a 6x6 area!"),
|
||||
WIFI("Free wifi anywhere you go", "you have a %maxValue%% chance of getting a inventory full of snowballs!"),
|
||||
|
||||
// Mutual
|
||||
MULTIPLIER("Multiplier", "your drops are multiplied by %maxValue%x!"),
|
||||
AUTOSELL("Auto-sell", "you automatically sell all of your items when your inventory is full"),
|
||||
|
||||
NONE("HTTP", "418 Teapot");
|
||||
|
||||
public final String name;
|
||||
public final String description;
|
||||
|
||||
AbilityType(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ability implements PersistentDataType<byte[], Ability> {
|
||||
public static final Ability EMPTY_INSTANCE = new Ability(AbilityType.NONE, 0, 0, 0, false);
|
||||
|
||||
public AbilityType type;
|
||||
|
||||
public double maxValue;
|
||||
public int unlockLevel;
|
||||
public double baseCost;
|
||||
|
||||
public boolean byDefault;
|
||||
|
||||
public Ability(AbilityType type, double maxValue, int unlockLevel, double baseCost, boolean byDefault) {
|
||||
this.type = type;
|
||||
this.maxValue = maxValue;
|
||||
this.unlockLevel = unlockLevel;
|
||||
this.baseCost = baseCost;
|
||||
this.byDefault = byDefault;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull Class<byte[]> getPrimitiveType() {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Class<Ability> getComplexType() {
|
||||
return Ability.class;
|
||||
}
|
||||
@Override
|
||||
public byte @NotNull [] toPrimitive(Ability complex, @NotNull PersistentDataAdapterContext context) {
|
||||
ByteBuffer bb = ByteBuffer.allocate(
|
||||
Integer.BYTES + // enum ordinal
|
||||
Double.BYTES + // maxValue
|
||||
Integer.BYTES + // unlockLevel
|
||||
Double.BYTES + // baseCost
|
||||
Short.BYTES // byDefault
|
||||
);
|
||||
|
||||
bb.putInt(complex.type.ordinal());
|
||||
bb.putDouble(complex.maxValue);
|
||||
bb.putInt(complex.unlockLevel);
|
||||
bb.putDouble(complex.baseCost);
|
||||
|
||||
if(complex.byDefault) {
|
||||
bb.putShort((short)1);
|
||||
} else {
|
||||
bb.putShort((short)0);
|
||||
}
|
||||
return bb.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Ability fromPrimitive(byte @NotNull [] primitive, @NotNull PersistentDataAdapterContext context) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(primitive);
|
||||
|
||||
AbilityType type = AbilityType.values()[bb.getInt()];
|
||||
double maxValue = bb.getDouble();
|
||||
int unlockLevel = bb.getInt();
|
||||
double baseCost = bb.getDouble();
|
||||
boolean byDefault = bb.getShort() == 1;
|
||||
|
||||
return new Ability(type, maxValue, unlockLevel, baseCost, byDefault);
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable Integer getLevel(@NotNull ItemStack item) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return null;
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
|
||||
return Snowcore.getLevelFromExp(pdc.get(ToolUtils.TOTAL_EXPERIENCE, PersistentDataType.INTEGER));
|
||||
}
|
||||
|
||||
public static @Nullable Integer getExp(@NotNull ItemStack item) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return null;
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
|
||||
return pdc.get(ToolUtils.EXPERIENCE, PersistentDataType.INTEGER);
|
||||
}
|
||||
|
||||
public static @Nullable ItemStack setExp(Tool<?> tool, @NotNull ItemStack item, Integer exp) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return null;
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
pdc.set(ToolUtils.EXPERIENCE, PersistentDataType.INTEGER, exp);
|
||||
item.setItemMeta(meta);
|
||||
|
||||
return tool.renderItem(item);
|
||||
}
|
||||
public static @Nullable List<Enchantment> getEnchantments(@NotNull ItemStack item) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return null;
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
|
||||
return pdc.get(ToolUtils.ENCHANTMENTS, PersistentDataType.LIST.listTypeFrom(ToolUtils.Enchantment.EMPTY_INSTANCE));
|
||||
}
|
||||
|
||||
public static @NotNull ItemStack setEnchantments(Tool<?> tool, @NotNull ItemStack item, @NotNull List<ToolUtils.Enchantment> enchantments) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return item;
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
pdc.set(ToolUtils.ENCHANTMENTS,
|
||||
PersistentDataType.LIST.listTypeFrom(ToolUtils.Enchantment.EMPTY_INSTANCE),
|
||||
enchantments);
|
||||
|
||||
item.setItemMeta(meta);
|
||||
return tool.renderItem(item);
|
||||
}
|
||||
|
||||
public static @Nullable List<ToolUtils.Ability> getAbilities(@NotNull ItemStack item) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return null;
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
|
||||
return pdc.get(ToolUtils.ABILITIES, PersistentDataType.LIST.listTypeFrom(ToolUtils.Ability.EMPTY_INSTANCE));
|
||||
}
|
||||
|
||||
public static @NotNull ItemStack setAbilities(Tool<?> tool, @NotNull ItemStack item, @NotNull List<ToolUtils.Ability> abilities) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return item;
|
||||
|
||||
PersistentDataContainer pdc = meta.getPersistentDataContainer();
|
||||
pdc.set(ToolUtils.ABILITIES,
|
||||
PersistentDataType.LIST.listTypeFrom(ToolUtils.Ability.EMPTY_INSTANCE),
|
||||
abilities);
|
||||
|
||||
item.setItemMeta(meta);
|
||||
return tool.renderItem(item);
|
||||
}
|
||||
|
||||
}
|
||||
400
src/main/java/ovh/sad/snowcore/listeners/BreakListener.java
Normal file
400
src/main/java/ovh/sad/snowcore/listeners/BreakListener.java
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
package ovh.sad.snowcore.listeners;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.title.Title;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.type.Snow;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import ovh.sad.snowcore.items.Hoe;
|
||||
import ovh.sad.snowcore.items.ToolUtils;
|
||||
import ovh.sad.snowcore.Snowcore;
|
||||
import ovh.sad.snowcore.items.Shovel;
|
||||
|
||||
|
||||
public class BreakListener implements Listener {
|
||||
@EventHandler
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
// Creative mode can break anything
|
||||
if (player.getGameMode() == GameMode.CREATIVE) return;
|
||||
|
||||
PlayerInventory inv = player.getInventory();
|
||||
ItemStack hand = inv.getItemInMainHand();
|
||||
Material tool = hand.getType();
|
||||
Material blockType = event.getBlock().getType();
|
||||
|
||||
Hoe hoe = Hoe.HoeDefinitions.ALL_HOES.get(tool);
|
||||
|
||||
// If block has a required hoe and player doesn't meet it
|
||||
if (hoe != null && !Hoe.HoeDefinitions.getAllBlocks(hoe).contains(blockType)) {
|
||||
event.setCancelled(true);
|
||||
Snowcore.sendActionBar(player,
|
||||
Component.text("You need at least a ")
|
||||
.append(Objects.requireNonNull(Hoe.HoeDefinitions.getRequiredHoe(blockType)).name())
|
||||
.append(Component.text(" to break this block!"))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(hoe != null) {
|
||||
event.setDropItems(false);
|
||||
List<ToolUtils.Ability> abilities = ToolUtils.getAbilities(hand);
|
||||
List<ToolUtils.Enchantment> enchants = ToolUtils.getEnchantments(hand);
|
||||
|
||||
int multiplier = 1;
|
||||
|
||||
Optional<ToolUtils.Ability> hasMultiplier
|
||||
= abilities.stream().filter(z -> z.type == ToolUtils.AbilityType.MULTIPLIER).findFirst();
|
||||
Optional<ToolUtils.Ability> hasJackpot
|
||||
= abilities.stream().filter(z -> z.type == ToolUtils.AbilityType.JACKPOT).findFirst();
|
||||
Optional<ToolUtils.Enchantment> windEnchantment
|
||||
= enchants.stream().filter(z -> z.type == ToolUtils.EnchantmentType.WIND).findFirst();
|
||||
Optional<ToolUtils.Enchantment> doubleDropEnchantment
|
||||
= enchants.stream().filter(z -> z.type == ToolUtils.EnchantmentType.DOUBLE_DROP).findFirst();
|
||||
|
||||
if(hasMultiplier.isPresent()) {
|
||||
multiplier = (int)Math.ceil(hasMultiplier.get().maxValue);
|
||||
}
|
||||
|
||||
|
||||
boolean jackpotHit = false;
|
||||
double random = Math.random(); // 0.0 - 1.0
|
||||
|
||||
if (hasJackpot.isPresent()) {
|
||||
if (random <= (hasJackpot.get().maxValue / 100)) { // 10% chance
|
||||
multiplier *= 5;
|
||||
jackpotHit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (jackpotHit) {
|
||||
player.sendActionBar(MiniMessage.miniMessage().deserialize("<yellow> ࿏࿏࿏࿏ <white> Jackpot! You got 5x drops!"));
|
||||
}
|
||||
|
||||
ArrayList<ItemStack> drops = new ArrayList<>();
|
||||
List<Material> breakableBlocks = Hoe.HoeDefinitions.getAllBlocks(hoe);
|
||||
List<Block> blocksToBreak = new ArrayList<>();
|
||||
blocksToBreak.add(event.getBlock());
|
||||
|
||||
if (abilities.stream().anyMatch(z -> z.type == ToolUtils.AbilityType.THREE_BY_THREE)) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
for (int dz = -1; dz <= 1; dz++) {
|
||||
if (dx == 0 && dz == 0) continue; // skip center (already added)
|
||||
Block b = event.getBlock().getWorld().getBlockAt(
|
||||
event.getBlock().getX() + dx,
|
||||
event.getBlock().getY(),
|
||||
event.getBlock().getZ() + dz
|
||||
);
|
||||
if (breakableBlocks.contains(b.getType())) {
|
||||
blocksToBreak.add(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(windEnchantment.isPresent() && Math.random() <= (double) windEnchantment.get().amount / 100.0) {
|
||||
org.bukkit.util.Vector randomDir = new org.bukkit.util.Vector(
|
||||
(Math.random() * 2.0) - 1.0,
|
||||
0,
|
||||
(Math.random() * 2.0) - 1.0
|
||||
).normalize();
|
||||
|
||||
World world = event.getBlock().getWorld();
|
||||
Block origin = event.getBlock();
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
org.bukkit.util.Vector offset = randomDir.clone().multiply(i);
|
||||
|
||||
Block b = world.getBlockAt(
|
||||
origin.getX() + offset.getBlockX(),
|
||||
origin.getY(),
|
||||
origin.getZ() + offset.getBlockZ()
|
||||
);
|
||||
|
||||
if (breakableBlocks.contains(b.getType())) {
|
||||
blocksToBreak.add(b);
|
||||
}
|
||||
}
|
||||
|
||||
//org.bukkit.util.Vector velocityBoost = randomDir.clone().multiply(0.2);
|
||||
//velocityBoost.setY(0.1);
|
||||
//player.setVelocity(player.getVelocity().add(velocityBoost));
|
||||
}
|
||||
|
||||
for (Block b : blocksToBreak) {
|
||||
Collection<ItemStack> dropsForThisBlock = b.getDrops(hand);
|
||||
|
||||
if(dropsForThisBlock.isEmpty()) {
|
||||
drops.add(new ItemStack(b.getType(), 1));
|
||||
}
|
||||
|
||||
drops.addAll(dropsForThisBlock);
|
||||
|
||||
b.setType(Material.AIR);
|
||||
}
|
||||
|
||||
Optional<ToolUtils.Ability> hasAutosell
|
||||
= abilities.stream().filter(z -> z.type == ToolUtils.AbilityType.AUTOSELL).findFirst();
|
||||
Optional<ToolUtils.Enchantment> fortuneLevel = enchants.stream().filter(z -> z.type == ToolUtils.EnchantmentType.FORTUNE).findFirst();
|
||||
|
||||
int exp = 0;
|
||||
|
||||
for (ItemStack drop : drops) {
|
||||
int amount = drop.getAmount();
|
||||
|
||||
if(fortuneLevel.isPresent()) {
|
||||
amount = Snowcore.fortuneToDrops(fortuneLevel.get().amount, drop.getAmount());
|
||||
}
|
||||
|
||||
if(doubleDropEnchantment.isPresent() && Math.random() <= (double) doubleDropEnchantment.get().amount / 100.0) {
|
||||
drop.setAmount((int) Math.ceil(Math.pow(amount * multiplier, 2)));
|
||||
|
||||
} else {
|
||||
drop.setAmount(amount * multiplier);
|
||||
}
|
||||
|
||||
exp += amount * multiplier;
|
||||
|
||||
HashMap<Integer, ItemStack> leftover = inv.addItem(drop);
|
||||
if(!leftover.isEmpty()) {
|
||||
if(hasAutosell.isPresent()) {
|
||||
Snowcore.sellInventory(player);
|
||||
} else {
|
||||
fullInventory(player);
|
||||
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ItemStack stack = hoe.increaseEXP(hand, exp);
|
||||
|
||||
inv.setItemInMainHand(stack);
|
||||
|
||||
player.incrementStatistic(Statistic.MINE_BLOCK, Material.BEDROCK, breakableBlocks.size()); // bedrock because i'd like to group all flowers up into a single statistic. this works well enough!
|
||||
event.getBlock().setType(Material.AIR);
|
||||
} else {
|
||||
if(Tag.ITEMS_SHOVELS.isTagged(tool) && blockType.equals(Material.SNOW)) {
|
||||
event.setDropItems(false);
|
||||
Snow snowData = (Snow) event.getBlock().getBlockData();
|
||||
Shovel shovel = Shovel.ShovelDefinitions.ALL_SHOVELS.get(tool);
|
||||
|
||||
List<ToolUtils.Ability> abilities = ToolUtils.getAbilities(hand);
|
||||
List<ToolUtils.Enchantment> enchants = ToolUtils.getEnchantments(hand);
|
||||
|
||||
int amountOfSnow = snowData.getLayers();
|
||||
|
||||
Block origin = event.getBlock();
|
||||
var world = origin.getWorld();
|
||||
|
||||
ToolUtils.Ability nukeAbility = abilities.stream()
|
||||
.filter(a -> a.type == ToolUtils.AbilityType.NUKE)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
ToolUtils.Enchantment sweepEnchantment = enchants.stream()
|
||||
.filter(a -> a.type == ToolUtils.EnchantmentType.SWEEPER)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
ToolUtils.Enchantment layerRemoverEnchantment = enchants.stream()
|
||||
.filter(a -> a.type == ToolUtils.EnchantmentType.LAYER_REMOVER)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
ToolUtils.Ability multiplierAbility = abilities.stream()
|
||||
.filter(a -> a.type == ToolUtils.AbilityType.MULTIPLIER)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
boolean hasNuke = nukeAbility != null;
|
||||
double chance = hasNuke ? nukeAbility.maxValue : 0.0;
|
||||
|
||||
boolean triggerNuke = hasNuke && Math.random() <= chance / 100.0;
|
||||
|
||||
List<Pair<Block, Integer>> targets = new ArrayList<>();
|
||||
|
||||
targets.add(Pair.of(event.getBlock(), amountOfSnow));
|
||||
|
||||
if (sweepEnchantment != null &&
|
||||
Math.random() <= (double) sweepEnchantment.amount / 100.0) {
|
||||
|
||||
// player horizontal facing
|
||||
org.bukkit.util.Vector forward = player.getLocation().getDirection().setY(0).normalize();
|
||||
|
||||
// perpendicular sideways vector
|
||||
org.bukkit.util.Vector side = new org.bukkit.util.Vector(-forward.getZ(), 0, forward.getX());
|
||||
|
||||
for (int depth = 1; depth <= 2; depth++) {
|
||||
for (int width = -1; width <= 1; width++) {
|
||||
|
||||
org.bukkit.util.Vector offset = forward.clone().multiply(depth)
|
||||
.add(side.clone().multiply(width));
|
||||
|
||||
Block b = origin.getRelative(
|
||||
offset.getBlockX(),
|
||||
0,
|
||||
offset.getBlockZ()
|
||||
);
|
||||
|
||||
if (b.getType() == Material.SNOW) {
|
||||
Snow snow = (Snow) b.getBlockData();
|
||||
int snowToRemove = Math.min(2, snow.getLayers());
|
||||
amountOfSnow += snowToRemove;
|
||||
targets.add(Pair.of(b, snowToRemove));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(layerRemoverEnchantment != null && Math.random() <= (double) layerRemoverEnchantment.amount / 100.0) {
|
||||
org.bukkit.util.Vector forward = player.getLocation().getDirection().setY(0).normalize();
|
||||
org.bukkit.util.Vector side = new org.bukkit.util.Vector(-forward.getZ(), 0, forward.getX());
|
||||
|
||||
for (int depth = 1; depth <= 6; depth++) {
|
||||
org.bukkit.util.Vector forwardOffset = forward.clone().multiply(depth);
|
||||
|
||||
Block center = origin.getRelative(
|
||||
forwardOffset.getBlockX(),
|
||||
0,
|
||||
forwardOffset.getBlockZ()
|
||||
);
|
||||
|
||||
if (center.getType() == Material.SNOW) {
|
||||
Snow snow = (Snow) center.getBlockData();
|
||||
int layers = snow.getLayers();
|
||||
amountOfSnow += layers;
|
||||
targets.add(Pair.of(center, layers));
|
||||
}
|
||||
|
||||
for (int dir = -1; dir <= 1; dir += 2) {
|
||||
org.bukkit.util.Vector sideOffset = forwardOffset.clone()
|
||||
.add(side.clone().multiply(dir));
|
||||
|
||||
Block sideBlock = origin.getRelative(
|
||||
sideOffset.getBlockX(),
|
||||
0,
|
||||
sideOffset.getBlockZ()
|
||||
);
|
||||
|
||||
if (sideBlock.getType() == Material.SNOW) {
|
||||
Snow snow = (Snow) sideBlock.getBlockData();
|
||||
int snowToRemove = Math.min(
|
||||
1 + (int)(Math.random() * 2),
|
||||
snow.getLayers()
|
||||
);
|
||||
|
||||
amountOfSnow += snowToRemove;
|
||||
targets.add(Pair.of(sideBlock, snowToRemove));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (triggerNuke) {
|
||||
for (int dx = -6; dx <= 6; dx++) {
|
||||
for (int dz = -6; dz <= 6; dz++) {
|
||||
|
||||
if (dx == 0 && dz == 0) continue;
|
||||
|
||||
Block b = world.getBlockAt(
|
||||
origin.getX() + dx,
|
||||
origin.getY(),
|
||||
origin.getZ() + dz
|
||||
);
|
||||
|
||||
if (b.getType() != Material.SNOW) continue;
|
||||
|
||||
Snow snow = (Snow) b.getBlockData();
|
||||
int snowToRemove = Math.min(3, snow.getLayers());
|
||||
|
||||
amountOfSnow += snowToRemove;
|
||||
targets.add(Pair.of(b, snowToRemove)); // store for later destruction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Optional<ToolUtils.Enchantment> fortune = enchants.stream().filter(z-> z.type == ToolUtils.EnchantmentType.FORTUNE).findFirst();
|
||||
|
||||
if(fortune.isPresent()) {
|
||||
amountOfSnow = Snowcore.fortuneToDrops(fortune.get().amount, amountOfSnow);
|
||||
}
|
||||
|
||||
if(multiplierAbility != null) {
|
||||
amountOfSnow += (int) Math.ceil(multiplierAbility.maxValue);
|
||||
}
|
||||
HashMap<Integer, ItemStack> leftover =
|
||||
inv.addItem(new ItemStack(Material.SNOWBALL, amountOfSnow));
|
||||
|
||||
if (!leftover.isEmpty()) {
|
||||
boolean hasAutosell = abilities.stream()
|
||||
.anyMatch(a -> a.type == ToolUtils.AbilityType.AUTOSELL);
|
||||
|
||||
if (hasAutosell) {
|
||||
Snowcore.sellInventory(player);
|
||||
event.setCancelled(true);
|
||||
return; // nothing destroyed
|
||||
} else {
|
||||
fullInventory(player);
|
||||
event.setCancelled(true);
|
||||
return; // nothing destroyed
|
||||
}
|
||||
}
|
||||
|
||||
for (Pair<Block, Integer> p : targets) {
|
||||
Snow snow = (Snow) p.getLeft().getBlockData();
|
||||
int layers = snow.getLayers();
|
||||
|
||||
if (layers <= p.getRight()) {
|
||||
p.getLeft().setType(Material.AIR, false);
|
||||
} else {
|
||||
snow.setLayers(layers - p.getRight());
|
||||
p.getLeft().setBlockData(snow);
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack stack = shovel.increaseEXP(hand, amountOfSnow);
|
||||
|
||||
player.getInventory().setItemInMainHand(stack);
|
||||
|
||||
player.incrementStatistic(Statistic.MINE_BLOCK, Material.SNOW, amountOfSnow);
|
||||
event.getBlock().setType(Material.AIR);
|
||||
event.setCancelled(true);
|
||||
} else {
|
||||
if(blockType.equals(Material.SNOW)) {
|
||||
Snowcore.sendActionBar(player, Component.text("You need a shovel to break snow with!"));
|
||||
} else {
|
||||
Snowcore.sendActionBar(player, Component.text("You do not have permission to break this block."));
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fullInventory(Player player) {
|
||||
player.showTitle(Title.title(
|
||||
Component.text("⚠ ⚠ WARNING ⚠ ⚠")
|
||||
.style(Style.style(TextColor.color(255, 0, 0), TextDecoration.BOLD)),
|
||||
Component.text("Your inventory is full! Use /sell")
|
||||
.style(Style.style(TextColor.color(255, 255, 0)))
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
266
src/main/java/ovh/sad/snowcore/listeners/InteractListener.java
Normal file
266
src/main/java/ovh/sad/snowcore/listeners/InteractListener.java
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
package ovh.sad.snowcore.listeners;
|
||||
|
||||
import dev.triumphteam.gui.guis.Gui;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerArmorStandManipulateEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import ovh.sad.snowcore.Snowcore;
|
||||
import ovh.sad.snowcore.guis.UpgradeYourToolsGui;
|
||||
import ovh.sad.snowcore.items.Hoe;
|
||||
import dev.triumphteam.gui.guis.GuiItem;
|
||||
import dev.triumphteam.gui.builder.item.ItemBuilder;
|
||||
import org.bukkit.Material;
|
||||
import ovh.sad.snowcore.items.ToolUtils;
|
||||
import ovh.sad.snowcore.items.Shovel;
|
||||
import ovh.sad.snowcore.items.Tool;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InteractListener implements Listener {
|
||||
@EventHandler
|
||||
public void glass(PlayerInteractEntityEvent event) {
|
||||
UUID entityUuid = event.getRightClicked().getUniqueId();
|
||||
|
||||
if(entityUuid.equals(UUID.fromString("96a1d63d-f9ba-490c-bee7-f5edb73a08e8"))) {
|
||||
event.setCancelled(true);
|
||||
UpgradeYourToolsGui.open(event);
|
||||
} else if(entityUuid.equals(UUID.fromString("237d77c8-a25f-4cd8-882e-c9da2d62ea79"))) {
|
||||
event.setCancelled(true);
|
||||
event.getPlayer().sendMessage(MiniMessage.miniMessage().deserialize("<yellow> \uD83D\uDEA7\uD83D\uDEA7 Under construction!"));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void armorStand(PlayerArmorStandManipulateEvent pasme) {
|
||||
if(pasme.getPlayer().getGameMode() != GameMode.CREATIVE) {
|
||||
pasme.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInteract(PlayerInteractEvent piEvent) {
|
||||
|
||||
Player player = piEvent.getPlayer();
|
||||
ItemStack item = player.getInventory().getItemInMainHand();
|
||||
|
||||
if (piEvent.getClickedBlock() != null) return;
|
||||
if (!player.isSneaking()) return;
|
||||
|
||||
Tool<?> tool = Hoe.HoeDefinitions.ALL_HOES.get(item.getType());
|
||||
if(tool == null) {
|
||||
tool = Shovel.ShovelDefinitions.ALL_SHOVELS.get(item.getType());
|
||||
}
|
||||
|
||||
if(tool == null) return;
|
||||
|
||||
// Create GUI
|
||||
Gui gui = Gui.gui()
|
||||
.title(Component.text("Upgrade your Tool"))
|
||||
.rows(6)
|
||||
.create();
|
||||
|
||||
GuiItem filler = ItemBuilder.from(Material.BLACK_STAINED_GLASS_PANE)
|
||||
.name(Component.text(" "))
|
||||
.asGuiItem();
|
||||
|
||||
for (int row = 1; row <= 6; row++) {
|
||||
for (int col = 1; col <= 9; col++) {
|
||||
if (row == 1 && col == 5)
|
||||
continue;
|
||||
|
||||
if ((row == 2 || row == 3) && (col >= 2 && col <= 8)) continue;
|
||||
|
||||
if ((row == 4 || row == 5) && (col >= 2 && col <= 8)) continue;
|
||||
|
||||
gui.setItem(row, col, filler);
|
||||
}
|
||||
}
|
||||
|
||||
Map<ToolUtils.EnchantmentType, ToolUtils.Enchantment> ownedEnchants =
|
||||
Objects.requireNonNull(ToolUtils.getEnchantments(item))
|
||||
.stream()
|
||||
.collect(Collectors.toMap(e -> e.type, e -> e));
|
||||
|
||||
Map<ToolUtils.AbilityType, ToolUtils.Ability> ownedAbilities =
|
||||
Objects.requireNonNull(ToolUtils.getAbilities(item))
|
||||
.stream()
|
||||
.collect(Collectors.toMap(a -> a.type, a -> a));
|
||||
|
||||
int exp = ToolUtils.getExp(item);
|
||||
int hoeLevel = ToolUtils.getLevel(item);
|
||||
Iterator<ToolUtils.Enchantment> definedEnchants = tool.definedEnchantments.stream().map(ToolUtils.Enchantment::copy).iterator();
|
||||
for (int row = 2; row <= 3 && definedEnchants.hasNext(); row++) {
|
||||
for (int col = 2; col <= 8 && definedEnchants.hasNext(); col++) {
|
||||
|
||||
ToolUtils.Enchantment def = definedEnchants.next();
|
||||
ToolUtils.Enchantment owned = ownedEnchants.get(def.type);
|
||||
|
||||
boolean has = owned != null;
|
||||
int level = has ? owned.amount : 0;
|
||||
int nextLevel = level + 1;
|
||||
int cost = def.getExpRequired(nextLevel);
|
||||
|
||||
Component name;
|
||||
List<Component> lore = new ArrayList<>();
|
||||
Integer hasEnchantmentLimit = tool.definedEnchantLimits.get(def.type);
|
||||
|
||||
if(hasEnchantmentLimit != null && level >= hasEnchantmentLimit) {
|
||||
name = Component.text("❌ " + def.type.name + " is at max level!", NamedTextColor.RED);
|
||||
} else if (has) {
|
||||
name = Component.text("✔ " + def.type.name + " Lv." + level, NamedTextColor.GREEN);
|
||||
|
||||
lore.add(Component.text("Click to upgrade → Lv." + nextLevel, NamedTextColor.YELLOW));
|
||||
lore.add(Component.text("Cost: " + cost + " EXP", NamedTextColor.GOLD));
|
||||
} else {
|
||||
name = Component.text(def.type.name, NamedTextColor.YELLOW);
|
||||
|
||||
lore.add(Component.text("Unlock for " + cost + " EXP", NamedTextColor.GOLD));
|
||||
}
|
||||
|
||||
Tool<?> finalTool = tool;
|
||||
gui.setItem(row, col,
|
||||
ItemBuilder.from(Material.ENCHANTED_BOOK)
|
||||
.name(name)
|
||||
.lore(lore)
|
||||
.asGuiItem(ev -> {
|
||||
ev.setCancelled(true);
|
||||
Player whoClicked = (Player) ev.getWhoClicked();
|
||||
|
||||
if(level >= finalTool.definedEnchantLimits.get(def.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (exp >= cost) {
|
||||
if(has) {
|
||||
owned.amount = owned.amount + 1;
|
||||
ownedEnchants.put(owned.type, owned);
|
||||
} else {
|
||||
ToolUtils.Enchantment copy = def.copy();
|
||||
|
||||
copy.amount = copy.amount + 1;
|
||||
ownedEnchants.put(copy.type, copy);
|
||||
}
|
||||
|
||||
ItemStack realItem = ToolUtils.setExp(
|
||||
finalTool,
|
||||
ToolUtils.setEnchantments(finalTool, item, ownedEnchants.values().stream().toList()),
|
||||
exp - cost
|
||||
);
|
||||
|
||||
whoClicked.getInventory().setItemInMainHand(realItem);
|
||||
whoClicked.sendMessage(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
"<green>✔ Unlocked <yellow>" + def.type.name() +
|
||||
"</yellow> for <aqua>" + (int) cost + " EXP</aqua>"
|
||||
)
|
||||
);
|
||||
gui.close(whoClicked);
|
||||
} else {
|
||||
whoClicked.sendMessage(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
"<red>✘ Not enough EXP! Need <aqua>" + (int) cost +
|
||||
" EXP </aqua>, you have <aqua>" + (int) exp + " EXP</aqua>"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<ToolUtils.Ability> definedAbilities = tool.definedAbilities.stream().iterator();
|
||||
|
||||
for (int row = 4; row <= 5 && definedAbilities.hasNext(); row++) {
|
||||
for (int col = 2; col <= 8 && definedAbilities.hasNext(); col++) {
|
||||
|
||||
ToolUtils.Ability def = definedAbilities.next();
|
||||
ToolUtils.Ability owned = ownedAbilities.get(def.type);
|
||||
|
||||
boolean has = owned != null || def.byDefault;
|
||||
boolean unlocked = hoeLevel >= def.unlockLevel;
|
||||
|
||||
Component name;
|
||||
List<Component> lore = new ArrayList<>();
|
||||
|
||||
if (has) {
|
||||
name = Component.text("✔ " + def.type.name, NamedTextColor.GREEN);
|
||||
lore.add(Component.text("Already unlocked", NamedTextColor.GRAY));
|
||||
|
||||
} else if (!unlocked) {
|
||||
name = Component.text("🔒 " + def.type.name, NamedTextColor.RED);
|
||||
lore.add(Component.text("Unlocks at level " + def.unlockLevel, NamedTextColor.GRAY));
|
||||
|
||||
} else {
|
||||
name = Component.text(def.type.name, NamedTextColor.YELLOW);
|
||||
lore.add(Component.text("Cost: " + (int) def.baseCost + "$", NamedTextColor.GOLD));
|
||||
lore.add(Component.text("Click to unlock", NamedTextColor.GRAY));
|
||||
}
|
||||
|
||||
Tool<?> finalTool1 = tool;
|
||||
gui.setItem(row, col,
|
||||
ItemBuilder.from(Material.NETHER_STAR)
|
||||
.name(name)
|
||||
.lore(lore)
|
||||
.asGuiItem(ev -> {
|
||||
ev.setCancelled(true);
|
||||
Player whoClicked = (Player) ev.getWhoClicked();
|
||||
|
||||
if(def.unlockLevel > hoeLevel) {
|
||||
whoClicked.sendMessage(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
"<red>✘ Your level is not high enough. Required level is " + def.unlockLevel
|
||||
)
|
||||
);
|
||||
return;
|
||||
|
||||
}
|
||||
double cost = def.baseCost;
|
||||
double balance = Snowcore.econ.getBalance(whoClicked);
|
||||
|
||||
if (balance >= cost) {
|
||||
Snowcore.econ.withdrawPlayer(whoClicked, cost);
|
||||
|
||||
ownedAbilities.put(def.type, def);
|
||||
ItemStack realItem =
|
||||
ToolUtils.setAbilities(finalTool1, item, ownedAbilities.values().stream().toList());
|
||||
whoClicked.getInventory().setItemInMainHand(realItem);
|
||||
whoClicked.sendMessage(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
"<green>✔ Unlocked <yellow>" + def.type.name() +
|
||||
"</yellow> for <gold>$" + (int) cost + "</gold>"
|
||||
)
|
||||
);
|
||||
gui.close(whoClicked);
|
||||
} else {
|
||||
whoClicked.sendMessage(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
"<red>✘ Not enough money! Need <gold>$" + (int) cost +
|
||||
"</gold>, you have <yellow>$" + (int) balance + "</yellow>"
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gui.setItem(1, 5, new GuiItem(item));
|
||||
|
||||
gui.setDefaultClickAction(ev -> ev.setCancelled(true));
|
||||
|
||||
gui.open(player);
|
||||
}
|
||||
}
|
||||
18
src/main/java/ovh/sad/snowcore/listeners/PlaceListener.java
Normal file
18
src/main/java/ovh/sad/snowcore/listeners/PlaceListener.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package ovh.sad.snowcore.listeners;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import ovh.sad.snowcore.Snowcore;
|
||||
|
||||
public class PlaceListener implements Listener {
|
||||
@EventHandler
|
||||
public void onPlace(BlockPlaceEvent event) {
|
||||
if(event.getPlayer().getGameMode() != GameMode.CREATIVE) {
|
||||
Snowcore.sendActionBar(event.getPlayer(), Component.text("You cannot place blocks here!"));
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/main/resources/plugin.yml
Normal file
8
src/main/resources/plugin.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
name: snowcore
|
||||
version: '1.0-SNAPSHOT'
|
||||
main: ovh.sad.snowcore.Snowcore
|
||||
api-version: '1.21'
|
||||
depend:
|
||||
- Vault
|
||||
- EconomySystem
|
||||
- PlaceholderAPI
|
||||
Loading…
Add table
Add a link
Reference in a new issue