diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3c37caf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,118 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# 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* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..42c13972 --- /dev/null +++ b/build.gradle @@ -0,0 +1,89 @@ +plugins { + id 'java' + id 'com.github.johnrengelman.shadow' version '7.1.2' +} + +group = 'net.momirealms' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() + maven { + name = 'papermc-repo' + url = 'https://papermc.io/repo/repository/maven-public/' + } + maven { + name = 'sonatype' + url = 'https://oss.sonatype.org/content/groups/public/' + } + maven { + name = "sonatype-oss-snapshots1" + url = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + } + maven { + name = "dmulloy2-repo" + url = "https://repo.dmulloy2.net/repository/public/" + } + maven { + name = "clip-repo" + url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/' + } + maven { + name = "NBT-API" + url = "https://repo.codemc.org/repository/maven-public/" + } + maven { + name = "sk89q-repo" + url = "https://maven.enginehub.org/repo/" + } + maven { + name = "Lumine Releases" + url = "https://mvn.lumine.io/repository/maven-public" + } +} + +dependencies { + compileOnly 'com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT' + compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.8.0" + compileOnly 'me.clip:placeholderapi:2.11.1' + compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.7' + compileOnly 'io.lumine:Mythic-Dist:5.0.3-SNAPSHOT' + implementation("net.kyori:adventure-api:4.11.0") + implementation("net.kyori:adventure-platform-bukkit:4.1.1") + implementation("net.kyori:adventure-text-minimessage:4.11.0") + implementation 'de.tr7zw:item-nbt-api:2.10.0' +} + +def targetJavaVersion = 16 +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + sourceCompatibility = javaVersion + targetCompatibility = javaVersion + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } +} + +tasks.withType(JavaCompile).configureEach { + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + options.release = targetJavaVersion + } +} + +processResources { + def props = [version: version] + inputs.properties props + filteringCharset 'UTF-8' + filesMatching('plugin.yml') { + expand props + } +} + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +shadowJar { + relocate 'net.kyori', 'libs.kyori' + relocate 'de.tr7zw', 'libs.tr7zw' +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..e69de29b diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..3da45c16 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/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/master/subprojects/plugins/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 + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&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 + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + 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 + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# 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" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +: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%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..0c50946b --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'CustomFishing' diff --git a/src/main/java/net/momirealms/customfishing/AdventureManager.java b/src/main/java/net/momirealms/customfishing/AdventureManager.java new file mode 100644 index 00000000..0edaa58f --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/AdventureManager.java @@ -0,0 +1,35 @@ +package net.momirealms.customfishing; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.title.Title; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.time.Duration; + +public class AdventureManager { + + public static void consoleMessage(String s) { + Audience au = CustomFishing.adventure.sender(Bukkit.getConsoleSender()); + MiniMessage mm = MiniMessage.miniMessage(); + Component parsed = mm.deserialize(s); + au.sendMessage(parsed); + } + + public static void playerMessage(Player player, String s) { + Audience au = CustomFishing.adventure.player(player); + MiniMessage mm = MiniMessage.miniMessage(); + Component parsed = mm.deserialize(s); + au.sendMessage(parsed); + } + + public static void playerTitle(Player player, String s1, String s2, int in, int duration, int out) { + Audience au = CustomFishing.adventure.player(player); + MiniMessage mm = MiniMessage.miniMessage(); + Title.Times times = Title.Times.times(Duration.ofMillis(in), Duration.ofMillis(duration), Duration.ofMillis(out)); + Title title = Title.title(mm.deserialize(s1), mm.deserialize(s2), times); + au.showTitle(title); + } +} diff --git a/src/main/java/net/momirealms/customfishing/ConfigReader.java b/src/main/java/net/momirealms/customfishing/ConfigReader.java new file mode 100644 index 00000000..5d324953 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/ConfigReader.java @@ -0,0 +1,491 @@ +package net.momirealms.customfishing; + +import net.momirealms.customfishing.requirements.*; +import net.momirealms.customfishing.utils.*; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.util.*; + +public class ConfigReader{ + + public static HashMap LOOT = new HashMap<>(); + public static HashMap LOOTITEM = new HashMap<>(); + public static HashMap UTIL = new HashMap<>(); + public static HashMap UTILITEM = new HashMap<>(); + public static HashMap LAYOUT = new HashMap<>(); + + private static YamlConfiguration getConfig(String configName) { + File file = new File(CustomFishing.instance.getDataFolder(), configName); + if (!file.exists()) { + CustomFishing.instance.saveResource(configName, false); + } + return YamlConfiguration.loadConfiguration(file); + } + + public static void Reload() { + Config.loadConfig(); + Message.loadMessage(); + Title.loadTitle(); + loadLoot(); + loadUtil(); + } + + public static class Config { + + public static boolean wg; + public static boolean mm; + public static boolean papi; + public static boolean season; + public static boolean vanillaDrop; + public static boolean needOpenWater; + public static String season_papi; + public static int fishFinderCoolDown; + + public static void loadConfig() { + CustomFishing.instance.saveDefaultConfig(); + CustomFishing.instance.reloadConfig(); + FileConfiguration config = CustomFishing.instance.getConfig(); + + wg = config.getBoolean("config.integrations.WorldGuard"); + if (wg){ + if (Bukkit.getPluginManager().getPlugin("WorldGuard") == null){ + AdventureManager.consoleMessage("[CustomFishing] 未检测到 WorldGuard!"); + wg = false; + }else { + AdventureManager.consoleMessage("[CustomFishing] WorldGuard Hooked!"); + } + } + mm = config.getBoolean("config.integrations.MythicMobs"); + if (mm){ + if (Bukkit.getPluginManager().getPlugin("MythicMobs") == null){ + AdventureManager.consoleMessage("[CustomFishing] 未检测到 MythicMobs!"); + mm = false; + }else { + AdventureManager.consoleMessage("[CustomFishing] MythicMobs Hooked!"); + } + } + papi = config.getBoolean("config.integrations.PlaceholderAPI"); + if (papi){ + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") == null){ + AdventureManager.consoleMessage("[CustomFishing] 未检测到 PlaceholderAPI!"); + papi = false; + }else { + AdventureManager.consoleMessage("[CustomFishing] PlaceholderAPI Hooked!"); + } + } + season = config.getBoolean("config.season.enable"); + if (!papi && season){ + season = false; + AdventureManager.consoleMessage("[CustomFishing] 使用季节特性请先打开 PlaceholderAPI 兼容!"); + } + + if (season){ + season_papi = config.getString("config.season.papi"); + }else { + season_papi = null; + } + + vanillaDrop = config.getBoolean("config.vanilla-loot-when-no-custom-fish"); + needOpenWater = config.getBoolean("config.need-open-water"); + fishFinderCoolDown = config.getInt("config.fishfinder-cooldown"); + + /* + 计算获取布局 + */ + LAYOUT.clear(); + Set keys = Objects.requireNonNull(config.getConfigurationSection("config.success-rate")).getKeys(false); + keys.forEach(key -> { + int range = config.getInt("config.success-rate." + key + ".range"); + Set rates = Objects.requireNonNull(config.getConfigurationSection("config.success-rate." + key + ".layout")).getKeys(false); + double[] successRate = new double[rates.size()]; + for(int i = 0; i < rates.size(); i++){ + successRate[i] = config.getDouble("config.success-rate." + key + ".layout." +(i + 1)); + } + int size = rates.size()*range -1; + LayoutUtil layout = new LayoutUtil(key, range, successRate, size); + layout.setTitle(config.getString("config.success-rate." + key + ".title"," ")); + layout.setBar(config.getString("config.success-rate." + key + ".subtitle.bar","뀃")); + layout.setEnd(config.getString("config.success-rate." + key + ".subtitle.end","")); + layout.setStart(config.getString("config.success-rate." + key + ".subtitle.start","")); + layout.setPointer(config.getString("config.success-rate." + key + ".subtitle.pointer","뀄")); + layout.setPointerOffset(config.getString("config.success-rate." + key + ".subtitle.pointer_offset","뀂")); + layout.setOffset(config.getString("config.success-rate." + key + ".subtitle.offset","뀁")); + LAYOUT.put(key, layout); + }); + } + } + + public static class Message { + + public static String prefix; + public static String reload; + public static String escape; + public static String noPerm; + public static String notExist; + public static String noConsole; + public static String wrongAmount; + public static String lackArgs; + public static String notOnline; + public static String giveItem; + public static String getItem; + public static String coolDown; + public static String possibleLoots; + public static String splitChar; + public static String noLoot; + public static String notOpenWater; + + public static void loadMessage() { + YamlConfiguration config = getConfig("messages.yml"); + + prefix = config.getString("messages.prefix"); + reload = config.getString("messages.reload"); + escape = config.getString("messages.escape"); + noPerm = config.getString("messages.no-perm"); + notExist = config.getString("messages.not-exist"); + noConsole = config.getString("messages.no-console"); + wrongAmount = config.getString("messages.wrong-amount"); + lackArgs = config.getString("messages.lack-args"); + notOnline = config.getString("messages.not-online"); + giveItem = config.getString("messages.give-item"); + getItem = config.getString("messages.get-item"); + coolDown = config.getString("messages.cooldown"); + possibleLoots = config.getString("messages.possible-loots"); + splitChar = config.getString("messages.split-char"); + noLoot = config.getString("messages.no-loot"); + notOpenWater = config.getString("messages.not-open-water"); + } + } + + public static class Title { + + public static List success_title; + public static List success_subtitle; + public static int success_in; + public static int success_out; + public static int success_stay; + public static List failure_title; + public static List failure_subtitle; + public static int failure_in; + public static int failure_out; + public static int failure_stay; + + public static void loadTitle() { + YamlConfiguration config = getConfig("titles.yml"); + + success_title = config.getStringList("titles.success.title"); + success_subtitle = config.getStringList("titles.success.subtitle"); + success_in = config.getInt("titles.success.fade.in")*50; + success_out = config.getInt("titles.success.fade.out")*50; + success_stay = config.getInt("titles.success.fade.stay")*50; + failure_title = config.getStringList("titles.failure.title"); + failure_subtitle = config.getStringList("titles.failure.subtitle"); + failure_in = config.getInt("titles.failure.fade.in")*50; + failure_out = config.getInt("titles.failure.fade.out")*50; + failure_stay = config.getInt("titles.failure.fade.stay")*50; + } + } + + /* + 载入Loot战利品 + */ + public static void loadLoot() { + + LOOT.clear(); + LOOTITEM.clear(); + + YamlConfiguration config = getConfig("loots.yml"); + Set keys = Objects.requireNonNull(config.getConfigurationSection("items")).getKeys(false); + keys.forEach(key -> { + /* + 必设置的内容,为构造所需 + */ + String name; + if (config.contains("items." + key + ".display.name")) { + name = config.getString("items." + key + ".display.name"); + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的物品名称!"); + return; + } + Difficulty difficulty; + if (config.contains("items." + key + ".difficulty")) { + String[] split = StringUtils.split(config.getString("items." + key + ".difficulty"), "-"); + assert split != null; + if (Integer.parseInt(split[1]) <= 0 || Integer.parseInt(split[0]) <= 0){ + AdventureManager.consoleMessage("[CustomFishing] 错误! " + key + " 的捕获难度必须为正整数与正整数的组合!"); + return; + }else { + difficulty = new Difficulty(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + } + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的捕获难度!"); + return; + } + int weight; + if (config.contains("items." + key + ".weight")) { + weight = config.getInt("items." + key + ".weight"); + if (weight <= 0){ + AdventureManager.consoleMessage("[CustomFishing] 错误! " + key + " 的捕获权重必须为正整数!"); + return; + } + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的捕获权重!"); + return; + } + int time; + if (config.contains("items." + key + ".time")) { + time = config.getInt("items." + key + ".time"); + if (time <= 0){ + AdventureManager.consoleMessage("[CustomFishing] 错误! " + key + " 的捕捉时间必须为正整数!"); + return; + } + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的捕捉时间!"); + return; + } + + //新建单例 + LootInstance loot = new LootInstance(key, name, difficulty, weight, time); + + /* + 必须设置的内容,但并非构造所需 + */ + if (config.contains("items." + key + ".material")) { + loot.setMaterial(config.getString("items." + key + ".material")); + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的物品材质!"); + return; + } + /* + 可选的设置内容 + */ + if (config.contains("items." + key + ".display.lore")) + loot.setLore(config.getStringList("items." + key + ".display.lore")); + + if (config.contains("items." + key + ".nbt")) + loot.setNbt(config.getMapList("items." + key + ".nbt").get(0)); + + if (config.contains("items."+ key +".nick")){ + loot.setNick(config.getString("items."+key+".nick")); + }else { + loot.setNick(loot.getName()); + } + + if (config.contains("items." + key + ".action.message")) + loot.setMsg(config.getString("items." + key + ".action.message")); + if (config.contains("items." + key + ".action.command")) + loot.setCommands(config.getStringList("items." + key + ".action.command")); + if (config.contains("items." + key + "layout")) + loot.setLayout(config.getString("items." + key + "layout")); + /* + 设置捕获条件 + */ + if (config.contains("items." + key + ".requirements")){ + List requirements = new ArrayList<>(); + Objects.requireNonNull(config.getConfigurationSection("items." + key + ".requirements")).getKeys(false).forEach(requirement -> { + switch (requirement){ + case "weather" -> requirements.add(new Weather(config.getStringList("items." + key + ".requirements.weather"))); + case "ypos" -> requirements.add(new YPos(config.getStringList("items." + key + ".requirements.ypos"))); + case "season" -> { + if (Config.season){ + requirements.add(new Season(config.getStringList("items." + key + ".requirements.season"))); + }else { + AdventureManager.consoleMessage("[CustomFishing] 使用季节特性请先在 config.yml 中启用季节特性!"); + } + } + case "world" -> requirements.add(new World(config.getStringList("items." + key + ".requirements.world"))); + case "biome" -> requirements.add(new Biome(config.getStringList("items." + key + ".requirements.biome"))); + case "permission" -> requirements.add(new Permission(config.getString("items." + key + ".requirements.permission"))); + case "region" -> { + if (Config.wg){ + requirements.add(new Region(config.getStringList("items." + key + ".requirements.regions"))); + }else { + AdventureManager.consoleMessage("[CustomFishing] 指定钓鱼区域需要启用 WorldGuard 兼容!"); + } + } + case "time" -> requirements.add(new Time(config.getStringList("items." + key + ".requirements.time"))); + } + }); + loot.setRequirements(requirements); + } + //添加单例进缓存 + LOOT.put(key, loot); + //添加根据单例生成的NBT物品进缓存 + LootInstance.addLoot2cache(key); + }); + + if (config.contains("mobs") && Config.mm){ + Set mobs = Objects.requireNonNull(config.getConfigurationSection("mobs")).getKeys(false); + mobs.forEach(key -> { + + /* + 必设置的内容,为构造所需 + */ + String name; + if (config.contains("mobs." + key + ".name")) { + name = config.getString("mobs." + key + ".name"); + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的怪物名称!"); + return; + } + Difficulty difficulty; + if (config.contains("mobs." + key + ".difficulty")) { + String[] split = StringUtils.split(config.getString("mobs." + key + ".difficulty"), "-"); + assert split != null; + if (Integer.parseInt(split[1]) <= 0 || Integer.parseInt(split[0]) <= 0){ + AdventureManager.consoleMessage("[CustomFishing] 错误! " + key + " 的捕获难度必须为正整数与正整数的组合!"); + return; + }else { + difficulty = new Difficulty(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + } + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的捕获难度!"); + return; + } + int weight; + if (config.contains("mobs." + key + ".weight")) { + weight = config.getInt("mobs." + key + ".weight"); + if (weight <= 0){ + AdventureManager.consoleMessage("[CustomFishing] 错误! " + key + " 的捕获权重必须为正整数!"); + return; + } + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的捕获权重!"); + return; + } + int time; + if (config.contains("mobs." + key + ".time")) { + time = config.getInt("mobs." + key + ".time"); + if (time <= 0){ + AdventureManager.consoleMessage("[CustomFishing] 错误! " + key + " 的捕捉时间必须为正整数!"); + return; + } + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的捕捉时间!"); + return; + } + //新建单例 + LootInstance loot = new LootInstance(key, name, difficulty, weight, time); + //设置昵称 + loot.setNick(name); + //设置MM怪ID + if (config.contains("mobs." + key + ".mythicmobsID")) { + loot.setMm(config.getString("mobs." + key + ".mythicmobsID")); + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的MM怪ID!"); + return; + } + //设置MM怪位移 + if (config.contains("mobs." + key + ".vector.horizontal") && config.contains("mobs." + key + ".vector.vertical")) { + loot.setVectorUtil(new VectorUtil(config.getDouble("mobs." + key + ".vector.horizontal"), config.getDouble("mobs." + key + ".vector.vertical"))); + } else { + loot.setVectorUtil(new VectorUtil(1.1, 1.3)); + } + + if (config.contains("mobs." + key + ".level")) + loot.setMmLevel(config.getInt("mobs." + key + ".level", 0)); + if (config.contains("mobs." + key + ".action.message")) + loot.setMsg(config.getString("mobs." + key + ".action.message")); + if (config.contains("mobs." + key + ".action.command")) + loot.setCommands(config.getStringList("mobs." + key + ".action.command")); + if (config.contains("mobs." + key + "layout")) + loot.setLayout(config.getString("mobs." + key + "layout")); + /* + 设置捕获条件 + */ + if (config.contains("mobs." + key + ".requirements")){ + List requirements = new ArrayList<>(); + Objects.requireNonNull(config.getConfigurationSection("mobs." + key + ".requirements")).getKeys(false).forEach(requirement -> { + switch (requirement){ + case "weather" -> requirements.add(new Weather(config.getStringList("mobs." + key + ".requirements.weather"))); + case "ypos" -> requirements.add(new YPos(config.getStringList("mobs." + key + ".requirements.ypos"))); + case "season" -> { + if (Config.season){ + requirements.add(new Season(config.getStringList("mobs." + key + ".requirements.season"))); + }else { + AdventureManager.consoleMessage("[CustomFishing] 使用季节特性请先在 config.yml 中启用季节特性!"); + } + } + case "world" -> requirements.add(new World(config.getStringList("mobs." + key + ".requirements.world"))); + case "biome" -> requirements.add(new Biome(config.getStringList("mobs." + key + ".requirements.biome"))); + case "permission" -> requirements.add(new Permission(config.getString("mobs." + key + ".requirements.permission"))); + case "region" -> { + if (Config.wg){ + requirements.add(new Region(config.getStringList("mobs." + key + ".requirements.regions"))); + }else { + AdventureManager.consoleMessage("[CustomFishing] 指定钓鱼区域需要启用 WorldGuard 兼容!"); + } + } + case "time" -> requirements.add(new Time(config.getStringList("mobs." + key + ".requirements.time"))); + } + }); + loot.setRequirements(requirements); + } + //丢入缓存 + LOOT.put(key, loot); + }); + if (keys.size() != LOOTITEM.size() || mobs.size() != LOOT.size()- LOOTITEM.size()) { + AdventureManager.consoleMessage("[CustomFishing] loots.yml 文件存在配置错误!"); + } else { + AdventureManager.consoleMessage("[CustomFishing] 从 loots.yml 载入了" + keys.size() + "条物品数据"); + AdventureManager.consoleMessage("[CustomFishing] 从 loots.yml 载入了" + mobs.size() + "条怪物数据"); + } + return; + } + if (keys.size() != LOOTITEM.size()){ + AdventureManager.consoleMessage("[CustomFishing] loots.yml 文件存在配置错误!"); + } else { + AdventureManager.consoleMessage("[CustomFishing] 从 loots.yml 载入了" + keys.size() + "条物品数据"); + } + } + + /* + 载入util物品 + */ + public static void loadUtil() { + + UTIL.clear(); + UTILITEM.clear(); + + YamlConfiguration config = getConfig("utils.yml"); + Set keys = Objects.requireNonNull(config.getConfigurationSection("utils")).getKeys(false); + keys.forEach(key -> { + /* + 必设置的内容,为构造所需 + */ + String name; + if (config.contains("utils." + key + ".display.name")) { + name = config.getString("utils." + key + ".display.name"); + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的物品名称!"); + return; + } + String material; + if (config.contains("utils." + key + ".material")) { + material = config.getString("utils." + key + ".material"); + } else { + AdventureManager.consoleMessage("[CustomFishing] 错误! 未设置 " + key + " 的物品材质!"); + return; + } + //构造 + UtilInstance utilInstance = new UtilInstance(key, name, material); + + if (config.contains("utils." + key + ".display.lore")) + utilInstance.setLore(config.getStringList("utils." + key + ".display.lore")); + if (config.contains("utils." + key + ".nbt")) + utilInstance.setNbt(config.getMapList("utils." + key + ".nbt").get(0)); + + UTIL.put(key, utilInstance); + UtilInstance.addUtil2cache(key); + }); + if (keys.size() != UTILITEM.size()){ + AdventureManager.consoleMessage("[CustomFishing] utils.yml 文件存在配置错误!"); + } else { + AdventureManager.consoleMessage("[CustomFishing] 从 utils.yml 载入了" + keys.size() + "条工具数据"); + } + } +} diff --git a/src/main/java/net/momirealms/customfishing/CustomFishing.java b/src/main/java/net/momirealms/customfishing/CustomFishing.java new file mode 100644 index 00000000..cc3e42e8 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/CustomFishing.java @@ -0,0 +1,36 @@ +package net.momirealms.customfishing; + +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.momirealms.customfishing.command.Execute; +import net.momirealms.customfishing.command.TabComplete; +import net.momirealms.customfishing.listener.PlayerListener; +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Objects; + +public final class CustomFishing extends JavaPlugin { + + public static JavaPlugin instance; + public static BukkitAudiences adventure; + + @Override + public void onEnable() { + instance = this; + adventure = BukkitAudiences.create(this); + Objects.requireNonNull(Bukkit.getPluginCommand("customfishing")).setExecutor(new Execute()); + Objects.requireNonNull(Bukkit.getPluginCommand("customfishing")).setTabCompleter(new TabComplete()); + Bukkit.getPluginManager().registerEvents(new PlayerListener(),this); + ConfigReader.Reload(); + AdventureManager.consoleMessage("[CustomFishing] 插件已加载! 作者:小默米 QQ:3266959688"); + } + + @Override + public void onDisable() { + AdventureManager.consoleMessage("[CustomFishing] 插件已卸载! 作者:小默米 QQ:3266959688"); + if(adventure != null) { + adventure.close(); + adventure = null; + } + } +} diff --git a/src/main/java/net/momirealms/customfishing/command/Execute.java b/src/main/java/net/momirealms/customfishing/command/Execute.java new file mode 100644 index 00000000..4b8b6c27 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/command/Execute.java @@ -0,0 +1,205 @@ +package net.momirealms.customfishing.command; + +import net.momirealms.customfishing.AdventureManager; +import net.momirealms.customfishing.ConfigReader; +import net.momirealms.customfishing.utils.LootInstance; +import net.momirealms.customfishing.utils.UtilInstance; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import javax.annotation.ParametersAreNonnullByDefault; + +public class Execute implements CommandExecutor { + @Override + @ParametersAreNonnullByDefault + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + //没有权限的快走啦 + if (!(sender.hasPermission("customfishing.admin") || sender.isOp())) { + AdventureManager.playerMessage((Player) sender,ConfigReader.Message.prefix + ConfigReader.Message.noPerm); + return true; + } + //参数打不全的赶紧走开 + if (args.length < 1){ + lackArgs(sender); + return true; + } + //重载命令 + if (args[0].equalsIgnoreCase("reload")) { + ConfigReader.Reload(); + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender, ConfigReader.Message.prefix + ConfigReader.Message.reload); + }else { + AdventureManager.consoleMessage(ConfigReader.Message.prefix + ConfigReader.Message.reload); + } + return true; + } + //获取物品命令 + if (args[0].equalsIgnoreCase("items")) { + if (args.length < 4){ + lackArgs(sender); + return true; + } + if (args[1].equalsIgnoreCase("loot")) { + if (args[2].equalsIgnoreCase("get")) { + //检验参数长度 [0]items [1]loot [2]get [3]xxx [4](amount) + if (sender instanceof Player player){ + //是否存在于缓存中 + if (!ConfigReader.LOOTITEM.containsKey(args[3])){ + noItem(sender); + return true; + } + if (args.length == 4){ + LootInstance.givePlayerLoot(player, args[3], 1); + AdventureManager.playerMessage(player, ConfigReader.Message.prefix + ConfigReader.Message.getItem.replace("{Amount}", "1").replace("{Item}",args[3])); + }else { + if (Integer.parseInt(args[4]) < 1){ + wrongAmount(sender); + return true; + } + LootInstance.givePlayerLoot(player, args[3], Integer.parseInt(args[4])); + AdventureManager.playerMessage(player, ConfigReader.Message.prefix + ConfigReader.Message.getItem.replace("{Amount}", args[4]).replace("{Item}",args[3])); + } + }else { + AdventureManager.consoleMessage(ConfigReader.Message.prefix + ConfigReader.Message.noConsole); + } + return true; + } + if (args[2].equalsIgnoreCase("give")) { + //检验参数长度 [0]items [1]loot [2]give [3]player [4]xxx [5](amount) + if (args.length < 5){ + lackArgs(sender); + return true; + } + Player player = Bukkit.getPlayer(args[3]); + //玩家是否在线 + if (player == null){ + notOnline(sender); + return true; + } + //是否存在于缓存中 + if (!ConfigReader.LOOTITEM.containsKey(args[4])){ + noItem(sender); + return true; + } + if (args.length == 5){ + LootInstance.givePlayerLoot(player, args[4], 1); + giveItem(sender, args[3], args[4], 1); + }else { + if (Integer.parseInt(args[5]) < 1){ + wrongAmount(sender); + return true; + } + LootInstance.givePlayerLoot(player, args[4], Integer.parseInt(args[5])); + giveItem(sender, args[3], args[4], Integer.parseInt(args[5])); + } + return true; + } + } + /* + 给予实用物品 + */ + else if(args[1].equalsIgnoreCase("util")){ + if (args[2].equalsIgnoreCase("get")) { + //检验参数长度 [0]items [1]loot [2]get [3]xxx [4](amount) + if (sender instanceof Player player){ + //是否存在于缓存中 + if (!ConfigReader.UTIL.containsKey(args[3])){ + noItem(sender); + return true; + } + if (args.length == 4){ + UtilInstance.givePlayerUtil(player, args[3], 1); + AdventureManager.playerMessage(player, ConfigReader.Message.prefix + ConfigReader.Message.getItem.replace("{Amount}", "1").replace("{Item}",args[3])); + }else { + if (Integer.parseInt(args[4]) < 1){ + wrongAmount(sender); + return true; + } + UtilInstance.givePlayerUtil(player, args[3], Integer.parseInt(args[4])); + AdventureManager.playerMessage(player, ConfigReader.Message.prefix + ConfigReader.Message.getItem.replace("{Amount}", args[4]).replace("{Item}",args[3])); + } + }else { + AdventureManager.consoleMessage(ConfigReader.Message.prefix + ConfigReader.Message.noConsole); + } + return true; + } + if (args[2].equalsIgnoreCase("give")) { + //检验参数长度 [0]items [1]loot [2]give [3]player [4]xxx [5](amount) + if (args.length < 5){ + lackArgs(sender); + return true; + } + Player player = Bukkit.getPlayer(args[3]); + //玩家是否在线 + if (player == null){ + notOnline(sender); + return true; + } + //是否存在于缓存中 + if (!ConfigReader.UTIL.containsKey(args[4])){ + noItem(sender); + return true; + } + if (args.length == 5){ + UtilInstance.givePlayerUtil(player, args[4], 1); + giveItem(sender, args[3], args[4], 1); + }else { + if (Integer.parseInt(args[5]) < 1){ + wrongAmount(sender); + return true; + } + UtilInstance.givePlayerUtil(player, args[4], Integer.parseInt(args[5])); + giveItem(sender, args[3], args[4], Integer.parseInt(args[5])); + } + return true; + } + } + } + return true; + } + + + private void lackArgs(CommandSender sender){ + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigReader.Message.prefix + ConfigReader.Message.lackArgs); + }else { + AdventureManager.consoleMessage(ConfigReader.Message.prefix + ConfigReader.Message.lackArgs); + } + } + + private void notOnline(CommandSender sender){ + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigReader.Message.prefix + ConfigReader.Message.notOnline); + }else { + AdventureManager.consoleMessage(ConfigReader.Message.prefix + ConfigReader.Message.notOnline); + } + } + + private void noItem(CommandSender sender){ + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigReader.Message.prefix + ConfigReader.Message.notExist); + }else { + AdventureManager.consoleMessage(ConfigReader.Message.prefix + ConfigReader.Message.notExist); + } + } + + private void giveItem(CommandSender sender, String name, String item, int amount){ + String string = ConfigReader.Message.prefix + ConfigReader.Message.giveItem.replace("{Amount}", String.valueOf(amount)).replace("{Player}",name).replace("{Item}",item); + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender, string); + }else { + AdventureManager.consoleMessage(string); + } + } + + private void wrongAmount(CommandSender sender){ + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender, ConfigReader.Message.prefix + ConfigReader.Message.wrongAmount); + }else { + AdventureManager.consoleMessage(ConfigReader.Message.prefix + ConfigReader.Message.wrongAmount); + } + } +} diff --git a/src/main/java/net/momirealms/customfishing/command/TabComplete.java b/src/main/java/net/momirealms/customfishing/command/TabComplete.java new file mode 100644 index 00000000..903bec8f --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/command/TabComplete.java @@ -0,0 +1,84 @@ +package net.momirealms.customfishing.command; + +import net.momirealms.customfishing.ConfigReader; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TabComplete implements TabCompleter { + @Override + @ParametersAreNonnullByDefault + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + //没有权限还想补全? + if (!(sender.hasPermission("customfishing.admin") || sender.isOp())) { + return null; + } + if (args.length == 1){ + return Arrays.asList("reload","items"); + } + if (args.length == 2){ + if (args[0].equalsIgnoreCase("items")){ + return Arrays.asList("loot","bait","rod","util"); + } + } + if (args.length == 3){ + if (args[0].equalsIgnoreCase("items")){ + if (args[1].equalsIgnoreCase("loot") || args[1].equalsIgnoreCase("util") || args[1].equalsIgnoreCase("rod") || args[1].equalsIgnoreCase("bait")){ + return Arrays.asList("get","give"); + } + } + } + if (args.length == 4){ + if (args[0].equalsIgnoreCase("items")){ + if (args[1].equalsIgnoreCase("loot")){ + if (args[2].equalsIgnoreCase("give")){ + return online_players(); + } + if (args[2].equalsIgnoreCase("get")){ + return loots(); + } + }else if(args[1].equalsIgnoreCase("util")){ + if (args[2].equalsIgnoreCase("give")){ + return online_players(); + } + if (args[2].equalsIgnoreCase("get")){ + return utils(); + } + } + } + } + if (args.length == 5){ + if (args[0].equalsIgnoreCase("items")){ + if (args[1].equalsIgnoreCase("loot")){ + if (args[2].equalsIgnoreCase("give")){ + return loots(); + } + }else if(args[1].equalsIgnoreCase("util")){ + if (args[2].equalsIgnoreCase("give")){ + return utils(); + } + } + } + } + return null; + } + + private List online_players(){ + List online = new ArrayList<>(); + Bukkit.getOnlinePlayers().forEach((player -> online.add(player.getName()))); + return online; + } + + private List loots(){ + return new ArrayList<>(ConfigReader.LOOTITEM.keySet()); + } + private List utils(){ + return new ArrayList<>(ConfigReader.UTIL.keySet()); + } +} diff --git a/src/main/java/net/momirealms/customfishing/listener/PlayerListener.java b/src/main/java/net/momirealms/customfishing/listener/PlayerListener.java new file mode 100644 index 00000000..58efd5df --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/listener/PlayerListener.java @@ -0,0 +1,242 @@ +package net.momirealms.customfishing.listener; + +import de.tr7zw.changeme.nbtapi.NBTItem; +import net.momirealms.customfishing.AdventureManager; +import net.momirealms.customfishing.ConfigReader; +import net.momirealms.customfishing.requirements.FishingCondition; +import net.momirealms.customfishing.requirements.Requirement; +import net.momirealms.customfishing.timer.Timer; +import net.momirealms.customfishing.utils.FishingPlayer; +import net.momirealms.customfishing.utils.LayoutUtil; +import net.momirealms.customfishing.utils.LootInstance; +import net.momirealms.customfishing.utils.MMUtil; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.Vector; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class PlayerListener implements Listener { + + private final HashMap coolDown = new HashMap<>(); + private final HashMap nextLoot = new HashMap<>(); + public static ConcurrentHashMap fishingPlayers = new ConcurrentHashMap<>(); + + @EventHandler + public void onFish(PlayerFishEvent event){ + PlayerFishEvent.State state = event.getState(); + Player player = event.getPlayer(); + //抛竿 + if (state.equals(PlayerFishEvent.State.FISHING)){ + //设置冷却时间 + long time = System.currentTimeMillis(); + if (time - (coolDown.getOrDefault(player, time - 1000)) < 1000) { + return; + } + coolDown.put(player, time); + //获取抛竿位置处可能的Loot实例列表 + List possibleLoots = getPossibleLootList(new FishingCondition(player, event.getHook().getLocation())); + if (possibleLoots.size() == 0){ + nextLoot.put(player, null); + return; + } + //计算总权重 + int totalWeight = 0; + for (LootInstance loot : possibleLoots){ + totalWeight += loot.getWeight(); + } + //计算每种鱼权重所占的比例并输入数组 + double[] weightRatios = new double[possibleLoots.size()]; + int index = 0; + for (LootInstance loot : possibleLoots) { + double weight = loot.getWeight(); + weightRatios[index++] = weight / totalWeight; + } + //根据权重比例划分定义域 + double[] weights = new double[possibleLoots.size()]; + double startPos = 0; + for (int i = 0; i < index; i++) { + weights[i] = startPos + weightRatios[i]; + startPos += weightRatios[i]; + } + //根据随机数所落区间获取Loot实例 + double random = Math.random(); + int pos = Arrays.binarySearch(weights, random); + + if (pos < 0) { + //二分法,数组中不存在该元素,则会返回 -(插入点 + 1) + pos = -pos - 1; + } else { + //如果存在,那真是中大奖了! + nextLoot.put(player, possibleLoots.get(pos)); + return; + } + if (pos < weights.length && random < weights[pos]) { + nextLoot.put(player, possibleLoots.get(pos)); + return; + } + //以防万一,丢入空值 + nextLoot.put(player, null); + } + + //咬钩 + else if (state.equals(PlayerFishEvent.State.BITE)){ + //如果没有特殊鱼就返回 + if (nextLoot.get(player) == null) return; + //如果正在钓一个鱼了,那么也返回 + if (fishingPlayers.get(player) != null) return; + LootInstance lootInstance = nextLoot.get(player); + //获取布局名,或是随机布局 + String layout = Optional.ofNullable(lootInstance.getLayout()).orElseGet(() ->{ + Random generator = new Random(); + Object[] values = ConfigReader.LAYOUT.keySet().toArray(); + return (String) values[generator.nextInt(values.length)]; + }); + //根据鱼的时间放入玩家实例,并应用药水效果 + fishingPlayers.put(player, + new FishingPlayer(System.currentTimeMillis() + lootInstance.getTime(), + new Timer(player, lootInstance.getDifficulty(), layout) + ) + ); + player.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, lootInstance.getTime()/50,3)); + } + + //正常钓到鱼 & 正常收杆 + else if (state.equals(PlayerFishEvent.State.CAUGHT_FISH) || state.equals(PlayerFishEvent.State.REEL_IN)){ + //发现有特殊鱼,那么必须掉落特殊鱼 + if (fishingPlayers.get(player) != null){ + //清除原版战利品 + if (event.getCaught() != null){ + event.getCaught().remove(); + } + LootInstance lootInstance = nextLoot.get(player); + LayoutUtil layout = ConfigReader.LAYOUT.get(fishingPlayers.get(player).getTimer().getLayout()); + int last = (fishingPlayers.get(player).getTimer().getTimerTask().getProgress() + 1)/layout.getRange(); + fishingPlayers.remove(player); + player.removePotionEffect(PotionEffectType.SLOW); + if (!event.getHook().isInOpenWater()){ + AdventureManager.playerMessage(player, ConfigReader.Message.prefix + ConfigReader.Message.notOpenWater); + return; + } + if (Math.random() < layout.getSuccessRate()[last]){ + //捕鱼成功 + Location location = event.getHook().getLocation(); + //钓上来的是MM怪吗 + if (lootInstance.getMm() != null){ + MMUtil.summonMM(player.getLocation(), location, lootInstance); + }else { + Entity item = location.getWorld().dropItem(location, ConfigReader.LOOTITEM.get(lootInstance.getKey())); + Vector vector = player.getLocation().subtract(location).toVector().multiply(0.1); + vector = vector.setY((vector.getY()+0.2)*1.2); + item.setVelocity(vector); + } + if (lootInstance.getMsg() != null){ + //发送消息 + AdventureManager.playerMessage(player, ConfigReader.Message.prefix + lootInstance.getMsg().replace("{loot}",lootInstance.getNick()).replace("{player}", player.getName())); + } + if (lootInstance.getCommands() != null){ + //执行指令 + lootInstance.getCommands().forEach(command ->{ + String finalCommand = command. + replaceAll("\\{x}", String.valueOf(Math.round(location.getX()))). + replaceAll("\\{y}", String.valueOf(Math.round(location.getY()))). + replaceAll("\\{z}", String.valueOf(Math.round(location.getZ()))). + replaceAll("\\{player}", event.getPlayer().getName()). + replaceAll("\\{world}", player.getWorld().getName()). + replaceAll("\\{loot}", lootInstance.getNick()); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCommand); + }); + } + //发送Title + AdventureManager.playerTitle(player, ConfigReader.Title.success_title.get((int) (ConfigReader.Title.success_title.size()*Math.random())).replace("{loot}",lootInstance.getNick()), ConfigReader.Title.success_subtitle.get((int) (ConfigReader.Title.success_subtitle.size()*Math.random())).replace("{loot}",lootInstance.getNick()), ConfigReader.Title.success_in, ConfigReader.Title.success_stay, ConfigReader.Title.success_out); + }else { + //移除正在钓鱼的状态 + fishingPlayers.remove(player); + //捕鱼失败Title + AdventureManager.playerTitle(player, ConfigReader.Title.failure_title.get((int) (ConfigReader.Title.failure_title.size()*Math.random())), ConfigReader.Title.failure_subtitle.get((int) (ConfigReader.Title.failure_subtitle.size()*Math.random())), ConfigReader.Title.failure_in, ConfigReader.Title.failure_stay, ConfigReader.Title.failure_out); + } + } + //筛选后发现这个地方没有特殊鱼 + else { + //是否能钓到原版掉落物 + if (!ConfigReader.Config.vanillaDrop){ + if (event.getCaught() != null){ + event.getCaught().remove(); + } + } + } + } + } + + @EventHandler + public void onQUit(PlayerQuitEvent event){ + Player player = event.getPlayer(); + player.removePotionEffect(PotionEffectType.SLOW); + coolDown.remove(player); + nextLoot.remove(player); + fishingPlayers.remove(player); + } + + @EventHandler + public void onInteract(PlayerInteractEvent event){ + if (!event.hasItem()) return; + if (!(event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK)) return; + NBTItem nbtItem = new NBTItem(event.getItem()); + if (nbtItem.getCompound("CustomFishing") == null) return; + if (nbtItem.getCompound("CustomFishing").getString("type").equals("util") || nbtItem.getCompound("CustomFishing").getString("id").equals("fishfinder")){ + Player player = event.getPlayer(); + //设置冷却时间 + long time = System.currentTimeMillis(); + if (time - (coolDown.getOrDefault(player, time - ConfigReader.Config.fishFinderCoolDown)) < ConfigReader.Config.fishFinderCoolDown) { + AdventureManager.playerMessage(player, ConfigReader.Message.prefix + ConfigReader.Message.coolDown); + return; + } + coolDown.put(player, time); + //获取玩家位置处可能的Loot实例列表 + List possibleLoots = getPossibleLootList(new FishingCondition(player, player.getLocation())); + if (possibleLoots.size() == 0){ + AdventureManager.playerMessage(player, ConfigReader.Message.prefix + ConfigReader.Message.noLoot); + return; + } + StringBuilder stringBuilder = new StringBuilder(ConfigReader.Message.prefix + ConfigReader.Message.possibleLoots); + possibleLoots.forEach(loot -> stringBuilder.append(loot.getNick()).append(ConfigReader.Message.splitChar)); + AdventureManager.playerMessage(player, stringBuilder.substring(0, stringBuilder.length()-1)); + } + } + + /* + 获取可能的Loot列表 + */ + private List getPossibleLootList(FishingCondition fishingCondition) { + List available = new ArrayList<>(); + ConfigReader.LOOT.keySet().forEach(key -> { + LootInstance loot = ConfigReader.LOOT.get(key); + List requirements = loot.getRequirements(); + if (requirements == null){ + available.add(loot); + }else { + boolean isMet = true; + for (Requirement requirement : requirements){ + if (!requirement.isConditionMet(fishingCondition)){ + isMet = false; + } + } + if (isMet){ + available.add(loot); + } + } + }); + return available; + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customfishing/requirements/Biome.java b/src/main/java/net/momirealms/customfishing/requirements/Biome.java new file mode 100644 index 00000000..a0244c43 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/Biome.java @@ -0,0 +1,21 @@ +package net.momirealms.customfishing.requirements; + +import java.util.List; + +public record Biome(List biomes) implements Requirement { + + public List getBiomes() { + return this.biomes; + } + + @Override + public boolean isConditionMet(FishingCondition fishingCondition) { + String currentBiome = fishingCondition.getLocation().getBlock().getBiome().getKey().toString(); + for (String biome : biomes) { + if (currentBiome.equalsIgnoreCase(biome)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/net/momirealms/customfishing/requirements/FishingCondition.java b/src/main/java/net/momirealms/customfishing/requirements/FishingCondition.java new file mode 100644 index 00000000..c9777f12 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/FishingCondition.java @@ -0,0 +1,12 @@ +package net.momirealms.customfishing.requirements; + +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public record FishingCondition(Player player, Location location) { + + public Location getLocation() { return location; } + public Player getPlayer() { + return player; + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customfishing/requirements/Permission.java b/src/main/java/net/momirealms/customfishing/requirements/Permission.java new file mode 100644 index 00000000..7c55df17 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/Permission.java @@ -0,0 +1,13 @@ +package net.momirealms.customfishing.requirements; + +public record Permission(String permission) implements Requirement { + + public String getPermission() { + return this.permission; + } + + @Override + public boolean isConditionMet(FishingCondition fishingCondition) { + return fishingCondition.getPlayer().hasPermission(permission); + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customfishing/requirements/Region.java b/src/main/java/net/momirealms/customfishing/requirements/Region.java new file mode 100644 index 00000000..b350b742 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/Region.java @@ -0,0 +1,30 @@ +package net.momirealms.customfishing.requirements; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import com.sk89q.worldguard.protection.regions.RegionQuery; + +import java.util.List; + +public record Region(List regions) implements Requirement { + + public List getRegions() { + return this.regions; + } + + @Override + public boolean isConditionMet(FishingCondition fishingCondition) { + RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); + RegionQuery query = container.createQuery(); + ApplicableRegionSet set = query.getApplicableRegions(BukkitAdapter.adapt(fishingCondition.getLocation())); + for (ProtectedRegion protectedRegion : set) { + if (regions.contains(protectedRegion.getId())) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/net/momirealms/customfishing/requirements/Requirement.java b/src/main/java/net/momirealms/customfishing/requirements/Requirement.java new file mode 100644 index 00000000..bedda4f0 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/Requirement.java @@ -0,0 +1,5 @@ +package net.momirealms.customfishing.requirements; + +public interface Requirement { + boolean isConditionMet(FishingCondition fishingCondition); +} diff --git a/src/main/java/net/momirealms/customfishing/requirements/Season.java b/src/main/java/net/momirealms/customfishing/requirements/Season.java new file mode 100644 index 00000000..5c5d6f29 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/Season.java @@ -0,0 +1,25 @@ +package net.momirealms.customfishing.requirements; + +import me.clip.placeholderapi.PlaceholderAPI; +import net.momirealms.customfishing.ConfigReader; +import org.bukkit.ChatColor; + +import java.util.List; + +public record Season(List seasons) implements Requirement { + + public List getSeasons() { + return this.seasons; + } + + @Override + public boolean isConditionMet(FishingCondition fishingCondition) { + String currentSeason = ChatColor.stripColor(PlaceholderAPI.setPlaceholders(fishingCondition.getPlayer(), ConfigReader.Config.season_papi)); + for (String season : seasons) { + if (season.equalsIgnoreCase(currentSeason)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customfishing/requirements/Time.java b/src/main/java/net/momirealms/customfishing/requirements/Time.java new file mode 100644 index 00000000..5a7222d5 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/Time.java @@ -0,0 +1,24 @@ +package net.momirealms.customfishing.requirements; + +import org.apache.commons.lang.StringUtils; + +import java.util.List; + +public record Time(List times) implements Requirement{ + + public List getTimes() { + return this.times; + } + + @Override + public boolean isConditionMet(FishingCondition fishingCondition) { + long time = fishingCondition.getLocation().getWorld().getTime(); + for (String range : times) { + String[] timeMinMax = StringUtils.split(range, "~"); + if (time > Long.parseLong(timeMinMax[0]) && time < Long.parseLong(timeMinMax[1])) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/net/momirealms/customfishing/requirements/Weather.java b/src/main/java/net/momirealms/customfishing/requirements/Weather.java new file mode 100644 index 00000000..9adaa148 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/Weather.java @@ -0,0 +1,36 @@ +package net.momirealms.customfishing.requirements; + +import net.momirealms.customfishing.AdventureManager; +import org.bukkit.World; + +import java.util.List; + +public record Weather(List weathers) implements Requirement { + + public List getWeathers() { + return this.weathers; + } + + @Override + public boolean isConditionMet(FishingCondition fishingCondition) { + World world = fishingCondition.getLocation().getWorld(); + if (world != null) { + String currentWeather; + if (world.isThundering()) { + currentWeather = "thunder"; + } else if (world.isClearWeather()) { + currentWeather = "clear"; + } else { + currentWeather = "rain"; + } + for (String weather : weathers) { + if (weather.equalsIgnoreCase(currentWeather)) { + return true; + } + } + return false; + } + AdventureManager.consoleMessage("[CustomFishing] 这条消息不应该出现,玩家钓鱼时所处的世界并不存在!"); + return false; + } +} diff --git a/src/main/java/net/momirealms/customfishing/requirements/World.java b/src/main/java/net/momirealms/customfishing/requirements/World.java new file mode 100644 index 00000000..08a39e1a --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/World.java @@ -0,0 +1,22 @@ +package net.momirealms.customfishing.requirements; + +import net.momirealms.customfishing.AdventureManager; + +import java.util.List; + +public record World(List worlds) implements Requirement { + + public List getWorlds() { + return this.worlds; + } + + @Override + public boolean isConditionMet(FishingCondition fishingCondition) { + org.bukkit.World world = fishingCondition.getLocation().getWorld(); + if (world != null) { + return worlds.contains(world.getName()); + } + AdventureManager.consoleMessage("[CustomFishing] 这条消息不应该出现,玩家钓鱼时所处的世界并不存在!"); + return false; + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customfishing/requirements/YPos.java b/src/main/java/net/momirealms/customfishing/requirements/YPos.java new file mode 100644 index 00000000..d7e6afe1 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/requirements/YPos.java @@ -0,0 +1,24 @@ +package net.momirealms.customfishing.requirements; + +import org.apache.commons.lang.StringUtils; + +import java.util.List; + +public record YPos(List yPos) implements Requirement { + + public List getYPos() { + return this.yPos; + } + + @Override + public boolean isConditionMet(FishingCondition fishingCondition) { + int y = (int) fishingCondition.getLocation().getY(); + for (String range : yPos) { + String[] yMinMax = StringUtils.split(range, "~"); + if (y > Integer.parseInt(yMinMax[0]) && y < Integer.parseInt(yMinMax[1])) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customfishing/timer/Timer.java b/src/main/java/net/momirealms/customfishing/timer/Timer.java new file mode 100644 index 00000000..e3c39caa --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/timer/Timer.java @@ -0,0 +1,24 @@ +package net.momirealms.customfishing.timer; + +import net.momirealms.customfishing.CustomFishing; +import net.momirealms.customfishing.utils.Difficulty; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; + +public class Timer { + + private final TimerTask timerTask; + private final BukkitTask task; + private final String layout; + + public Timer(Player player, Difficulty difficulty, String layout) { + this.layout = layout; + this.timerTask = new TimerTask(player, difficulty, layout); + this.task = timerTask.runTaskTimerAsynchronously(CustomFishing.instance, 1,1); + timerTask.setTaskID(task.getTaskId()); + } + + public TimerTask getTimerTask(){ return this.timerTask; } + public int getTaskID (){ return this.task.getTaskId(); } + public String getLayout(){return this.layout;} +} diff --git a/src/main/java/net/momirealms/customfishing/timer/TimerTask.java b/src/main/java/net/momirealms/customfishing/timer/TimerTask.java new file mode 100644 index 00000000..8b98bcdf --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/timer/TimerTask.java @@ -0,0 +1,111 @@ +package net.momirealms.customfishing.timer; + +import net.momirealms.customfishing.AdventureManager; +import net.momirealms.customfishing.ConfigReader; +import net.momirealms.customfishing.CustomFishing; +import net.momirealms.customfishing.utils.Difficulty; +import net.momirealms.customfishing.utils.LayoutUtil; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import static net.momirealms.customfishing.listener.PlayerListener.fishingPlayers; + +public class TimerTask extends BukkitRunnable { + + private final Player player; + private final Difficulty difficulty; + private int taskID; + private int progress; + private int internalTimer; + private final int size; + private boolean face; + + private final String start; + private final String bar; + private final String pointer; + private final String offset; + private final String end; + private final String pointerOffset; + private final String title; + + public TimerTask(Player player, Difficulty difficulty, String layout){ + this.player = player; + this.difficulty = difficulty; + this.progress = 0; + this.internalTimer = 0; + this.face = true; + LayoutUtil layoutUtil = ConfigReader.LAYOUT.get(layout); + this.start = layoutUtil.getStart(); + this.bar = layoutUtil.getBar(); + this.pointer = layoutUtil.getPointer(); + this.offset = layoutUtil.getOffset(); + this.end = layoutUtil.getEnd(); + this.pointerOffset = layoutUtil.getPointerOffset(); + this.title = layoutUtil.getTitle(); + this.size = layoutUtil.getSize(); + } + + public int getProgress() { return this.progress; } + public void setTaskID(int taskID){ + this.taskID = taskID; + } + + @Override + public void run() { + //移除提前收杆玩家 + if (fishingPlayers.get(player) == null){ + Bukkit.getScheduler().cancelTask(taskID); + return; + } + //移除切换物品的玩家 + if (player.getInventory().getItemInMainHand().getType() != Material.FISHING_ROD){ + fishingPlayers.remove(player); + Bukkit.getScheduler().cancelTask(taskID); + Bukkit.getScheduler().callSyncMethod(CustomFishing.instance, ()-> { + player.removePotionEffect(PotionEffectType.SLOW); + return null; + }); + return; + } + //移除超时玩家 + if (System.currentTimeMillis() > fishingPlayers.get(player).getFishingTime()){ + fishingPlayers.remove(player); + Bukkit.getScheduler().cancelTask(taskID); + return; + } + int timer = difficulty.getTimer() - 1; + int speed = difficulty.getSpeed(); + //设置指针方向 + if (progress <= speed - 1){ + face = true; + }else if(progress >= size - speed + 1){ + face = false; + } + //内部计时器操控 + if (internalTimer < timer){ + internalTimer++; + }else { + if (face){ + internalTimer -= timer; + progress += speed; + }else { + internalTimer -= timer; + progress -= speed; + } + } + //发送title + StringBuilder stringBuilder = new StringBuilder(start + bar + pointerOffset); + for (int index = 0; index <= size; index++){ + if (index == progress){ + stringBuilder.append(pointer); + }else { + stringBuilder.append(offset); + } + } + stringBuilder.append(end); + AdventureManager.playerTitle(player, title, stringBuilder.toString(),0,300,0); + } +} diff --git a/src/main/java/net/momirealms/customfishing/utils/Difficulty.java b/src/main/java/net/momirealms/customfishing/utils/Difficulty.java new file mode 100644 index 00000000..3e82259a --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/utils/Difficulty.java @@ -0,0 +1,12 @@ +package net.momirealms.customfishing.utils; + +public record Difficulty(int timer, int speed) { + + public int getTimer() { + return this.timer; + } + + public int getSpeed() { + return this.speed; + } +} diff --git a/src/main/java/net/momirealms/customfishing/utils/FishingPlayer.java b/src/main/java/net/momirealms/customfishing/utils/FishingPlayer.java new file mode 100644 index 00000000..3d04b68a --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/utils/FishingPlayer.java @@ -0,0 +1,14 @@ +package net.momirealms.customfishing.utils; + +import net.momirealms.customfishing.timer.Timer; + +public record FishingPlayer(Long fishingTime, Timer timer) { + + public Long getFishingTime() { + return this.fishingTime; + } + + public Timer getTimer() { + return this.timer; + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customfishing/utils/LayoutUtil.java b/src/main/java/net/momirealms/customfishing/utils/LayoutUtil.java new file mode 100644 index 00000000..0b4ce83d --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/utils/LayoutUtil.java @@ -0,0 +1,44 @@ +package net.momirealms.customfishing.utils; + +public class LayoutUtil { + + private final String key; + private final int range; + private final double[] successRate; + private final int size; + + private String start; + private String bar; + private String pointer; + private String offset; + private String end; + private String pointerOffset; + private String title; + + public LayoutUtil(String key, int range, double[] successRate, int size){ + this.key = key; + this.range = range; + this.successRate = successRate; + this.size = size; + } + + public void setBar(String bar) {this.bar = bar;} + public void setEnd(String end) {this.end = end;} + public void setOffset(String offset) {this.offset = offset;} + public void setPointer(String pointer) {this.pointer = pointer;} + public void setPointerOffset(String pointerOffset) {this.pointerOffset = pointerOffset;} + public void setStart(String start) {this.start = start;} + public void setTitle(String title) {this.title = title;} + + public String getKey(){return this.key;} + public int getRange(){return this.range;} + public double[] getSuccessRate(){return this.successRate;} + public int getSize(){return this.size;} + public String getBar() {return bar;} + public String getEnd() {return end;} + public String getOffset() {return offset;} + public String getPointer() {return pointer;} + public String getPointerOffset() {return pointerOffset;} + public String getStart() {return start;} + public String getTitle() {return title;} +} diff --git a/src/main/java/net/momirealms/customfishing/utils/LootInstance.java b/src/main/java/net/momirealms/customfishing/utils/LootInstance.java new file mode 100644 index 00000000..279ce2a4 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/utils/LootInstance.java @@ -0,0 +1,122 @@ +package net.momirealms.customfishing.utils; + +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.momirealms.customfishing.ConfigReader; +import net.momirealms.customfishing.requirements.Requirement; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.*; + +public class LootInstance { + + private final String key; + private final String name; + private String nick; + private List lore; + private Map nbt; + private String material; + private String msg; + private String mm; + private String layout; + private VectorUtil vectorUtil; + private final Difficulty difficulty; + private final int weight; + private List requirements; + private final int time; + private int mmLevel; + private List commands; + + public LootInstance(String key, String name, Difficulty difficulty, int weight, int time){ + this.key = key; + this.name = name; + this.difficulty = difficulty; + this.weight = weight; + this.time = time; + } + + public String getKey(){ + return this.key; + } + public String getNick(){ return this.nick; } + public String getMsg(){ return this.msg; } + public String getLayout(){ return this.layout; } + public String getMm(){ return this.mm; } + public List getLore(){ + return this.lore; + } + public List getCommands(){return this.commands;} + public Difficulty getDifficulty(){ + return this.difficulty; + } + public int getWeight(){ + return this.weight; + } + public String getName(){ + return this.name; + } + public String getMaterial(){ + return this.material; + } + public Map getNbt(){ + return this.nbt; + } + public List getRequirements() { return this.requirements; } + public int getTime(){ return this.time; } + public int getMmLevel(){ return this.mmLevel; } + public VectorUtil getVectorUtil(){ return this.vectorUtil; } + + public void setLore(List lore){ + this.lore = lore; + } + public void setNbt(Map nbt){ + this.nbt = nbt; + } + public void setRequirements(List requirements) { this.requirements = requirements; } + public void setMaterial(String material){ this.material = material; } + public void setNick(String nick){ this.nick = nick; } + public void setMsg(String msg){ this.msg = msg; } + public void setMm(String mm){ this.mm = mm; } + public void setLayout(String layout){ this.layout = layout; } + public void setVectorUtil(VectorUtil vectorUtil){ this.vectorUtil = vectorUtil; } + public void setCommands(List commands){ this.commands = commands; } + public void setMmLevel(int mmLevel){ this.mmLevel = mmLevel; } + + /* + 将实例转换为缓存中的NBT物品 + */ + public static void addLoot2cache(String lootKey){ + //从缓存中请求物品Item + LootInstance loot = ConfigReader.LOOT.get(lootKey); + ItemStack itemStack = new ItemStack(Material.valueOf(loot.material.toUpperCase())); + NBTItem nbtItem = new NBTItem(itemStack); + //设置Name和Lore + NBTCompound display = nbtItem.addCompound("display"); + display.setString("Name", GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(""+loot.name))); + if(loot.lore != null){ + List lores = display.getStringList("Lore"); + loot.lore.forEach(lore -> lores.add(GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(""+lore)))); + } + //设置NBT + //添加物品进入缓存 + if (loot.nbt != null){ + NBTUtil nbtUtil = new NBTUtil(loot.nbt, nbtItem.getItem()); + ConfigReader.LOOTITEM.put(lootKey, nbtUtil.getNBTItem().getItem()); + }else { + ConfigReader.LOOTITEM.put(lootKey, nbtItem.getItem()); + } + } + + /* + 给予玩家某NBT物品 + */ + public static void givePlayerLoot(Player player, String lootKey, int amount){ + ItemStack itemStack = ConfigReader.LOOTITEM.get(lootKey); + itemStack.setAmount(amount); + player.getInventory().addItem(itemStack); + } +} diff --git a/src/main/java/net/momirealms/customfishing/utils/MMUtil.java b/src/main/java/net/momirealms/customfishing/utils/MMUtil.java new file mode 100644 index 00000000..fd22db2e --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/utils/MMUtil.java @@ -0,0 +1,34 @@ +package net.momirealms.customfishing.utils; + +import io.lumine.mythic.api.adapters.AbstractLocation; +import io.lumine.mythic.api.adapters.AbstractVector; +import io.lumine.mythic.api.mobs.MobManager; +import io.lumine.mythic.api.mobs.MythicMob; +import io.lumine.mythic.bukkit.MythicBukkit; +import io.lumine.mythic.bukkit.utils.serialize.Position; +import io.lumine.mythic.core.mobs.ActiveMob; +import org.bukkit.Location; +import org.bukkit.util.Vector; + +import java.util.Optional; + +public class MMUtil { + + public static void summonMM(Location pLocation, Location bLocation, LootInstance loot){ + MobManager mobManager = MythicBukkit.inst().getMobManager(); + Optional mythicMob = mobManager.getMythicMob(loot.getMm()); + if (mythicMob.isPresent()) { + //获取MM怪实例 + MythicMob theMob = mythicMob.get(); + //生成MM怪 + Position position = Position.of(bLocation); + AbstractLocation abstractLocation = new AbstractLocation(position); + ActiveMob activeMob = theMob.spawn(abstractLocation, loot.getMmLevel()); + + VectorUtil vectorUtil = loot.getVectorUtil(); + Vector vector = pLocation.subtract(bLocation).toVector().multiply((vectorUtil.getHorizontal())-1); + vector = vector.setY((vector.getY()+0.2)*vectorUtil.getVertical()); + activeMob.getEntity().setVelocity(new AbstractVector(vector.getX(),vector.getY(),vector.getZ())); + } + } +} diff --git a/src/main/java/net/momirealms/customfishing/utils/NBTUtil.java b/src/main/java/net/momirealms/customfishing/utils/NBTUtil.java new file mode 100644 index 00000000..ec62eabe --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/utils/NBTUtil.java @@ -0,0 +1,75 @@ +package net.momirealms.customfishing.utils; + +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import net.momirealms.customfishing.AdventureManager; +import org.bukkit.inventory.ItemStack; + +import java.util.*; + +public class NBTUtil { + + private final Map nbt; + private final NBTItem nbtItem; + + public NBTUtil(Map nbt, ItemStack itemStack){ + this.nbt = nbt; + this.nbtItem = new NBTItem(itemStack); + } + + public NBTItem getNBTItem(){ + nbt.keySet().forEach(key -> { + if (nbt.get(key) instanceof Map map){ + nbtItem.addCompound((String) key); + setTags(map, nbtItem.getCompound((String) key)); + }else { + setNbt(nbtItem, (String) key, nbt.get(key)); + } + }); + return this.nbtItem; + } + + private void setTags(Map map, NBTCompound nbtCompound){ + map.keySet().forEach(key -> { + if (map.get(key) instanceof Map map2){ + nbtCompound.addCompound((String) key); + setTags(map2, nbtCompound.getCompound((String) key)); + }else { + setNbt(nbtCompound, (String) key, map.get(key)); + } + }); + } + + private void setNbt(NBTCompound nbtCompound, String key, Object value){ + if (value instanceof String string){ + if (string.startsWith("(Int) ")){ + nbtCompound.setInteger(key, Integer.valueOf(string.substring(6))); + }else if (string.startsWith("(String) ")){ + nbtCompound.setString(key, string.substring(9)); + }else if (string.startsWith("(Long) ")){ + nbtCompound.setLong(key, Long.valueOf(string.substring(7))); + }else if (string.startsWith("(Float) ")){ + nbtCompound.setFloat(key, Float.valueOf(string.substring(8))); + } else if (string.startsWith("(Double) ")){ + nbtCompound.setDouble(key, Double.valueOf(string.substring(9))); + }else if (string.startsWith("(Short) ")){ + nbtCompound.setShort(key, Short.valueOf(string.substring(8))); + }else if (string.startsWith("(Boolean) ")){ + nbtCompound.setBoolean(key, Boolean.valueOf(string.substring(10))); + }else if (string.startsWith("(UUID) ")){ + nbtCompound.setUUID(key, UUID.fromString(string.substring(7))); + }else if (string.startsWith("(Byte) ")){ + nbtCompound.setByte(key, Byte.valueOf(string.substring(7))); + }else { + nbtCompound.setString(key, string); + } + }else { + try { + nbtCompound.setInteger(key, (Integer) value); + }catch (ClassCastException e){ + e.printStackTrace(); + AdventureManager.consoleMessage("[CustomFishing] 非Int类型数字必须加上强制转换标识!"); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customfishing/utils/UtilInstance.java b/src/main/java/net/momirealms/customfishing/utils/UtilInstance.java new file mode 100644 index 00000000..84def4e0 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/utils/UtilInstance.java @@ -0,0 +1,85 @@ +package net.momirealms.customfishing.utils; + +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.momirealms.customfishing.ConfigReader; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Map; + +public class UtilInstance { + + private final String key; + private final String name; + private List lore; + private Map nbt; + private final String material; + + public UtilInstance(String key, String name, String material){ + this.key = key; + this.name = name; + this.material = material; + } + + public String getKey(){ + return this.key; + } + public List getLore(){ + return this.lore; + } + public String getName(){ + return this.name; + } + public String getMaterial(){ + return this.material; + } + public Map getNbt(){ + return this.nbt; + } + + public void setLore(List lore){ + this.lore = lore; + } + public void setNbt(Map nbt){ + this.nbt = nbt; + } + + /* + 将实例转换为缓存中的NBT物品 + */ + public static void addUtil2cache(String utilKey){ + //从缓存中请求物品Item + UtilInstance util = ConfigReader.UTIL.get(utilKey); + ItemStack itemStack = new ItemStack(Material.valueOf(util.material.toUpperCase())); + NBTItem nbtItem = new NBTItem(itemStack); + //设置Name和Lore + NBTCompound display = nbtItem.addCompound("display"); + display.setString("Name", GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(""+util.name))); + if (util.lore != null){ + List lores = display.getStringList("Lore"); + util.lore.forEach(lore -> lores.add(GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(""+lore)))); + } + //设置NBT + //添加物品进入缓存 + if (util.nbt != null){ + NBTUtil nbtUtil = new NBTUtil(util.nbt, nbtItem.getItem()); + ConfigReader.UTILITEM.put(utilKey, nbtUtil.getNBTItem().getItem()); + }else { + ConfigReader.UTILITEM.put(utilKey, nbtItem.getItem()); + } + } + + /* + 给予玩家某NBT物品 + */ + public static void givePlayerUtil(Player player, String UtilKey, int amount){ + ItemStack itemStack = ConfigReader.UTILITEM.get(UtilKey); + itemStack.setAmount(amount); + player.getInventory().addItem(itemStack); + } +} diff --git a/src/main/java/net/momirealms/customfishing/utils/VectorUtil.java b/src/main/java/net/momirealms/customfishing/utils/VectorUtil.java new file mode 100644 index 00000000..049041b6 --- /dev/null +++ b/src/main/java/net/momirealms/customfishing/utils/VectorUtil.java @@ -0,0 +1,15 @@ +package net.momirealms.customfishing.utils; + +public class VectorUtil { + + private final double horizontal; + private final double vertical; + + public VectorUtil(double horizontal, double vertical){ + this.horizontal = horizontal; + this.vertical = vertical; + } + + public double getHorizontal(){return this.horizontal;} + public double getVertical(){return this.vertical;} +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 00000000..58f3024c --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,89 @@ +config: + #插件兼容 + integrations: + #限定区域钓鱼需要启用WorldGuard兼容 + WorldGuard: false + #钓MM怪需要启用MythicMobs兼容 + MythicMobs: false + #使用季节特性启用PlaceholderAPI兼容 + PlaceholderAPI: false + + #季节特性 + season: + #是否启用 + enable: false + #需要解析的季节变量 + papi: '%customcrops_season%' + + #假如某个地方不存在特殊鱼,是否能让玩家获得原版的钓鱼战利品 + #如果设置为false,那么玩家无法在不满足条件的地方钓起任何物品 + #你也可以根据原版钓鱼战利品写入本插件内以接近原版 + vanilla-loot-when-no-custom-fish: false + + #只有在开放水域才能钓到特殊鱼 + need-open-water: true + + #成功率 + #你可以自定义区域数量,这为自定义UI提供了可能 + success-rate: + bar1: + #每个判定区间的像素个数 + #默认配置为16个像素每区间 + #如果你对自定义UI很感兴趣建议联系作者提供相关帮助 + range: 16 + title: '鱼上钩了,集中注意!' + subtitle: + start: '' + bar: '뀃' + pointer_offset: '뀂' + pointer: '뀄' + offset: '뀁' + end: '' + layout: + 1: 0 + 2: 0 + 3: 0 + 4: 0.1 + 5: 0.5 + 6: 1 + 7: 0.5 + 8: 0.1 + 9: 0 + 10: 0 + 11: 0 + bar2: + range: 8 + title: '鱼上钩了,集中注意!' + subtitle: + start: '' + bar: '뀃' + pointer_offset: '뀂' + pointer: '뀄' + offset: '뀁' + end: '' + layout: + 1: 0 + 2: 0 + 3: 0 + 4: 0 + 5: 0 + 6: 0 + 7: 0.1 + 8: 0.1 + 9: 0.3 + 10: 0.3 + 11: 1 + 12: 0.3 + 13: 0.1 + 14: 0 + 15: 0.1 + 16: 0.3 + 17: 1 + 18: 0.3 + 19: 0.1 + 20: 0 + 21: 0 + 22: 0 + + #使用找鱼器的冷却时间(ms) + fishfinder-cooldown: 3000 \ No newline at end of file diff --git a/src/main/resources/loots.yml b/src/main/resources/loots.yml new file mode 100644 index 00000000..3714b5a5 --- /dev/null +++ b/src/main/resources/loots.yml @@ -0,0 +1,120 @@ +#物品库说明: +#除了display.name/weight/difficulty/material/time为必填项目,其他都是可选项目 +#items与mobs不可重复,否则无法正确进行权重分配 +#物品支持MiniMessage Format +#https://docs.adventure.kyori.net/minimessage/format.html + +items: + #最基本的物品需要填写如下项目 + rubbish: + material: paper + display: + name: '垃圾' + weight: 50 + time: 100000 + difficulty: 1-1 + + #物品内部ID + rainbow_fish: + #物品的原型 + material: cod + #物品的昵称,若不填写则默认为物品名 + nick: '彩虹鱼' + #物品的显示样式 + display: + name: '✖七✖色✖彩✖虹✖鱼✖' + lore: + - 'This is a rainbow fish!' + - ' 这是一条七色彩虹鱼' + #自定义NBT + #可类型(Int) (Byte) (String) (Float) (String) (Double) (Short) (Long) (UUID) (Boolean) + nbt: + - Itemsadder: + namespace: '(String) momirealms' + id: '(String) rainbow_fish' + CustomModelData: '(Int) 1' + SomeNBT: + NBTS: + byte: '(Byte) 127' + float: '(Float) 3.14159' + #钓到鱼后发生的动作 + action: + #钓到鱼后的提示信息 + message: '恭喜你钓到了一条 {loot}!' + #钓到鱼后执行的指令 + command: + - 'say 玩家{player}在{world},{x},{y},{z}钓到了一条{loot}!' + #钓到此鱼的权重 + weight: 10 + + # 钓此鱼的难度 + # 难度设置,左侧为每多少tick移动一个单位,右侧为一个单位的像素距离 + # 1-1指每1tick移动1像素 2-3指每2tick移动3像素 + difficulty: 1-25 + + #指定钓此鱼的UI布局 + #若不设置则每次都随机 + layout: bar1 + + # 玩家有多少时间捕鱼 + # 超过指定时间鱼会跑(单位:毫秒) + time: 5000 + #钓到此鱼的条件 + requirements: + #能够钓到此鱼的群系 + #支持自定义生物群系 + biome: + - minecraft:plains + - minecraft:taiga + #Y轴范围条件 + ypos: + - 50~100 + - 150~200 + #钓到此鱼的天气需求 + weather: + - rain + - clear + - thunder + #钓到此鱼所需的权限 + permission: 'customfishing.rainbowfish' + #指定此鱼出现的世界 + world: + - world + #指定此鱼出现的时间 + time: + - 0~12000 + #region: + # - fishingpool + #适合钓到此鱼的季节 + #删除此设置则可在任何季节捕获 + #season: + # - 春 + # - 秋 + +mobs: + skeletalknight: + #MM怪的ID + mythicmobsID: SkeletalKnight + #MM怪等级,如果有则设置,无则忽略 + level: 0 + #这个怪物的名字,用于在消息中提示 + name: '骷髅骑士' + #决定了MM怪的出场方式 + vector: + #水平位移乘数 + horizontal: 1.1 + #竖直位移乘数 + vertical: 1.2 + action: + message: '{loot} 登场!' + command: + - 'say 玩家{player}在{world},{x},{y},{z}被{loot}追杀!' + weight: 10 + difficulty: 1-6 + time: 5000 + requirements: + weather: + - rain + - thunder + world: + - world \ No newline at end of file diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml new file mode 100644 index 00000000..62613b74 --- /dev/null +++ b/src/main/resources/messages.yml @@ -0,0 +1,19 @@ +#所有位置均可使用MiniMessage Format +#https://docs.adventure.kyori.net/minimessage/format.html +messages: + prefix: '[CustomFishing] ' + reload: '重载成功.' + no-perm: '你没有权限!' + not-online: '玩家不在线!' + not-exist: '此物品不存在!' + escape: '太久没拉钩鱼儿跑走啦!' + give-item: '成功给予玩家 {Player} {Amount}x {Item}.' + get-item: '成功获得 {Amount}x {Item}.' + no-console: '这个指令不能由控制台执行!' + wrong-amount: '不能给玩家数量为负数的物品!' + lack-args: '参数不足.' + cooldown: '你使用找鱼器的速度太快了!' + possible-loots: '此处可能钓到: ' + split-char: ',' + no-loot: '这个地方什么鱼都没有!' + not-open-water: '这里不是开放水域,你将无法获得任何奖励!' \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 00000000..b8b4f09d --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,14 @@ +name: CustomFishing +version: '${version}' +main: net.momirealms.customfishing.CustomFishing +api-version: 1.16 +authors: [ XiaoMoMi ] +softdepend: + - MythicMobs + - PlaceholderAPI + - WorldGuard +commands: + customfishing: + usage: /customfishing + description: main command + permission: customfishing.admin \ No newline at end of file diff --git a/src/main/resources/titles.yml b/src/main/resources/titles.yml new file mode 100644 index 00000000..d8a78215 --- /dev/null +++ b/src/main/resources/titles.yml @@ -0,0 +1,27 @@ +#对于不想启用的title设置title和subtitle为空即可 +#所有位置均可使用MiniMessage Format +#https://docs.adventure.kyori.net/minimessage/format.html +titles: + failure: + title: + - '真菜!' + - '失败!' + subtitle: + - '鱼已经逃跑了' + - '下次再努力吧' + #单位 tick + fade: + in: 10 + stay: 30 + out: 10 + success: + title: + - '成功!' + - '漂亮!' + subtitle: + - '你捕捉到了一条 {loot}' + - '嗨害嗨 {loot} 来咯!' + fade: + in: 10 + stay: 30 + out: 10 \ No newline at end of file diff --git a/src/main/resources/utils.yml b/src/main/resources/utils.yml new file mode 100644 index 00000000..22d20450 --- /dev/null +++ b/src/main/resources/utils.yml @@ -0,0 +1,14 @@ +utils: + fishfinder: + material: compass + display: + name: '找鱼器' + lore: + - '右键查看这个地方能钓到什么鱼吧!' + #NBT是本插件识别找鱼器的唯一方法 + #如果你想使用IA物品那请在IA的物品配置里增加 nbt: {CustomFishing: fishfinder} + nbt: + - CustomModelData: '(Int) 1' + CustomFishing: + type: '(String) util' + id: '(String) fishfinder' \ No newline at end of file