commit 3201c67fd59a0625d1e7c33d7e36723ccf9f344b Author: Patrick <147879351+WinniePatGG@users.noreply.github.com> Date: Fri May 1 19:04:16 2026 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..835e8dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.gradle +.idea +build \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..498f345 --- /dev/null +++ b/build.gradle @@ -0,0 +1,53 @@ +plugins { + id 'java' + id("xyz.jpenilla.run-paper") version "2.3.1" +} + +group = 'de.winniepat' +version = '1.0' + +repositories { + mavenCentral() + maven { + name = "papermc-repo" + url = "https://repo.papermc.io/repository/maven-public/" + } +} + +dependencies { + compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") + compileOnly 'net.luckperms:api:5.4' +} + +tasks { + runServer { + 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 + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e69de29 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e644113 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a441313 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..b740cf1 --- /dev/null +++ b/gradlew @@ -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" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..25da30d --- /dev/null +++ b/gradlew.bat @@ -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 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..2ce0545 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'KingdomClashSurvival' diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/KingdomClashSurvival.java b/src/main/java/de/winniepat/kingdomClashSurvival/KingdomClashSurvival.java new file mode 100644 index 0000000..8278723 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/KingdomClashSurvival.java @@ -0,0 +1,343 @@ +package de.winniepat.kingdomClashSurvival; + +import de.winniepat.kingdomClashSurvival.bloodmoon.*; +import de.winniepat.kingdomClashSurvival.commands.*; +import de.winniepat.kingdomClashSurvival.crafting.*; +import de.winniepat.kingdomClashSurvival.items.PlayerTracker; +import de.winniepat.kingdomClashSurvival.items.VillageBlessing; +import de.winniepat.kingdomClashSurvival.listeners.*; +import de.winniepat.kingdomClashSurvival.listeners.balance.TNTProtectionListener; +import de.winniepat.kingdomClashSurvival.listeners.items.*; +import de.winniepat.kingdomClashSurvival.listeners.items.playertracker.PlayerTrackerListener; +import de.winniepat.kingdomClashSurvival.listeners.items.villageblessing.PreventBellPlacementListener; +import de.winniepat.kingdomClashSurvival.listeners.items.villageblessing.VillageBlessingListener; +import de.winniepat.kingdomClashSurvival.listeners.team.BlueTeamListener; +import de.winniepat.kingdomClashSurvival.listeners.team.GreenTeamListener; +import de.winniepat.kingdomClashSurvival.listeners.team.RedTeamListener; +import de.winniepat.kingdomClashSurvival.listeners.team.YellowTeamListener; +import de.winniepat.kingdomClashSurvival.managers.*; +import de.winniepat.kingdomClashSurvival.polls.PollResultGUI; +import de.winniepat.kingdomClashSurvival.polls.PollVoteGUI; +import de.winniepat.kingdomClashSurvival.tasks.*; +import net.kyori.adventure.text.Component; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.*; +import de.winniepat.kingdomClashSurvival.gui.TeamSelectionGUI; +import de.winniepat.kingdomClashSurvival.util.MaceLimiter; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class KingdomClashSurvival extends JavaPlugin { + + public static KingdomClashSurvival instance; + + public static final int MACE_LIMIT = 3; + private int craftedMaces = 0; + private File maceFile; + private FileConfiguration maceConfig; + + private boolean teamSizeBalancingEnabled; + + private KingdomCommands cmds; + private TeamManager teamManager; + private CombatManager combatManager; + private ItemManager itemManager; + private TeleportManager teleportManager; + private TeamSelectionGUI teamGUI; + private CraftedItemsDataManager craftedItemsDataManager; + private BloodmoonManager bloodmoonManager; + private VillageBlessing villageBlessing; + private LuckPerms luckPerms; + private StarterKitManager starterKitManager; + + @Override + public void onEnable() { + instance = this; + setupMaceFile(); + loadCraftedMaces(); + saveDefaultConfig(); + this.teamSizeBalancingEnabled = getConfig().getBoolean("settings.enable-team-size-balancing", true); + + this.teamManager = new TeamManager(this); + this.teleportManager = new TeleportManager(this); + this.teamGUI = new TeamSelectionGUI(this, teamManager); + combatManager = new CombatManager(this); + this.itemManager = new ItemManager(this); + this.cmds = new KingdomCommands(this, teamManager, teleportManager); + this.craftedItemsDataManager = new CraftedItemsDataManager(this); + this.villageBlessing = new VillageBlessing(this); + this.starterKitManager = new StarterKitManager(this); + + if (getServer().getPluginManager().getPlugin("LuckPerms") == null) { + getLogger().severe("LuckPerms is not installed! Disabling plugin."); + getServer().getPluginManager().disablePlugin(this); + return; + } + + luckPerms = LuckPermsProvider.get(); + + registerBloodMoon(); + getLogger().info("Registered BloodMoon!"); + registerPollsystem(); + getLogger().info("Registered Pollsystem!"); + registerCommands(); + getLogger().info("Registered commands!"); + registerListeners(); + getLogger().info("Registered listeners!"); + registerRecipes(); + getLogger().info("Registered recipes!"); + + new PassiveEffectTask(teamManager).runTaskTimer(this, 0L, 20L); + new CombatActionBarTask(combatManager).runTaskTimer(this, 0L, 5L); + getLogger().info("Registered Tasks!"); + + getLogger().info("enabled."); + } + + @Override + public void onDisable() { + if (craftedItemsDataManager != null) { + craftedItemsDataManager.saveConfig(); + } + + if (this.starterKitManager != null) { + this.starterKitManager.saveData(); + } + bloodmoonManager.endBloodmoon(); + getLogger().info("disabled."); + } + + public static KingdomClashSurvival getInstance() { + return instance; + } + + public CombatManager getCombatManager() { + return combatManager; + } + + public int getCraftedMaces() { + return craftedMaces; + } + + private void setupMaceFile() { + maceFile = new File(getDataFolder(), "maces.yml"); + + if (!maceFile.exists()) { + try { + getDataFolder().mkdirs(); + maceFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + maceConfig = YamlConfiguration.loadConfiguration(maceFile); + } + + private void registerCommands() { + Objects.requireNonNull(getCommand("spawn")).setExecutor(cmds); + Objects.requireNonNull(getCommand("basetp")).setExecutor(cmds); + Objects.requireNonNull(getCommand("setbase")).setExecutor(cmds); + Objects.requireNonNull(getCommand("setteam")).setExecutor(cmds); + Objects.requireNonNull(getCommand("getteam")).setExecutor(cmds); + Objects.requireNonNull(getCommand("day")).setExecutor(new DayCommand()); + Objects.requireNonNull(getCommand("sun")).setExecutor(new SunCommand()); + Objects.requireNonNull(getCommand("bloodmoon")).setExecutor(new BloodmoonCommand(bloodmoonManager)); + Objects.requireNonNull(getCommand("kingdomclashsurvivalreload")).setExecutor(new ReloadCommand(this)); + } + + private void registerListeners() { + getServer().getPluginManager().registerEvents(new BlueTeamListener(teamManager), this); + getServer().getPluginManager().registerEvents(new RedTeamListener(teamManager), this); + getServer().getPluginManager().registerEvents(new GreenTeamListener(this, teamManager), this); + getServer().getPluginManager().registerEvents(new YellowTeamListener(teamManager), this); + + getServer().getPluginManager().registerEvents(new MaceEnchantListener(), this); + getServer().getPluginManager().registerEvents(new MaceLimiter(this), this); + + getServer().getPluginManager().registerEvents(new TeamSelectionListener(this, teamManager, teamGUI, starterKitManager), this); + getServer().getPluginManager().registerEvents(new PlayerJoinGUIOpener(teamManager, teamGUI), this); + + getServer().getPluginManager().registerEvents(new InvisPlayerKillListener(), this); + getServer().getPluginManager().registerEvents(new CombatListener(combatManager, teamManager), this); + getServer().getPluginManager().registerEvents(new FriendlyFireListener(teamManager), this); + getServer().getPluginManager().registerEvents(new GamemodeCombatListener(), this); + getServer().getPluginManager().registerEvents(new ItemCraftingLimiterListener(this, craftedItemsDataManager, teamManager), this); + + getServer().getPluginManager().registerEvents(new TNTProtectionListener(), this); + getServer().getPluginManager().registerEvents(new RecipesGiveListener(this), this); + + getServer().getPluginManager().registerEvents(new PlayerTrackerListener(), this); + getServer().getPluginManager().registerEvents(new VillageBlessingListener(villageBlessing), this); + getServer().getPluginManager().registerEvents(new PreventBellPlacementListener(villageBlessing), this); + + getServer().getPluginManager().registerEvents(new BlueShieldListener(this, teamManager), this); + getServer().getPluginManager().registerEvents(new GreenFeatherListener(this, teamManager), this); + getServer().getPluginManager().registerEvents(new RedWeaponListener(this, teamManager), this); + getServer().getPluginManager().registerEvents(new YellowPickaxeListener(this, teamManager), this); + } + + private void registerPollsystem() { + File pollDataFile = new File(getDataFolder(), "polls.yml"); + PollManager pollManager = new PollManager(pollDataFile); + if (!pollDataFile.getParentFile().exists()) { + pollDataFile.getParentFile().mkdirs(); + } + Objects.requireNonNull(getCommand("pollstart")).setExecutor(new PollCommand(pollManager, this)); + Objects.requireNonNull(getCommand("pollvote")).setExecutor((sender, cmd, label, args) -> { + if (sender instanceof Player player) { + new PollVoteGUI(pollManager, this).openVoteGUI(player); + } + return true; + }); + Objects.requireNonNull(getCommand("pollresults")).setExecutor((sender, cmd, label, args) -> { + if (sender instanceof Player player && player.hasPermission("kingdomclash.poll.view")) { + new PollResultGUI(pollManager).openResultsGUI(player); + } + return true; + }); + getServer().getPluginManager().registerEvents(new PollVoteGUI(pollManager, this), this); + } + + private void registerRecipes() { + + TridentRecipe.register(this); + OPGapRecipe.register(this); + TotemRecipe.register(this); + + NamespacedKey redKey = new NamespacedKey(this, "red_weapon"); + Bukkit.addRecipe(itemManager.getRedWeaponRecipe(redKey)); + + NamespacedKey yellowKey = new NamespacedKey(this, "yellow_pickaxe"); + Bukkit.addRecipe(itemManager.getYellowPickaxeRecipe(yellowKey)); + + NamespacedKey blueKey = new NamespacedKey(this, "blue_shield"); + Bukkit.addRecipe(itemManager.getBlueShieldRecipe(blueKey)); + + NamespacedKey greenKey = new NamespacedKey(this, "green_cloak"); + Bukkit.addRecipe(itemManager.getGreenFeatherRecipe(greenKey)); + + getServer().addRecipe(PlayerTracker.getRecipe()); + getServer().addRecipe(this.villageBlessing.getRecipe()); + } + + private void registerBloodMoon() { + this.bloodmoonManager = new BloodmoonManager(this); + getServer().getPluginManager().registerEvents(new BloodmoonListener(bloodmoonManager, this), this); + new BloodmoonTask(this, bloodmoonManager).start(); + } + + public void saveCraftedMaces() { + try { + maceConfig.set("crafted-maces", craftedMaces); + maceConfig.save(maceFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void loadCraftedMaces() { + craftedMaces = maceConfig.getInt("crafted-maces", 0); + } + + public void incrementCraftedMaces() { + craftedMaces++; + } + + public boolean isTeamSizeBalancingEnabled() { + return teamSizeBalancingEnabled; + } + + public LuckPerms getLuckPerms() { + return luckPerms; + } + + private void unregisterRecipes() { + NamespacedKey redKey = new NamespacedKey(this, "red_weapon"); + Bukkit.removeRecipe(redKey); + + NamespacedKey yellowKey = new NamespacedKey(this, "yellow_pickaxe"); + Bukkit.removeRecipe(yellowKey); + + NamespacedKey blueKey = new NamespacedKey(this, "blue_shield"); + Bukkit.removeRecipe(blueKey); + + NamespacedKey greenKey = new NamespacedKey(this, "green_cloak"); + Bukkit.removeRecipe(greenKey); + + Bukkit.removeRecipe(PlayerTracker.KEY); + Bukkit.removeRecipe(VillageBlessing.KEY); + Bukkit.removeRecipe(TridentRecipe.key); + Bukkit.removeRecipe(OPGapRecipe.key); + Bukkit.removeRecipe(TotemRecipe.key); + + } + + private void disablePlugin() { + if (this.starterKitManager != null) { + this.starterKitManager.saveData(); + } + + if (craftedItemsDataManager != null) { + craftedItemsDataManager.saveConfig(); + } + + unregisterRecipes(); + + saveCraftedMaces(); + + getServer().getServicesManager().unregisterAll(this); + + getLogger().info("Plugin disabled state cleanup complete."); + } + + public boolean reloadPlugin(CommandSender sender) { + try { + disablePlugin(); + sender.sendMessage(Component.text("Disabled Plugin!")); + + this.teamManager = new TeamManager(this); + sender.sendMessage(Component.text("Registered TeamManager!")); + this.teleportManager = new TeleportManager(this); + sender.sendMessage(Component.text("Registered TeleportManager!")); + this.teamGUI = new TeamSelectionGUI(this, teamManager); + sender.sendMessage(Component.text("Registered TeamSelectionGUI!")); + combatManager = new CombatManager(this); + sender.sendMessage(Component.text("Registered CombatManager!")); + this.itemManager = new ItemManager(this); + sender.sendMessage(Component.text("Registered ItemManager!")); + this.cmds = new KingdomCommands(this, teamManager, teleportManager); + sender.sendMessage(Component.text("Registered KingdomCommands!")); + this.craftedItemsDataManager = new CraftedItemsDataManager(this); + sender.sendMessage(Component.text("Registered CraftedItemsDataManager!")); + this.villageBlessing = new VillageBlessing(this); + sender.sendMessage(Component.text("Registered VillageBlessing!")); + this.starterKitManager = new StarterKitManager(this); + sender.sendMessage(Component.text("Registered StarterKitManager!")); + + registerCommands(); + sender.sendMessage(Component.text("Registered Commands!")); + registerRecipes(); + sender.sendMessage(Component.text("Registered Recipes!")); + registerListeners(); + sender.sendMessage(Component.text("Registered Listeners!")); + + return true; + } catch (Exception e) { + getLogger().severe("FATAL ERROR during reload:"); + e.printStackTrace(); + return false; + } + } + +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/TeamType.java b/src/main/java/de/winniepat/kingdomClashSurvival/TeamType.java new file mode 100644 index 0000000..ce50846 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/TeamType.java @@ -0,0 +1,21 @@ +package de.winniepat.kingdomClashSurvival; + +import net.kyori.adventure.text.format.NamedTextColor; + +public enum TeamType { + BLUE(NamedTextColor.BLUE), + RED(NamedTextColor.RED), + GREEN(NamedTextColor.GREEN), + YELLOW(NamedTextColor.YELLOW), + NONE(NamedTextColor.GRAY); + + private final NamedTextColor color; + + TeamType(NamedTextColor color) { + this.color = color; + } + + public NamedTextColor getColor() { + return this.color; + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonCommand.java b/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonCommand.java new file mode 100644 index 0000000..4d34cd0 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonCommand.java @@ -0,0 +1,33 @@ +package de.winniepat.kingdomClashSurvival.bloodmoon; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +public class BloodmoonCommand implements CommandExecutor { + + private final BloodmoonManager manager; + + public BloodmoonCommand(BloodmoonManager manager) { + this.manager = manager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("kingdomclash.admin")) { + sender.sendMessage(ChatColor.RED + "You do not have permission to do that."); + return true; + } + + if (manager.isActive()) { + manager.endBloodmoon(); + sender.sendMessage(ChatColor.GRAY + "☀ Bloodmoon ended."); + } else { + manager.startBloodmoon(); + sender.sendMessage(ChatColor.RED + "☠ Bloodmoon started."); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonListener.java new file mode 100644 index 0000000..bf6b9ee --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonListener.java @@ -0,0 +1,160 @@ +package de.winniepat.kingdomClashSurvival.bloodmoon; + +import org.bukkit.*; +import org.bukkit.attribute.Attribute; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Random; + +public class BloodmoonListener implements Listener { + + private final BloodmoonManager manager; + private final JavaPlugin plugin; + private final Random random = new Random(); + + public BloodmoonListener(BloodmoonManager manager, JavaPlugin plugin) { + this.manager = manager; + this.plugin = plugin; + } + + @EventHandler + public void onBedEnter(PlayerBedEnterEvent event) { + if (manager.isActive()) { + event.setCancelled(true); + event.getPlayer().sendMessage(ChatColor.RED + "You cannot sleep during a Bloodmoon!"); + } + } + + @EventHandler + public void onMobSpawn(CreatureSpawnEvent event) { + if (!manager.isActive()) return; + + LivingEntity entity = event.getEntity(); + + if (entity instanceof Zombie z && z.isBaby()) return; + + if (random.nextInt(100) < 20) { + spawnBoss(entity); + return; + } + + if (entity instanceof Creeper creeper) { + creeper.setExplosionRadius(6); + if (random.nextInt(100) < 30) { + creeper.setPowered(true); + } + } + + if (entity.getAttribute(Attribute.MAX_HEALTH) != null) { + entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue( + entity.getAttribute(Attribute.MAX_HEALTH).getBaseValue() * 1.5 + ); + entity.setHealth(entity.getAttribute(Attribute.MAX_HEALTH).getBaseValue()); + } + + if (entity.getAttribute(Attribute.ATTACK_DAMAGE) != null) { + entity.getAttribute(Attribute.ATTACK_DAMAGE).setBaseValue( + entity.getAttribute(Attribute.ATTACK_DAMAGE).getBaseValue() + 2 + ); + } + + if (entity.getAttribute(Attribute.MOVEMENT_SPEED) != null) { + entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue( + entity.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue() * 1.2 + ); + } + } + + private void spawnBoss(LivingEntity entity) { + if (!(entity instanceof Zombie || entity instanceof Skeleton || entity instanceof Husk || entity instanceof Drowned)) + return; + + entity.getPersistentDataContainer().set( + new NamespacedKey(plugin, "bloodmoon_boss"), + PersistentDataType.BYTE, + (byte) 1 + ); + + entity.setCustomName(ChatColor.DARK_RED + "Blood Knight"); + entity.setCustomNameVisible(true); + + if (entity instanceof Zombie || entity instanceof Husk || entity instanceof Drowned) { + entity.getEquipment().setHelmet(createItem(Material.DIAMOND_HELMET, "Blood Helmet")); + entity.getEquipment().setChestplate(createItem(Material.DIAMOND_CHESTPLATE, "Blood Chestplate")); + entity.getEquipment().setLeggings(createItem(Material.DIAMOND_LEGGINGS, "Blood Leggings")); + entity.getEquipment().setBoots(createItem(Material.DIAMOND_BOOTS, "Blood Boots")); + entity.getEquipment().setItemInMainHand(createItem(Material.DIAMOND_SWORD, "Blood Blade")); + } else if (entity instanceof Skeleton) { + entity.getEquipment().setHelmet(createItem(Material.DIAMOND_HELMET, "Blood Helmet")); + entity.getEquipment().setChestplate(createItem(Material.NETHERITE_CHESTPLATE, "Blood Chestplate")); + entity.getEquipment().setItemInMainHand(createItem(Material.BOW, "Blood Bow")); + } + + entity.getEquipment().setHelmetDropChance(0.001f); + entity.getEquipment().setChestplateDropChance(0.001f); + entity.getEquipment().setLeggingsDropChance(0.001f); + entity.getEquipment().setBootsDropChance(0.001f); + entity.getEquipment().setItemInMainHandDropChance(0.001f); + + if (entity.getAttribute(Attribute.MAX_HEALTH) != null) { + entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(80); + entity.setHealth(80); + } + if (entity.getAttribute(Attribute.MAX_HEALTH) != null) { + entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(12); + } + if (entity.getAttribute(Attribute.MOVEMENT_SPEED) != null) { + entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.35); + } + + startParticleEffect(entity); + } + + private ItemStack createItem(Material material, String name) { + ItemStack item = new ItemStack(material); + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + meta.setDisplayName(ChatColor.RED + name); + meta.addEnchant(Enchantment.PROTECTION, 2, true); + meta.addEnchant(Enchantment.UNBREAKING, 3, true); + meta.addEnchant(Enchantment.SHARPNESS, 2, true); + item.setItemMeta(meta); + } + return item; + } + + private void startParticleEffect(LivingEntity entity) { + new BukkitRunnable() { + int ticks = 0; + + @Override + public void run() { + if (entity == null || entity.isDead() || !entity.isValid()) { + cancel(); + return; + } + + entity.getWorld().spawnParticle( + Particle.DUST, + entity.getLocation().add(0, 1, 0), + 8, + 0.3, 0.5, 0.3, + 0, + new Particle.DustOptions(Color.RED, 1.5f) + ); + + if ((ticks += 5) > 20 * 300) cancel(); + } + }.runTaskTimer(plugin, 0L, 5L); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonManager.java b/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonManager.java new file mode 100644 index 0000000..85372cf --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonManager.java @@ -0,0 +1,113 @@ +package de.winniepat.kingdomClashSurvival.bloodmoon; + +import de.winniepat.kingdomClashSurvival.util.ColorUtil; +import org.bukkit.*; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import javax.swing.*; + +public class BloodmoonManager { + private final JavaPlugin plugin; + private boolean active = false; + private BossBar bloodmoonBar; + private int durationTicks = 20 * 60 * 8; + private int ticksElapsed = 0; + private BukkitRunnable task; + + private static final String bloodmoonText = ColorUtil.translateHexCodes("&#EE2C5Bʙ&#EB2C66ʟ&#E82B71ᴏ&#E42B7Cᴏ&#E12B88ᴅ&#DE2A93ᴍ&#DB2A9Eᴏ&#D729A9ᴏ&#D429B4ɴ"); + private static final String bloodmoonHasRisen = ColorUtil.translateHexCodes("&#EE2C5Bᴛ&#ED2C5Eʜ&#EC2C60ᴇ &#EB2C65ʙ&#EA2C68ʟ&#E92B6Bᴏ&#E92B6Dᴏ&#E82B70ᴅ&#E72B73ᴍ&#E62B75ᴏ&#E62B78ᴏ&#E52B7Aɴ &#E32B80ʜ&#E32B82ᴀ&#E22B85ꜱ &#E02A8Aʀ&#DF2A8Dɪ&#DF2A8Fꜱ&#DE2A92ᴇ&#DD2A95ɴ&#DC2A97.&#DC2A9A.&#DB2A9C. &#D92AA2ʙ&#D92AA4ᴇ &#D729AAᴡ&#D629ACᴀ&#D629AFʀ&#D529B1ʏ&#D429B4!"); + private static final String mobsAreStronger = ColorUtil.translateHexCodes("&#EE2C5Bᴍ&#ED2C5Fᴏ&#EC2C62ʙ&#EB2C66ꜱ &#E92B6Eᴀ&#E82B71ʀ&#E62B75ᴇ &#E42B7Cꜱ&#E32B80ᴛ&#E22B84ʀ&#E12B88ᴏ&#E02A8Bɴ&#DF2A8Fɢ&#DE2A93ᴇ&#DD2A96ʀ &#DB2A9Eᴛ&#D92AA1ᴏ&#D82AA5ɴ&#D729A9ɪ&#D629ADɢ&#D529B0ʜ&#D429B4ᴛ"); + private static final String bloodmoonNight = ColorUtil.translateHexCodes("&#EE2C5Bʙ&#EC2C61ʟ&#EA2C68ᴏ&#E82B6Eᴏ&#E72B74ᴅ&#E52B7Bᴍ&#E32B81ᴏ&#E12B88ᴏ&#DF2A8Eɴ &#DB2A9Bɴ&#DA2AA1ɪ&#D829A7ɢ&#D629AEʜ&#D429B4ᴛ"); + private static final String bloodmoonHasEnded = ColorUtil.translateHexCodes("F87A☀ F880ᴛ F884ʜ F887ᴇ F88Dʙ F890ʟ�AF793ᴏ�AF797ᴏ�AF79Aᴅ�AF79Dᴍ�AF7A0ᴏ�AF7A3ᴏ�BF7A6ɴ �BF7ADʜ�BF7B0ᴀ�BF7B3ꜱ �CF7B9ᴇ�CF7BDɴ�CF6C0ᴅ�CF6C3ᴇ�DF6C6ᴅ�DF6C9. �DF6D0ɪ�DF6D3ᴛ�EF6D6'�EF6D9ꜱ �EF6DFꜱ�EF6E3ᴀ�EF6E6ꜰ�FF6E9ᴇ �FF5EFᴀ�FF5F2ɢ�FF5F6ᴀ F5F9ɪ F5FCɴ F5FF."); + + public BloodmoonManager(JavaPlugin plugin) { + this.plugin = plugin; + } + + public void startBloodmoon() { + if (active) return; + active = true; + Bukkit.broadcastMessage(bloodmoonHasRisen); + for (Player player : Bukkit.getOnlinePlayers()) { + player.sendTitle(bloodmoonText, mobsAreStronger, 10, 100, 20); + player.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 20 * 60 * 8, 1, false, false, false)); + player.playSound(player.getLocation(), Sound.ENTITY_WITHER_SPAWN, 1.0f, 1.0f); + + player.spawnParticle(Particle.DUST, player.getLocation().add(0, 10, 0), 100,5, 5, 5, 0, new Particle.DustOptions(Color.RED, 2)); + } + + Bukkit.getScheduler().runTaskTimer(plugin, () -> { + for (Player player : Bukkit.getOnlinePlayers()) { + if (active) { + player.spawnParticle(Particle.DUST, player.getLocation().add( + Math.random() * 10 - 5, Math.random() * 5, Math.random() * 10 - 5), + 5, 0, 0, 0, 0, new Particle.DustOptions(Color.RED, 1)); + } + } + }, 0L, 1L); + + bloodmoonBar = Bukkit.createBossBar(bloodmoonNight, BarColor.RED, BarStyle.SOLID); + bloodmoonBar.setProgress(1.0); + + for (Player player : Bukkit.getOnlinePlayers()) { + bloodmoonBar.addPlayer(player); + } + + task = new BukkitRunnable() { + @Override + public void run() { + ticksElapsed += 20; + double progress = Math.max(0, 1.0 - (double) ticksElapsed / durationTicks); + bloodmoonBar.setProgress(progress); + + if (ticksElapsed >= durationTicks) { + endBloodmoon(); + } + } + }; + task.runTaskTimer(plugin, 20L, 20L); + + } + + public void endBloodmoon() { + if (!active) return; + active = false; + + Bukkit.broadcastMessage(bloodmoonHasEnded); + NamespacedKey key = new NamespacedKey(plugin, "bloodmoon_boss"); + + for (World world : Bukkit.getWorlds()) { + for (LivingEntity entity : world.getLivingEntities()) { + if (entity.getPersistentDataContainer().has(key, PersistentDataType.BYTE)) { + entity.getWorld().playSound(entity.getLocation(), Sound.ENTITY_WITHER_DEATH, 1f, 0.5f); + + entity.remove(); + } + } + } + if (task != null) { + task.cancel(); + task = null; + } + + if (bloodmoonBar != null) { + bloodmoonBar.removeAll(); + bloodmoonBar = null; + } + + ticksElapsed = 0; + } + + public boolean isActive() { + return active; + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonTask.java b/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonTask.java new file mode 100644 index 0000000..8953edd --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/bloodmoon/BloodmoonTask.java @@ -0,0 +1,47 @@ +package de.winniepat.kingdomClashSurvival.bloodmoon; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Random; + +public class BloodmoonTask { + + private final JavaPlugin plugin; + private final BloodmoonManager manager; + + private boolean checkedThisNight = false; + + public BloodmoonTask(JavaPlugin plugin, BloodmoonManager manager) { + this.plugin = plugin; + this.manager = manager; + } + + public void start() { + new BukkitRunnable() { + @Override + public void run() { + World world = Bukkit.getWorlds().get(0); + long time = world.getTime(); + if (time >= 13000 && time <= 13100) { + if (!checkedThisNight) { + checkedThisNight = true; + + if (new Random().nextInt(100) < 5) { + manager.startBloodmoon(); + } + } + } + + if (time >= 0 && time < 1000) { + if (manager.isActive()) { + manager.endBloodmoon(); + } + checkedThisNight = false; + } + } + }.runTaskTimer(plugin, 0L, 100L); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/commands/DayCommand.java b/src/main/java/de/winniepat/kingdomClashSurvival/commands/DayCommand.java new file mode 100644 index 0000000..024d047 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/commands/DayCommand.java @@ -0,0 +1,17 @@ +package de.winniepat.kingdomClashSurvival.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.jetbrains.annotations.NotNull; + +public class DayCommand implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + Bukkit.getWorlds().forEach(world -> { + world.setTime(0); + }); + + return true; + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/commands/KingdomCommands.java b/src/main/java/de/winniepat/kingdomClashSurvival/commands/KingdomCommands.java new file mode 100644 index 0000000..6893f89 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/commands/KingdomCommands.java @@ -0,0 +1,104 @@ +package de.winniepat.kingdomClashSurvival.commands; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.*; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.node.types.InheritanceNode; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class KingdomCommands implements CommandExecutor { + + private final KingdomClashSurvival plugin; + private final TeamManager teamManager; + private final TeleportManager teleportManager; + + public KingdomCommands(KingdomClashSurvival plugin, TeamManager teamManager, TeleportManager teleportManager) { + this.plugin = plugin; + this.teamManager = teamManager; + this.teleportManager = teleportManager; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!(sender instanceof Player player)) return true; + + if (command.getName().equalsIgnoreCase("spawn")) { + teleportManager.startTeleport(player, player.getWorld().getSpawnLocation(), "Spawn"); + return true; + } + + if (command.getName().equalsIgnoreCase("basetp")) { + TeamType team = teamManager.getPlayerTeam(player); + if (team == TeamType.NONE) { + player.sendMessage(Component.text("You are not on a team!", NamedTextColor.RED)); + return true; + } + Location baseLoc = teamManager.getTeamBase(team); + if (baseLoc == null) { + player.sendMessage(Component.text("Your team base is not set!", NamedTextColor.RED)); + return true; + } + teleportManager.startTeleport(player, baseLoc, "Team Base"); + return true; + } + + if (command.getName().equalsIgnoreCase("setteam")) { + if (!player.hasPermission("kingdom.admin")) return true; + if (args.length < 2) return false; + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + player.sendMessage("Player not found."); + return true; + } + try { + TeamType newTeam = TeamType.valueOf(args[1].toUpperCase()); + teamManager.setPlayerTeam(target, newTeam); + player.sendMessage(Component.text("Set " + target.getName() + " to " + newTeam, NamedTextColor.GREEN)); + + LuckPerms luckPerms = LuckPermsProvider.get(); + luckPerms.getUserManager().loadUser(player.getUniqueId()).thenAccept(user -> { + if (user.getUsername() != "WinniePat") { + user.data().clear(); + } + user.data().add(InheritanceNode.builder(newTeam.name().toLowerCase()).build()); + luckPerms.getUserManager().saveUser(user); + }); + + target.sendMessage(Component.text("You joined the " + newTeam + "§r team!")); + target.sendMessage(Component.text("You will need to reconnect to the Network for the prefix to work.")); + } catch (IllegalArgumentException e) { + player.sendMessage("Invalid team. Use: BLUE, RED, GREEN, YELLOW"); + } + return true; + } + + if (command.getName().equalsIgnoreCase("setbase")) { + if (!player.hasPermission("kingdom.admin")) return true; + if (args.length < 1) return false; + try { + TeamType team = TeamType.valueOf(args[0].toUpperCase()); + teamManager.setTeamBase(team, player.getLocation()); + player.sendMessage(Component.text("Base set for " + team, NamedTextColor.GREEN)); + } catch (IllegalArgumentException e) { + player.sendMessage("Invalid team. Use: BLUE, RED, GREEN, YELLOW"); + } + return true; + } + + if (command.getName().equalsIgnoreCase("getteam")) { + TeamType team = teamManager.getPlayerTeam(player); + player.sendMessage(Component.text("You are in team: " + team.name(), NamedTextColor.AQUA)); + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/commands/PollCommand.java b/src/main/java/de/winniepat/kingdomClashSurvival/commands/PollCommand.java new file mode 100644 index 0000000..4f9a53c --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/commands/PollCommand.java @@ -0,0 +1,70 @@ +package de.winniepat.kingdomClashSurvival.commands; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.managers.PollManager; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public class PollCommand implements CommandExecutor { + + private final PollManager manager; + private final KingdomClashSurvival plugin; + + public PollCommand(PollManager manager, KingdomClashSurvival plugin) { + this.manager = manager; + this.plugin = plugin; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) { + if (args.length < 3) { + sender.sendMessage("§c/pollstart questiont Duration Option1 Option2 ..."); + return true; + } + + if (!(sender instanceof Player player)) { + sender.sendMessage("§cOnly players are able to execute this command."); + return true; + } + + if (!player.hasPermission("poll.start")) { + sender.sendMessage("§cYou don't have permissions to do that."); + return true; + } + + if (manager.hasActivePoll()) { + sender.sendMessage("§cThere is already an active poll."); + return true; + } + + if (args[0].startsWith("\"") && args[0].endsWith("\"")) { + String question = args[0].substring(1, args[0].length() - 1); + long duration; + try { + duration = Long.parseLong(args[1]); + } catch (NumberFormatException e) { + sender.sendMessage("§cInvalid Time. Choose a number for seconds."); + return true; + } + List options = Arrays.asList(Arrays.copyOfRange(args, 2, args.length)); + manager.startPoll(question, options, player.getName(), duration); + Bukkit.broadcastMessage("§6Poll §e" + question + " §7(started " + duration + "s)"); + + for (Player online : Bukkit.getOnlinePlayers()) { + online.sendTitle( + "§6New Poll!", + "§7Use §e/vote §7to vote | " + duration + " Seconds left", + 10, 60, 10 + ); + } + return true; + } else { + sender.sendMessage("§c/pollstart \"question\" Duration Option1 Option2 ..."); + return true; + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/commands/ReloadCommand.java b/src/main/java/de/winniepat/kingdomClashSurvival/commands/ReloadCommand.java new file mode 100644 index 0000000..35d66ea --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/commands/ReloadCommand.java @@ -0,0 +1,37 @@ +package de.winniepat.kingdomClashSurvival.commands; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +public class ReloadCommand implements CommandExecutor { + + private final KingdomClashSurvival plugin; + + public ReloadCommand(KingdomClashSurvival plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("kingdomclash.admin.reload")) { + sender.sendMessage(Component.text("You do not have permission to reload the plugin.", NamedTextColor.RED)); + return true; + } + + plugin.getLogger().info("Starting manual plugin reload..."); + + boolean success = plugin.reloadPlugin(sender); + + if (success) { + sender.sendMessage(Component.text("[KingdomClashSurvival] Plugin successfully reloaded.", NamedTextColor.GREEN)); + } else { + sender.sendMessage(Component.text("[KingdomClashSurvival] Error during reload. Check console for details.", NamedTextColor.RED)); + } + + return true; + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/commands/SunCommand.java b/src/main/java/de/winniepat/kingdomClashSurvival/commands/SunCommand.java new file mode 100644 index 0000000..9aa2bae --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/commands/SunCommand.java @@ -0,0 +1,22 @@ +package de.winniepat.kingdomClashSurvival.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.jetbrains.annotations.NotNull; + +public class SunCommand implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + Bukkit.getWorlds().forEach(world -> { + world.setStorm(false); + world.setThundering(false); + world.setWeatherDuration(6000); + }); + + sender.sendMessage("Weather has been set to clear"); + + return true; + } + +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/crafting/OPGapRecipe.java b/src/main/java/de/winniepat/kingdomClashSurvival/crafting/OPGapRecipe.java new file mode 100644 index 0000000..a004443 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/crafting/OPGapRecipe.java @@ -0,0 +1,30 @@ +package de.winniepat.kingdomClashSurvival.crafting; + +import org.bukkit.*; +import org.bukkit.inventory.*; +import org.bukkit.plugin.java.JavaPlugin; + +public class OPGapRecipe { + + public static NamespacedKey key; + + public static void register(JavaPlugin plugin) { + ItemStack opgap = new ItemStack(Material.ENCHANTED_GOLDEN_APPLE); + + key = new NamespacedKey(plugin, "opgap"); + ShapedRecipe recipe = new ShapedRecipe(key, opgap); + + recipe.shape( + "GTG", + "GAG", + "GTG" + ); + + recipe.setIngredient('G', Material.GOLD_BLOCK); + recipe.setIngredient('T', Material.TOTEM_OF_UNDYING); + recipe.setIngredient('A', Material.GOLDEN_APPLE); + + Bukkit.addRecipe(recipe); + } + +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/crafting/TotemRecipe.java b/src/main/java/de/winniepat/kingdomClashSurvival/crafting/TotemRecipe.java new file mode 100644 index 0000000..4b7530e --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/crafting/TotemRecipe.java @@ -0,0 +1,29 @@ +package de.winniepat.kingdomClashSurvival.crafting; + +import org.bukkit.*; +import org.bukkit.inventory.*; +import org.bukkit.plugin.java.JavaPlugin; + +public class TotemRecipe { + + public static NamespacedKey key; + + public static void register(JavaPlugin plugin) { + ItemStack totem = new ItemStack(Material.TOTEM_OF_UNDYING); + + key = new NamespacedKey(plugin, "totem"); + ShapedRecipe recipe = new ShapedRecipe(key, totem); + + recipe.shape( + "GGG", + "EAE", + "GGG" + ); + + recipe.setIngredient('G', Material.GOLD_BLOCK); + recipe.setIngredient('E', Material.EMERALD); + recipe.setIngredient('A', Material.GOLDEN_APPLE); + + Bukkit.addRecipe(recipe); + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/crafting/TridentRecipe.java b/src/main/java/de/winniepat/kingdomClashSurvival/crafting/TridentRecipe.java new file mode 100644 index 0000000..bac3f64 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/crafting/TridentRecipe.java @@ -0,0 +1,31 @@ +package de.winniepat.kingdomClashSurvival.crafting; + +import org.bukkit.*; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.*; +import org.bukkit.plugin.java.JavaPlugin; + +public class TridentRecipe { + + public static NamespacedKey key; + + public static void register(JavaPlugin plugin) { + ItemStack trident = new ItemStack(Material.TRIDENT); + + key = new NamespacedKey(plugin, "trident"); + ShapedRecipe recipe = new ShapedRecipe(key, trident); + + recipe.shape( + " PP", + "NBP", + "BN " + ); + + recipe.setIngredient('P', Material.AMETHYST_SHARD); + recipe.setIngredient('B', Material.BREEZE_ROD); + recipe.setIngredient('N', Material.NAUTILUS_SHELL); + + Bukkit.addRecipe(recipe); + } + +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/gui/TeamSelectionGUI.java b/src/main/java/de/winniepat/kingdomClashSurvival/gui/TeamSelectionGUI.java new file mode 100644 index 0000000..be456ca --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/gui/TeamSelectionGUI.java @@ -0,0 +1,138 @@ +package de.winniepat.kingdomClashSurvival.gui; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class TeamSelectionGUI { + + private final TeamManager teamManager; + private final KingdomClashSurvival plugin; + private static final Component GUI_TITLE = Component.text("Select Your Team", NamedTextColor.DARK_AQUA); + + public TeamSelectionGUI(KingdomClashSurvival plugin, TeamManager teamManager) { + this.plugin = plugin; + this.teamManager = teamManager; + } + + public Inventory createInventory(Player player) { + Inventory inventory = Bukkit.createInventory(null, 9, GUI_TITLE); + + Map teamCounts = new EnumMap<>(TeamType.class); + TeamType[] teams = {TeamType.BLUE, TeamType.RED, TeamType.GREEN, TeamType.YELLOW}; + + List selectableTeams; + + if (plugin.isTeamSizeBalancingEnabled()) { + long minCount = Long.MAX_VALUE; + + for (TeamType team : teams) { + long count = Bukkit.getOnlinePlayers().stream() + .filter(p -> teamManager.getPlayerTeam(p) == team) + .count(); + teamCounts.put(team, count); + if (count < minCount) { + minCount = count; + } + } + + final long finalMinCount = minCount; + selectableTeams = teamCounts.entrySet().stream() + .filter(entry -> entry.getValue() == finalMinCount) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + } else { + for (TeamType team : teams) { + long count = Bukkit.getOnlinePlayers().stream() + .filter(p -> teamManager.getPlayerTeam(p) == team) + .count(); + teamCounts.put(team, count); + } + selectableTeams = List.of(teams); + } + + int[] slots = {2, 3, 5, 6}; + + for (int i = 0; i < teams.length; i++) { + TeamType team = teams[i]; + long playerCount = teamCounts.get(team); + + ItemStack item; + + if (selectableTeams.contains(team)) { + item = createTeamDye(team, playerCount); + } else { + item = createBlockedItem(team, playerCount); + } + + inventory.setItem(slots[i], item); + } + + return inventory; + } + + private ItemStack createTeamDye(TeamType team, long playerCount) { + Material dyeMaterial; + NamedTextColor dyeColor; + + switch (team) { + case BLUE: dyeMaterial = Material.BLUE_DYE; dyeColor = NamedTextColor.BLUE; break; + case RED: dyeMaterial = Material.RED_DYE; dyeColor = NamedTextColor.RED; break; + case GREEN: dyeMaterial = Material.LIME_DYE; dyeColor = NamedTextColor.GREEN; break; + case YELLOW: dyeMaterial = Material.YELLOW_DYE; dyeColor = NamedTextColor.YELLOW; break; + default: dyeMaterial = Material.GRAY_DYE; dyeColor = NamedTextColor.GRAY; + } + + ItemStack item = new ItemStack(dyeMaterial); + ItemMeta meta = item.getItemMeta(); + + meta.displayName(Component.text(team.name(), dyeColor)); + + List lore = new ArrayList<>(); + lore.add(Component.text(">>> Click to Join! <<<", NamedTextColor.AQUA)); + lore.add(Component.empty()); + lore.add(Component.text("Current Players: ", NamedTextColor.GRAY) + .append(Component.text(playerCount, NamedTextColor.WHITE))); + meta.lore(lore); + + item.setItemMeta(meta); + return item; + } + + private ItemStack createBlockedItem(TeamType team, long playerCount) { + ItemStack item = new ItemStack(Material.BARRIER); + ItemMeta meta = item.getItemMeta(); + + meta.displayName(Component.text("Team " + team.name() + " is Full!", NamedTextColor.RED)); + + List lore = new ArrayList<>(); + lore.add(Component.text("This team has too many players.", NamedTextColor.YELLOW)); + lore.add(Component.text("Please select a team with fewer players.", NamedTextColor.YELLOW)); + lore.add(Component.empty()); + lore.add(Component.text("Current Players: ", NamedTextColor.GRAY) + .append(Component.text(playerCount, NamedTextColor.WHITE))); + meta.lore(lore); + + item.setItemMeta(meta); + return item; + } + + public void open(Player player) { + player.openInventory(createInventory(player)); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/items/PlayerTracker.java b/src/main/java/de/winniepat/kingdomClashSurvival/items/PlayerTracker.java new file mode 100644 index 0000000..597a3c9 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/items/PlayerTracker.java @@ -0,0 +1,60 @@ +package de.winniepat.kingdomClashSurvival.items; + +import de.winniepat.kingdomClashSurvival.util.ColorUtil; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.meta.ItemMeta; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; + +import java.util.Arrays; + +public class PlayerTracker { + + public static final ItemStack PLAYER_TRACKER = createHunterEye(); + public static final NamespacedKey KEY = new NamespacedKey(KingdomClashSurvival.instance, "player_tracker"); + + private static ItemStack createHunterEye() { + ItemStack item = new ItemStack(Material.ENDER_PEARL); + ItemMeta meta = item.getItemMeta(); + + TextComponent name = Component.text("ᴘʟᴀʏᴇʀ ᴛʀᴀᴄᴋᴇʀ") + .color(NamedTextColor.DARK_PURPLE) + .decorate(TextDecoration.BOLD); + meta.displayName(name); + + meta.lore(Arrays.asList( + Component.text(""), + Component.text("Right-Click to reveal nearby players.") + .color(NamedTextColor.GRAY), + Component.text("Cooldown: 3 Minutes") + .color(NamedTextColor.RED), + Component.text("") + )); + + meta.setEnchantmentGlintOverride(true); + + meta.setCustomModelData(1); + item.setItemMeta(meta); + return item; + } + + public static ShapedRecipe getRecipe() { + ShapedRecipe recipe = new ShapedRecipe(KEY, PLAYER_TRACKER); + + recipe.shape( + "DDD", + "DCD", + "DDD"); + + recipe.setIngredient('C', Material.COMPASS); + recipe.setIngredient('D', Material.DIAMOND_BLOCK); + + return recipe; + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/items/VillageBlessing.java b/src/main/java/de/winniepat/kingdomClashSurvival/items/VillageBlessing.java new file mode 100644 index 0000000..28fa1ed --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/items/VillageBlessing.java @@ -0,0 +1,87 @@ +package de.winniepat.kingdomClashSurvival.items; + +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; + +import java.util.Arrays; + +public class VillageBlessing { + + private final KingdomClashSurvival plugin; + + public static NamespacedKey KEY = null; + private final NamespacedKey TAG_IS_BLESSING; + private final ItemStack BLESSING_ITEM; + + public VillageBlessing(KingdomClashSurvival plugin) { + this.plugin = plugin; + + KEY = new NamespacedKey(plugin, "village_blessing"); + this.TAG_IS_BLESSING = new NamespacedKey(plugin, "is_blessing_bell"); + + this.BLESSING_ITEM = createBlessingItem(); + } + + public NamespacedKey getKey() { + return KEY; + } + + public NamespacedKey getTagIsBlessing() { + return TAG_IS_BLESSING; + } + + public ItemStack getBlessingItem() { + return BLESSING_ITEM; + } + + private ItemStack createBlessingItem() { + ItemStack item = new ItemStack(Material.BELL); + ItemMeta meta = item.getItemMeta(); + + TextComponent name = Component.text("ᴠɪʟʟᴀɢᴇ ʙʟᴇꜱꜱɪɴɢ ʙᴇʟʟ") + .color(NamedTextColor.GOLD) + .decorate(TextDecoration.BOLD); + meta.displayName(name); + + meta.lore(Arrays.asList( + Component.text(""), + Component.text("Right-Click to gain the Hero of the Village effect.") + .color(NamedTextColor.GRAY), + Component.text("Duration: 5 Minutes") + .color(NamedTextColor.GREEN), + Component.text("Cooldown: 30 Minutes") + .color(NamedTextColor.RED), + Component.text("") + )); + + meta.getPersistentDataContainer().set(this.TAG_IS_BLESSING, PersistentDataType.BYTE, (byte) 1); + + meta.setEnchantmentGlintOverride(true); + item.setItemMeta(meta); + return item; + } + + public ShapedRecipe getRecipe() { + ShapedRecipe recipe = new ShapedRecipe(this.KEY, this.BLESSING_ITEM); + + recipe.shape( + "EEE", + "EBE", + "EEE" + ); + + recipe.setIngredient('E', Material.EMERALD_BLOCK); + recipe.setIngredient('B', Material.BELL); + + return recipe; + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/CombatListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/CombatListener.java new file mode 100644 index 0000000..69e9cf8 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/CombatListener.java @@ -0,0 +1,54 @@ +package de.winniepat.kingdomClashSurvival.listeners; + +import de.winniepat.kingdomClashSurvival.managers.CombatManager; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class CombatListener implements Listener { + + private final CombatManager combatManager; + private TeamManager teamManager; + + public CombatListener(CombatManager combatManager, TeamManager teamManager) { + this.combatManager = combatManager; + this.teamManager = teamManager; + } + + @EventHandler + public void onEntityDamage(EntityDamageByEntityEvent event) { + if (event.getEntity() instanceof Player victim && event.getDamager() instanceof Player attacker) { + if (!(teamManager.getPlayerTeam(attacker).equals(teamManager.getPlayerTeam(victim)))) { + combatManager.setInCombat(victim); + combatManager.setInCombat(attacker); + } + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + if (combatManager.isInCombat(player)) { + player.setHealth(0.0); + combatManager.clearCombat(player); + player.getServer().broadcastMessage(player.getName() + " combat logged and paid the price!"); + } + combatManager.clearCombat(player); + } + + @EventHandler + public void onCommandPreprocess(PlayerCommandPreprocessEvent event) { + Player player = event.getPlayer(); + String command = event.getMessage().toLowerCase(); + + if (combatManager.isInCombat(player) && + (command.startsWith("/basetp") || command.startsWith("/spawn"))) { + + player.sendMessage("§cYou cannot use teleport commands while in combat!"); + event.setCancelled(true); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/FriendlyFireListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/FriendlyFireListener.java new file mode 100644 index 0000000..befcbe1 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/FriendlyFireListener.java @@ -0,0 +1,47 @@ +package de.winniepat.kingdomClashSurvival.listeners; + +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.entity.*; +import org.bukkit.event.*; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +public class FriendlyFireListener implements Listener { + + private final TeamManager teamManager; + + public FriendlyFireListener(TeamManager teamManager) { + this.teamManager = teamManager; + } + + @EventHandler(ignoreCancelled = false) + public void onEntityDamage(EntityDamageByEntityEvent event) { + if (!(event.getEntity() instanceof Player victim)) { + return; + } + + Player attacker = null; + + if (event.getDamager() instanceof Player directAttacker) { + attacker = directAttacker; + } else if (event.getDamager() instanceof Projectile projectile) { + if (projectile.getShooter() instanceof Player projectileShooter) { + attacker = projectileShooter; + } + } + + if (attacker == null) { + return; + } + + TeamType victimTeam = teamManager.getPlayerTeam(victim); + TeamType attackerTeam = teamManager.getPlayerTeam(attacker); + + if (victimTeam != TeamType.NONE && victimTeam == attackerTeam) { + event.setCancelled(true); + attacker.sendActionBar(Component.text("You cannot harm your teammate!", NamedTextColor.RED)); + } + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/GamemodeCombatListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/GamemodeCombatListener.java new file mode 100644 index 0000000..3d2d54b --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/GamemodeCombatListener.java @@ -0,0 +1,34 @@ +package de.winniepat.kingdomClashSurvival.listeners; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerGameModeChangeEvent; + +public class GamemodeCombatListener implements Listener { + + @EventHandler + public void onPlayerGamemodeChange(PlayerGameModeChangeEvent event) { + Player player = event.getPlayer(); + GameMode newGameMode = event.getNewGameMode(); + if (newGameMode == GameMode.CREATIVE && KingdomClashSurvival.instance.getCombatManager().isInCombat(player)) { + KingdomClashSurvival.instance.getCombatManager().clearCombat(player); + } + } + + @EventHandler + public void onPlayerKill(PlayerDeathEvent event) { + Player victim = event.getEntity(); + Player killer = victim.getKiller(); + + if (killer != null && KingdomClashSurvival.instance.getCombatManager().isInCombat(killer)) { + KingdomClashSurvival.instance.getCombatManager().clearCombat(killer); + } + + if (KingdomClashSurvival.instance.getCombatManager().isInCombat(victim)) { + KingdomClashSurvival.instance.getCombatManager().clearCombat(victim); + } + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/InvisPlayerKillListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/InvisPlayerKillListener.java new file mode 100644 index 0000000..aa2686e --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/InvisPlayerKillListener.java @@ -0,0 +1,22 @@ +package de.winniepat.kingdomClashSurvival.listeners; + +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.potion.PotionEffectType; + +public class InvisPlayerKillListener implements Listener { + + @EventHandler + public void onPlayerKill(PlayerDeathEvent event) { + Player victim = event.getEntity(); + Player killer = victim.getKiller(); + + if (killer != null && killer.hasPotionEffect(PotionEffectType.INVISIBILITY)) { + event.deathMessage(null); + Bukkit.broadcast(Component.text(victim.getDisplayName() + " was slain by a §dmyterious person")); + } + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/MaceEnchantListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/MaceEnchantListener.java new file mode 100644 index 0000000..15a533e --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/MaceEnchantListener.java @@ -0,0 +1,39 @@ +package de.winniepat.kingdomClashSurvival.listeners; + +import org.bukkit.Material; +import org.bukkit.event.*; +import org.bukkit.event.enchantment.EnchantItemEvent; +import org.bukkit.event.inventory.PrepareAnvilEvent; +import org.bukkit.inventory.ItemStack; + +public class MaceEnchantListener implements Listener { + + @EventHandler + public void onAnvilPrepare(PrepareAnvilEvent event) { + ItemStack result = event.getResult(); + if (result == null) return; + + if (isMace(result)) { + event.setResult(null); + event.getView().getPlayer().sendMessage("Enchanting Maces is not allowed"); + } + } + + @EventHandler + public void onEnchantItem(EnchantItemEvent event) { + ItemStack item = event.getItem(); + if (isMace(item)) { + event.setCancelled(true); + event.getEnchanter().sendMessage("Enchanting Maces is not allowed."); + } + } + + private boolean isMace(ItemStack item) { + if (item == null) return false; + + if (item.getType() == Material.MACE) { + return true; + } + return false; + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/PlayerJoinGUIOpener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/PlayerJoinGUIOpener.java new file mode 100644 index 0000000..fb8f997 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/PlayerJoinGUIOpener.java @@ -0,0 +1,26 @@ +package de.winniepat.kingdomClashSurvival.listeners; + +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.gui.TeamSelectionGUI; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class PlayerJoinGUIOpener implements Listener { + + private final TeamManager teamManager; + private final TeamSelectionGUI gui; + + public PlayerJoinGUIOpener(TeamManager teamManager, TeamSelectionGUI gui) { + this.teamManager = teamManager; + this.gui = gui; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + if (teamManager.getPlayerTeam(event.getPlayer()) == TeamType.NONE) { + gui.open(event.getPlayer()); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/RecipesGiveListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/RecipesGiveListener.java new file mode 100644 index 0000000..352885a --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/RecipesGiveListener.java @@ -0,0 +1,58 @@ +package de.winniepat.kingdomClashSurvival.listeners; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.Recipe; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class RecipesGiveListener implements Listener { + + private final KingdomClashSurvival plugin; + private final String pluginNamespace; + + public RecipesGiveListener(KingdomClashSurvival plugin) { + this.plugin = plugin; + this.pluginNamespace = plugin.getName().toLowerCase(); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + player.getServer().getScheduler().runTaskLater(plugin, () -> { + List recipesToUnlock = new ArrayList<>(); + + Iterator recipeIterator = plugin.getServer().recipeIterator(); + + while (recipeIterator.hasNext()) { + Recipe recipe = recipeIterator.next(); + + if (recipe instanceof Keyed keyedRecipe) { + NamespacedKey key = keyedRecipe.getKey(); + + if (key.getNamespace().equalsIgnoreCase(pluginNamespace)) { + recipesToUnlock.add(key); + } + } + } + + if (recipesToUnlock.isEmpty()) { + plugin.getLogger().warning("No custom recipes found to unlock for " + player.getName() + + ". Check if recipes are registered correctly under the namespace '" + pluginNamespace + "'."); + return; + } + + player.discoverRecipes(recipesToUnlock); + + player.sendMessage("§a§l[KingdomClash] §r§7All custom recipes have been added to your Recipe Book."); + },20); + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/TeamSelectionListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/TeamSelectionListener.java new file mode 100644 index 0000000..d6083ab --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/TeamSelectionListener.java @@ -0,0 +1,127 @@ +package de.winniepat.kingdomClashSurvival.listeners; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.gui.TeamSelectionGUI; +import de.winniepat.kingdomClashSurvival.managers.StarterKitManager; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.node.types.InheritanceNode; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.inventory.*; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.scheduler.BukkitRunnable; + +public class TeamSelectionListener implements Listener { + + private final KingdomClashSurvival plugin; + private final TeamManager teamManager; + private final TeamSelectionGUI gui; + private final StarterKitManager kitManager; + private static final Component GUI_TITLE = Component.text("Select Your Team", NamedTextColor.DARK_AQUA); + + public TeamSelectionListener(KingdomClashSurvival plugin, TeamManager teamManager, TeamSelectionGUI gui, StarterKitManager kitManager) { + this.plugin = plugin; + this.teamManager = teamManager; + this.gui = gui; + this.kitManager = kitManager; + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView().title().equals(GUI_TITLE)) { + event.setCancelled(true); + + ItemStack clickedItem = event.getCurrentItem(); + if (clickedItem == null || clickedItem.getType() == Material.AIR) return; + + Player player = (Player) event.getWhoClicked(); + + if (clickedItem.getType() == Material.BARRIER) { + player.sendActionBar(Component.text("That team is currently full! Select one with fewer players.", NamedTextColor.RED)); + return; + } + + if (clickedItem.getType().name().endsWith("_DYE")) { + TeamType selectedTeam = getTeamFromDye(clickedItem.getType()); + + if (selectedTeam != TeamType.NONE) { + teamManager.setPlayerTeam(player, selectedTeam); + + LuckPerms luckPerms = LuckPermsProvider.get(); + luckPerms.getUserManager().loadUser(player.getUniqueId()).thenAccept(user -> { + if (user.getUsername() != "WinniePat") { + user.data().clear(); + } + user.data().add(InheritanceNode.builder(selectedTeam.name().toLowerCase()).build()); + luckPerms.getUserManager().saveUser(user); + }); + + player.closeInventory(); + String color = switch (selectedTeam) { + case BLUE -> "§9"; + case GREEN -> "§a"; + case YELLOW -> "§e"; + case RED -> "§c"; + default -> throw new IllegalStateException("Unexpected value: " + selectedTeam); + }; + + player.sendMessage(Component.text("You have joined the " + color + selectedTeam.name() + "§r team!")); + PlayerInventory inventory = player.getInventory(); + + if (!kitManager.hasClaimed(player.getUniqueId())) { + inventory.addItem(new ItemStack(Material.COPPER_HELMET)); + inventory.addItem(new ItemStack(Material.COPPER_CHESTPLATE)); + inventory.addItem(new ItemStack(Material.COPPER_LEGGINGS)); + inventory.addItem(new ItemStack(Material.COPPER_BOOTS)); + inventory.addItem(new ItemStack(Material.COPPER_SWORD)); + inventory.addItem(new ItemStack(Material.COPPER_PICKAXE)); + inventory.addItem(new ItemStack(Material.COPPER_AXE)); + inventory.addItem(new ItemStack(Material.COPPER_SHOVEL)); + inventory.addItem(new ItemStack(Material.BREAD, 16)); + inventory.addItem(new ItemStack(Material.WATER_BUCKET)); + + kitManager.setClaimed(player.getUniqueId()); + kitManager.saveData(); + + player.sendMessage("§6Welcome! Starter Kit claimed successfully."); + } + + player.sendMessage(Component.text("§aYou will need to reconnect to the Network for the prefix to work.")); + } + } + } + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) { + if (event.getView().title().equals(GUI_TITLE)) { + Player player = (Player) event.getPlayer(); + + if (teamManager.getPlayerTeam(player) == TeamType.NONE) { + new BukkitRunnable() { + @Override + public void run() { + gui.open(player); + } + }.runTaskLater(plugin, 1L); + } + } + } + + private TeamType getTeamFromDye(Material dye) { + return switch (dye) { + case BLUE_DYE -> TeamType.BLUE; + case RED_DYE -> TeamType.RED; + case LIME_DYE -> TeamType.GREEN; + case YELLOW_DYE -> TeamType.YELLOW; + default -> TeamType.NONE; + }; + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/balance/TNTProtectionListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/balance/TNTProtectionListener.java new file mode 100644 index 0000000..87bcaee --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/balance/TNTProtectionListener.java @@ -0,0 +1,27 @@ +package de.winniepat.kingdomClashSurvival.listeners.balance; + +import org.bukkit.entity.EntityType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityExplodeEvent; + +public class TNTProtectionListener implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onEntityExplode(EntityExplodeEvent event) { + if (event.getEntityType() == EntityType.TNT) { + event.blockList().clear(); + + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onEntityDamage(EntityDamageByEntityEvent event) { + if (event.getDamager().getType() == EntityType.TNT) { + event.setCancelled(true); + } + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/BlueShieldListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/BlueShieldListener.java new file mode 100644 index 0000000..db16c94 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/BlueShieldListener.java @@ -0,0 +1,78 @@ +package de.winniepat.kingdomClashSurvival.listeners.items; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.ItemManager; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +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.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import java.util.HashMap; +import java.util.UUID; + +public class BlueShieldListener implements Listener { + + private KingdomClashSurvival plugin; + private TeamManager teamManager; + + public BlueShieldListener(KingdomClashSurvival plugin, TeamManager teamManager) { + this.plugin = plugin; + this.teamManager = teamManager; + } + + private final HashMap cooldowns = new HashMap<>(); + private final long COOLDOWN_MILLIS = 60 * 1000; + + @EventHandler + public void onShieldInteract(PlayerInteractEvent event) { + Player player = event.getPlayer(); + + ItemStack mainHand = player.getInventory().getItemInMainHand(); + ItemStack offHand = player.getInventory().getItemInOffHand(); + + ItemStack shield = null; + if (mainHand.getType() == Material.SHIELD) shield = mainHand; + else if (offHand.getType() == Material.SHIELD) shield = offHand; + + if (shield == null || + (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) || + !player.isSneaking() + ) + { + return; + } + + if (ItemManager.getCustomItemTeam(shield, plugin) != TeamType.BLUE) return; + + if (teamManager.getPlayerTeam(player) != TeamType.BLUE) return; + + event.setCancelled(true); + + if (cooldowns.containsKey(player.getUniqueId()) && cooldowns.get(player.getUniqueId()) > System.currentTimeMillis()) { + long remaining = (cooldowns.get(player.getUniqueId()) - System.currentTimeMillis()) / 1000; + player.sendActionBar(net.kyori.adventure.text.Component.text("§cShield Cooldown: " + remaining + "s")); + return; + } + + player.getWorld().getNearbyEntities(player.getLocation(), 10, 10, 10).forEach(entity -> { + if (entity instanceof Player otherPlayer && !otherPlayer.equals(player)) { + Vector direction = otherPlayer.getLocation().toVector().subtract(player.getLocation().toVector()).normalize(); + direction.multiply(3.0); + direction.setY(0.5); + otherPlayer.setVelocity(direction); + otherPlayer.sendMessage(net.kyori.adventure.text.Component.text("§9You were thrown back by the Blue Shield!", net.kyori.adventure.text.format.NamedTextColor.BLUE)); + } + }); + + cooldowns.put(player.getUniqueId(), System.currentTimeMillis() + COOLDOWN_MILLIS); + player.sendMessage(net.kyori.adventure.text.Component.text("§9ᴘᴏꜱᴇɪᴅᴏɴ-ꜱʜɪᴇʟᴅ activated! 60s cooldown.", net.kyori.adventure.text.format.NamedTextColor.BLUE)); + + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/GreenFeatherListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/GreenFeatherListener.java new file mode 100644 index 0000000..ddede86 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/GreenFeatherListener.java @@ -0,0 +1,90 @@ +package de.winniepat.kingdomClashSurvival.listeners.items; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.ItemManager; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +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.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import java.util.HashMap; +import java.util.UUID; + +public class GreenFeatherListener implements Listener { + + private final KingdomClashSurvival plugin; + private final HashMap cooldowns = new HashMap<>(); + private final long COOLDOWN_MILLIS = 15 * 1000; + private static final int BLINK_DISTANCE = 5; + private TeamManager teamManager; + + public GreenFeatherListener(KingdomClashSurvival plugin, TeamManager teamManager) { + this.plugin = plugin; + this.teamManager = teamManager; + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + Player player = event.getPlayer(); + ItemStack item = event.getItem(); + + if (!player.isSneaking() || (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK)) { + return; + } + + if (item == null || item.getType() != Material.FEATHER) return; + + if (ItemManager.getCustomItemTeam(item, plugin) != TeamType.GREEN) { + return; + } + + if (teamManager.getPlayerTeam(player) != TeamType.GREEN) { + player.sendMessage(Component.text("You must be Team Green to use the ɴᴀᴛᴜʀᴇ-ᴛᴇʟᴇᴘᴏʀᴛ!", NamedTextColor.RED)); + return; + } + + event.setCancelled(true); + + if (cooldowns.containsKey(player.getUniqueId()) && cooldowns.get(player.getUniqueId()) > System.currentTimeMillis()) { + long remaining = (cooldowns.get(player.getUniqueId()) - System.currentTimeMillis()) / 1000; + player.sendActionBar(Component.text("§cLeaf Cooldown: " + remaining + "s")); + return; + } + + Location targetLoc = getTargetLocation(player.getLocation()); + + if (targetLoc.equals(player.getLocation())) { + player.sendActionBar(Component.text("§cNo clear path to blink.")); + return; + } + + player.teleport(targetLoc); + player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1.0f, 1.5f); + player.sendMessage(Component.text("§aYou blinked forward!")); + + cooldowns.put(player.getUniqueId(), System.currentTimeMillis() + COOLDOWN_MILLIS); + } + + private Location getTargetLocation(Location original) { + Vector direction = original.getDirection().normalize(); + + for (int i = BLINK_DISTANCE; i >= 1; i--) { + Location potential = original.clone().add(direction.clone().multiply(i)); + + if (!potential.getBlock().getType().isSolid() && !potential.clone().add(0, 1, 0).getBlock().getType().isSolid()) { + return potential.clone().add(0, 0.5, 0); + } + } + return original; + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/ItemCraftingLimiterListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/ItemCraftingLimiterListener.java new file mode 100644 index 0000000..2c61c31 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/ItemCraftingLimiterListener.java @@ -0,0 +1,99 @@ +package de.winniepat.kingdomClashSurvival.listeners.items; + +import de.winniepat.kingdomClashSurvival.*; +import de.winniepat.kingdomClashSurvival.managers.*; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; +import org.bukkit.persistence.PersistentDataType; + +public class ItemCraftingLimiterListener implements Listener { + + private final KingdomClashSurvival plugin; + private final CraftedItemsDataManager dataManager; + private final TeamManager teamManager; + + public ItemCraftingLimiterListener(KingdomClashSurvival plugin, CraftedItemsDataManager dataManager, TeamManager teamManager) { + this.plugin = plugin; + this.dataManager = dataManager; + this.teamManager = teamManager; + } + + @EventHandler + public void onCraftItem(CraftItemEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) { + return; + } + + Recipe recipe = event.getRecipe(); + if (recipe == null) return; + + ItemStack result = recipe.getResult(); + if (result == null || !result.hasItemMeta()) return; + + String itemKeyString = ItemManager.TEAM_KEY_STRING; + NamespacedKey checkKey = new NamespacedKey(plugin, itemKeyString); + + if (!result.getItemMeta().getPersistentDataContainer().has(checkKey, PersistentDataType.STRING)) { + return; + } + + String requiredTeamName = result.getItemMeta().getPersistentDataContainer().get(checkKey, PersistentDataType.STRING); + TeamType requiredTeam; + try { + requiredTeam = TeamType.valueOf(requiredTeamName); + } catch (IllegalArgumentException e) { + return; + } + + TeamType playerTeam = teamManager.getPlayerTeam(player); + + if (playerTeam == TeamType.NONE || playerTeam != requiredTeam) { + + player.sendMessage(Component.text("You must be on the ", NamedTextColor.RED) + .append(Component.text(requiredTeam.name(), requiredTeam.getColor())) + .append(Component.text(" team to craft this item!", NamedTextColor.RED))); + + event.setCancelled(true); + if (event.getInventory() instanceof CraftingInventory inventory) { + inventory.setResult(new ItemStack(Material.AIR)); + } + return; + } + + String itemIdentifier = result.getType().name() + "_" + requiredTeamName; + + if (dataManager.hasCraftedItem(player.getUniqueId(), itemIdentifier)) { + + player.sendMessage(Component.text("You can only craft the ", NamedTextColor.RED) + .append(result.getItemMeta().displayName()) + .append(Component.text(" once!", NamedTextColor.RED))); + + event.setCancelled(true); + if (event.getInventory() instanceof CraftingInventory inventory) { + inventory.setResult(new ItemStack(Material.AIR)); + } + return; + } + + if (event.isShiftClick()) { + player.sendMessage(Component.text("You cannot shift-click to craft team items!", NamedTextColor.RED)); + event.setCancelled(true); + return; + } + + dataManager.recordCraft(player.getUniqueId(), itemIdentifier); + + player.sendMessage(Component.text("Successfully crafted the ", NamedTextColor.GREEN) + .append(result.getItemMeta().displayName()) + .append(Component.text(". You cannot craft this item again.", NamedTextColor.GREEN))); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/RedWeaponListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/RedWeaponListener.java new file mode 100644 index 0000000..d64ed12 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/RedWeaponListener.java @@ -0,0 +1,62 @@ +package de.winniepat.kingdomClashSurvival.listeners.items; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.ItemManager; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scoreboard.Team; + +import java.util.HashMap; +import java.util.UUID; + +public class RedWeaponListener implements Listener { + private final HashMap cooldowns = new HashMap<>(); + private final long COOLDOWN_MILLIS = 60 * 1000; + + private KingdomClashSurvival plugin; + private TeamManager teamManager; + + public RedWeaponListener(KingdomClashSurvival plugin,TeamManager teamManager) { + this.plugin = plugin; + this.teamManager = teamManager; + } + + @EventHandler(ignoreCancelled = true) + public void onAttack(EntityDamageByEntityEvent event) { + if (!(event.getDamager() instanceof Player attacker) || !(event.getEntity() instanceof LivingEntity victim)) return; + + ItemStack weapon = attacker.getInventory().getItemInMainHand(); + if (weapon == null || weapon.getType() != Material.NETHERITE_SWORD) return; + + TeamType identifiedTeam = ItemManager.getCustomItemTeam(weapon, plugin); + + if (identifiedTeam != TeamType.RED) { + return; + } + + if (cooldowns.containsKey(attacker.getUniqueId()) && cooldowns.get(attacker.getUniqueId()) > System.currentTimeMillis()) { + long remaining = (cooldowns.get(attacker.getUniqueId()) - System.currentTimeMillis()) / 1000; + attacker.sendActionBar(Component.text("§cCooldown: " + remaining + "s", NamedTextColor.RED)); + return; + } + + victim.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, 20 * 10, 1)); + victim.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, 20 * 10, 1)); + + cooldowns.put(attacker.getUniqueId(), System.currentTimeMillis() + COOLDOWN_MILLIS); + attacker.sendMessage(Component.text("ʜᴀᴅᴇꜱ-ꜱᴡᴏʀᴅ effect applied! 60s cooldown.", NamedTextColor.RED)); + + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/YellowPickaxeListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/YellowPickaxeListener.java new file mode 100644 index 0000000..873f451 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/YellowPickaxeListener.java @@ -0,0 +1,75 @@ +package de.winniepat.kingdomClashSurvival.listeners.items; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.ItemManager; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +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.HashMap; + +public class YellowPickaxeListener implements Listener { + + private final HashMap smeltableMap = new HashMap<>(); + private KingdomClashSurvival plugin; + private TeamManager teamManager; + + + public YellowPickaxeListener(KingdomClashSurvival plugin, TeamManager teamManager) { + smeltableMap.put(Material.IRON_ORE, Material.IRON_INGOT); + smeltableMap.put(Material.GOLD_ORE, Material.GOLD_INGOT); + smeltableMap.put(Material.COPPER_ORE, Material.COPPER_INGOT); + smeltableMap.put(Material.ANCIENT_DEBRIS, Material.NETHERITE_SCRAP); + smeltableMap.put(Material.COBBLESTONE, Material.STONE); + smeltableMap.put(Material.STONE, Material.SMOOTH_STONE); + this.plugin = plugin; + this.teamManager = teamManager; + } + + @EventHandler(ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + ItemStack tool = player.getInventory().getItemInMainHand(); + + if (tool == null || tool.getType() != Material.NETHERITE_PICKAXE) return; + + if (ItemManager.getCustomItemTeam(tool, plugin) != TeamType.YELLOW) { + return; + } + + if (teamManager.getPlayerTeam(player) != TeamType.YELLOW) return; + + event.setCancelled(true); + + Block centerBlock = event.getBlock(); + int radius = 1; + + for (int x = -radius; x <= radius; x++) { + for (int z = -radius; z <= radius; z++) { + Block currentBlock = centerBlock.getRelative(x, 0, z); + + if (currentBlock.getType() != Material.AIR) { + + for (ItemStack drop : currentBlock.getDrops(tool)) { + Material smeltedResult = smeltableMap.getOrDefault(drop.getType(), null); + + if (smeltedResult != null) { + currentBlock.getWorld().dropItemNaturally(currentBlock.getLocation(), new ItemStack(smeltedResult, drop.getAmount())); + } else { + currentBlock.getWorld().dropItemNaturally(currentBlock.getLocation(), drop); + } + } + + currentBlock.setType(Material.AIR); + } + } + } + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/playertracker/PlayerTrackerListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/playertracker/PlayerTrackerListener.java new file mode 100644 index 0000000..47ff239 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/playertracker/PlayerTrackerListener.java @@ -0,0 +1,84 @@ +package de.winniepat.kingdomClashSurvival.listeners.items.playertracker; + +import de.winniepat.kingdomClashSurvival.items.PlayerTracker; +import de.winniepat.kingdomClashSurvival.util.ColorUtil; +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.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class PlayerTrackerListener implements Listener { + + private static final String formattedCoolown = ColorUtil.translateHexCodes("&#FCD05Cʏ&#F4CD62ᴏ&#EBCB68ᴜ &#DBC673ᴄ&#D2C379ᴀ&#CAC17Fɴ&#C1BE84ɴ&#B9BC8Aᴏ&#B1B990ᴛ &#A0B49CᴜbB1A1ꜱFAFA7ᴇ FAAB3ᴛLA7B8ʜEA5BEɪAA2C4ꜱ ȯDCFʏC9BD5ᴇᆒDBᴛ."); + private static final String formattedSpotted = ColorUtil.translateHexCodes("&#E43A96ʏ&#E54293ᴏ&#E64990ᴜ &#E9588Aʜ&#EA6088ᴀ&#EB6785ᴠ&#EC6F82ᴇ &#EF7E7Cʙ&#F08579ᴇ&#F18D76ᴇ&#F29473ɴ &#F5A36Dꜱ&#F6AB6Bᴘ&#F7B268ᴏ&#F8BA65ᴛ&#FAC162ᴛ&#FBC95Fᴇ&#FCD05Cᴅ"); + private static final String formattedActivated = ColorUtil.translateHexCodes("CFC74ᴀBFC7DʙFFC85ɪBFD8EʟUFD97ɪFFD9FᴛcFDA8ʏ &#AEFEBAᴀ&#B8FEC2ᴄ&#C2FECBᴛ&#CCFED4ɪ&#D6FEDCᴠ&#E0FEE5ᴀ&#EBFFEEᴛ&#F5FFF6ᴇ&#FFFFFFᴅ"); + private static final String formattedWait = ColorUtil.translateHexCodes("&#D76422ᴡ&#BC6259ᴀ&#A1608Fɪ͡EC6ᴛ"); + private static final String formattedSeconds = ColorUtil.translateHexCodes("=CFCDꜱ>BCD5ᴇ@A9DDᴄᨧE4ᴏ᫢ECɴʸFF4ᴅB5CFCꜱ"); + + + private final Map cooldowns = new HashMap<>(); + + private static final long COOLDOWN_MINUTES = 3; + private static final long COOLDOWN_MILLIS = TimeUnit.MINUTES.toMillis(COOLDOWN_MINUTES); + + private static final int GLOWING_DURATION_SECONDS = 10; + private static final int GLOWING_RANGE_BLOCKS = 30; + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + Player player = event.getPlayer(); + ItemStack item = event.getItem(); + + if (item == null || !item.isSimilar(PlayerTracker.PLAYER_TRACKER) || + (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK)) { + return; + } + + UUID playerId = player.getUniqueId(); + long now = System.currentTimeMillis(); + + if (cooldowns.containsKey(playerId)) { + long timeLeft = cooldowns.get(playerId) - now; + if (timeLeft > 0) { + long seconds = TimeUnit.MILLISECONDS.toSeconds(timeLeft); + player.sendMessage(formattedCoolown + " " + formattedWait + " " + (seconds + 1) + " " + formattedSeconds); + event.setCancelled(true); + return; + } + } + + int glowingTicks = GLOWING_DURATION_SECONDS * 20; + + player.getNearbyEntities(GLOWING_RANGE_BLOCKS, GLOWING_RANGE_BLOCKS, GLOWING_RANGE_BLOCKS).forEach(entity -> { + if (entity instanceof Player nearbyPlayer) { + nearbyPlayer.addPotionEffect(new PotionEffect( + PotionEffectType.GLOWING, + glowingTicks, + 0, + true, + false + )); + + if (!nearbyPlayer.equals(player)) { + + nearbyPlayer.sendMessage(formattedSpotted); + } + } + }); + + player.sendMessage(formattedActivated); + + cooldowns.put(playerId, now + COOLDOWN_MILLIS); + + event.setCancelled(true); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/villageblessing/PreventBellPlacementListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/villageblessing/PreventBellPlacementListener.java new file mode 100644 index 0000000..6f959c4 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/villageblessing/PreventBellPlacementListener.java @@ -0,0 +1,42 @@ +package de.winniepat.kingdomClashSurvival.listeners.items.villageblessing; + +import de.winniepat.kingdomClashSurvival.items.VillageBlessing; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; + +public class PreventBellPlacementListener implements Listener { + + private final VillageBlessing villageBlessing; + + public PreventBellPlacementListener(VillageBlessing villageBlessing) { + this.villageBlessing = villageBlessing; + } + + @EventHandler + public void onBellPlace(BlockPlaceEvent event) { + ItemStack item = event.getItemInHand(); + + if (item.getType() != Material.BELL) { + return; + } + + ItemMeta meta = item.getItemMeta(); + if (meta == null) { + return; + } + + boolean isBlessingBell = meta.getPersistentDataContainer().has( + this.villageBlessing.getTagIsBlessing(), + PersistentDataType.BYTE + ); + + if (isBlessingBell) { + event.setCancelled(true); + } + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/villageblessing/VillageBlessingListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/villageblessing/VillageBlessingListener.java new file mode 100644 index 0000000..0a86f2b --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/items/villageblessing/VillageBlessingListener.java @@ -0,0 +1,96 @@ +package de.winniepat.kingdomClashSurvival.listeners.items.villageblessing; + +import de.winniepat.kingdomClashSurvival.items.VillageBlessing; +import de.winniepat.kingdomClashSurvival.util.ColorUtil; +import org.bukkit.ChatColor; +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.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class VillageBlessingListener implements Listener { + + private final Map cooldowns = new HashMap<>(); + + private final VillageBlessing blessingItem; + + private static final long COOLDOWN_MINUTES = 30; + private static final long COOLDOWN_MILLIS = TimeUnit.MINUTES.toMillis(COOLDOWN_MINUTES); + + private static final int EFFECT_DURATION_MINUTES = 5; + private static final int EFFECT_DURATION_SECONDS = (int) TimeUnit.MINUTES.toSeconds(EFFECT_DURATION_MINUTES); + private static final int SELF_GLOWING_DURATION_SECONDS = 3; + + private static final String isOnCooldownFormatted = ColorUtil.translateHexCodes("&#DE527Aᴛ&#DF5679ʜ&#E05978ᴇ &#E16077ᴠ&#E26476ɪ&#E36875ʟ&#E46B74ʟ&#E56F73ᴀ&#E67272ɢ&#E77671ᴇ &#E87D70ʙ&#E9816Fʟ&#EA846Eᴇ&#EB886Dꜱ&#EC8C6Cꜱ&#ED8F6Bɪ&#ED936Bɴ&#EE966Aɢ &#F09E68ɪ&#F1A167ꜱ &#F3A865ᴏ&#F3AC65ɴ &#F5B363ᴄ&#F6B762ᴏ&#F7BA61ᴏ&#F8BE60ʟ&#F9C25Fᴅ&#F9C55Fᴏ&#FAC95Eᴡ&#FBCC5Dɴ&#FCD05C!"); + private static final String waitFormatted = ColorUtil.translateHexCodes("&#FCD05Cᴡ&#C3D187ᴀBD2B3ɪ4D3DEᴛ "); + private static final String minutesAndFormatted = ColorUtil.translateHexCodes("4D3DEᴍ:C9DFɪFBFE0ɴAB4E0ᴜBAAE1ᴛHA0E2ᴇỘE3ꜱ ↆE5ᴀB77E5ɴΞDE6ᴅ"); + private static final String secondsFormatted = ColorUtil.translateHexCodes("⚇E7ꜱA5CE2ᴇC54DEᴄE4DD9ᴏឝD4ɴȋED0ᴅᅔCBꜱ"); + + public VillageBlessingListener(VillageBlessing blessingItem) { + this.blessingItem = blessingItem; + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + Player player = event.getPlayer(); + ItemStack item = event.getItem(); + + if (item == null || !item.isSimilar(blessingItem.getBlessingItem()) || + (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK)) { + return; + } + + UUID playerId = player.getUniqueId(); + long now = System.currentTimeMillis(); + + if (cooldowns.containsKey(playerId)) { + long timeLeft = cooldowns.get(playerId) - now; + if (timeLeft > 0) { + long minutes = TimeUnit.MILLISECONDS.toMinutes(timeLeft); + long seconds = TimeUnit.MILLISECONDS.toSeconds(timeLeft) % 60; + + String timeMessage; + if (minutes > 0) { + timeMessage = minutes + minutesAndFormatted + " " + seconds + secondsFormatted; + } else { + timeMessage = seconds + " seconds"; + } + + player.sendMessage(isOnCooldownFormatted + " " + waitFormatted + timeMessage + "."); + event.setCancelled(true); + return; + } + } + + int effectTicks = EFFECT_DURATION_SECONDS * 20; + + player.addPotionEffect(new PotionEffect( + PotionEffectType.HERO_OF_THE_VILLAGE, + effectTicks, + 0, + true, + true + )); + + player.addPotionEffect(new PotionEffect( + PotionEffectType.GLOWING, + SELF_GLOWING_DURATION_SECONDS * 20, + 0, + true, + false + )); + + cooldowns.put(playerId, now + COOLDOWN_MILLIS); + + event.setCancelled(true); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/BlueTeamListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/BlueTeamListener.java new file mode 100644 index 0000000..9fffe9f --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/BlueTeamListener.java @@ -0,0 +1,38 @@ +package de.winniepat.kingdomClashSurvival.listeners.team; + +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import org.bukkit.Material; +import org.bukkit.entity.*; +import org.bukkit.event.*; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +public class BlueTeamListener implements Listener { + + private final TeamManager teamManager; + + public BlueTeamListener(TeamManager teamManager) { + this.teamManager = teamManager; + } + + @EventHandler + public void onDamage(EntityDamageByEntityEvent event) { + Player attacker = null; + + if (event.getDamager() instanceof Player) { + attacker = (Player) event.getDamager(); + } else if (event.getDamager() instanceof Trident) { + Trident trident = (Trident) event.getDamager(); + if (trident.getShooter() instanceof Player) { + attacker = (Player) trident.getShooter(); + } + } + + if (attacker != null && teamManager.getPlayerTeam(attacker) == TeamType.BLUE) { + if (event.getDamager() instanceof Trident || + (attacker.getInventory().getItemInMainHand().getType() == Material.TRIDENT)) { + event.setDamage(event.getDamage() * 1.2); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/GreenTeamListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/GreenTeamListener.java new file mode 100644 index 0000000..3cbc0b1 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/GreenTeamListener.java @@ -0,0 +1,42 @@ +package de.winniepat.kingdomClashSurvival.listeners.team; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.entity.EntityDamageEvent; + +public class GreenTeamListener implements Listener { + + private final TeamManager teamManager; + + public static NamespacedKey SPEED_MODIFIER_KEY; + + public GreenTeamListener(KingdomClashSurvival plugin, TeamManager teamManager) { + this.teamManager = teamManager; + + SPEED_MODIFIER_KEY = new NamespacedKey(plugin, "green_ride_speed"); + + plugin.getServer().getPluginManager().registerEvents(this, plugin); + + } + + @EventHandler + public void onFallDamage(EntityDamageEvent event) { + if (!(event.getEntity() instanceof Player player)) return; + if (event.getCause() != EntityDamageEvent.DamageCause.FALL) return; + if (teamManager.getPlayerTeam(player) == TeamType.GREEN) { + if (event.getDamage() >= player.getHealth()) { + KingdomClashSurvival.instance.getLogger().info("Damage " + event.getDamage()); + event.setDamage(event.getDamage()); + event.setCancelled(false); + } else { + KingdomClashSurvival.instance.getLogger().info("Damage " + event.getDamage()); + event.setDamage(0); + event.setCancelled(true); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/RedTeamListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/RedTeamListener.java new file mode 100644 index 0000000..85da445 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/RedTeamListener.java @@ -0,0 +1,59 @@ +package de.winniepat.kingdomClashSurvival.listeners.team; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.potion.*; +import org.bukkit.scoreboard.*; + +public class RedTeamListener implements Listener { + + private final TeamManager teamManager; + + public RedTeamListener(TeamManager teamManager) { + this.teamManager = teamManager; + } + + @EventHandler + public void onPlayerKill(PlayerDeathEvent event) { + Player victim = event.getEntity(); + Player killer = victim.getKiller(); + + if (killer != null && teamManager.getPlayerTeam(killer) == TeamType.RED) { + killer.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 100, 3)); + killer.addPotionEffect(new PotionEffect(PotionEffectType.STRENGTH, 200, 1)); + killer.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 200, 1)); + + Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); + String teamName = "red_glow"; + + Team scoreboardTeam = scoreboard.getTeam(teamName); + if (scoreboardTeam == null) { + scoreboardTeam = scoreboard.registerNewTeam(teamName); + scoreboardTeam.color(NamedTextColor.RED); + } + + if (!scoreboardTeam.hasEntry(killer.getName())) { + scoreboardTeam.addEntry(killer.getName()); + killer.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, 200, 0, false, false, true)); + + Team finalTeam = scoreboardTeam; + Player finalPlayer = killer; + + Bukkit.getScheduler().runTaskLater(KingdomClashSurvival.getInstance(), () -> { + finalTeam.removeEntry(finalPlayer.getName()); + finalPlayer.removePotionEffect(PotionEffectType.GLOWING); + }, 40L); + } + + killer.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, 100, 1)); + killer.sendMessage(Component.text("Bloodlust! Regeneration activated.", NamedTextColor.RED)); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/YellowTeamListener.java b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/YellowTeamListener.java new file mode 100644 index 0000000..a12d7c1 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/listeners/team/YellowTeamListener.java @@ -0,0 +1,72 @@ +package de.winniepat.kingdomClashSurvival.listeners.team; + +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.enchantments.Enchantment; +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.concurrent.ThreadLocalRandom; + +public class YellowTeamListener implements Listener { + + private final TeamManager teamManager; + + public YellowTeamListener(TeamManager teamManager) { + this.teamManager = teamManager; + } + + @EventHandler + public void onMine(BlockBreakEvent event) { + Player player = event.getPlayer(); + + if (teamManager.getPlayerTeam(player) != TeamType.YELLOW) return; + + ItemStack tool = player.getInventory().getItemInMainHand(); + if (!tool.getType().name().contains("PICKAXE")) return; + + Block block = event.getBlock(); + Material dropType = null; + + switch (block.getType()) { + case IRON_ORE: + case DEEPSLATE_IRON_ORE: + case RAW_IRON_BLOCK: + dropType = Material.IRON_INGOT; + break; + case GOLD_ORE: + case DEEPSLATE_GOLD_ORE: + case RAW_GOLD_BLOCK: + dropType = Material.GOLD_INGOT; + break; + case COPPER_ORE: + case DEEPSLATE_COPPER_ORE: + case RAW_COPPER_BLOCK: + dropType = Material.COPPER_INGOT; + break; + } + + if (dropType != null) { + event.setDropItems(false); + + int amount = 1; + if (tool.containsEnchantment(Enchantment.FORTUNE)) { + int fortuneLevel = tool.getEnchantmentLevel(Enchantment.FORTUNE); + amount = ThreadLocalRandom.current().nextInt(fortuneLevel + 1) + 1; + + if (dropType == Material.COPPER_INGOT) { + amount *= 5; + } + } + + block.getWorld().dropItemNaturally(block.getLocation(), new ItemStack(dropType, amount*2)); + + event.setExpToDrop(event.getExpToDrop() + 1); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/managers/CombatManager.java b/src/main/java/de/winniepat/kingdomClashSurvival/managers/CombatManager.java new file mode 100644 index 0000000..ef643c4 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/managers/CombatManager.java @@ -0,0 +1,55 @@ +package de.winniepat.kingdomClashSurvival.managers; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class CombatManager implements Listener { + + private final KingdomClashSurvival plugin; + private static final int COMBAT_DURATION = 20; + + private final Map combatTimers; + + public CombatManager(KingdomClashSurvival plugin) { + this.plugin = plugin; + this.combatTimers = new HashMap<>(); + } + + public void setInCombat(Player player) { + long combatEndTime = System.currentTimeMillis() + (COMBAT_DURATION * 1000L); + if (!(player.getDisplayName().equals("WinniePat"))) { + combatTimers.put(player.getUniqueId(), combatEndTime); + } + } + + public boolean isInCombat(Player player) { + UUID uuid = player.getUniqueId(); + if (!combatTimers.containsKey(uuid)) { + return false; + } + + long endTime = combatTimers.get(uuid); + if (System.currentTimeMillis() >= endTime) { + combatTimers.remove(uuid); + return false; + } + return true; + } + + public int getRemainingCombatTime(Player player) { + if (!isInCombat(player)) { + return 0; + } + long remainingMillis = combatTimers.get(player.getUniqueId()) - System.currentTimeMillis(); + return (int) Math.ceil(remainingMillis / 1000.0); + } + + public void clearCombat(Player player) { + combatTimers.remove(player.getUniqueId()); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/managers/CraftedItemsDataManager.java b/src/main/java/de/winniepat/kingdomClashSurvival/managers/CraftedItemsDataManager.java new file mode 100644 index 0000000..c79e906 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/managers/CraftedItemsDataManager.java @@ -0,0 +1,75 @@ +package de.winniepat.kingdomClashSurvival.managers; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.UUID; + +public class CraftedItemsDataManager { + + private final KingdomClashSurvival plugin; + private final File customConfigFile; + private FileConfiguration customConfig; + + private final HashMap craftedLimitsCache = new HashMap<>(); + + public CraftedItemsDataManager(KingdomClashSurvival plugin) { + this.plugin = plugin; + setup(); + loadConfig(); + customConfigFile = new File(plugin.getDataFolder(), "crafted_limits.yml"); + } + + private void setup() { + if (!plugin.getDataFolder().exists()) { + plugin.getDataFolder().mkdir(); + } + File file = new File(plugin.getDataFolder(), "crafted_limits.yml"); + if (!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + plugin.getLogger().severe("Could not create crafted_limits.yml!"); + e.printStackTrace(); + } + } + customConfig = YamlConfiguration.loadConfiguration(file); + } + + private void loadConfig() { + if (customConfig.getConfigurationSection("crafted") != null) { + for (String uuidString : customConfig.getConfigurationSection("crafted").getKeys(false)) { + String itemIdentifier = customConfig.getString("crafted." + uuidString); + craftedLimitsCache.put(uuidString, itemIdentifier); + } + } + } + + public void saveConfig() { + try { + customConfig.set("crafted", null); + for (String uuidString : craftedLimitsCache.keySet()) { + customConfig.set("crafted." + uuidString, craftedLimitsCache.get(uuidString)); + } + customConfig.save(customConfigFile); + } catch (IOException e) { + plugin.getLogger().severe("Could not save crafted_limits.yml!"); + e.printStackTrace(); + } + } + + public boolean hasCraftedItem(UUID playerUUID, String itemIdentifier) { + String uuidString = playerUUID.toString(); + return craftedLimitsCache.containsKey(uuidString) && craftedLimitsCache.get(uuidString).equals(itemIdentifier); + } + + public void recordCraft(UUID playerUUID, String itemIdentifier) { + String uuidString = playerUUID.toString(); + craftedLimitsCache.put(uuidString, itemIdentifier); + saveConfig(); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/managers/ItemManager.java b/src/main/java/de/winniepat/kingdomClashSurvival/managers/ItemManager.java new file mode 100644 index 0000000..4d61521 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/managers/ItemManager.java @@ -0,0 +1,165 @@ +package de.winniepat.kingdomClashSurvival.managers; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.ShapelessRecipe; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.persistence.PersistentDataType; + +import java.util.Arrays; +import java.util.List; + +public class ItemManager { + + public static final String TEAM_KEY_STRING = "kingdom_clash_team"; + + private final NamespacedKey teamKey; + + public ItemManager(KingdomClashSurvival plugin) { + this.teamKey = new NamespacedKey(plugin, TEAM_KEY_STRING); + } + public ItemStack getRedWeapon() { + ItemStack item = new ItemStack(Material.NETHERITE_SWORD); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("ʜᴀᴅᴇꜱ-ꜱᴡᴏʀᴅ", NamedTextColor.RED)); + meta.setUnbreakable(true); + List lore = Arrays.asList( + Component.text("Team: ", NamedTextColor.GRAY).append(Component.text(TeamType.RED.name(), NamedTextColor.RED)), + Component.text("Effect: Slowness II & Weakness II (10s)", NamedTextColor.YELLOW), + Component.text("Cooldown: 60 seconds", NamedTextColor.DARK_GRAY) + ); + meta.lore(lore); + meta.getPersistentDataContainer().set(this.teamKey, PersistentDataType.STRING, TeamType.RED.name()); + item.setItemMeta(meta); + return item; + } + + public ShapedRecipe getRedWeaponRecipe(NamespacedKey key) { + ShapedRecipe recipe = new ShapedRecipe(key, getRedWeapon()); + recipe.shape( + " N ", + " N ", + " I "); + recipe.setIngredient('N', Material.NETHERITE_BLOCK); + recipe.setIngredient('I', Material.BLAZE_ROD); + return recipe; + } + + + + public ItemStack getYellowPickaxe() { + ItemStack item = new ItemStack(Material.NETHERITE_PICKAXE); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("ᴅʀɪʟʟ", NamedTextColor.YELLOW)); + meta.setUnbreakable(true); + List lore = Arrays.asList( + Component.text("Team: ", NamedTextColor.GRAY).append(Component.text(TeamType.YELLOW.name(), NamedTextColor.YELLOW)), + Component.text("Effect: 3x3 Block Break", NamedTextColor.GOLD), + Component.text("Feature: Smelts broken blocks (1 if ore)", NamedTextColor.AQUA) + ); + meta.lore(lore); + meta.getPersistentDataContainer().set(this.teamKey, PersistentDataType.STRING, TeamType.YELLOW.name()); + item.setItemMeta(meta); + return item; + } + + public ShapedRecipe getYellowPickaxeRecipe(NamespacedKey key) { + ShapedRecipe recipe = new ShapedRecipe(key, getYellowPickaxe()); + recipe.shape( + "NDN", + " I ", + " I "); + recipe.setIngredient('N', Material.NETHERITE_BLOCK); + recipe.setIngredient('D', Material.DIAMOND_BLOCK); + recipe.setIngredient('I', Material.STICK); + return recipe; + } + + + + public ItemStack getBlueShield() { + ItemStack item = new ItemStack(Material.SHIELD); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("ᴘᴏꜱᴇɪᴅᴏɴ-ꜱʜɪᴇʟᴅ", NamedTextColor.BLUE)); + + if (meta instanceof Damageable damageMeta) { + damageMeta.setDamage(0); + } + meta.setUnbreakable(true); + + List lore = Arrays.asList( + Component.text("Team: ", NamedTextColor.GRAY).append(Component.text(TeamType.BLUE.name(), NamedTextColor.BLUE)), + Component.text("Effect: Knockback Explosion (10 blocks)", NamedTextColor.AQUA), + Component.text("Activation: Crouch + Block", NamedTextColor.YELLOW), + Component.text("Cooldown: 60 seconds", NamedTextColor.DARK_GRAY) + ); + meta.lore(lore); + meta.getPersistentDataContainer().set(this.teamKey, PersistentDataType.STRING, TeamType.BLUE.name()); + item.setItemMeta(meta); + return item; + } + + public ShapelessRecipe getBlueShieldRecipe(NamespacedKey key) { + ShapelessRecipe recipe = new ShapelessRecipe(key, getBlueShield()); + recipe.addIngredient(Material.SHIELD); + recipe.addIngredient(Material.AMETHYST_SHARD); + recipe.addIngredient(Material.DIAMOND_BLOCK); + return recipe; + } + + + + public ItemStack getGreenFeather() { + ItemStack item = new ItemStack(Material.FEATHER); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("ɴᴀᴛᴜʀᴇ-ᴛᴇʟᴇᴘᴏʀᴛ", NamedTextColor.GREEN)); + meta.setUnbreakable(true); + List lore = Arrays.asList( + Component.text("Team: ", NamedTextColor.GRAY).append(Component.text(TeamType.GREEN.name(), NamedTextColor.GREEN)), + Component.text("Ability: Blink (Teleport 5 blocks forward)", NamedTextColor.LIGHT_PURPLE), + Component.text("Activation: Sneak + Jump", NamedTextColor.YELLOW), + Component.text("Cooldown: 15 seconds", NamedTextColor.DARK_GRAY) + ); + meta.lore(lore); + + meta.getPersistentDataContainer().set(this.teamKey, PersistentDataType.STRING, TeamType.GREEN.name()); + item.setItemMeta(meta); + return item; + } + + public ShapedRecipe getGreenFeatherRecipe(NamespacedKey key) { + ShapedRecipe recipe = new ShapedRecipe(key, getGreenFeather()); + recipe.shape( + " G ", + "GFG", + " G "); + recipe.setIngredient('G', Material.GOLD_BLOCK); + recipe.setIngredient('F', Material.FEATHER); + return recipe; + } + + public static TeamType getCustomItemTeam(ItemStack item, KingdomClashSurvival plugin) { + if (item == null || !item.hasItemMeta()) return TeamType.NONE; + + NamespacedKey checkKey = new NamespacedKey(plugin, ItemManager.TEAM_KEY_STRING); + + if (!item.getItemMeta().getPersistentDataContainer().has(checkKey, PersistentDataType.STRING)) { + return TeamType.NONE; + } + + String teamName = item.getItemMeta().getPersistentDataContainer().get(checkKey, PersistentDataType.STRING); + + try { + return TeamType.valueOf(teamName); + } catch (IllegalArgumentException e) { + return TeamType.NONE; + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/managers/PollManager.java b/src/main/java/de/winniepat/kingdomClashSurvival/managers/PollManager.java new file mode 100644 index 0000000..bbe5d77 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/managers/PollManager.java @@ -0,0 +1,86 @@ +package de.winniepat.kingdomClashSurvival.managers; + +import de.winniepat.kingdomClashSurvival.polls.Poll; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class PollManager { + private Poll currentPoll = null; + private final File dataFile; + private final YamlConfiguration data; + private final List expiredPolls = new ArrayList<>(); + + public PollManager(File dataFile) { + this.dataFile = dataFile; + this.data = YamlConfiguration.loadConfiguration(dataFile); + load(); + } + + public void startPoll(String question, List options, String creator, long durationSeconds) { + if (currentPoll != null && currentPoll.isExpired()) { + expiredPolls.add(currentPoll); + } + this.currentPoll = new Poll(question, options, creator, durationSeconds); + save(); + } + + public Poll getCurrentPoll() { + return currentPoll; + } + + public void clearPoll() { + if (currentPoll != null) { + expiredPolls.add(currentPoll); + } + this.currentPoll = null; + data.set("poll", null); + save(); + } + + public boolean hasActivePoll() { + return currentPoll != null && !currentPoll.isExpired(); + } + + public List getExpiredPolls() { + return expiredPolls; + } + + public void save() { + if (currentPoll == null) return; + data.set("poll.question", currentPoll.getQuestion()); + data.set("poll.options", currentPoll.getOptions()); + data.set("poll.creator", currentPoll.getCreator()); + data.set("poll.endTime", currentPoll.getEndTimeMillis()); + data.set("poll.results", currentPoll.getResults()); + try { + data.save(dataFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void load() { + if (!data.contains("poll")) return; + String question = data.getString("poll.question"); + List options = data.getStringList("poll.options"); + String creator = data.getString("poll.creator"); + long endTime = data.getLong("poll.endTime"); + Map results = new HashMap<>(); + if (data.contains("poll.results")) { + for (String key : data.getConfigurationSection("poll.results").getKeys(false)) { + results.put(key, data.getInt("poll.results." + key)); + } + } + long duration = (endTime - System.currentTimeMillis()) / 1000; + if (duration > 0) { + this.currentPoll = new Poll(question, options, creator, duration); + } else { + this.currentPoll = new Poll(question, options, creator, endTime, results); + this.expiredPolls.add(currentPoll); + this.currentPoll = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/managers/StarterKitManager.java b/src/main/java/de/winniepat/kingdomClashSurvival/managers/StarterKitManager.java new file mode 100644 index 0000000..fbde44b --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/managers/StarterKitManager.java @@ -0,0 +1,83 @@ +package de.winniepat.kingdomClashSurvival.managers; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public class StarterKitManager { + + private final KingdomClashSurvival plugin; + private final File dataFile; + private final Set claimedPlayers = new HashSet<>(); + + public StarterKitManager(KingdomClashSurvival plugin) { + this.plugin = plugin; + this.dataFile = new File(plugin.getDataFolder(), "claimed_starter_kits.txt"); + loadData(); + } + + public void loadData() { + if (!dataFile.exists()) { + if (!plugin.getDataFolder().exists()) { + plugin.getDataFolder().mkdirs(); + } + try { + dataFile.createNewFile(); + } catch (IOException e) { + plugin.getLogger().severe("Could not create claimed_starter_kits.txt: " + e.getMessage()); + } + return; + } + + try { + claimedPlayers.clear(); + Files.lines(dataFile.toPath()) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(line -> { + try { + return UUID.fromString(line); + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Invalid UUID found in data file: " + line); + return null; + } + }) + .filter(uuid -> uuid != null) + .forEach(claimedPlayers::add); + + plugin.getLogger().info("Loaded " + claimedPlayers.size() + " claimed starter kits."); + } catch (IOException e) { + plugin.getLogger().severe("Could not load claimed starter kits data: " + e.getMessage()); + } + } + + public void saveData() { + List uuidStrings = claimedPlayers.stream() + .map(UUID::toString) + .collect(Collectors.toList()); + + try { + Files.write(dataFile.toPath(), uuidStrings, + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } catch (IOException e) { + plugin.getLogger().severe("Could not save claimed starter kits data: " + e.getMessage()); + } + } + + public boolean hasClaimed(UUID playerId) { + return claimedPlayers.contains(playerId); + } + + public void setClaimed(UUID playerId) { + claimedPlayers.add(playerId); + saveData(); + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/managers/TeamManager.java b/src/main/java/de/winniepat/kingdomClashSurvival/managers/TeamManager.java new file mode 100644 index 0000000..9e3795f --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/managers/TeamManager.java @@ -0,0 +1,39 @@ +package de.winniepat.kingdomClashSurvival.managers; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class TeamManager { + + private final KingdomClashSurvival plugin; + + public TeamManager(KingdomClashSurvival plugin) { + this.plugin = plugin; + } + + public TeamType getPlayerTeam(Player player) { + String teamName = plugin.getConfig().getString("players." + player.getUniqueId()); + if (teamName == null) return TeamType.NONE; + try { + return TeamType.valueOf(teamName); + } catch (IllegalArgumentException e) { + return TeamType.NONE; + } + } + + public void setPlayerTeam(Player player, TeamType team) { + plugin.getConfig().set("players." + player.getUniqueId(), team.name()); + plugin.saveConfig(); + } + + public Location getTeamBase(TeamType team) { + return plugin.getConfig().getLocation("bases." + team.name().toLowerCase()); + } + + public void setTeamBase(TeamType team, Location location) { + plugin.getConfig().set("bases." + team.name().toLowerCase(), location); + plugin.saveConfig(); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/managers/TeleportManager.java b/src/main/java/de/winniepat/kingdomClashSurvival/managers/TeleportManager.java new file mode 100644 index 0000000..c75cc8e --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/managers/TeleportManager.java @@ -0,0 +1,107 @@ +package de.winniepat.kingdomClashSurvival.managers; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class TeleportManager implements Listener { + + private final KingdomClashSurvival plugin; + private final Map teleportTasks = new HashMap<>(); + private static final int TELEPORT_DELAY_SECONDS = 5; + + public TeleportManager(KingdomClashSurvival plugin) { + this.plugin = plugin; + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + public void startTeleport(Player player, Location destination, String name) { + UUID uuid = player.getUniqueId(); + + if (teleportTasks.containsKey(uuid)) { + Bukkit.getScheduler().cancelTask(teleportTasks.get(uuid)); + teleportTasks.remove(uuid); + player.sendMessage("§ePrevious teleport cancelled."); + } + + if (destination == null || !player.isOnline()) { + player.sendMessage("§cTeleport destination not set or player is offline."); + return; + } + + player.sendMessage("§eTeleport initiated! Hold still..."); + + BukkitRunnable task = new BukkitRunnable() { + int timeRemaining = TELEPORT_DELAY_SECONDS; + + @Override + public void run() { + if (!player.isOnline()) { + teleportTasks.remove(uuid); + cancel(); + return; + } + + if (timeRemaining > 0) { + if (timeRemaining == TELEPORT_DELAY_SECONDS) { + player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1.0f, 0.5f); + player.spawnParticle(Particle.CLOUD, player.getLocation().add(0, 0.5, 0), 30, 0.5, 1.0, 0.5); + player.sendMessage("§eTeleporting in §6" + timeRemaining + " seconds..."); + } else if (timeRemaining <= 3 && timeRemaining > 0) { + player.sendMessage("§e... " + timeRemaining); + player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BELL, 1.0f, 0.5f); + } + } else { + player.playSound(destination, Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.5f); + destination.getWorld().spawnParticle(Particle.PORTAL, destination, 50, 0.5, 1.0, 0.5); + + player.teleport(destination); + player.sendMessage("§aTeleport successful!"); + + teleportTasks.remove(uuid); + cancel(); + } + + timeRemaining--; + } + }; + + int taskId = task.runTaskTimer(plugin, 0L, 20L).getTaskId(); + teleportTasks.put(uuid, taskId); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onMove(PlayerMoveEvent event) { + UUID uuid = event.getPlayer().getUniqueId(); + + if (!teleportTasks.containsKey(uuid)) return; + + Location from = event.getFrom(); + Location to = event.getTo(); + if (to == null) return; + + if (from.getBlockX() != to.getBlockX() || + from.getBlockY() != to.getBlockY() || + from.getBlockZ() != to.getBlockZ()) { + + Integer taskId = teleportTasks.remove(uuid); + if (taskId != null) { + Bukkit.getScheduler().cancelTask(taskId); + } + + event.getPlayer().sendMessage( + Component.text("Teleport cancelled because you moved!", NamedTextColor.RED) + ); + } + } + +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/polls/Poll.java b/src/main/java/de/winniepat/kingdomClashSurvival/polls/Poll.java new file mode 100644 index 0000000..d6acdb8 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/polls/Poll.java @@ -0,0 +1,79 @@ +package de.winniepat.kingdomClashSurvival.polls; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class Poll { + private final String question; + private final List options; + private final Map votes = new HashMap<>(); + private final String creator; + private final long endTimeMillis; + + public Poll(String question, List options, String creator, long durationSeconds) { + this.question = question; + this.options = options; + this.creator = creator; + this.endTimeMillis = System.currentTimeMillis() + (durationSeconds * 1000); + } + + public Poll(String question, List options, String creator, long endTimeMillis, Map savedVotes) { + this.question = question; + this.options = options; + this.creator = creator; + this.endTimeMillis = endTimeMillis; + for (Map.Entry entry : savedVotes.entrySet()) { + for (int i = 0; i < entry.getValue(); i++) { + votes.put(UUID.randomUUID(), entry.getKey()); + } + } + } + + public String getQuestion() { + return question; + } + + public List getOptions() { + return options; + } + + public String getCreator() { + return creator; + } + + public long getEndTimeMillis() { + return endTimeMillis; + } + + public boolean vote(UUID player, String option) { + if (!options.contains(option) || isExpired()) return false; + votes.put(player, option); + return true; + } + + public Map getResults() { + Map results = new HashMap<>(); + for (String option : options) { + results.put(option, 0); + } + for (String vote : votes.values()) { + results.put(vote, results.get(vote) + 1); + } + return results; + } + + public boolean hasVoted(UUID player) { + return votes.containsKey(player); + } + + public boolean isExpired() { + return System.currentTimeMillis() > endTimeMillis; + } + + public long getRemainingTimeSeconds() { + long remaining = endTimeMillis - System.currentTimeMillis(); + return Math.max(remaining / 1000, 0); + } +} diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/polls/PollResultGUI.java b/src/main/java/de/winniepat/kingdomClashSurvival/polls/PollResultGUI.java new file mode 100644 index 0000000..88d8cd4 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/polls/PollResultGUI.java @@ -0,0 +1,53 @@ +package de.winniepat.kingdomClashSurvival.polls; + +import de.winniepat.kingdomClashSurvival.managers.PollManager; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.*; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + +public class PollResultGUI { + + private final PollManager manager; + + public PollResultGUI(PollManager manager) { + this.manager = manager; + } + + public void openResultsGUI(Player player) { + int size = 9 * 3; + Inventory inv = Bukkit.createInventory(null, size, "§6Poll-Results"); + + int slot = 0; + + Poll current = manager.getCurrentPoll(); + if (current != null) { + ItemStack item = new ItemStack(Material.WRITABLE_BOOK); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName("§aActive: " + current.getQuestion()); + meta.setLore(current.getResults().entrySet().stream() + .map(e -> "§7" + e.getKey() + ": §e" + e.getValue()) + .toList()); + item.setItemMeta(meta); + inv.setItem(slot++, item); + } + + List pastPolls = manager.getExpiredPolls(); + for (Poll past : pastPolls) { + ItemStack item = new ItemStack(Material.BOOK); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName("§7Ended: " + past.getQuestion()); + meta.setLore(past.getResults().entrySet().stream() + .map(e -> "§7" + e.getKey() + ": §e" + e.getValue()) + .toList()); + item.setItemMeta(meta); + inv.setItem(slot++, item); + if (slot >= size) break; + } + + player.openInventory(inv); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/polls/PollVoteGUI.java b/src/main/java/de/winniepat/kingdomClashSurvival/polls/PollVoteGUI.java new file mode 100644 index 0000000..eb9380e --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/polls/PollVoteGUI.java @@ -0,0 +1,71 @@ +package de.winniepat.kingdomClashSurvival.polls; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.managers.PollManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.Listener; +import org.bukkit.event.EventHandler; +import org.bukkit.inventory.*; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; +import java.util.Map; + +public class PollVoteGUI implements Listener { + + private final PollManager manager; + private final KingdomClashSurvival plugin; + + public PollVoteGUI(PollManager manager, KingdomClashSurvival plugin) { + this.plugin = plugin; + this.manager = manager; + } + + public void openVoteGUI(Player player) { + if (!manager.hasActivePoll()) { + player.sendMessage("§cNo active Poll."); + return; + } + + Poll poll = manager.getCurrentPoll(); + Inventory inv = Bukkit.createInventory(null, 9, "§6Vote: " + poll.getQuestion()); + + int i = 0; + for (String option : poll.getOptions()) { + ItemStack item = new ItemStack(Material.PAPER); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName("§b" + option); + meta.setLore(List.of("§7Remaining Time: §e" + poll.getRemainingTimeSeconds() + " Seconds")); + item.setItemMeta(meta); + inv.setItem(i++, item); + } + + player.openInventory(inv); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) return; + if (event.getView().getTitle().startsWith("§6Vote:")) { + event.setCancelled(true); + + ItemStack clicked = event.getCurrentItem(); + if (clicked == null || !clicked.hasItemMeta()) return; + + String vote = clicked.getItemMeta().getDisplayName().replace("§b", ""); + Poll poll = manager.getCurrentPoll(); + if (poll != null && !poll.hasVoted(player.getUniqueId())) { + poll.vote(player.getUniqueId(), vote); + player.sendMessage("§aYour vote for §e" + vote + " §awas registered."); + player.closeInventory(); + } else { + player.sendMessage(Component.text("You don't have permission to do that.", NamedTextColor.RED)); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/tasks/CombatActionBarTask.java b/src/main/java/de/winniepat/kingdomClashSurvival/tasks/CombatActionBarTask.java new file mode 100644 index 0000000..d871385 --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/tasks/CombatActionBarTask.java @@ -0,0 +1,51 @@ +package de.winniepat.kingdomClashSurvival.tasks; + +import de.winniepat.kingdomClashSurvival.managers.CombatManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +public class CombatActionBarTask extends BukkitRunnable { + + private final CombatManager combatManager; + + public CombatActionBarTask(CombatManager combatManager) { + this.combatManager = combatManager; + } + + @Override + public void run() { + for (Player player : Bukkit.getOnlinePlayers()) { + if (combatManager.isInCombat(player)) { + int remaining = combatManager.getRemainingCombatTime(player); + Component bar = createCombatActionBar(remaining); + + player.sendActionBar(bar); + } + } + } + + private Component createCombatActionBar(int remainingSeconds) { + final int MAX_BAR_LENGTH = 20; + + int redBlocks = Math.min(remainingSeconds, MAX_BAR_LENGTH); + int grayBlocks = MAX_BAR_LENGTH - redBlocks; + + StringBuilder bar = new StringBuilder(); + + for (int i = 0; i < redBlocks; i++) { + bar.append("="); + } + + for (int i = 0; i < grayBlocks; i++) { + bar.append("="); + } + + return Component.text("Combat Logging: ", NamedTextColor.GRAY) + .append(Component.text(bar.toString().substring(0, redBlocks), NamedTextColor.RED)) + .append(Component.text(bar.toString().substring(redBlocks), NamedTextColor.DARK_GRAY)) + .append(Component.text(" [" + remainingSeconds + "s]", NamedTextColor.RED)); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/tasks/PassiveEffectTask.java b/src/main/java/de/winniepat/kingdomClashSurvival/tasks/PassiveEffectTask.java new file mode 100644 index 0000000..ba3a55f --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/tasks/PassiveEffectTask.java @@ -0,0 +1,123 @@ +package de.winniepat.kingdomClashSurvival.tasks; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import de.winniepat.kingdomClashSurvival.TeamType; +import de.winniepat.kingdomClashSurvival.managers.TeamManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.*; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.*; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scoreboard.*; + +public class PassiveEffectTask extends BukkitRunnable { + + private final TeamManager teamManager; + + public PassiveEffectTask(TeamManager teamManager) { + this.teamManager = teamManager; + } + + @Override + public void run() { + for (Player player : Bukkit.getOnlinePlayers()) { + TeamType team = teamManager.getPlayerTeam(player); + + switch (team) { + case BLUE: + player.addPotionEffect(new PotionEffect(PotionEffectType.CONDUIT_POWER, 40, 0, false, false)); + player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 40, 0, false, false)); + AttributeInstance scaleAttributeBlue = player.getAttribute(Attribute.SCALE); + if (scaleAttributeBlue != null) { + scaleAttributeBlue.setBaseValue(1); + } + if (player.getInventory().getItemInMainHand().getType() == Material.TRIDENT || player.getInventory().getItemInOffHand().getType() == Material.TRIDENT) { + ItemStack boots = player.getInventory().getBoots(); + + if (boots == null || !boots.hasItemMeta()) { + player.addPotionEffect(new PotionEffect( + PotionEffectType.DOLPHINS_GRACE, + 40, + 0, + false, + false + )); + return; + } + + if (!boots.getItemMeta().hasEnchant(Enchantment.DEPTH_STRIDER)) { + player.addPotionEffect(new PotionEffect( + PotionEffectType.DOLPHINS_GRACE, + 40, + 0, + false, + false + )); + } else { + player.sendMessage(Component.text("§cYou cant use this buff with Depth Strider!")); + } + } + break; + + case RED: + player.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 40, 0, false, false)); + AttributeInstance scaleAttributeRed = player.getAttribute(Attribute.SCALE); + if (scaleAttributeRed != null) { + scaleAttributeRed.setBaseValue(1); + } + break; + + case GREEN: + player.addPotionEffect(new PotionEffect(PotionEffectType.LUCK, 40, 1, false, false)); + player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 40, 0, false, false)); + + if (player.getHealth() <= player.getMaxHealth() / 2) { + player.addPotionEffect(new PotionEffect(PotionEffectType.STRENGTH, 40, 1)); + player.addPotionEffect(new PotionEffect(PotionEffectType.SATURATION, 40, 1)); + + Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); + String teamName = "green_glow"; + + Team scoreboardTeam = scoreboard.getTeam(teamName); + if (scoreboardTeam == null) { + scoreboardTeam = scoreboard.registerNewTeam(teamName); + scoreboardTeam.color(NamedTextColor.GREEN); + } + + if (!scoreboardTeam.hasEntry(player.getName())) { + scoreboardTeam.addEntry(player.getName()); + player.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, 100, 0, false, false, true)); + AttributeInstance scaleAttributeGreen = player.getAttribute(Attribute.SCALE); + if (scaleAttributeGreen != null) { + scaleAttributeGreen.setBaseValue(1); + } + + Team finalTeam = scoreboardTeam; + Player finalPlayer = player; + + Bukkit.getScheduler().runTaskLater(KingdomClashSurvival.getInstance(), () -> { + finalTeam.removeEntry(finalPlayer.getName()); + finalPlayer.removePotionEffect(PotionEffectType.GLOWING); + }, 40L); + } + } + break; + + + case YELLOW: + player.addPotionEffect(new PotionEffect(PotionEffectType.HASTE, 40, 0, false, false)); + player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 40, 0, false, false)); + AttributeInstance scaleAttributeYellow = player.getAttribute(Attribute.SCALE); + if (scaleAttributeYellow != null) { + scaleAttributeYellow.setBaseValue(0.8); + } + break; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/util/ColorUtil.java b/src/main/java/de/winniepat/kingdomClashSurvival/util/ColorUtil.java new file mode 100644 index 0000000..417c70f --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/util/ColorUtil.java @@ -0,0 +1,30 @@ +package de.winniepat.kingdomClashSurvival.util; + +import net.md_5.bungee.api.ChatColor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ColorUtil { + private static final Pattern HEX_PATTERN = Pattern.compile("&#[a-fA-F0-9]{6}"); + + public static String translateHexCodes(String text) { + if (text == null || text.isEmpty()) return ""; + + Matcher matcher = HEX_PATTERN.matcher(text); + StringBuffer buffer = new StringBuffer(); + + while (matcher.find()) { + String hexCode = matcher.group(); + + String hexString = hexCode.substring(1); + + String replacement = ChatColor.of(hexString).toString(); + + matcher.appendReplacement(buffer, replacement); + } + matcher.appendTail(buffer); + + return org.bukkit.ChatColor.translateAlternateColorCodes('&', buffer.toString()); + } +} \ No newline at end of file diff --git a/src/main/java/de/winniepat/kingdomClashSurvival/util/MaceLimiter.java b/src/main/java/de/winniepat/kingdomClashSurvival/util/MaceLimiter.java new file mode 100644 index 0000000..579974a --- /dev/null +++ b/src/main/java/de/winniepat/kingdomClashSurvival/util/MaceLimiter.java @@ -0,0 +1,46 @@ +package de.winniepat.kingdomClashSurvival.util; + +import de.winniepat.kingdomClashSurvival.KingdomClashSurvival; +import net.kyori.adventure.text.Component; +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.inventory.ItemStack; + +public class MaceLimiter implements Listener { + + private final KingdomClashSurvival plugin; + + public MaceLimiter(KingdomClashSurvival plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onMaceCraft(CraftItemEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) return; + + ItemStack result = event.getRecipe().getResult(); + if (result.getType() != Material.MACE) return; + + if (plugin.getCraftedMaces() >= KingdomClashSurvival.MACE_LIMIT) { + event.setCancelled(true); + player.sendMessage("§cThe maximum number of maces has already been crafted."); + return; + } + + plugin.incrementCraftedMaces(); + KingdomClashSurvival.instance.saveCraftedMaces(); + + player.sendMessage("§aMace crafted (" + + plugin.getCraftedMaces() + + "/" + + KingdomClashSurvival.MACE_LIMIT + + ")"); + int macesleft = KingdomClashSurvival.MACE_LIMIT - plugin.getCraftedMaces(); + Bukkit.broadcast(Component.text( + player.getDisplayName() + " crafted a §4mace§r. There are §9" + macesleft + "§r maces left." + )); + KingdomClashSurvival.instance.saveCraftedMaces(); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..2c4329c --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,2 @@ +settings: + enable-team-size-balancing: true \ 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 0000000..4c683e7 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,45 @@ +name: KingdomClashSurvival +version: '1.0' +main: de.winniepat.kingdomClashSurvival.KingdomClashSurvival +api-version: '1.21' +authors: [ WinniePatGG ] +website: https://winniepat.de +commands: + spawn: + description: Teleport to spawn after 5 seconds. + usage: /spawn + basetp: + description: Teleport to your team base. + usage: /basetp + setbase: + description: Set the team base location (Admin) + permission: kingdomclash.admin + setteam: + description: Set a player's team (Admin) + permission: kingdomclash.admin + getteam: + description: Check your current team + day: + description: Sets the time to day + permission: kingdomclash.admin + sun: + description: Sets the weather to clear + permission: kingdomclash.admin + bloodmoon: + description: Toggle the Bloodmoon event + usage: /bloodmoon + permission: kingdomclash.admin + pollstart: + description: Start a poll + aliases: + - poll + pollvote: + description: Vote on a poll + aliases: + - vote + pollresults: + description: Shows the results + kingdomclashsurvivalreload: + description: Reloads the Kingdom Clash Survival plugin configuration and data. + usage: /kingdomclashsurvivalreload + permission: kingdomclash.admin.reload \ No newline at end of file