16 Commits

Author SHA1 Message Date
97b73b0c55 🦋 Debugging for the 1.2.
All checks were successful
Auto Build / Build (push) Successful in 2m40s
2025-12-06 16:40:12 +01:00
d6103b078b 😭 Tcoww -> Tcoww.instance
All checks were successful
Auto Build / Build (push) Successful in 4m26s
2025-11-30 19:04:28 +01:00
25804f76e0 Merge remote-tracking branch 'origin/master'
All checks were successful
Auto Build / Build (push) Successful in 2m55s
2025-11-30 17:23:17 +01:00
fa5a54c8b9 ✔️ Prepare plugin 1.2 version. 2025-11-30 17:22:48 +01:00
ca15b3deeb Try to patch upload-artifact
All checks were successful
Auto Build / Build (push) Successful in 3m18s
2025-11-29 12:47:21 +00:00
71fd3af404 👍 Change the upload path
Some checks failed
Auto Build / Build (push) Failing after 4m15s
2025-11-29 12:21:45 +00:00
8b0a814f87 🙃 Try build only with gradlew
All checks were successful
Auto Build / Build (push) Successful in 2m23s
2025-11-29 12:13:47 +00:00
f0d1e27085 Add exe permission on wrapper file.
Some checks failed
Auto Build / Build (push) Has been cancelled
2025-11-29 12:55:30 +01:00
34f430f48c Add gradle wrapper file.
Some checks failed
Auto Build / Build (push) Has been cancelled
2025-11-29 12:46:40 +01:00
79edd803c0 🔧Move build.yaml.
Some checks failed
Auto Build / Build (push) Has been cancelled
2025-11-16 13:17:40 +01:00
b89f31fea3 🔧Try to add a gitea actions. 2025-11-16 13:15:52 +01:00
a5a7701005 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	README.md
2025-11-11 14:04:21 +01:00
64541b9e03 🪢Bad link in Readme 2025-11-11 14:03:45 +01:00
07eb8b214e 🪢Bad link in Readme 2025-11-11 14:02:29 +01:00
bd721923ae 🎉Update version.
📝Update README.md.
2025-11-11 13:52:15 +01:00
2c533c443e 🔥Update to latest paper version
🔨Add possibility to kick an offline or online player from the game.
🍗Add Saturation effect.
🎙️Change VoiceChat to Simple Voice Chat.
😨Werewolf one shot player now.
2025-11-11 13:29:46 +01:00
50 changed files with 2547 additions and 1550 deletions

View File

@@ -0,0 +1,21 @@
name: Auto Build
on: [push]
jobs:
Build:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v5
- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
- name: Build
run: ./gradlew build
- name: Upload build
uses: christopherhx/gitea-upload-artifact@v4
with:
name: build
path: ${{ gitea.workspace }}/build/libs/tcoww-*-all.jar

View File

@@ -1,5 +1,7 @@
# TCOWW
TCOWW is a small paper plugin created for an event on the French Discord server, [The Court of Gaming](https://discord.gg/evCYTnRrpU).
# 🐺 TCOWW
TCOWW (The court of werewolf) is a small paper plugin created for an event on the French Discord server, [The Court of Gaming](https://discord.gg/evCYTnRrpU) 🔥.
The event is a simple game of "Loup-Garou de Thiercelieux" in Minecraft with a role-play dimension.
Write in kotlin with the paper 1.21.4 api.
🔨 Write in kotlin with paper.
👉 [Latest Release](https://azur84.servehttp.com/Azur/TCOWW/releases)

View File

@@ -1,12 +1,20 @@
plugins {
kotlin("jvm") version "2.2.0-Beta1"
id("com.gradleup.shadow") version "8.3.0"
id("xyz.jpenilla.run-paper") version "2.3.1"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.16"
id("com.gradleup.shadow") version "9.2.2"
id("xyz.jpenilla.run-paper") version "3.0.2"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
id("org.jlleitschuh.gradle.ktlint") version "14.0.1"
}
group = "fr.azur"
version = "1.0-SNAPSHOT"
val version: String by project
val minecraftVersion: String by project
val paperVersion: String by project
val skinRestorerVersion: String by project
val voiceChatVersion: String by project
val voiceChatApiVersion: String by project
val luckPermsVersion: String by project
repositories {
mavenCentral()
@@ -20,18 +28,19 @@ repositories {
maven("https://repo.codemc.org/repository/maven-public/") {
name = "codemc"
}
maven("https://repo.plasmoverse.com/releases")
maven("https://repo.plasmoverse.com/snapshots")
maven("https://maven.maxhenkel.de/repository/public") {
name = "svc"
}
}
dependencies {
paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
paperweight.paperDevBundle("$minecraftVersion-$paperVersion")
compileOnly("net.skinsrestorer:skinsrestorer-api:15.6.3")
compileOnly("su.plo.voice.api:server:2.1.4")
compileOnly("net.skinsrestorer:skinsrestorer-api:$skinRestorerVersion")
compileOnly("de.maxhenkel.voicechat:voicechat-api:$voiceChatApiVersion")
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
}
val targetJavaVersion = 21
@@ -42,10 +51,11 @@ kotlin {
tasks {
runServer {
downloadPlugins {
modrinth("plasmo-voice", "spigot-2.1.4")
modrinth("skinsrestorer", "15.6.3")
modrinth("skinsrestorer", skinRestorerVersion)
modrinth("luckperms", "v$luckPermsVersion-bukkit")
modrinth("simple-voice-chat", "bukkit-$voiceChatVersion")
}
minecraftVersion("1.21.4")
minecraftVersion(minecraftVersion)
}
build {
dependsOn("shadowJar")
@@ -62,8 +72,5 @@ tasks {
archiveBaseName.set("tcoww")
minimize()
// exclude("META-INF/*.kotlin_module")
// exclude("META-INF/*.version")
}
}
}

View File

@@ -0,0 +1,7 @@
version=1.2.0
paperVersion=R0.1-SNAPSHOT
minecraftVersion=1.21.10
skinRestorerVersion=15.9.0
voiceChatApiVersion=2.6.0
voiceChatVersion=2.6.6
luckPermsVersion=5.5.17

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -1 +1,7 @@
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

248
gradlew vendored Executable file
View File

@@ -0,0 +1,248 @@
#!/bin/sh
#
# Copyright © 2015 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.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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
# 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" )
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, 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" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# 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" "$@"

93
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,93 @@
@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
@rem SPDX-License-Identifier: Apache-2.0
@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
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
: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

View File

@@ -1,51 +1,75 @@
package fr.azur.tcoww
import fr.azur.tcoww.events.GameEvent
import de.maxhenkel.voicechat.api.BukkitVoicechatService
import fr.azur.tcoww.events.GameEvents
import fr.azur.tcoww.events.PhaseEvents
import fr.azur.tcoww.events.PowerEvents
import fr.azur.tcoww.events.ToolsEvents
import fr.azur.tcoww.plasmovoice.addon.PlasmoAddon
import fr.azur.tcoww.roles.*
import fr.azur.tcoww.utils.Skin
import net.skinsrestorer.api.SkinsRestorer
import net.skinsrestorer.api.SkinsRestorerProvider
import fr.azur.tcoww.events.UIEvents
import fr.azur.tcoww.roles.Child
import fr.azur.tcoww.roles.FortuneTeller
import fr.azur.tcoww.roles.Hunter
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.roles.Villager
import fr.azur.tcoww.roles.Werewolf
import fr.azur.tcoww.roles.Witch
import fr.azur.tcoww.utils.VoiceChatPlugin
import fr.azur.tcoww.utils.skins.Manager
import fr.azur.tcoww.utils.skins.WerewolfSkin
import org.bukkit.configuration.serialization.ConfigurationSerialization
import org.bukkit.plugin.java.JavaPlugin
import su.plo.voice.api.server.PlasmoVoiceServer
import org.bukkit.scoreboard.Team
class Tcoww : JavaPlugin() {
private lateinit var skinsRestorer: SkinsRestorer
private lateinit var skinManager: Skin
val plasmoAddon = PlasmoAddon()
init {
instance = this
}
override fun onEnable() {
saveResource("config.yml", false)
ConfigurationSerialization.registerClass(WerewolfSkin::class.java)
saveDefaultConfig()
PlasmoVoiceServer.getAddonsLoader().load(plasmoAddon)
Manager.handle()
skinsRestorer = SkinsRestorerProvider.get()
skinManager = Skin(this, skinsRestorer)
server.pluginManager.apply {
registerEvents(UIEvents, this@Tcoww)
registerEvents(GameEvents, this@Tcoww)
registerEvents(PowerEvents, this@Tcoww)
registerEvents(ToolsEvents, this@Tcoww)
registerEvents(PhaseEvents, this@Tcoww)
}
server.pluginManager.registerEvents(ToolsEvents(this), this)
server.pluginManager.registerEvents(GameEvent(this, skinManager), this)
val service = server.servicesManager.load(BukkitVoicechatService::class.java)
service?.registerPlugin(VoiceChatPlugin)
registerRoles()
val mainScoreboard = server.scoreboardManager.mainScoreboard
if (mainScoreboard.getTeam("NoCollision") == null) {
val team = server.scoreboardManager.mainScoreboard.registerNewTeam("NoCollision")
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER)
}
}
override fun onDisable() {
}
fun reload(withPlasmoAddon: Boolean) {
fun reload() {
this.reloadConfig()
skinManager.reloadSkin()
if (withPlasmoAddon) plasmoAddon.onConfigLoaded()
Manager.reloadSkin()
}
private fun registerRoles() {
Role.registerRole(Villager())
Role.registerRole(Werewolf())
Role.registerRole(Child())
Role.registerRole(FortuneTeller())
Role.registerRole(Hunter())
Role.registerRole(Witch())
Role.registerRole(Villager)
Role.registerRole(Werewolf)
Role.registerRole(Child)
Role.registerRole(FortuneTeller)
Role.registerRole(Hunter)
Role.registerRole(Witch)
}
companion object {
lateinit var instance: Tcoww
}
}

View File

@@ -1,24 +1,26 @@
package fr.azur.tcoww
import fr.azur.tcoww.commands.Power
import fr.azur.tcoww.commands.Exclude
import fr.azur.tcoww.commands.GameCommand
import fr.azur.tcoww.commands.ReloadCommands
import fr.azur.tcoww.commands.TimeGest
import fr.azur.tcoww.commands.Tools
import fr.azur.tcoww.commands.Vote
import io.papermc.paper.plugin.bootstrap.BootstrapContext
import io.papermc.paper.plugin.bootstrap.PluginBootstrap
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents
class TcowwBootstrap : PluginBootstrap {
object TcowwBootstrap : PluginBootstrap {
override fun bootstrap(context: BootstrapContext) {
context.lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS) { commands ->
commands.registrar().register(Tools().root.build())
commands.registrar().register(TimeGest().root.build())
commands.registrar().register(Power().root.build())
commands.registrar().register(GameCommand().root.build())
commands.registrar().register(Vote().root.build())
commands.registrar().register(ReloadCommands().root.build())
context.lifecycleManager.apply {
registerEventHandler(LifecycleEvents.COMMANDS) { commands ->
commands.registrar().apply {
register(Tools.root)
register(GameCommand.root)
commands.registrar().register(Vote.root)
register(ReloadCommands.root)
commands.registrar().register(Exclude.root)
}
}
}
}
}
}

View File

@@ -0,0 +1,34 @@
package fr.azur.tcoww.commands
import com.mojang.brigadier.Command
import com.mojang.brigadier.tree.LiteralCommandNode
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.utils.Players.kill
import io.papermc.paper.command.brigadier.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands
import io.papermc.paper.command.brigadier.argument.ArgumentTypes
import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver
import org.bukkit.Bukkit
object Exclude {
val root: LiteralCommandNode<CommandSourceStack> =
Commands
.literal("exclude")
.requires { sender ->
sender.sender.isOp
}.then(
Commands.argument("players", ArgumentTypes.playerProfiles()).executes { ctx ->
val targetResolver = ctx.getArgument("players", PlayerProfileListResolver::class.java)
val target = targetResolver.resolve(ctx.source).first()
val uuid = target.id
if (uuid != null) {
val player = Bukkit.getPlayer(uuid)
if (player != null) {
val game = Game.current ?: return@executes Command.SINGLE_SUCCESS
player.kill()
}
}
Command.SINGLE_SUCCESS
},
).build()
}

View File

@@ -1,67 +1,90 @@
package fr.azur.tcoww.commands
import com.mojang.brigadier.Command
import com.mojang.brigadier.arguments.StringArgumentType
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.tree.LiteralCommandNode
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.roles.Villager
import fr.azur.tcoww.roles.Werewolf
import fr.azur.tcoww.ui.GameConfig
import io.papermc.paper.command.brigadier.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands
import io.papermc.paper.command.brigadier.argument.ArgumentTypes
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver
import io.papermc.paper.dialog.Dialog
import io.papermc.paper.registry.data.dialog.ActionButton
import io.papermc.paper.registry.data.dialog.DialogBase
import io.papermc.paper.registry.data.dialog.action.DialogAction
import io.papermc.paper.registry.data.dialog.type.DialogType
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.event.ClickEvent
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Bukkit
import org.bukkit.GameMode
import org.bukkit.NamespacedKey
import org.bukkit.World
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
class GameCommand {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("game")
.requires { sender ->
sender.sender.isOp
}.then(
Commands.literal("start")
.then(
Commands.argument("players", ArgumentTypes.players()).then(
Commands.argument("roles", StringArgumentType.greedyString()).suggests { ctx, builder ->
val roleswithoutauto =
Role.registerRoles.filter { role -> role.value !is Villager && role.value !is Werewolf }
val rolessplit = builder.remaining.split(" ")
val startstr = rolessplit.dropLast(1).joinToString(separator = " ")
roleswithoutauto.forEach { role ->
if (role.key.toString().startsWith(rolessplit.last())) {
builder.suggest("$startstr${if (startstr.isEmpty()) "" else " "}${role.key}")
}
}
if (builder.remaining != "" && !builder.remaining.endsWith(" ")) {
roleswithoutauto.forEach { role ->
builder.suggest("${builder.remaining} ${role.key}")
}
}
builder.buildFuture()
}.executes { ctx ->
val plugin = Bukkit.getPluginManager().getPlugin("tcoww")!!
val targetResolver = ctx.getArgument("players", PlayerSelectorArgumentResolver::class.java)
val targets = targetResolver.resolve(ctx.source)
val rolesstr = StringArgumentType.getString(ctx, "roles")
val rolessplit = rolesstr.split(" ")
val roles = rolessplit.map { str ->
Role.registerRoles[NamespacedKey.fromString(str)]
}
val correctrole = roles.filterNotNull()
Game(plugin, ctx.source.location.world, targets, correctrole)
Command.SINGLE_SUCCESS
}
)
object GameCommand {
private fun createDialog(
plugin: Plugin,
world: World,
players: Iterable<Player>,
roles: Map<NamespacedKey, Int>,
): Dialog =
Dialog.create { builder ->
builder
.empty()
.base(
DialogBase
.builder(Component.text("Start the Game", NamedTextColor.GREEN))
.canCloseWithEscape(true)
.build(),
).type(
DialogType.confirmation(
ActionButton
.builder(Component.text("Start", NamedTextColor.GREEN))
.action(
DialogAction.staticAction(
ClickEvent.callback {
Game(plugin, world, players, roles)
it.closeDialog()
},
),
).build(),
ActionButton
.builder(Component.text("Cancel", NamedTextColor.RED))
.action(DialogAction.staticAction(ClickEvent.callback { it.closeDialog() }))
.build(),
),
)
).then(
Commands.literal("stop").executes { ctx ->
Game.current?.stopGame(true)
Command.SINGLE_SUCCESS
}
)
}
}
val root: LiteralCommandNode<CommandSourceStack> =
Commands
.literal("game")
.requires { sender ->
sender.sender.isOp
}.then(
Commands
.literal("start")
.executes { ctx ->
val executor = ctx.source.executor ?: ctx.source.sender
if (executor is Player) {
val players =
Bukkit
.getOnlinePlayers()
.filter { it.gameMode == GameMode.ADVENTURE || it.gameMode == GameMode.SURVIVAL }
val menu = GameConfig(players.size)
executor.openInventory(menu.inventory)
menu.future.thenAccept {
val dialog = createDialog(Tcoww.instance, executor.world, players, it)
executor.showDialog(dialog)
}
}
Command.SINGLE_SUCCESS
},
).then(
Commands.literal("stop").executes { ctx ->
Game.current?.stopGame(true)
Command.SINGLE_SUCCESS
},
).build()
}

View File

@@ -1,156 +0,0 @@
package fr.azur.tcoww.commands
import com.mojang.brigadier.Command
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.roles.FortuneTeller
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.roles.Witch
import fr.azur.tcoww.utils.Stockage
import io.papermc.paper.command.brigadier.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands
import io.papermc.paper.command.brigadier.argument.ArgumentTypes
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.event.HoverEvent
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Bukkit
import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
import org.bukkit.persistence.PersistentDataType
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
class Power {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("power")
.requires { ctx -> ctx.sender as? Player != null && Role.getRole(ctx.sender as Player)?.hasPowerCommand == true }
.then(
Commands.literal("kill")
.requires { ctx ->
val player = ctx.executor as? Player ?: return@requires false
val role = Role.getRole(player)
(role is Witch && player.hasPowerItem(1))
}
.then(
Commands.argument("target", ArgumentTypes.player())
.suggests { _, builder ->
Game.current?.playersMutable?.forEach { builder.suggest(it.name) }
builder.buildFuture()
}
.executes { ctx ->
val target = ctx.getTargetPlayer("target") ?: return@executes 0
val player = ctx.source.getPlayer() ?: return@executes 0
if (Game.current?.playersMutable?.contains(target) == true) {
target.addPotionEffect(PotionEffect(PotionEffectType.WITHER, 600, 4))
player.removePowerItem(1)
}
Command.SINGLE_SUCCESS
}
)
)
.then(
Commands.literal("save")
.requires { ctx ->
val player = ctx.executor as? Player ?: return@requires false
val role = Role.getRole(player)
role is Witch && player.hasPowerItem(2)
}
.then(
Commands.argument("target", ArgumentTypes.player())
.executes { ctx ->
val target = ctx.getTargetPlayer("target") ?: return@executes 0
val player = ctx.source.getPlayer() ?: return@executes 0
if (Game.current?.playersMutable?.contains(target) == true &&
target.persistentDataContainer.get(
NamespacedKey("tcoww", "dead"),
PersistentDataType.BOOLEAN
) == true
) {
target.persistentDataContainer.set(
NamespacedKey("tcoww", "dead"),
PersistentDataType.BOOLEAN,
false
)
Stockage.backLocation[target.uniqueId]?.let { target.teleport(it) }
target.isInvulnerable = false
target.sendMessage(Component.text("Vous avez été résucité.").color(NamedTextColor.AQUA))
player.sendMessage(
Component.text("Vous avez résuciter ").color(NamedTextColor.AQUA)
.append(player.displayName())
)
player.removePowerItem(2)
}
Command.SINGLE_SUCCESS
}
)
)
.then(
Commands.literal("view")
.requires { ctx ->
val player = ctx.executor as? Player ?: return@requires false
val role = Role.getRole(player)
role is FortuneTeller && player.hasPowerItem(4)
}
.then(
Commands.argument("target", ArgumentTypes.player())
.suggests { _, builder ->
Game.current?.playersMutable?.forEach { builder.suggest(it.name) }
builder.buildFuture()
}
.executes { ctx ->
val target = ctx.getTargetPlayer("target") ?: return@executes 0
val player = ctx.source.getPlayer() ?: return@executes 0
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") ?: return@executes 0
if (Game.current?.playersMutable?.contains(target) == true) {
val item = player.getPowerItem(4) ?: return@executes 0
val timeGestion = Game.current!!.timeGestion
val cooldown =
((timeGestion.dayDuration + timeGestion.voteDuration + timeGestion.nightDuration + timeGestion.crepuscularDuration - 30) * 2) * 20
val cloneditem = item.clone()
player.inventory.remove(item)
Bukkit.getScheduler().runTaskLater(plugin, Runnable {
player.inventory.addItem(cloneditem)
}, cooldown.toLong())
val targetrole = Role.getRole(target)
player.sendMessage(
target.displayName().append(Component.text(" est "))
.append(
(targetrole?.displayName ?: Component.text("Inconnu").hoverEvent(
HoverEvent.showText(
targetrole?.description ?: Component.empty()
)
))
)
)
}
Command.SINGLE_SUCCESS
}
)
)
private fun CommandSourceStack.getPlayer(): Player? = (this.executor ?: this.sender) as? Player
private fun Player.hasPowerItem(id: Int): Boolean = this.inventory.any {
it?.persistentDataContainer?.get(NamespacedKey("tcoww", "power"), PersistentDataType.INTEGER) == id
}
private fun Player.getPowerItem(id: Int) = this.inventory.firstOrNull {
it?.persistentDataContainer?.get(NamespacedKey("tcoww", "power"), PersistentDataType.INTEGER) == id
}
private fun Player.removePowerItem(id: Int) {
val item = getPowerItem(id) ?: return
this.inventory.remove(item)
}
private fun com.mojang.brigadier.context.CommandContext<CommandSourceStack>.getTargetPlayer(name: String): Player? {
val resolver = this.getArgument(name, PlayerSelectorArgumentResolver::class.java)
return resolver.resolve(this.source).firstOrNull()
}
}

View File

@@ -1,18 +1,21 @@
package fr.azur.tcoww.commands
import com.mojang.brigadier.Command
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.tree.LiteralCommandNode
import io.papermc.paper.command.brigadier.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.entity.Player
class ReloadCommands {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("reloadcommands").executes { ctx ->
val sender = ctx.source.sender as? Player
sender?.updateCommands()
sender?.sendMessage(Component.text("Commands rechargés !").color(NamedTextColor.GOLD))
Command.SINGLE_SUCCESS
}
}
object ReloadCommands {
val root: LiteralCommandNode<CommandSourceStack> =
Commands
.literal("reload-commands")
.executes { ctx ->
val sender = ctx.source.sender as? Player
sender?.updateCommands()
sender?.sendMessage(Component.text("Commands rechargés !", NamedTextColor.GOLD))
Command.SINGLE_SUCCESS
}.build()
}

View File

@@ -1,44 +0,0 @@
package fr.azur.tcoww.commands
import com.mojang.brigadier.Command
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import fr.azur.tcoww.game.TimeGestion
import io.papermc.paper.command.brigadier.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands
import org.bukkit.Bukkit
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitTask
class TimeGest {
var timeGestion: TimeGestion? = null
var sheduler: BukkitTask? = null
var plugin: Plugin? = null
private fun kill() {
sheduler?.cancel()
sheduler = null
timeGestion = null
}
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("timegest")
.requires { sender ->
sender.sender.isOp
}.then(
Commands.literal("start").executes { ctx ->
if (plugin == null) plugin = Bukkit.getPluginManager().getPlugin("tcoww")!!
val executor = ctx.source.executor
if (executor == null) throw Error("No executor !")
if (timeGestion != null || sheduler != null) kill()
timeGestion = TimeGestion(plugin!!, executor.world)
sheduler = Bukkit.getScheduler().runTaskTimer(plugin!!, Runnable {
timeGestion!!.tick()
}, 0L, 1L)
Command.SINGLE_SUCCESS
}
).then(
Commands.literal("kill").executes {
kill()
Command.SINGLE_SUCCESS
}
)
}

View File

@@ -3,7 +3,7 @@ package fr.azur.tcoww.commands
import com.mojang.brigadier.Command
import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.arguments.StringArgumentType
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.tree.LiteralCommandNode
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.items.CustomItems
import io.papermc.paper.command.brigadier.CommandSourceStack
@@ -16,131 +16,139 @@ import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.entity.Player
class Tools {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("tools")
.requires { sender ->
sender.sender.isOp
}.then(
Commands.literal("location")
.then(
Commands.literal("bed").executes { ctx ->
val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.BedLocTool.item)
Command.SINGLE_SUCCESS
}.then(
Commands.literal("get").executes { ctx ->
ctx.source.sender.sendMessage(sendBed())
Command.SINGLE_SUCCESS
}
).then(
Commands.literal("remove").then(
Commands.argument("index", IntegerArgumentType.integer(0))
.executes { ctx ->
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") ?: return@executes 0
val index = IntegerArgumentType.getInteger(ctx, "index")
val list = plugin.config.getMapList("bedLocation")
plugin.config.set("bedLocation", list.minus(list[index]))
plugin.saveConfig()
object Tools {
val root: LiteralCommandNode<CommandSourceStack> =
Commands
.literal("tools")
.requires { sender ->
sender.sender.isOp
}.then(
Commands
.literal("location")
.then(
Commands
.literal("bed")
.executes { ctx ->
val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.BedLocTool.item)
Command.SINGLE_SUCCESS
}.then(
Commands.literal("get").executes { ctx ->
ctx.source.sender.sendMessage(sendBed())
Command.SINGLE_SUCCESS
}
))
).then(
Commands.literal("spawn").executes { ctx ->
val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.SpawnLocTool.item)
Command.SINGLE_SUCCESS
}
).then(
Commands.literal("lobby").executes { ctx ->
val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.LobbyLocTool.item)
Command.SINGLE_SUCCESS
}
)
).then(
Commands.literal("reload").executes { ctx ->
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") as Tcoww
plugin.reload(true)
Command.SINGLE_SUCCESS
}
).then(
Commands.literal("customitem").then(
Commands.argument("item", StringArgumentType.greedyString())
.suggests { ctx, builder ->
CustomItems.entries.forEach {
builder.suggest(it.name)
}
builder.buildFuture()
}
.executes { ctx ->
val itemName = StringArgumentType.getString(ctx, "item")
val item = CustomItems.valueOf(itemName).item
val exe = ctx.source.executor
if (exe is Player) {
exe.inventory.addItem(item)
}
Command.SINGLE_SUCCESS
}
)
)
},
).then(
Commands.literal("remove").then(
Commands
.argument("index", IntegerArgumentType.integer(0))
.executes { ctx ->
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") ?: return@executes 0
val index = IntegerArgumentType.getInteger(ctx, "index")
val list =
plugin.config
.getList("bed-locations")
.orEmpty()
.mapNotNull { it as? Location }
plugin.config.set("bed-locations", list.drop(index))
plugin.saveConfig()
ctx.source.sender.sendMessage(sendBed())
Command.SINGLE_SUCCESS
},
),
),
).then(
Commands.literal("spawn").executes { ctx ->
val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.SpawnLocTool.item)
Command.SINGLE_SUCCESS
},
).then(
Commands.literal("lobby").executes { ctx ->
val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.LobbyLocTool.item)
Command.SINGLE_SUCCESS
},
),
).then(
Commands.literal("reload").executes {
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") as Tcoww
plugin.reload()
Command.SINGLE_SUCCESS
},
).then(
Commands.literal("customitem").then(
Commands
.argument("item", StringArgumentType.greedyString())
.suggests { _, builder ->
CustomItems.entries.forEach {
builder.suggest(it.name)
}
builder.buildFuture()
}.executes { ctx ->
val itemName = StringArgumentType.getString(ctx, "item")
val item = CustomItems.valueOf(itemName).item
val exe = ctx.source.executor
if (exe is Player) {
exe.inventory.addItem(item)
}
Command.SINGLE_SUCCESS
},
),
).build()
fun sendBed(): Component {
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") ?: return Component.empty()
val bedlist = plugin.config.getMapList("bedLocation")
val bedLocationComponent = bedlist.map {
if (it as? Map<String, Object> != null) {
val loc = Location.deserialize(it as Map<String, Object>)
Component.text("[${loc.x}, ${loc.y}, ${loc.z}] ")
.color(NamedTextColor.LIGHT_PURPLE)
.append(
Component.text("teleport")
.clickEvent(
ClickEvent.runCommand("/minecraft:tp ${loc.x.toInt()} ${loc.y.toInt()} ${loc.z.toInt()}")
)
.color(NamedTextColor.LIGHT_PURPLE)
.decorate(TextDecoration.UNDERLINED)
)
.appendSpace()
.append(
Component.text("remove")
.clickEvent(
ClickEvent.runCommand(
"/tcoww:tools location bed remove ${
bedlist.indexOf(
it
)
}"
)
)
.color(NamedTextColor.LIGHT_PURPLE)
.decorate(TextDecoration.UNDERLINED)
)
.appendNewline()
} else Component.text("[unkown location] ")
.color(NamedTextColor.LIGHT_PURPLE)
.append(
Component.text("remove")
.clickEvent(
ClickEvent.runCommand(
"/tcoww:tools location bed remove ${
bedlist.indexOf(
it
)
}"
)
)
val bedList =
plugin.config
.getList("bed-locations")
.orEmpty()
val bedLocationComponent =
bedList.mapIndexed { index, element ->
if (element is Location) {
Component
.text("[${element.x}, ${element.y}, ${element.z}] ")
.color(NamedTextColor.LIGHT_PURPLE)
.decorate(TextDecoration.UNDERLINED)
)
.appendNewline()
}
return Component.text("Current bed locations :")
.append(
Component
.text("teleport")
.clickEvent(
ClickEvent.runCommand("/minecraft:tp ${element.x} ${element.y} ${element.z}"),
).color(NamedTextColor.LIGHT_PURPLE)
.decorate(TextDecoration.UNDERLINED),
).appendSpace()
.append(
Component
.text("remove")
.clickEvent(
ClickEvent.runCommand(
"/tcoww:tools location bed remove $index",
),
).color(NamedTextColor.LIGHT_PURPLE)
.decorate(TextDecoration.UNDERLINED),
).appendNewline()
} else {
Component
.text("[unkown location] ")
.color(NamedTextColor.LIGHT_PURPLE)
.append(
Component
.text("remove")
.clickEvent(
ClickEvent.runCommand(
"/tcoww:tools location bed remove $index",
),
).color(NamedTextColor.LIGHT_PURPLE)
.decorate(TextDecoration.UNDERLINED),
).appendNewline()
}
}
return Component
.text("Current bed locations :")
.color(NamedTextColor.DARK_PURPLE)
.appendNewline()
.append(bedLocationComponent)
}
}
}

View File

@@ -1,32 +1,53 @@
package fr.azur.tcoww.commands
import com.mojang.brigadier.Command
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.tree.LiteralCommandNode
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.game.Phase
import fr.azur.tcoww.utils.Stockage
import fr.azur.tcoww.game.NightLightCycle
import fr.azur.tcoww.ui.PlayerSelectMenu
import io.papermc.paper.command.brigadier.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands
import io.papermc.paper.command.brigadier.argument.ArgumentTypes
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver
import org.bukkit.entity.Player
import java.util.UUID
class Vote {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("vote").then(
Commands.argument("target", ArgumentTypes.player())
.requires { ctx ->
Game.current?.timeGestion?.phase == Phase.VOTE && Game.current!!.playersMutable.contains(ctx.sender)
}.suggests { ctx, builder ->
Game.current?.playersMutable?.forEach { player -> builder.suggest(player.name) }
builder.buildFuture()
}.executes { ctx ->
val targetResolver = ctx.getArgument("target", PlayerSelectorArgumentResolver::class.java)
val target = targetResolver.resolve(ctx.source).first()
object Vote {
var vote = mutableMapOf<UUID, UUID>()
val root: LiteralCommandNode<CommandSourceStack> =
Commands
.literal("vote")
.then(
Commands
.argument("target", ArgumentTypes.player())
.requires { ctx ->
Game.current?.timeGestion?.phase == NightLightCycle.Phase.VOTE &&
Game.current!!.remainingPlayer.contains(ctx.sender)
}.suggests { _, builder ->
Game.current?.remainingPlayer?.forEach { player -> builder.suggest(player.name) }
builder.buildFuture()
}.executes { ctx ->
val targetResolver = ctx.getArgument("target", PlayerSelectorArgumentResolver::class.java)
val target = targetResolver.resolve(ctx.source).first()
val player = (ctx.source.executor ?: ctx.source.sender as Player) as Player
if (Game.current?.remainingPlayer?.contains(target) ?: false) {
vote[player.uniqueId] = target.uniqueId
}
Command.SINGLE_SUCCESS
},
).executes { ctx ->
val current = Game.current ?: return@executes Command.SINGLE_SUCCESS
val player = (ctx.source.executor ?: ctx.source.sender as Player) as Player
if (Game.current?.playersMutable?.contains(target) ?: false) {
Stockage.vote[player.uniqueId] = target.uniqueId
val menu = PlayerSelectMenu(current.remainingPlayer)
player.openInventory(menu.inventory)
menu.future.whenComplete { selectedPlayer, _ ->
val target = selectedPlayer ?: return@whenComplete
vote[player.uniqueId] = target.uniqueId
}
Command.SINGLE_SUCCESS
}
)
}
}.build()
}

View File

@@ -1,432 +0,0 @@
package fr.azur.tcoww.events
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.game.Phase
import fr.azur.tcoww.items.CustomItems
import fr.azur.tcoww.roles.Child
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.roles.Witch
import fr.azur.tcoww.utils.BedGestion
import fr.azur.tcoww.utils.Skin
import fr.azur.tcoww.utils.Stockage
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.event.ClickEvent
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import net.kyori.adventure.title.Title
import org.bukkit.*
import org.bukkit.attribute.Attribute
import org.bukkit.attribute.AttributeModifier
import org.bukkit.entity.EntityType
import org.bukkit.entity.ItemDisplay
import org.bukkit.entity.Player
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.PlayerDeathEvent
import org.bukkit.event.inventory.InventoryMoveItemEvent
import org.bukkit.event.player.*
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.SkullMeta
import org.bukkit.persistence.PersistentDataType
import org.bukkit.plugin.Plugin
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import java.time.Duration
class GameEvent(val plugin: Plugin, val skinUtils: Skin) : Listener {
@EventHandler
fun phaseChange(event: TimePhaseChangeEvent) {
Game.current?.playersMutable?.forEach { it.updateCommands() }
when (event.newPhase) {
Phase.DAY -> {
event.world.players.forEach { player ->
player.sendMessage(Component.text("Le jour se lève.").color(NamedTextColor.GOLD))
player.clearActivePotionEffects()
player.persistentDataContainer.set(
NamespacedKey("tcoww", "insomie"),
PersistentDataType.BOOLEAN,
false
)
if (player.role?.lg == true) {
player.inventory.forEach {
if (it != null && it.persistentDataContainer.get(
NamespacedKey("tcoww", "power"),
PersistentDataType.INTEGER
) == 3
) {
player.inventory.remove(it)
}
if (player.isTransformed()) {
player.tfHuman()
}
}
}
}
}
Phase.VOTE -> {
event.world.players.forEach { player ->
player.sendMessage(
Component.text("Le jour se couche, ce soir un vote est organisé.")
.color(NamedTextColor.DARK_AQUA)
.appendNewline()
.append(
Component.text("Faite /vote <pseudo> pour voter.")
.color(NamedTextColor.AQUA)
)
)
}
}
Phase.NIGHT -> {
if (Stockage.vote.isNotEmpty()) {
val voteMap = Stockage.vote.values.groupingBy { it }.eachCount()
val mostVoted = voteMap.maxByOrNull { it.value }?.key
mostVoted?.let { uuid ->
Bukkit.getPlayer(uuid)?.let { votedPlayer ->
votedPlayer.fireTicks = 1200
Bukkit.getScheduler().runTaskLater(plugin, Runnable {
votedPlayer.health = 0.0
}, 60)
Game.current?.playersMutable?.forEach {
it.sendMessage(
Component.text("${votedPlayer.name} a reçu le plus de vote.")
.color(NamedTextColor.RED)
)
}
}
}
Stockage.vote.clear()
}
Game.current?.wwcankill = true
event.world.players.forEach { player ->
player.sendMessage(
Component.text("La lune se léve et les loups sont de sortie. Vous devriez dormir.")
.color(NamedTextColor.DARK_PURPLE)
)
if (player.role?.lg ?: false) {
player.sendMessage(
Component.text("Vous pouvez vous transformer.").color(NamedTextColor.DARK_PURPLE)
)
player.inventory.addItem(CustomItems.WereWolfTransformItem.item)
}
}
Bukkit.getScheduler().runTaskLater(plugin, Runnable {
val openBed = plugin.config.getMapList("bedLocation").mapNotNull {
if (it as? Map<String, Object> != null) Location.deserialize(it as Map<String, Object>) else null
}.filter { !BedGestion.isOcupied(it.block) }.toMutableList()
Game.current?.playersMutable?.forEach { player ->
if (!player.isSleeping && !player.persistentDataContainer.getOrDefault(
NamespacedKey("tcoww", "dead"),
PersistentDataType.BOOLEAN,
false
) && !player.isTransformed()
) {
val loc = openBed.removeFirst()
player.sleep(loc, true)
val role = player.role
if (role is Child || role?.lg == true) {
player.sendMessage(
Component.text(
"Tout le monde est couché. Vous pouvez vous lever.",
NamedTextColor.AQUA
)
)
}
}
}
}, plugin.config.getLong("playerSleep"))
}
Phase.CREPUSCULAR -> {
val current = Game.current ?: return
val deadPlayers = current.playersMutable.filter {
it.persistentDataContainer.get(NamespacedKey("tcoww", "dead"), PersistentDataType.BOOLEAN) == true
}
deadPlayers.forEach { player ->
player.gameMode = GameMode.SPECTATOR
Stockage.backLocation[player.uniqueId]?.let { player.teleport(it) }
player.kill(current)
}
current.playersMutable.removeAll(deadPlayers)
if (current.gameEnded()) {
current.playersMutable.forEach {
if (it.isTransformed()) it.tfHuman()
}
current.stopGame()
}
}
}
}
@EventHandler
fun powerItemDrop(event: PlayerDropItemEvent) {
val item = event.itemDrop.itemStack
if (event.player.gameMode != GameMode.CREATIVE &&
item.itemMeta.persistentDataContainer.has(NamespacedKey("tcoww", "power"), PersistentDataType.INTEGER)
) {
event.isCancelled = true
}
}
@EventHandler
fun powerItemTransfer(event: InventoryMoveItemEvent) {
val item = event.item
if (event.source != event.destination &&
item.itemMeta.persistentDataContainer.has(NamespacedKey("tcoww", "power"), PersistentDataType.INTEGER)
) {
event.isCancelled = true
}
}
@EventHandler
fun sleepLeaveEvent(event: PlayerBedLeaveEvent) {
val role = event.player.role
if (Game.current?.timeGestion?.phase == Phase.NIGHT &&
role !is Child && role?.lg != true &&
!event.player.persistentDataContainer.getOrDefault(
NamespacedKey("tcoww", "insomie"),
PersistentDataType.BOOLEAN,
false
)
) {
event.isCancelled = true
}
}
@EventHandler
fun clickEvent(event: PlayerInteractEvent) {
val item = event.item ?: return
if (item.persistentDataContainer.get(NamespacedKey("tcoww", "power"), PersistentDataType.INTEGER) == 3) {
val player = event.player
player.inventory.remove(item)
if (player.isTransformed()) {
player.tfHuman()
} else {
player.tfWerewolf()
}
}
}
@EventHandler
fun onDeath(event: PlayerDeathEvent) {
val current = Game.current ?: return
val player = event.player
if (!current.playersMutable.contains(player)) return
event.isCancelled = true
if (current.timeGestion.phase == Phase.NIGHT) {
Stockage.backLocation[player.uniqueId] = player.location
Bukkit.getScheduler().runTaskLater(plugin, Runnable {
player.teleport(plugin.config.getLocation("lobbyLocation")!!)
player.clearActivePotionEffects()
player.fireTicks = 0
}, 1)
player.isInvulnerable = true
player.persistentDataContainer.set(NamespacedKey("tcoww", "dead"), PersistentDataType.BOOLEAN, true)
player.sendMessage(
Component.text("Vous étes mort... Mais vous pouvez être encore résucité.").color(
NamedTextColor.RED
)
)
current.playersMutable.forEach { p ->
if (p.role is Witch && p.inventory.any {
it != null && it.persistentDataContainer.get(
NamespacedKey("tcoww", "power"),
PersistentDataType.INTEGER
) == 2
}) {
p.sendMessage(
Component.text("Cette nuit, ${player.name} est mort dans d'atroces souffrances. Vous avez le pouvoir de le/la sauver.\n")
.color(NamedTextColor.DARK_PURPLE)
.append(
Component.text("Cliquez ici pour le/la ressusciter.")
.color(NamedTextColor.DARK_PURPLE)
.decorate(TextDecoration.UNDERLINED)
.clickEvent(ClickEvent.suggestCommand("/power save ${player.name}"))
)
)
}
}
} else {
player.kill(current)
}
}
@EventHandler
fun onHit(event: EntityDamageByEntityEvent) {
val current = Game.current ?: return
if (current.timeGestion.phase != Phase.NIGHT) return
val damager = event.damager
val target = event.entity
if (damager is Player && target is Player &&
current.playersMutable.contains(damager) &&
current.playersMutable.contains(target) &&
damager.role?.lg == true &&
damager.isTransformed() &&
target.role?.lg != true &&
current.wwcankill
) {
current.wwcankill = false
target.addPotionEffect(PotionEffect(PotionEffectType.WITHER, 1200, 10))
target.addPotionEffect(PotionEffect(PotionEffectType.POISON, 1200, 10))
target.persistentDataContainer.set(NamespacedKey("tcoww", "insomie"), PersistentDataType.BOOLEAN, true)
if (target.isSleeping) target.wakeup(false)
}
}
@EventHandler(
priority = EventPriority.MONITOR,
ignoreCancelled = true
)
fun startSleep(event: PlayerBedEnterEvent) {
event.player.addPotionEffect(
PotionEffect(
PotionEffectType.REGENERATION,
PotionEffect.INFINITE_DURATION,
1,
false,
false
)
)
event.player.addPotionEffect(
PotionEffect(
PotionEffectType.SATURATION,
PotionEffect.INFINITE_DURATION,
1,
false,
false
)
)
}
@EventHandler(
priority = EventPriority.MONITOR,
ignoreCancelled = true
)
fun endSleep(event: PlayerBedLeaveEvent) {
event.player.removePotionEffect(PotionEffectType.SATURATION)
event.player.removePotionEffect(PotionEffectType.REGENERATION)
}
@EventHandler
fun disconnect(event: PlayerQuitEvent) {
val current = Game.current ?: return
val player = event.player
if (!current.playersMutable.contains(player)) return
Bukkit.getScheduler().runTaskLater(plugin, Runnable {
if (!player.isConnected) {
player.kill(current)
}
}, 2_400)
}
fun Player.kill(current: Game) {
if (isTransformed()) tfHuman()
val item = ItemStack.of(Material.PLAYER_HEAD)
val skullmeta = item.itemMeta as SkullMeta
skullmeta.owningPlayer = player
item.itemMeta = skullmeta
val loc = this.location.add(0.0, 1.5, 0.0)
val entity = this.world.spawnEntity(loc, EntityType.ITEM_DISPLAY) as ItemDisplay
entity.setItemStack(item)
Bukkit.getScheduler().runTaskLater(
plugin,
Runnable {
entity.remove()
},
2400L
)
this.gameMode = GameMode.SPECTATOR
this.isInvulnerable = false
current.playersMutable.remove(player)
this.showTitle(
Title.title(
Component.text("Vous êtes mort.").color(NamedTextColor.DARK_RED),
Component.empty(),
Title.Times.times(Duration.ZERO, Duration.ofSeconds(2), Duration.ofMillis(500))
)
)
if (current.gameEnded()) current.stopGame()
}
fun Player.tfWerewolf() {
if (isTransformed()) return
skinUtils.applyWerewolfFur(this)
persistentDataContainer.set(
NamespacedKey("tcoww", "wwtranform"),
PersistentDataType.BOOLEAN,
true
)
val speedatr = getAttribute(Attribute.MOVEMENT_SPEED)!!
val scaleatr = getAttribute(Attribute.SCALE)!!
speedatr.addModifier(
AttributeModifier(
NamespacedKey("tcoww", "wwtranform"),
0.1,
AttributeModifier.Operation.ADD_NUMBER
)
)
scaleatr.addModifier(
AttributeModifier(
NamespacedKey("tcoww", "wwtranform"),
0.1,
AttributeModifier.Operation.ADD_NUMBER
)
)
}
fun Player.tfHuman() {
if (!isTransformed()) return
skinUtils.unApplySkin(this)
persistentDataContainer.set(
NamespacedKey("tcoww", "wwtranform"),
PersistentDataType.BOOLEAN,
false
)
val speedatr = getAttribute(Attribute.MOVEMENT_SPEED)!!
val scaleatr = getAttribute(Attribute.SCALE)!!
speedatr.removeModifier(
NamespacedKey("tcoww", "wwtranform")
)
scaleatr.removeModifier(
NamespacedKey("tcoww", "wwtranform")
)
}
fun Player.isTransformed(): Boolean {
return persistentDataContainer.getOrDefault(
NamespacedKey("tcoww", "wwtranform"),
PersistentDataType.BOOLEAN,
false
)
}
val Player.role
get() = Role.getRole(this)
}

View File

@@ -0,0 +1,258 @@
package fr.azur.tcoww.events
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.events.PowerEvents.backLocation
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.game.NightLightCycle
import fr.azur.tcoww.roles.Child
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.roles.Role.Companion.werewolfRole
import fr.azur.tcoww.roles.Werewolf
import fr.azur.tcoww.roles.Witch
import fr.azur.tcoww.utils.DataKeys.Insomnia
import fr.azur.tcoww.utils.DataKeys.PlayerDead
import fr.azur.tcoww.utils.DataKeys.PowerItems
import fr.azur.tcoww.utils.Players
import fr.azur.tcoww.utils.Players.isTransformed
import fr.azur.tcoww.utils.Players.kill
import fr.azur.tcoww.utils.VoiceChatPlugin
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.event.ClickEvent
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import org.bukkit.Bukkit
import org.bukkit.GameMode
import org.bukkit.NamespacedKey
import org.bukkit.entity.Mannequin
import org.bukkit.entity.Player
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.EntityRegainHealthEvent
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.player.PlayerBedEnterEvent
import org.bukkit.event.player.PlayerBedLeaveEvent
import org.bukkit.event.player.PlayerInteractEntityEvent
import org.bukkit.inventory.meta.Damageable
import org.bukkit.persistence.PersistentDataType
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
object GameEvents : Listener {
@EventHandler
fun onDeath(event: PlayerDeathEvent) {
val current = Game.current ?: return
val player = event.player
if (!current.remainingPlayer.contains(player)) return
event.isCancelled = true
if (current.timeGestion.phase == NightLightCycle.Phase.NIGHT) {
backLocation[player.uniqueId] = player.location
val lobby = Tcoww.instance.config.getLocation("lobby-location")
Bukkit.getScheduler().runTaskLater(
Tcoww.instance,
Runnable {
if (lobby != null) {
player.teleport(lobby)
} else {
player.gameMode = GameMode.SPECTATOR
}
player.clearActivePotionEffects()
player.fireTicks = 0
},
1,
)
player.isInvulnerable = true
player.persistentDataContainer.set(PlayerDead, PersistentDataType.BOOLEAN, true)
player.sendMessage(
Component.text(
"Vous étes mort... Mais vous pouvez être encore résucité.",
NamedTextColor.RED,
),
)
current.remainingPlayer.forEach { p ->
if (p.werewolfRole is Witch &&
p.inventory.any {
it != null &&
it.persistentDataContainer.get(
PowerItems,
PersistentDataType.INTEGER,
) == 2 &&
(it.itemMeta as Damageable).damage != 1
}
) {
p.sendMessage(
Component
.text(
"Cette nuit, ${player.name} est mort dans d'atroces souffrances. Vous avez le pouvoir de le/la sauver.\n",
NamedTextColor.DARK_PURPLE,
).append(
Component
.text("Cliquez ici pour le/la ressusciter.", NamedTextColor.DARK_PURPLE)
.decorate(TextDecoration.UNDERLINED)
.clickEvent(
ClickEvent.callback {
val item =
p.inventory.find {
it != null &&
it.persistentDataContainer.get(
PowerItems,
PersistentDataType.INTEGER,
) == 2 &&
(it.itemMeta as Damageable).damage != 1
} ?: return@callback
if (player.persistentDataContainer.get(
PlayerDead,
PersistentDataType.BOOLEAN,
) == false
) {
return@callback
}
player.inventory.remove(item)
player.persistentDataContainer.set(
NamespacedKey("tcoww", "dead"),
PersistentDataType.BOOLEAN,
false,
)
backLocation[player.uniqueId]?.let { player.teleport(it) }
player.isInvulnerable = false
player.sendMessage(
Component.text("Vous avez été résucité.", NamedTextColor.AQUA),
)
player.sendMessage(
Component
.text("Vous avez résuciter ")
.color(NamedTextColor.AQUA)
.append(player.displayName()),
)
},
),
),
)
}
}
} else {
player.kill()
}
}
@EventHandler
fun onHit(event: EntityDamageByEntityEvent) {
val current = Game.current ?: return
if (current.timeGestion.phase != NightLightCycle.Phase.NIGHT || Werewolf.remainingKill < 1) return
val damager = event.damager
val target = event.entity
if (damager is Player &&
target is Player &&
current.remainingPlayer.contains(damager) &&
current.remainingPlayer.contains(target) &&
damager.werewolfRole.team == Role.Team.LG &&
damager.isTransformed() &&
target.werewolfRole.team != Role.Team.LG
) {
target.health = 0.0
target.persistentDataContainer.set(Insomnia, PersistentDataType.BOOLEAN, true)
if (target.isSleeping) target.wakeup(false)
Werewolf.remainingKill--
}
}
@EventHandler
fun sleepLeaveEvent(event: PlayerBedLeaveEvent) {
val role = event.player.werewolfRole
if (Game.current?.timeGestion?.phase == NightLightCycle.Phase.NIGHT &&
role !is Child &&
role.team != Role.Team.LG &&
!event.player.persistentDataContainer.getOrDefault(
Insomnia,
PersistentDataType.BOOLEAN,
false,
)
) {
event.isCancelled = true
}
}
@EventHandler(
priority = EventPriority.MONITOR,
ignoreCancelled = true,
)
fun startSleep(event: PlayerBedEnterEvent) {
if (event.player.werewolfRole.team == Role.Team.LG) {
VoiceChatPlugin.addWerewolf(event.player)
} else {
VoiceChatPlugin.createSoloGroup(event.player)
}
event.player.addPotionEffect(
PotionEffect(
PotionEffectType.REGENERATION,
PotionEffect.INFINITE_DURATION,
1,
false,
false,
),
)
event.player.addPotionEffect(
PotionEffect(
PotionEffectType.BLINDNESS,
PotionEffect.INFINITE_DURATION,
1,
false,
false,
),
)
}
@EventHandler(
priority = EventPriority.MONITOR,
)
fun endSleep(event: PlayerBedLeaveEvent) {
event.player.removePotionEffect(PotionEffectType.REGENERATION)
event.player.removePotionEffect(PotionEffectType.BLINDNESS)
}
@EventHandler
fun clickOnCorpse(event: PlayerInteractEntityEvent) {
val corpse = event.rightClicked
if (corpse is Mannequin &&
corpse.persistentDataContainer.getOrDefault(Players.corpseKey, PersistentDataType.BOOLEAN, false)
) {
val deadPlayer = Bukkit.getPlayer(corpse.profile.uuid()!!)!!
val playerRole =
Role.registerRoles.getValue(
NamespacedKey.fromString(
corpse.persistentDataContainer.get(
Players.corpseRoleKey,
PersistentDataType.STRING,
)!!,
)!!,
)
event.player.sendMessage {
Component
.text("Ceci est le cadavre de ", NamedTextColor.DARK_RED)
.append(deadPlayer.displayName())
.append(Component.text(".", NamedTextColor.DARK_RED))
.appendNewline()
.append(Component.text("Il était ", NamedTextColor.DARK_RED))
.append(playerRole.displayName)
.append(Component.text(".", NamedTextColor.DARK_RED))
}
}
}
@EventHandler
fun disablePotionRegen(event: EntityRegainHealthEvent) {
val player = event.entity as? Player ?: return
if (player.hasPotionEffect(PotionEffectType.WITHER)) {
event.isCancelled = true
}
}
}

View File

@@ -0,0 +1,198 @@
package fr.azur.tcoww.events
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.commands.Vote
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.game.NightLightCycle
import fr.azur.tcoww.roles.Child
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.roles.Role.Companion.werewolfRole
import fr.azur.tcoww.roles.Werewolf
import fr.azur.tcoww.utils.BedGestion.isOccupied
import fr.azur.tcoww.utils.DataKeys.Insomnia
import fr.azur.tcoww.utils.DataKeys.PlayerDead
import fr.azur.tcoww.utils.DataKeys.PowerItems
import fr.azur.tcoww.utils.Players.corpseKey
import fr.azur.tcoww.utils.Players.isTransformed
import fr.azur.tcoww.utils.Players.kill
import fr.azur.tcoww.utils.Players.tfHuman
import fr.azur.tcoww.utils.VoiceChatPlugin
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Bukkit
import org.bukkit.GameMode
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.persistence.PersistentDataType
object PhaseEvents : Listener {
@EventHandler
fun phaseChange(event: NightLightCycle.NightLightCyclePhaseChangeEvent) {
val current = Game.current ?: return
current.remainingPlayer.forEach { it.updateCommands() }
when (event.newPhase) {
NightLightCycle.Phase.DAY -> {
event.world.players.forEach { player ->
VoiceChatPlugin.degroupPlayer(player)
player.sendMessage(Component.text("Le jour se lève.", NamedTextColor.GOLD))
player.clearActivePotionEffects()
player.persistentDataContainer.set(
Insomnia,
PersistentDataType.BOOLEAN,
false,
)
if (player.werewolfRole.team == Role.Team.LG) {
player.inventory.forEach {
if (it != null &&
it.persistentDataContainer.get(
PowerItems,
PersistentDataType.INTEGER,
) == 3
) {
player.setCooldown(it, ((event.duration.day + event.duration.vote) * 20))
}
if (player.isTransformed()) {
player.tfHuman()
}
}
}
}
}
NightLightCycle.Phase.VOTE -> {
event.world.entities
.filter {
it.persistentDataContainer.getOrDefault(
corpseKey,
PersistentDataType.BOOLEAN,
false,
)
}.forEach { it.remove() }
event.world.players.forEach { player ->
if (event.dayCount >= 1) {
player.sendMessage(
Component
.text("Le jour se couche, ce soir un vote est organisé.")
.color(NamedTextColor.DARK_AQUA)
.appendNewline()
.append(
Component
.text("Faite /vote pour voter.")
.color(NamedTextColor.AQUA),
),
)
} else {
player.sendMessage(
Component
.text("Le jour se couche, ce soir aucun vote n'est organisé. Faute de preuve.", NamedTextColor.DARK_AQUA),
)
}
}
}
NightLightCycle.Phase.NIGHT -> {
if (Vote.vote.isNotEmpty() && current.timeGestion.dayCount >= 1) {
val voteMap =
Vote.vote.values
.groupingBy { it }
.eachCount()
val mostVoted = voteMap.maxByOrNull { it.value }?.key
mostVoted?.let { uuid ->
Bukkit.getPlayer(uuid)?.let { votedPlayer ->
votedPlayer.fireTicks = 1200
Bukkit.getScheduler().runTaskLater(
Tcoww.instance,
Runnable {
votedPlayer.health = 0.0
},
60,
)
current.remainingPlayer.forEach {
it.sendMessage(
Component
.text("${votedPlayer.name} a reçu le plus de vote.")
.color(NamedTextColor.RED),
)
}
}
}
Vote.vote.clear()
}
Werewolf.remainingKill = 1
event.world.players.forEach { player ->
player.sendMessage(
Component
.text("La lune se léve et les loups sont de sortie. Vous devriez dormir.")
.color(NamedTextColor.DARK_PURPLE),
)
if (player.werewolfRole.team == Role.Team.LG) {
player.sendMessage(
Component.text("Vous pouvez vous transformer.", NamedTextColor.DARK_PURPLE),
)
}
}
Bukkit.getScheduler().runTaskLater(
Tcoww.instance,
Runnable {
val openBed =
current.beds
.filter { !it.block.isOccupied() }
.toMutableList()
current.remainingPlayer.forEach { player ->
if (!player.isSleeping &&
!player.persistentDataContainer.getOrDefault(
PlayerDead,
PersistentDataType.BOOLEAN,
false,
) &&
!player.isTransformed()
) {
val loc = openBed.removeFirst()
player.sleep(loc, true)
val role = player.werewolfRole
if (role is Child || role.team == Role.Team.LG) {
player.sendMessage(
Component.text(
"Tout le monde est couché. Vous pouvez vous lever.",
NamedTextColor.AQUA,
),
)
}
}
}
},
Tcoww.instance.config.getLong("player-sleep") * 20,
)
}
NightLightCycle.Phase.CREPUSCULAR -> {
val current = Game.current ?: return
val deadPlayers =
current.remainingPlayer.filter {
it.persistentDataContainer.get(PlayerDead, PersistentDataType.BOOLEAN) == true
}
deadPlayers.forEach { player ->
player.gameMode = GameMode.SPECTATOR
PowerEvents.backLocation[player.uniqueId]?.let { player.teleport(it) }
player.kill()
}
current.remainingPlayer.removeAll(deadPlayers)
// if (current.gameEnded()) {
// current.remainingPlayer.forEach {
// if (it.isTransformed()) it.tfHuman()
// }
// current.stopGame()
// }
}
}
}
}

View File

@@ -0,0 +1,154 @@
package fr.azur.tcoww.events
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.ui.PlayerSelectMenu
import fr.azur.tcoww.utils.DataKeys
import fr.azur.tcoww.utils.Players.isTransformed
import fr.azur.tcoww.utils.Players.tfHuman
import fr.azur.tcoww.utils.Players.tfWerewolf
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.event.HoverEvent
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.NamespacedKey
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryMoveItemEvent
import org.bukkit.event.player.PlayerDropItemEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.persistence.PersistentDataType
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import java.util.UUID
object PowerEvents : Listener {
var backLocation = mutableMapOf<UUID, Location>()
@EventHandler
fun powerItemDrop(event: PlayerDropItemEvent) {
val item = event.itemDrop.itemStack
if (event.player.gameMode != GameMode.CREATIVE &&
item.itemMeta.persistentDataContainer.has(DataKeys.PowerItems, PersistentDataType.INTEGER)
) {
event.isCancelled = true
}
}
@EventHandler
fun powerItemTransfer(event: InventoryMoveItemEvent) {
val item = event.item
if (event.source != event.destination &&
item.itemMeta.persistentDataContainer.has(DataKeys.PowerItems, PersistentDataType.INTEGER)
) {
event.isCancelled = true
}
}
@EventHandler
fun clickEvent(event: PlayerInteractEvent) {
val item = event.item ?: return
val player = event.player
if (!item.persistentDataContainer.has(NamespacedKey("tcoww", "power"))) return
event.isCancelled = true
if (player.hasCooldown(item)) return
when (item.persistentDataContainer.get(NamespacedKey("tcoww", "power"), PersistentDataType.INTEGER)) {
1 -> {
val current = Game.current ?: return
val menu = PlayerSelectMenu(current.remainingPlayer)
player.openInventory(menu.inventory)
menu.future.whenComplete { selectedPlayer, _ ->
val target = selectedPlayer ?: return@whenComplete
player.inventory.remove(item)
target.addPotionEffect(PotionEffect(PotionEffectType.WITHER, 600, 4))
}
}
2 -> {
val current = Game.current ?: return
val menu =
PlayerSelectMenu(
current.remainingPlayer.filter {
it.persistentDataContainer.get(
NamespacedKey("tcoww", "dead"),
PersistentDataType.BOOLEAN,
) == true
},
)
player.openInventory(menu.inventory)
menu.future.whenComplete { selectedPlayer, _ ->
val target = selectedPlayer ?: return@whenComplete
player.inventory.remove(item)
target.persistentDataContainer.set(
NamespacedKey("tcoww", "dead"),
PersistentDataType.BOOLEAN,
false,
)
backLocation[target.uniqueId]?.let { target.teleport(it) }
target.isInvulnerable = false
target.sendMessage(Component.text("Vous avez été résucité.", NamedTextColor.AQUA))
player.sendMessage(
Component
.text("Vous avez résuciter ")
.color(NamedTextColor.AQUA)
.append(player.displayName()),
)
}
}
3 -> {
if (player.isTransformed()) {
player.tfHuman()
} else {
player.tfWerewolf()
}
player.setCooldown(item, 200)
}
4 -> {
val current = Game.current ?: return
val timeGestion = current.timeGestion
val menu = PlayerSelectMenu(current.remainingPlayer)
player.openInventory(menu.inventory)
menu.future.whenComplete { selectedPlayer, _ ->
val target = selectedPlayer ?: return@whenComplete
val cooldown =
(
(
timeGestion.duration.day + timeGestion.duration.vote + timeGestion.duration.night +
timeGestion.duration.crepuscular -
30
) *
2
) *
20
player.setCooldown(item, cooldown)
val targetRole = Role.getRole(target)
player.sendMessage(
target
.displayName()
.append(Component.text(" est "))
.append(
targetRole?.displayName ?: Component.text("Inconnu").hoverEvent(
HoverEvent.showText(
targetRole?.lineDescription ?: Component.empty(),
),
),
),
)
}
}
}
}
}

View File

@@ -1,20 +0,0 @@
package fr.azur.tcoww.events
import fr.azur.tcoww.game.Phase
import org.bukkit.World
import org.bukkit.event.Event
import org.bukkit.event.HandlerList
class TimePhaseChangeEvent(
val world: World,
val newPhase: Phase
) : Event() {
override fun getHandlers(): HandlerList {
return handlerList
}
companion object {
@JvmStatic
val handlerList: HandlerList = HandlerList()
}
}

View File

@@ -1,47 +1,56 @@
package fr.azur.tcoww.events
import fr.azur.tcoww.utils.BedGestion
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.utils.BedGestion.getHeadBed
import fr.azur.tcoww.utils.BedGestion.isBed
import fr.azur.tcoww.utils.DataKeys
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.NamespacedKey
import org.bukkit.Location
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.persistence.PersistentDataType
import org.bukkit.plugin.java.JavaPlugin
class ToolsEvents(val plugin: JavaPlugin) : Listener {
object ToolsEvents : Listener {
@EventHandler
fun clickEvent(event: PlayerInteractEvent) {
val tool = event.player.inventory.itemInMainHand
if (!tool.persistentDataContainer.has(NamespacedKey("tcoww", "tools"))) return
if (!tool.persistentDataContainer.has(DataKeys.ToolsItems) || event.player.hasCooldown(tool)) return
when (tool.persistentDataContainer.get(NamespacedKey("tcoww", "tools"), PersistentDataType.INTEGER)) {
event.player.setCooldown(tool, 20)
when (tool.persistentDataContainer.get(DataKeys.ToolsItems, PersistentDataType.INTEGER)) {
1 -> {
event.isCancelled = true
val interactloc = event.interactionPoint
if (interactloc != null) {
if (BedGestion.isBed(interactloc.block)) {
val list = plugin.config.getMapList("bedLocation")
val interactLoc = event.interactionPoint
if (interactLoc != null) {
if (interactLoc.block.isBed()) {
val list =
Tcoww.instance.config
.getList("bed-locations")
.orEmpty()
.mapNotNull { it as? Location }
.toMutableList()
val block = BedGestion.getHeadBed(interactloc.block)
val block = interactLoc.block.getHeadBed()
if (!list.contains(block.location.serialize())) {
list.add(block.location.serialize())
plugin.config.set("bedLocation", list)
plugin.saveConfig()
if (!list.contains(block.location)) {
list.add(block.location)
Tcoww.instance.config.set("bed-locations", list)
Tcoww.instance.saveConfig()
event.player.sendMessage(
Component.text("Bed location add.").color(NamedTextColor.LIGHT_PURPLE)
Component.text("Bed location add.", NamedTextColor.LIGHT_PURPLE),
)
} else {
event.player.sendMessage(
Component.text("Bed location already exists.").color(NamedTextColor.DARK_RED)
Component.text("Bed location already exists.", NamedTextColor.DARK_RED),
)
}
} else {
event.player.sendMessage(
Component.text("It's not a bed...").color(NamedTextColor.DARK_RED)
Component.text("It's not a bed...", NamedTextColor.DARK_RED),
)
}
}
@@ -49,34 +58,29 @@ class ToolsEvents(val plugin: JavaPlugin) : Listener {
2 -> {
event.isCancelled = true
val interactloc = event.interactionPoint
if (interactloc != null) {
interactloc.x = interactloc.blockX.toDouble()
interactloc.y = interactloc.blockY.toDouble() + 1
interactloc.z = interactloc.blockZ.toDouble()
plugin.config.set("spawnLocation", interactloc)
plugin.saveConfig()
event.player.sendMessage(Component.text("Spawn location set.").color(NamedTextColor.LIGHT_PURPLE))
val interactLoc = event.interactionPoint
if (interactLoc != null) {
interactLoc.x = interactLoc.blockX.toDouble()
interactLoc.y = interactLoc.blockY.toDouble() + 1
interactLoc.z = interactLoc.blockZ.toDouble()
Tcoww.instance.config.set("spawn-location", interactLoc)
Tcoww.instance.saveConfig()
event.player.sendMessage(Component.text("Spawn location set.", NamedTextColor.LIGHT_PURPLE))
}
}
3 -> {
event.isCancelled = true
val interactloc = event.interactionPoint
if (interactloc != null) {
interactloc.x = interactloc.blockX.toDouble()
interactloc.y = interactloc.blockY.toDouble() + 1
interactloc.z = interactloc.blockZ.toDouble()
plugin.config.set("lobbyLocation", interactloc)
plugin.saveConfig()
event.player.sendMessage(Component.text("Lobby location set.").color(NamedTextColor.LIGHT_PURPLE))
val interactLoc = event.interactionPoint
if (interactLoc != null) {
interactLoc.x = interactLoc.blockX.toDouble()
interactLoc.y = interactLoc.blockY.toDouble() + 1
interactLoc.z = interactLoc.blockZ.toDouble()
Tcoww.instance.config.set("lobby-location", interactLoc)
Tcoww.instance.saveConfig()
event.player.sendMessage(Component.text("Lobby location set.", NamedTextColor.LIGHT_PURPLE))
}
}
}
}
// @EventHandler
// fun reloadAfterWorld(worldInitEvent: WorldInitEvent) {
//
// }
}
}

View File

@@ -0,0 +1,27 @@
package fr.azur.tcoww.events
import fr.azur.tcoww.ui.CustomUI
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
object UIEvents : Listener {
@EventHandler
fun onInventoryClick(event: InventoryClickEvent) {
val inventory = event.inventory
val holder = inventory.getHolder(false)
if (holder is CustomUI) {
holder.onClick(event)
}
}
@EventHandler
fun onInventoryClick(event: InventoryCloseEvent) {
val inventory = event.inventory
val holder = inventory.getHolder(false)
if (holder is CustomUI) {
holder.onClose(event)
}
}
}

View File

@@ -1,72 +1,72 @@
package fr.azur.tcoww.game
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.roles.Villager
import fr.azur.tcoww.roles.Werewolf
import fr.azur.tcoww.roles.Role.Companion.werewolfRole
import fr.azur.tcoww.utils.DataKeys.Insomnia
import fr.azur.tcoww.utils.DataKeys.PlayerDead
import fr.azur.tcoww.utils.Players.tfHuman
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Bukkit
import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.NamespacedKey
import org.bukkit.World
import org.bukkit.entity.Player
import org.bukkit.persistence.PersistentDataType
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitTask
import kotlin.math.ceil
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
class Game(var plugin: Plugin, val world: World, val players: Iterable<Player>, var roles: Iterable<Role>) {
val werewolfPercentage: Float = plugin.config.getInt("wwcount").toFloat()
val timeGestion: TimeGestion
val playersMutable = players.toMutableList()
val timeGestShedule: BukkitTask
var wwcankill = false
class Game(
var plugin: Plugin,
val world: World,
val players: Iterable<Player>,
var roles: Map<NamespacedKey, Int>,
) {
val timeGestion: NightLightCycle
val remainingPlayer = players.toMutableList()
val beds =
Tcoww.instance.config
.getList("bed-locations")
.orEmpty()
.mapNotNull { it as? Location }
init {
players.forEach { player ->
player.persistentDataContainer.set(NamespacedKey("tcoww", "dead"), PersistentDataType.BOOLEAN, false)
player.persistentDataContainer.set(NamespacedKey("tcoww", "wwtranform"), PersistentDataType.BOOLEAN, false)
player.teleport(plugin.config.getLocation("spawnLocation")!!)
player.tfHuman()
val spawnLoc = plugin.config.getLocation("spawn-location")
if (spawnLoc != null) player.teleport(spawnLoc)
player.inventory.clear()
player.gameMode = GameMode.ADVENTURE
player.health = 20.0
player.addPotionEffect(
PotionEffect(
PotionEffectType.SATURATION,
PotionEffect.INFINITE_DURATION,
1,
false,
false,
),
)
}
assignRoles()
timeGestion = TimeGestion(plugin, world)
timeGestShedule = Bukkit.getScheduler().runTaskTimer(plugin, Runnable {
timeGestion.tick()
}, 0L, 1L)
timeGestion = NightLightCycle(plugin, world)
current = this
}
fun getWerewolfInt(): Int {
val playerCount = players.count()
return ceil(playerCount * (werewolfPercentage / 100f)).toInt()
}
private fun assignRoles() {
val playerList = players.shuffled().toMutableList()
val werewolfCount = getWerewolfInt()
val specialRolesCount = roles.count()
if (werewolfCount + specialRolesCount > playerList.size) {
throw IllegalArgumentException("Not enough players for the given number of werewolves and special roles.")
}
repeat(werewolfCount) { i ->
Role.setRole(playerList[i], Werewolf())
}
roles.forEachIndexed { idx, role ->
Role.setRole(playerList[werewolfCount + idx], role)
}
for (i in werewolfCount + specialRolesCount until playerList.size) {
Role.setRole(playerList[i], Villager())
val playersShuffled = players.shuffled()
var playerIndex = 0
roles.forEach {
val role = Role.registerRoles.getValue(it.key)
for (i in 0..<it.value) {
playersShuffled[playerIndex].werewolfRole = role
playerIndex++
}
}
}
@@ -74,56 +74,51 @@ class Game(var plugin: Plugin, val world: World, val players: Iterable<Player>,
var current: Game? = null
}
fun gameEnded(): Boolean {
var hasWerewolf = false
var hasVillager = false
for (player in playersMutable) {
val isWerewolf = Role.getRole(player)?.lg ?: false
if (isWerewolf) hasWerewolf = true else hasVillager = true
if (hasWerewolf && hasVillager) return false
}
return true
fun checkGameEnd(): Boolean {
val team = remainingPlayer.map { player -> player.werewolfRole.team }.toSet()
return team.count() <= 1
}
fun stopGame(withoutResult: Boolean = false) {
current ?: return
current = null
timeGestShedule.cancel()
timeGestion.stop()
world.time = 18000
val endComponent =
Component
.text("La partie est terminée !", NamedTextColor.DARK_GREEN)
val winComponent =
Component
.text("Victoire des ", NamedTextColor.DARK_GREEN)
.append(remainingPlayer[0].werewolfRole.team.displayName)
.append(Component.text(".", NamedTextColor.DARK_GREEN))
players.forEach { player ->
player.gameMode = GameMode.SPECTATOR
player.sendMessage(
Component.text("La partie est terminée !", NamedTextColor.DARK_GREEN)
.appendNewline()
.append {
if (withoutResult)
Component.empty()
else {
val villageHasWolf = playersMutable.any {
Role.getRole(it)?.lg ?: false
}
if (villageHasWolf) {
Component.text(
"Les loups-garous ont finalement dévoré le village.",
NamedTextColor.DARK_RED
)
} else {
Component.text(
"Les villageois ont finalement sauvé le village.",
NamedTextColor.DARK_BLUE
)
}
}
}
)
}
Bukkit.getScheduler().runTaskLater(plugin, Runnable {
players.forEach { player ->
player.teleport(plugin.config.getLocation("lobbyLocation")!!)
player.gameMode = GameMode.ADVENTURE
player.sendMessage(endComponent)
if (remainingPlayer[0].werewolfRole.team == Role.Team.Solo) {
TODO()
} else {
if (!withoutResult) player.sendMessage(winComponent)
}
}, 200)
}
val lobbyLoc = plugin.config.getLocation("lobby-location")
Bukkit.getScheduler().runTaskLater(
plugin,
Runnable {
players.forEach { player ->
if (lobbyLoc != null) player.teleport(lobbyLoc)
player.gameMode = GameMode.ADVENTURE
player.persistentDataContainer.remove(PlayerDead)
player.persistentDataContainer.remove(Insomnia)
player.inventory.clear()
player.resetCooldown()
}
},
200,
)
}
}

View File

@@ -0,0 +1,133 @@
package fr.azur.tcoww.game
import org.bukkit.Bukkit
import org.bukkit.GameRule
import org.bukkit.World
import org.bukkit.event.Event
import org.bukkit.event.HandlerList
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitTask
class NightLightCycle(
plugin: Plugin,
val world: World,
) {
enum class Phase {
DAY,
VOTE,
NIGHT,
CREPUSCULAR,
}
class NightLightCyclePhaseChangeEvent(
val world: World,
val newPhase: Phase,
val duration: Duration,
val dayCount: Int,
) : Event() {
override fun getHandlers(): HandlerList = handlerList
companion object {
@JvmStatic
val handlerList: HandlerList = HandlerList()
}
}
data class Duration(
val day: Int,
val vote: Int,
val night: Int,
val crepuscular: Int,
) {
companion object {
fun fromPlugin(plugin: Plugin): Duration {
val dayDuration = plugin.config.getInt("duration.day")
val voteDuration = plugin.config.getInt("duration.vote")
val nightDuration = plugin.config.getInt("duration.night")
val crepuscularDuration = plugin.config.getInt("duration.crepuscular")
return Duration(dayDuration, voteDuration, nightDuration, crepuscularDuration)
}
}
}
var phase: Phase = Phase.DAY
val phases = Phase.entries
val process: BukkitTask
val duration = Duration.fromPlugin(plugin)
var targetInstant: Long = 0
var tick: Float = 0F
var tickOrigin: Int = 0
var realDifference: Long = 0
var tickDifference: Int = 0
var realOrigin: Long = 0
var dayCount: Int = 0
init {
world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
changePhase(Phase.DAY)
process =
Bukkit.getScheduler().runTaskTimer(
plugin,
Runnable {
this.tick()
},
0L,
1L,
)
}
fun changePhase(newPhase: Phase) {
realOrigin = System.currentTimeMillis()
when (newPhase) {
Phase.DAY -> {
tickDifference = 12_000
tickOrigin = 0
realDifference = duration.day * 1000L
}
Phase.VOTE -> {
tickDifference = 1_000
tickOrigin = 12_000
realDifference = duration.vote * 1000L
}
Phase.NIGHT -> {
tickDifference = 10_000
tickOrigin = 13_000
realDifference = duration.night * 1000L
}
Phase.CREPUSCULAR -> {
tickDifference = 1_000
tickOrigin = 23_000
realDifference = duration.crepuscular * 1000L
}
}
targetInstant = System.currentTimeMillis() + realDifference
tick = tickDifference / realDifference.toFloat()
phase = newPhase
Bukkit.getPluginManager().callEvent(NightLightCyclePhaseChangeEvent(world, newPhase, duration, dayCount))
}
fun tick() {
val realtime = System.currentTimeMillis()
if (realtime >= targetInstant) {
val phase = phases[(phases.indexOf(phase) + 1) % phases.count()]
if (phase == Phase.DAY) {
dayCount += 1
}
changePhase(phase)
}
val result = tickOrigin + (realtime - realOrigin) * tick
world.time = result.toLong().coerceIn(0, 23999)
}
fun stop() {
process.cancel()
}
}

View File

@@ -1,8 +0,0 @@
package fr.azur.tcoww.game
enum class Phase {
DAY,
VOTE,
NIGHT,
CREPUSCULAR
}

View File

@@ -1,81 +0,0 @@
package fr.azur.tcoww.game
import fr.azur.tcoww.events.TimePhaseChangeEvent
import org.bukkit.Bukkit
import org.bukkit.World
import org.bukkit.GameRule
import org.bukkit.plugin.Plugin
import java.util.Calendar
class TimeGestion(plugin: Plugin, val world: World) {
var calendar: Calendar = Calendar.getInstance()
var targetCalendar: Calendar = calendar.clone() as Calendar
var difference: Long
var differencetick: Int
val dayDuration: Int = plugin.config.getInt("duration.day")
val voteDuration: Int = plugin.config.getInt("duration.vote")
val nightDuration: Int = plugin.config.getInt("duration.night")
val crepuscularDuration: Int = plugin.config.getInt("duration.crepuscular")
var phase: Phase = Phase.DAY
var tickcorrecter = 0
init {
world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
targetCalendar.add(Calendar.SECOND, dayDuration)
difference = targetCalendar.timeInMillis - calendar.timeInMillis
differencetick = 12500
Bukkit.getPluginManager().callEvent(TimePhaseChangeEvent(world,phase))
}
fun changePhase() {
calendar = Calendar.getInstance()
targetCalendar = calendar.clone() as Calendar
when (phase) {
// vote
Phase.DAY -> {
phase = Phase.VOTE
tickcorrecter = 12_500
differencetick = 1_500
targetCalendar.add(Calendar.SECOND, voteDuration)
}
// night
Phase.VOTE -> {
phase = Phase.NIGHT
tickcorrecter = 14_000
differencetick = 8_500
targetCalendar.add(Calendar.SECOND, nightDuration)
}
// crepuscular
Phase.NIGHT -> {
phase = Phase.CREPUSCULAR
tickcorrecter = 22_500
differencetick = 1_500
targetCalendar.add(Calendar.SECOND, crepuscularDuration)
}
// day
Phase.CREPUSCULAR -> {
phase = Phase.DAY
tickcorrecter = 0
differencetick = 12_500
targetCalendar.add(Calendar.SECOND, dayDuration)
}
}
Bukkit.getPluginManager().callEvent(TimePhaseChangeEvent(world,phase))
difference = targetCalendar.timeInMillis - calendar.timeInMillis
}
fun tick() {
if (System.currentTimeMillis() >= targetCalendar.timeInMillis) {
changePhase()
}
if (difference == 0L) return
val result = tickcorrecter + ((differencetick.toDouble() * (System.currentTimeMillis() - calendar.timeInMillis)) / difference).toInt()
world.time = result.coerceIn(0, 23999).toLong()
}
}

View File

@@ -2,12 +2,12 @@ package fr.azur.tcoww.items
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.utils.DataKeys
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import org.bukkit.Color
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemRarity
@@ -19,181 +19,216 @@ import org.bukkit.persistence.PersistentDataType
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
enum class CustomItems(val cache: Boolean, val supplier: () -> ItemStack) {
enum class CustomItems(
val cache: Boolean,
val supplier: () -> ItemStack,
) {
WereWolfTransformItem(true, {
ItemStack.of(Material.BONE).apply {
itemMeta = itemMeta.apply {
customName(Component.text("Os de transformation").color(NamedTextColor.DARK_RED))
persistentDataContainer.set(
NamespacedKey("tcoww", "power"),
PersistentDataType.INTEGER,
3
)
lore(
listOf(
Component.text("Clique pour basculer en forme loup-garou.")
.color(NamedTextColor.GOLD)
itemMeta =
itemMeta.apply {
customName(Component.text("Os de transformation", NamedTextColor.DARK_RED))
persistentDataContainer.set(
DataKeys.PowerItems,
PersistentDataType.INTEGER,
3,
)
)
}
lore(
listOf(
Component
.text("Clique pour basculer en forme loup-garou.")
.color(NamedTextColor.GOLD),
),
)
}
}
}),
GunBullet(true, {
ItemStack(Material.TIPPED_ARROW).apply {
itemMeta = (itemMeta as PotionMeta).apply {
color = Color.ORANGE
displayName(Component.text("Balle de fusil.", NamedTextColor.YELLOW))
basePotionType = basePotionType.apply {
addCustomEffect(PotionEffect(PotionEffectType.INSTANT_DAMAGE, 20, 99), true)
itemMeta =
(itemMeta as PotionMeta).apply {
color = Color.ORANGE
displayName(Component.text("Balle de fusil.", NamedTextColor.YELLOW))
basePotionType =
basePotionType.apply {
addCustomEffect(PotionEffect(PotionEffectType.INSTANT_DAMAGE, 20, 99), true)
}
}
}
}
}),
BlasTechDL44(true, {
ItemStack.of(Material.CROSSBOW).apply {
itemMeta = (itemMeta as CrossbowMeta).apply {
addChargedProjectile(GunBullet.item)
displayName(Component.text("BlasTech DL-44", NamedTextColor.GOLD))
addEnchant(Enchantment.INFINITY, 1, true)
}
itemMeta = (itemMeta as Damageable).apply {
setMaxDamage(1)
}
itemMeta =
(itemMeta as CrossbowMeta).apply {
addChargedProjectile(GunBullet.item)
displayName(Component.text("BlasTech DL-44", NamedTextColor.GOLD))
addEnchant(Enchantment.INFINITY, 1, true)
}
itemMeta =
(itemMeta as Damageable).apply {
setMaxDamage(1)
}
}
}),
CrystalBall(true, {
ItemStack.of(Material.GLASS).apply {
itemMeta = itemMeta.apply {
customName(
Component.text("Boule de cristal").color(NamedTextColor.AQUA)
)
persistentDataContainer.set(
NamespacedKey("tcoww", "power"),
PersistentDataType.INTEGER,
4
)
lore(listOf(Component.text("/power view <pseudo> pour l'utiliser.").color(NamedTextColor.GOLD)))
}
itemMeta =
itemMeta.apply {
customName(
Component.text("Boule de cristal", NamedTextColor.AQUA),
)
persistentDataContainer.set(
DataKeys.PowerItems,
PersistentDataType.INTEGER,
4,
)
setMaxStackSize(1)
lore(listOf(Component.text("Click droit pour utiliser.", NamedTextColor.GOLD)))
}
}
}
),
}),
DeathPotion(true, {
ItemStack.of(Material.POTION).apply {
itemMeta = (itemMeta as PotionMeta).apply {
customName(
Component.text("Potion de poison").color(NamedTextColor.DARK_PURPLE)
)
persistentDataContainer.set(
NamespacedKey("tcoww", "power"),
PersistentDataType.INTEGER,
1
)
color = Color.BLACK
lore(
listOf(
Component.text("/power kill <pseudo> pour l'utiliser.").color(NamedTextColor.GOLD)
itemMeta =
(itemMeta as PotionMeta).apply {
setMaxStackSize(1)
customName(
Component.text("Potion de poison", NamedTextColor.DARK_PURPLE),
)
)
}
persistentDataContainer.set(
DataKeys.PowerItems,
PersistentDataType.INTEGER,
1,
)
color = Color.BLACK
lore(
listOf(
Component.text("Click droit pour utiliser.", NamedTextColor.GOLD),
),
)
}
itemMeta =
(itemMeta as Damageable).apply {
setMaxDamage(1)
}
}
}),
HealPotion(true, {
ItemStack.of(Material.POTION).apply {
itemMeta = (itemMeta as PotionMeta).apply {
customName(
Component.text("Potion de vie").color(NamedTextColor.DARK_PURPLE)
)
persistentDataContainer.set(
NamespacedKey("tcoww", "power"),
PersistentDataType.INTEGER,
2
)
color = Color.RED
lore(listOf(Component.text("/power save <pseudo> pour l'utiliser.").color(NamedTextColor.GOLD)))
}
itemMeta =
(itemMeta as PotionMeta).apply {
setMaxStackSize(1)
customName(
Component.text("Potion de vie", NamedTextColor.DARK_PURPLE),
)
persistentDataContainer.set(
DataKeys.PowerItems,
PersistentDataType.INTEGER,
2,
)
color = Color.RED
lore(listOf(Component.text("Click droit pour utiliser.", NamedTextColor.GOLD)))
}
itemMeta =
(itemMeta as Damageable).apply {
setMaxDamage(1)
}
}
}),
SpawnLocTool(true, {
ItemStack.of(Material.SUGAR_CANE).apply {
itemMeta = itemMeta.apply {
setRarity(ItemRarity.EPIC)
persistentDataContainer.set(
NamespacedKey("tcoww", "tools"),
PersistentDataType.INTEGER,
2
)
lore(listOf(Component.text("Spawn location tool").color(NamedTextColor.GOLD)))
}
itemMeta =
itemMeta.apply {
setRarity(ItemRarity.EPIC)
persistentDataContainer.set(
DataKeys.ToolsItems,
PersistentDataType.INTEGER,
2,
)
lore(listOf(Component.text("Spawn location tool", NamedTextColor.GOLD)))
}
}
}),
BedLocTool(true, {
ItemStack.of(Material.WOODEN_HOE).apply {
itemMeta = itemMeta.apply {
setRarity(ItemRarity.EPIC)
persistentDataContainer.set(
NamespacedKey("tcoww", "tools"),
PersistentDataType.INTEGER,
1
)
lore(listOf(Component.text("bed location tool").color(NamedTextColor.GOLD)))
}
itemMeta =
itemMeta.apply {
setRarity(ItemRarity.EPIC)
persistentDataContainer.set(
DataKeys.ToolsItems,
PersistentDataType.INTEGER,
1,
)
lore(listOf(Component.text("bed location tool", NamedTextColor.GOLD)))
}
}
}),
LobbyLocTool(true, {
ItemStack.of(Material.POPPED_CHORUS_FRUIT).apply {
itemMeta = itemMeta.apply {
setRarity(ItemRarity.EPIC)
persistentDataContainer.set(
NamespacedKey("tcoww", "tools"),
PersistentDataType.INTEGER,
3
)
lore(listOf(Component.text("Lobby location tool").color(NamedTextColor.GOLD)))
}
itemMeta =
itemMeta.apply {
setRarity(ItemRarity.EPIC)
persistentDataContainer.set(
DataKeys.ToolsItems,
PersistentDataType.INTEGER,
3,
)
lore(listOf(Component.text("Lobby location tool", NamedTextColor.GOLD)))
}
}
}),
ListHint(false, {
val finalLore = buildList {
add(
Component.text("C'est une liste de nom.", NamedTextColor.RED)
)
add(
Component.text("Il y a écrit que un de ces 3 citoyens", NamedTextColor.DARK_RED)
)
add(
Component.text("est un loup-garou.", NamedTextColor.DARK_RED)
)
val finalLore =
buildList {
add(
Component.text("C'est une liste de nom.", NamedTextColor.RED),
)
add(
Component.text("Il y a écrit que un de ces 3 citoyens", NamedTextColor.DARK_RED),
)
add(
Component.text("est un loup-garou.", NamedTextColor.DARK_RED),
)
val players = Game.current?.playersMutable?.shuffled().orEmpty()
val players =
Game.current
?.remainingPlayer
?.shuffled()
.orEmpty()
val (villagers, werewolves) = players.partition {
Role.getRole(it)?.lg == false
val (villagers, werewolves) =
players.partition {
Role.getRole(it)?.team != Role.Team.LG
}
val lg = werewolves.firstOrNull()
val names = mutableListOf<Player>()
if (lg != null) names.add(lg)
names.addAll(villagers.take(2))
val colors = NamedColors.namedColors.shuffled().toMutableList()
names.shuffled().forEach {
val color = colors.removeFirst()
add(Component.text("- ").append(it.displayName()).color(color))
}
if (isEmpty()) {
add(Component.text("nothing", NamedTextColor.DARK_RED, TextDecoration.OBFUSCATED))
}
}
val lg = werewolves.firstOrNull()
val names = mutableListOf<Player>()
if (lg != null) names.add(lg)
names.addAll(villagers.take(2))
val colors = NamedColors.namedColors.shuffled().toMutableList()
names.shuffled().forEach {
val color = colors.removeFirst()
add(Component.text("- ").append(it.displayName()).color(color))
}
if (isEmpty()) {
add(Component.text("nothing", NamedTextColor.DARK_RED, TextDecoration.OBFUSCATED))
}
}
ItemStack(Material.PAPER).apply {
itemMeta = itemMeta.apply {
displayName(Component.text("Liste de nom", NamedTextColor.GOLD))
lore(finalLore)
}
itemMeta =
itemMeta.apply {
displayName(Component.text("Liste de nom", NamedTextColor.GOLD))
lore(finalLore)
}
}
});
}),
;
private var cachedItem: ItemStack? = null
@@ -206,21 +241,27 @@ enum class CustomItems(val cache: Boolean, val supplier: () -> ItemStack) {
}
}
data object NamedColors {
val namedColors = listOf(
NamedTextColor.DARK_RED,
NamedTextColor.RED,
NamedTextColor.GOLD,
NamedTextColor.YELLOW,
NamedTextColor.DARK_GREEN,
NamedTextColor.GREEN,
NamedTextColor.AQUA,
NamedTextColor.DARK_AQUA,
NamedTextColor.DARK_BLUE,
NamedTextColor.BLUE,
NamedTextColor.LIGHT_PURPLE,
NamedTextColor.DARK_PURPLE
)
interface DamageablePotion :
Damageable,
PotionMeta {
override fun clone(): DamageablePotion
}
}
data object NamedColors {
val namedColors =
listOf(
NamedTextColor.DARK_RED,
NamedTextColor.RED,
NamedTextColor.GOLD,
NamedTextColor.YELLOW,
NamedTextColor.DARK_GREEN,
NamedTextColor.GREEN,
NamedTextColor.AQUA,
NamedTextColor.DARK_AQUA,
NamedTextColor.DARK_BLUE,
NamedTextColor.BLUE,
NamedTextColor.LIGHT_PURPLE,
NamedTextColor.DARK_PURPLE,
)
}
}

View File

@@ -1,37 +0,0 @@
package fr.azur.tcoww.plasmovoice.addon
import su.plo.voice.api.addon.AddonInitializer
import su.plo.voice.api.addon.InjectPlasmoVoice
import su.plo.voice.api.addon.annotation.Addon
import su.plo.voice.api.event.EventSubscribe
import su.plo.voice.api.server.PlasmoVoiceServer
import su.plo.voice.api.server.event.player.PlayerInfoCreateEvent
@Addon(
id = "tcoww",
name = "The court of werewolf",
version = "1.0.0",
authors = ["Azur"]
)
class PlasmoAddon : AddonInitializer {
@InjectPlasmoVoice
private lateinit var voiceServer: PlasmoVoiceServer
override fun onAddonInitialize() {
onConfigLoaded()
}
override fun onAddonShutdown() {
}
fun onConfigLoaded() {
}
@EventSubscribe
fun onPlayerInfoCreate(event: PlayerInfoCreateEvent) {
event.voicePlayerInfo.playerNick = "..."
}
}

View File

@@ -2,21 +2,22 @@ package fr.azur.tcoww.roles
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
class Child : Role {
override fun handle(player: Player) {}
override fun onDeath(player: Player) {}
override var lg = false
override var id = NamespacedKey("tcoww", "child")
override val displayName = Component.text("Petite Fille")
object Child : Role {
override val id = NamespacedKey("tcoww", "child")
override val team = Role.Team.Villager
override val displayName = Component.text("Petite Fille", NamedTextColor.LIGHT_PURPLE)
override val icon = Material.MAGENTA_WOOL
override val order: Int = 2
override val hasPowerCommand = false
override val description =
Component.text("Extrêmement discrette et au coeur du risque.", NamedTextColor.BLUE).appendNewline()
.append(
Component.text("Vous n'êtes pas obliger de dormir la nuit, vous pouvez donc observer les loups le soir.", NamedTextColor.BLUE)
)
}
listOf(
Component.text("Extrêmement discrette et au coeur du risque.", NamedTextColor.BLUE),
Component.text(
"Vous n'êtes pas obliger de dormir la nuit, vous pouvez donc observer les loups le soir.",
NamedTextColor.BLUE,
),
)
}

View File

@@ -3,23 +3,27 @@ package fr.azur.tcoww.roles
import fr.azur.tcoww.items.CustomItems
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
class FortuneTeller : Role {
object FortuneTeller : Role {
override fun handle(player: Player) {
player.inventory.addItem(CustomItems.CrystalBall.item)
player.inventory.setItem(9, CustomItems.CrystalBall.item)
}
override fun onDeath(player: Player) {}
override var lg = false
override val team = Role.Team.Villager
override var id = NamespacedKey("tcoww", "fortune_teller")
override val displayName = Component.text("Voyante")
override val icon = Material.GLASS
override val order: Int = 3
override val displayName = Component.text("Voyante", NamedTextColor.AQUA)
override val hasPowerCommand = true
override val description =
Component.text("Vous voyez des choses mais quoi ?.", NamedTextColor.BLUE).appendNewline()
.append(
Component.text("Tout les 2 jours vous pouvez regarder le role d'un joueur.", NamedTextColor.BLUE)
)
}
listOf(
Component.text("Vous voyez des choses mais quoi ?.", NamedTextColor.BLUE),
Component
.text("Tout les 2 jours vous pouvez regarder le role d'un joueur grâce à votre ", NamedTextColor.BLUE)
.append(CustomItems.CrystalBall.item.displayName())
.append(Component.text(".", NamedTextColor.BLUE)),
)
}

View File

@@ -3,32 +3,32 @@ package fr.azur.tcoww.roles
import fr.azur.tcoww.items.CustomItems
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
class Hunter : Role {
object Hunter : Role {
override fun handle(player: Player) {
player.inventory.addItem(CustomItems.BlasTechDL44.item)
player.inventory.setItem(9, CustomItems.BlasTechDL44.item)
}
override fun onDeath(player: Player) {
TODO("Not yet implemented")
}
override var lg = false
override val team = Role.Team.Villager
override var id = NamespacedKey("tcoww", "hunter")
override val displayName = Component.text("Chaseur")
override val displayName = Component.text("Chaseur", NamedTextColor.GOLD)
override val icon = Material.ARROW
override val order: Int = 5
override val hasPowerCommand = false
override val description =
Component.text(
"Vous êtes la personne, la plus redouter des animaux (et en particulier des loups).",
NamedTextColor.BLUE
).appendNewline()
.append(
Component.text("Vous possedez un ", NamedTextColor.BLUE)
.append(CustomItems.BlasTechDL44.item.displayName())
.append(
Component.text(" qui permet de one shot n'importe quoi... (Usage Unique.)", NamedTextColor.BLUE)
)
)
}
listOf(
Component.text(
"Vous êtes la personne, la plus redouter des animaux (et en particulier des loups).",
NamedTextColor.BLUE,
),
Component
.text("Vous possedez un ", NamedTextColor.BLUE)
.append(CustomItems.BlasTechDL44.item.displayName())
.append(
Component.text(" qui permet de one shot n'importe quoi... (Usage Unique)", NamedTextColor.BLUE),
),
)
}

View File

@@ -1,49 +1,91 @@
package fr.azur.tcoww.roles
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.JoinConfiguration
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
import org.bukkit.persistence.PersistentDataType
interface Role {
fun handle(player: Player)
fun onDeath(player: Player)
val lg: Boolean
enum class Team {
LG {
override val displayName = Component.text("loups-garous").color(NamedTextColor.DARK_RED)
},
Villager {
override val displayName: Component = Component.text("villageois", NamedTextColor.GREEN)
},
Solo {
override val displayName: Component = Component.text("solo", NamedTextColor.YELLOW)
}, ;
abstract val displayName: Component
}
fun handle(player: Player) {}
fun onDeath(player: Player) {}
fun onVote(player: Player) {}
val id: NamespacedKey
val icon: Material
val order: Int
val team: Team
val displayName: Component
val hasPowerCommand: Boolean
val description: Component
val description: List<Component>
val lineDescription: Component
get() {
val joinConfig =
JoinConfiguration
.builder()
.separator(Component.newline())
.build()
return Component.join(joinConfig, description)
}
companion object {
var registerRoles = hashMapOf<NamespacedKey, Role>()
private val rolekey = NamespacedKey("tcoww", "role")
fun setRole(player: Player, role: Role) {
player.persistentDataContainer.set(
rolekey,
PersistentDataType.STRING,
role.id.toString()
)
private val roleKey = NamespacedKey("tcoww", "role")
fun setRole(
player: Player,
role: Role,
) {
player.sendMessage(
Component.text("Votre role est ").append(role.displayName).color(NamedTextColor.DARK_AQUA)
Component.text("Votre role est ", NamedTextColor.DARK_AQUA).append(role.displayName),
)
player.sendMessage(role.description.appendNewline().append {
if (role.lg)
Component.text("Vous gagnez avec les loups-garous.", NamedTextColor.AQUA)
else
Component.text("Vous gagnez avec les villageois.", NamedTextColor.AQUA)
})
player.sendMessage(role.lineDescription)
player.sendMessage(
if (role.team == Team.Solo) {
Component.text("Vous gagnez Seul.", NamedTextColor.AQUA)
} else {
Component
.text("Vous gagnez avec les ", NamedTextColor.AQUA)
.append(role.team.displayName)
.append(Component.text(".", NamedTextColor.AQUA))
},
)
role.handle(player)
player.persistentDataContainer.set(roleKey, PersistentDataType.STRING, role.id.toString())
}
fun getRole(player: Player): Role? {
val rolestr = player.persistentDataContainer.get(rolekey, PersistentDataType.STRING)
if (rolestr == null) return null
return registerRoles[NamespacedKey.fromString(rolestr)]
val roleKey = player.persistentDataContainer.get(roleKey, PersistentDataType.STRING) ?: return null
return registerRoles[NamespacedKey.fromString(roleKey)]
}
var Player.werewolfRole: Role
get() = getRole(this)!!
set(role) = setRole(this, role)
fun registerRole(role: Role) {
registerRoles.put(role.id, role)
registerRoles[role.id] = role
}
}
}
}

View File

@@ -2,25 +2,19 @@ package fr.azur.tcoww.roles
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
class Villager : Role {
override fun handle(player: Player) {
}
override fun onDeath(player: Player) {
}
override var lg: Boolean = false
object Villager : Role {
override val team = Role.Team.Villager
override var id = NamespacedKey("tcoww", "villager")
override val displayName = Component.text("Villageois")
override val displayName = Component.text("Villageois", NamedTextColor.GRAY)
override val icon = Material.DIRT
override val order: Int = 0
override val hasPowerCommand = false
override val description =
Component.text("En tant que villageois, vous n'avez aucun pouvoir.", NamedTextColor.BLUE).appendNewline()
.append(
Component.text("Profiter en pour risquer pour trouver des indices ! ^^", NamedTextColor.BLUE)
)
}
listOf(
Component.text("En tant que villageois, vous n'avez aucun pouvoir.", NamedTextColor.BLUE),
Component.text("Profiter en pour risquer pour trouver des indices ! ^^", NamedTextColor.BLUE),
)
}

View File

@@ -1,25 +1,40 @@
package fr.azur.tcoww.roles
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.items.CustomItems
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
class Werewolf : Role {
override fun handle(player: Player) {}
override fun onDeath(player: Player) {}
override var lg = true
object Werewolf : Role {
var remainingKill = 0
override val team = Role.Team.LG
override var id = NamespacedKey("tcoww", "werewolf")
override val displayName = Component.text("Loup-Garou")
override val displayName = Component.text("Loup-Garou", NamedTextColor.DARK_GRAY)
override val icon = Material.BONE
override val order: Int = 1
override val hasPowerCommand = false
override val description =
Component.text("Pour la faire courte, vous avez faim, très faim.", NamedTextColor.BLUE).appendNewline()
.append(
Component.text(
"Votre but est de tuer les villageois. Sans vous faire atraper et voter. Toute les nuits, vous pouvez vous tranformer en loup pour manger un villageois.",
NamedTextColor.BLUE
)
)
}
listOf(
Component.text("Pour la faire courte, vous avez faim, très faim.", NamedTextColor.BLUE),
Component.text(
"Votre but est de tuer les villageois. Sans vous faire atraper et voter.",
NamedTextColor.BLUE,
),
Component.text(
"Toute les nuits, vous pouvez vous tranformer en loup pour manger un villageois.",
NamedTextColor.BLUE,
),
)
override fun handle(player: Player) {
val tfItem = CustomItems.WereWolfTransformItem.item
player.inventory.setItem(9, tfItem)
player.setCooldown(
tfItem,
((Tcoww.instance.config.getInt("duration.day") + Tcoww.instance.config.getInt("duration.day")) * 20),
)
}
}

View File

@@ -3,27 +3,28 @@ package fr.azur.tcoww.roles
import fr.azur.tcoww.items.CustomItems
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
class Witch : Role {
object Witch : Role {
override fun handle(player: Player) {
player.inventory.addItem(CustomItems.DeathPotion.item)
player.inventory.addItem(CustomItems.HealPotion.item)
player.inventory.setItem(9, CustomItems.DeathPotion.item)
player.inventory.setItem(10, CustomItems.HealPotion.item)
}
override fun onDeath(player: Player) {}
override var lg = false
override val team = Role.Team.Villager
override var id = NamespacedKey("tcoww", "witch")
override val displayName = Component.text("Sorcière")
override val icon = Material.GLASS_BOTTLE
override val order: Int = 4
override val hasPowerCommand = true
override val description =
Component.text("Une potion = Une solution.", NamedTextColor.BLUE).appendNewline()
.append(
Component.text(
"Vous possédez deux potions : une pour tuer et une pour ressusciter quelqu'un.",
NamedTextColor.BLUE
)
).appendNewline()
}
listOf(
Component.text("Une potion = Une solution.", NamedTextColor.BLUE),
Component.text(
"Vous possédez deux potions : une pour tuer et une pour ressusciter quelqu'un.",
NamedTextColor.BLUE,
),
)
}

View File

@@ -0,0 +1,11 @@
package fr.azur.tcoww.ui
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.inventory.InventoryHolder
interface CustomUI : InventoryHolder {
fun onClick(event: InventoryClickEvent) {}
fun onClose(event: InventoryCloseEvent) {}
}

View File

@@ -0,0 +1,121 @@
package fr.azur.tcoww.ui
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.roles.Role
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.NamespacedKey
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import java.util.concurrent.CompletableFuture
import kotlin.math.ceil
class GameConfig(
playerCount: Int,
) : CustomUI {
// [index, [role key, amount]]
val rolesConfig = hashMapOf<Int, Pair<NamespacedKey, Int>>()
val size = (Role.registerRoles.count() / 9 + 1) * 9
val future = CompletableFuture<Map<NamespacedKey, Int>>()
private val inventory =
Tcoww.instance.server.createInventory(this, size, Component.text("Game Config.", NamedTextColor.GREEN)).apply {
Role.registerRoles.values.sortedBy { it.order }.onEachIndexed { index, role ->
setItem(index, createItem(index, role))
}
}
init {
val werewolfCount = ceil((playerCount * (Tcoww.instance.config.getInt("werewolf-default-percentage") / 100f))).toInt()
editItem(0, playerCount - werewolfCount)
editItem(1, werewolfCount)
}
private fun createItem(
index: Int,
role: Role,
): ItemStack {
rolesConfig[index] = Pair(role.id, 0)
return ItemStack.of(role.icon).apply {
itemMeta =
(itemMeta as Damageable).apply {
lore(
role.description,
)
itemName(
role.displayName,
)
setMaxStackSize(1)
setMaxDamage(1)
damage = 1
}
}
}
private fun editItem(
index: Int,
newAmount: Int,
) {
if (newAmount !in 0..99) throw IllegalArgumentException("Amount must be in 0 and 99 !")
rolesConfig[index] = Pair(rolesConfig[index]!!.first, newAmount)
val item = inventory.getItem(index) ?: return
item.itemMeta =
(item.itemMeta as Damageable).apply {
if (newAmount == 0) {
setMaxStackSize(1)
setMaxDamage(1)
damage = 1
} else {
damage = 0
setMaxStackSize(99)
item.amount = newAmount
}
}
}
override fun getInventory(): Inventory = inventory
override fun onClick(event: InventoryClickEvent) {
event.isCancelled = true
val index = event.slot
if (index !in 0..<Role.registerRoles.size || event.clickedInventory != inventory) return
if (event.isLeftClick) {
for (i in 0..<Role.registerRoles.size) {
if (i == index) continue
val count = rolesConfig[i]!!.second
if (count != 0) {
val targetCount = rolesConfig[index]!!.second
editItem(i, count - 1)
editItem(index, targetCount + 1)
break
}
}
} else if (event.isRightClick) {
for (i in 0..<Role.registerRoles.size) {
if (i == index) continue
val count = rolesConfig[i]!!.second
if (count != 0) {
val targetCount = rolesConfig[index]!!.second
if (targetCount == 0) break
editItem(i, count + 1)
editItem(index, targetCount - 1)
break
}
}
}
}
override fun onClose(event: InventoryCloseEvent) {
future.complete(
buildMap {
rolesConfig.forEach {
if (it.value.second != 0) put(it.value.first, it.value.second)
}
},
)
}
}

View File

@@ -0,0 +1,55 @@
package fr.azur.tcoww.ui
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.utils.Players.isTransformed
import fr.azur.tcoww.utils.skins.Manager
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.SkullMeta
import java.util.concurrent.CompletableFuture
class PlayerSelectMenu(
val players: Iterable<Player>,
) : CustomUI {
val size = (players.count() / 9 + 1) * 9
val future = CompletableFuture<Player?>()
private val inventory =
Tcoww.instance.server.createInventory(this, size, Component.text("Select a player.", NamedTextColor.GREEN)).apply {
players.forEachIndexed { index, player ->
val item =
ItemStack.of(Material.PLAYER_HEAD).apply {
itemMeta =
(itemMeta as SkullMeta).apply {
playerProfile =
if (player.isTransformed()) {
Manager.oldPlayerProfile[player.uniqueId]
} else {
player.playerProfile
}
}
}
setItem(index, item)
}
}
override fun getInventory(): Inventory = inventory
override fun onClick(event: InventoryClickEvent) {
val index = event.slot
event.isCancelled = true
if (index !in 0..<players.count() || event.clickedInventory != inventory) return
future.complete(players.elementAt(index))
event.inventory.close()
}
override fun onClose(event: InventoryCloseEvent) {
future.complete(null)
}
}

View File

@@ -4,21 +4,19 @@ import org.bukkit.block.Block
import org.bukkit.block.data.type.Bed
object BedGestion {
fun isBed(block: Block): Boolean {
return block.blockData is Bed
}
fun Block.isBed() = blockData is Bed
fun getHeadBed(block: Block): Block {
val bedData = block.blockData as Bed
fun Block.getHeadBed(): Block {
val bedData = blockData as Bed
return if (bedData.part == Bed.Part.FOOT) {
block.getRelative(bedData.facing)
getRelative(bedData.facing)
} else {
block
this
}
}
fun isOcupied(block: Block): Boolean {
val bedData = block.blockData as Bed
fun Block.isOccupied(): Boolean {
val bedData = blockData as Bed
return bedData.isOccupied
}
}
}

View File

@@ -0,0 +1,11 @@
package fr.azur.tcoww.utils
import fr.azur.tcoww.Tcoww
import org.bukkit.NamespacedKey
data object DataKeys {
val PlayerDead = NamespacedKey(Tcoww.instance, "dead")
val PowerItems = NamespacedKey(Tcoww.instance, "power")
val Insomnia = NamespacedKey(Tcoww.instance, "insomnia")
val ToolsItems = NamespacedKey(Tcoww.instance, "tools")
}

View File

@@ -0,0 +1,118 @@
package fr.azur.tcoww.utils
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.roles.Role.Companion.werewolfRole
import fr.azur.tcoww.utils.skins.Manager
import io.papermc.paper.datacomponent.item.ResolvableProfile
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.title.Title
import org.bukkit.Bukkit
import org.bukkit.GameMode
import org.bukkit.NamespacedKey
import org.bukkit.attribute.Attribute
import org.bukkit.attribute.AttributeModifier
import org.bukkit.entity.Mannequin
import org.bukkit.entity.Player
import org.bukkit.entity.Pose
import org.bukkit.persistence.PersistentDataType
import java.time.Duration
object Players {
private val transformKey = NamespacedKey("tcoww", "werewolf_transform")
val corpseKey = NamespacedKey("tcoww", "corpse")
val corpseRoleKey = NamespacedKey("tcoww", "corpse_role")
private val noCollisionTeam =
Bukkit
.getServer()
.scoreboardManager.mainScoreboard
.getTeam("NoCollision")
fun Player.tfWerewolf() {
if (isTransformed()) return
Manager.applyWerewolfFur(this)
persistentDataContainer.set(
transformKey,
PersistentDataType.BOOLEAN,
true,
)
val speedAtr = getAttribute(Attribute.MOVEMENT_SPEED)!!
val scaleAtr = getAttribute(Attribute.SCALE)!!
speedAtr.addModifier(
AttributeModifier(
transformKey,
0.02,
AttributeModifier.Operation.ADD_NUMBER,
),
)
scaleAtr.addModifier(
AttributeModifier(
transformKey,
0.1,
AttributeModifier.Operation.ADD_NUMBER,
),
)
}
fun Player.tfHuman() {
if (!isTransformed()) return
Manager.unapplySkin(this)
persistentDataContainer.set(
transformKey,
PersistentDataType.BOOLEAN,
false,
)
val speedAtr = getAttribute(Attribute.MOVEMENT_SPEED)!!
val scaleAtr = getAttribute(Attribute.SCALE)!!
speedAtr.removeModifier(transformKey)
scaleAtr.removeModifier(transformKey)
}
fun Player.isTransformed(): Boolean =
persistentDataContainer.getOrDefault(
transformKey,
PersistentDataType.BOOLEAN,
false,
)
fun Player.kill() {
if (isTransformed()) tfHuman()
val current = Game.current ?: return
val corpse = createCorpse(this)
corpse.spawnAt(this.location)
this.gameMode = GameMode.SPECTATOR
this.isInvulnerable = false
current.remainingPlayer.remove(this)
this.showTitle(
Title.title(
Component.text("Vous êtes mort.", NamedTextColor.DARK_RED),
Component.empty(),
Title.Times.times(Duration.ZERO, Duration.ofSeconds(2), Duration.ofMillis(500)),
),
)
if (current.checkGameEnd()) current.stopGame()
}
fun createCorpse(player: Player): Mannequin =
player.world.createEntity(player.location, Mannequin::class.java).apply {
profile = ResolvableProfile.resolvableProfile(player.playerProfile)
noCollisionTeam?.addEntity(this)
isInvulnerable = true
pose = Pose.SLEEPING
persistentDataContainer.set(corpseKey, PersistentDataType.BOOLEAN, true)
val role = player.werewolfRole
persistentDataContainer.set(corpseRoleKey, PersistentDataType.STRING, role.id.toString())
}
}

View File

@@ -1,65 +0,0 @@
package fr.azur.tcoww.utils
import net.skinsrestorer.api.SkinsRestorer
import net.skinsrestorer.api.property.SkinProperty
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
import java.util.*
import kotlin.jvm.optionals.getOrNull
import kotlin.random.Random
class Skin(private val plugin: Plugin, private val skinsRestorer: SkinsRestorer) {
var maxSkinId: Int = 0
private set
init {
reloadSkin()
}
fun reloadSkin() {
val skinsValue = plugin.config.getStringList("wwskinsvalue")
val skinsSignature = plugin.config.getStringList("wwskinssignature")
Stockage.wwPlayerFur.clear()
maxSkinId = 0
skinsValue.zip(skinsSignature).forEachIndexed { index, (value, signature) ->
skinsRestorer.skinStorage.setCustomSkinData("tcoww_$index", SkinProperty.of(value, signature))
maxSkinId = index
}
}
fun applyWerewolfFur(player: Player) {
val skin = skinsRestorer.playerStorage.getSkinIdOfPlayer(player.uniqueId)
Stockage.oldPlayerSkin[player.uniqueId] = skin.getOrNull()
val result = skinsRestorer.skinStorage.findOrCreateSkinData("tcoww_${getWerewolfFur(player.uniqueId)}")
if (!result.isEmpty) {
skinsRestorer.playerStorage.setSkinIdOfPlayer(player.uniqueId, result.get().identifier)
skinsRestorer.getSkinApplier(Player::class.java).applySkin(player)
}
}
fun unApplySkin(player: Player) {
val skin = Stockage.oldPlayerSkin[player.uniqueId]
if (skin == null) {
skinsRestorer.playerStorage.removeSkinIdOfPlayer(player.uniqueId)
} else {
skinsRestorer.playerStorage.setSkinIdOfPlayer(player.uniqueId,skin)
}
skinsRestorer.getSkinApplier(Player::class.java).applySkin(player)
}
private fun getWerewolfFur(playerUUID: UUID): Int {
return Stockage.wwPlayerFur[playerUUID]
?: randomFur(playerUUID)
}
private fun randomFur(playerUUID: UUID): Int {
val id = if (maxSkinId == 0) 0 else Random(playerUUID.mostSignificantBits xor playerUUID.leastSignificantBits).nextInt(maxSkinId)
Stockage.wwPlayerFur[playerUUID] = id
return id
}
}

View File

@@ -1,13 +0,0 @@
package fr.azur.tcoww.utils
import net.skinsrestorer.api.property.SkinIdentifier
import org.bukkit.Location
import java.util.*
object Stockage {
var backLocation = mutableMapOf<UUID, Location>()
var vote = mutableMapOf<UUID, UUID>()
var oldPlayerSkin = mutableMapOf<UUID, SkinIdentifier?>()
var wwPlayerFur = mutableMapOf<UUID, Int>()
}

View File

@@ -0,0 +1,73 @@
package fr.azur.tcoww.utils
import de.maxhenkel.voicechat.api.Group
import de.maxhenkel.voicechat.api.VoicechatPlugin
import de.maxhenkel.voicechat.api.VoicechatServerApi
import de.maxhenkel.voicechat.api.events.EventRegistration
import de.maxhenkel.voicechat.api.events.LeaveGroupEvent
import de.maxhenkel.voicechat.api.events.VoicechatServerStartedEvent
import fr.azur.tcoww.game.Game
import fr.azur.tcoww.game.NightLightCycle
import org.bukkit.entity.Player
import java.util.UUID
import kotlin.random.Random
object VoiceChatPlugin : VoicechatPlugin {
lateinit var api: VoicechatServerApi
lateinit var werewolfGroup: Group
override fun getPluginId(): String = "tcoww"
override fun registerEvents(registration: EventRegistration) {
registration.registerEvent(VoicechatServerStartedEvent::class.java, this::onServerStarted)
registration.registerEvent(LeaveGroupEvent::class.java, this::onGroupRemove)
}
private fun onServerStarted(event: VoicechatServerStartedEvent) {
api = event.voicechat
werewolfGroup =
api
.groupBuilder()
.setPersistent(true)
.setName("werewolf")
.setPassword(Random.nextInt().toString())
.setType(Group.Type.NORMAL)
.setHidden(true)
.build()
}
private fun onGroupRemove(event: LeaveGroupEvent) {
val current = Game.current ?: return
if ((
current.timeGestion.phase == NightLightCycle.Phase.NIGHT ||
current.timeGestion.phase == NightLightCycle.Phase.CREPUSCULAR
)
) {
event.cancel()
}
}
fun addWerewolf(player: Player) {
val connection = api.getConnectionOf(player.uniqueId) ?: return
connection.group = werewolfGroup
}
fun createSoloGroup(player: Player) {
val connection = api.getConnectionOf(player.uniqueId) ?: return
val soloGroup =
api
.groupBuilder()
.setName(UUID.randomUUID().toString())
.setHidden(true)
.setPassword(Random.nextInt().toString())
.setType(Group.Type.ISOLATED)
.setPersistent(false)
.build()
connection.group = soloGroup
}
fun degroupPlayer(player: Player) {
val connection = api.getConnectionOf(player.uniqueId) ?: return
connection.group = null
}
}

View File

@@ -0,0 +1,80 @@
package fr.azur.tcoww.utils.skins
import com.destroystokyo.paper.profile.PlayerProfile
import fr.azur.tcoww.Tcoww
import net.skinsrestorer.api.SkinsRestorer
import net.skinsrestorer.api.SkinsRestorerProvider
import net.skinsrestorer.api.property.SkinIdentifier
import net.skinsrestorer.api.property.SkinProperty
import org.bukkit.entity.Player
import java.util.UUID
import kotlin.jvm.optionals.getOrNull
import kotlin.random.Random
object Manager {
var maxSkinId: Int = 0
private lateinit var skinsRestorer: SkinsRestorer
var oldPlayerSkin = mutableMapOf<UUID, SkinIdentifier?>()
var oldPlayerProfile = mutableMapOf<UUID, PlayerProfile>()
var wwPlayerFur = mutableMapOf<UUID, Int>()
fun handle() {
skinsRestorer = SkinsRestorerProvider.get()
reloadSkin()
}
fun reloadSkin() {
val skinsValue =
Tcoww.instance.config
.getList("werewolf-skins")
.orEmpty()
.mapNotNull { it as? WerewolfSkin }
wwPlayerFur.clear()
maxSkinId = skinsValue.size
skinsValue.forEachIndexed { index, skin ->
skinsRestorer.skinStorage.setCustomSkinData("tcoww_$index", SkinProperty.of(skin.value, skin.signature))
}
}
fun applyWerewolfFur(player: Player) {
val skin = skinsRestorer.playerStorage.getSkinIdOfPlayer(player.uniqueId)
oldPlayerSkin[player.uniqueId] = skin.getOrNull()
oldPlayerProfile[player.uniqueId] = player.playerProfile
val result = skinsRestorer.skinStorage.findOrCreateSkinData("tcoww_${getWerewolfFur(player.uniqueId)}")
if (!result.isEmpty) {
skinsRestorer.playerStorage.setSkinIdOfPlayer(player.uniqueId, result.get().identifier)
skinsRestorer.getSkinApplier(Player::class.java).applySkin(player)
}
}
fun unapplySkin(player: Player) {
val skin = oldPlayerSkin[player.uniqueId]
if (skin == null) {
skinsRestorer.playerStorage.removeSkinIdOfPlayer(player.uniqueId)
} else {
skinsRestorer.playerStorage.setSkinIdOfPlayer(player.uniqueId, skin)
}
skinsRestorer.getSkinApplier(Player::class.java).applySkin(player)
}
private fun getWerewolfFur(playerUUID: UUID): Int =
wwPlayerFur[playerUUID]
?: randomFur(playerUUID)
private fun randomFur(playerUUID: UUID): Int {
val id =
if (maxSkinId == 0) {
0
} else {
Random(playerUUID.mostSignificantBits xor playerUUID.leastSignificantBits).nextInt(
maxSkinId,
)
}
wwPlayerFur[playerUUID] = id
return id
}
}

View File

@@ -0,0 +1,22 @@
package fr.azur.tcoww.utils.skins
import org.bukkit.configuration.serialization.ConfigurationSerializable
data class WerewolfSkin(
val value: String,
val signature: String,
) : ConfigurationSerializable {
constructor(args: MutableMap<String, Object>) : this(
args["value"] as String,
args["signature"] as String,
)
override fun serialize(): Map<String, String> {
val data: MutableMap<String, String> = HashMap()
data["value"] = value
data["signature"] = signature
return data
}
}

View File

@@ -1,38 +1,35 @@
spawnLocation:
==: org.bukkit.Location
world: world
x: 0.0
y: 0.0
z: 0.0
pitch: 0.0
yaw: 0.0
lobbyLocation:
==: org.bukkit.Location
world: world
x: 0.0
y: 0.0
z: 0.0
pitch: 0.0
yaw: 0.0
# Bukkit Location
spawn-location: null
# Bukkit Location
lobby-location: null
# List of Bukkit Location
bed-locations: null
duration: # In seconds
day: 600
vote: 120
night: 300
crepuscular: 20
wwcount: 10
werewolf-default-percentage: 20
# The skin value
wwskinsvalue: [
"ewogICJ0aW1lc3RhbXAiIDogMTYxNzQ2NzI1ODQxMCwKICAicHJvZmlsZUlkIiA6ICJjMGYzYjI3YTUwMDE0YzVhYjIxZDc5ZGRlMTAxZGZlMiIsCiAgInByb2ZpbGVOYW1lIiA6ICJDVUNGTDEzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2Y5NmM4OWUwZWYwZmVlZjZkODc4ZDUzYzQ4OWIyMzliNDdlNmViODQyZDJmM2MzZTlhZDdkMjliZmYxYTM0OWUiCiAgICB9CiAgfQp9",
"ewogICJ0aW1lc3RhbXAiIDogMTYxNTYxMTkwOTY4MCwKICAicHJvZmlsZUlkIiA6ICIwMGM2Yjk0YTY5YmU0MzY3OTkwOTQxNjFjMjAxOWI3ZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJLQUVWRVJZIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2I0MGRjNTcxZGUwZGQ4ZmQ5NzBjNzNmOTE2NGJjMTQ0Njk1MWFlNDhkNWFiZTQyMzI5MGVlYjFhODc4NDU1ZmUiCiAgICB9CiAgfQp9",
"ewogICJ0aW1lc3RhbXAiIDogMTc0Nzk4MDc5ODQyMywKICAicHJvZmlsZUlkIiA6ICI2NWI5NTU1YWQyMGU0NWM5YjFkNmU3MjQwNjU0NTBkNCIsCiAgInByb2ZpbGVOYW1lIiA6ICJKYXZhUHJvZmlsZU5hbWVYIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzJlZTlmNjcxYzFmNjY4Y2IwYWUwNGE5YzhiZWM0OTVlYTVmNzNmNTNmYmY3ZjM5MmNkYjdlMTc1ZmVjZDFjYmUiCiAgICB9CiAgfQp9",
"ewogICJ0aW1lc3RhbXAiIDogMTY0Mjc4ODk3NDg1NSwKICAicHJvZmlsZUlkIiA6ICIzYzE0YmVkNDFiOGE0MDIzOGM3MDgzMTA1NzEwMTZmYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJOb2Jpa28iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWZlNzIzMDdjZTEyNmI1Y2I0ZDQ0ZGFlZTcwZDI1ZmQwNDMwOGJjN2M0MzI4OWM3ZDk3MDY4NDY5NWI2MGJhIgogICAgfQogIH0KfQ=="
]
# The skin signature (see mineskin.org)
wwskinssignature: [
"uWIu/2R3zImj87H7x//O/04eJ1SwsHvLSxaj1YQuI1vBndUVHhN42Ja7AOJoAieVO8yN1qn/rJj1oK5z5koQ2fZwjNCc0mKTrakCMWGqjaAImDw8RWriUQ3ClPCw6NtCmB1qVUp60kUEpGha/btUSsAEUnVZ0ghM4SXTcS2izbDe/dRfbi5EDr2Uue3sz6+med3hjecZzrKJUX19wP1WwPifEOH2X/smzzGOdqlQtylMZQh3nNeho/GBwbDVXsXsR6LcDgoInDnw63cDV7n4dTs6giGmcx0knhDWH24Z/vrgNSqzbNY9hIgVBNWUqs/1o7/xrbK1HilYBPAiVO0F6+vJOpzdFrpEZVr0M++u+X2Zkt1sfshKZqt0O07KbH8h4xj3J+6iDF5RHHFGmZy0awFGZGIi8wqlffwQfZ+l7nPU1hpMD5DkArxBWJWgnquyCNpQ44OlmTpPRa/gopRpuMwJ1r6vDPhgIavJX6cKQ2lKTYKG2/HB+ZEl/IUtuXxKO7bFe4KOvjJpMwBjOmzqbn2sk3GqbSg8NfNMFA4FjRBvHXOaMsDCqz0MJxLFKvIfYRVWG8XAU6BU82RoS3qGFSOhI6N/DvD7dx1V40q52iWv2j4/S41P5zrtMqQm7wofocptz6sS7GLE1u9rPCBLRIck0wzi3OF7MV2p4wlw6lw=",
"kVGwx98YPc6tIZ306TiGI5eoK4v7oEjIvCaOPS03jq4LjBWV12NBTWW67u+HrI2/2m3r8pvAyx1Ddpw4YWyaDbaG0FvLuurWbETqtwvyvVx7R3BZHgw2EnVOYk4+WPmA8xUgzoIbZF/Ch4bSqP7UlZy7/fz4AQwFK/qLMzApOAeV4Kzn4p6+MK2uOkfSjGgwxqAO/+TZj6u01mk1NKNh4ors9RGDG3DBHZKSxTG/PzY5ErKABRXkB/RBMA5IvU5Rzn5cH+Sfkus/7LtYp5u+5i+HcpN/klyBBw4D5di3WzFOAIX78ZiyKzKzWpSN0z6Cb2DZdo1ufeZH9mw8nv+A8VfpGXwphwde+oGBP4l4OffE7YvbnWyVHmlG0L36iI2/FkWQl91GIT15YBHrjeQ9pmvbzCQUMyAHi2jZePTTcT1rDnSzBk7fc9IwfcBRNyqbveTNu9zikfx0W6GnXAbV4qOTIIVEHlpxQ9KW017sqmgiF5l/3ZVwqbEQzglNehYHA55vRfKY0HKbGB54k1oTnM3X7mNXIu6fk8dVgCvT8lVdDm3LWcE6aWF8Yp6QrXNvTNW1yxSRAhigsHrvbajp2Odg18lMTng0oh/4JiYOQAgb6tFdRtQ4nrSVHRBOEvY09IRFzVTTd7n2JaYZ6QcCMOiKOX25fv22rbjSeAZ/Whk=",
"wGc2rNbc7+nziEDMKFoBIltHZwx5PQfsfMGyRTfTUdtVi3O4iu0idcpTsTVJ80njIFZvmLpzlwjPu6ULrXJwgQQyGCNdp60jQMl9vGjzgJzs6uqhSCke3EvjOCuAGkv2ScRY5hDGQOnIruxwmgYgDeXHnujt+zTBvW+imxLVaZhXoKVPyqlvm6njWp0XECyAi8DFKbzIdDVp+NsRzIGjKeVoMgWd4xlNazUZGgTsXNCD/+aZOJ1LxA51I8Wgsm888MUXnaGhNI3U4jJNKHAPZMS4++JEuwWzlptJdZIa1jOfwMD6HvIPKEmC5+eDCRFvPIEh84VqErtNTPEUAz7wZAJvO+j+x5A/9VnQmKJhEBxBjlw1Ky+mQcLJnRX/x9jc4EvEd9gWGIUj46y3IF8jWn71eXvinSJ4Op4LQEiv5BfuyMTfY2xwFTlGWjdYF6Xnac+Gz6+E1gjCBSuqKWd56QFDcrqCcvbG50ERhenCUEhUgI33rXNxS/hwz2iClVM26GFJlUtZkZ7/021FjEQNLEYivI1ZkHe9ip8sxx2QVKd5k7Ui/hXkP4xSal3okL4Ar/cThMh0aoWzziYsoAahtoxFWEags7lNOffMoLGmPbgGVRUGHogNu2CES0SNs4AhHVUVERo8OTJHx46XJxXqvLzeEn6zXFD63XXQR8AyXTQ=",
"hJ/FhIMQY9G/BqOvGJ0nhuJk/7Y/q/unCpeC7LxGvcYjXzsY/kcInwUzgfjRqNI0oI5ggOAdxIiaGvBRqWCshjxcx6iRyJZBMKwFchM1baHntRl22yxvBA16gpMxbWTihQ0Jtz9m/Dj8clYgyEkMOKFEOya5YiyyqjK+YOl5xWiM1pGAomFixqr7VBLk/NE7e+qYXuFjDl3IZ1OP+tgdgyjkqUZE3OBGQfXP6wvTFjgk/maCPUmzffnbw/yH4JZIa3N/vhK+jzowcV+ymZK/9+10ge8hIZ5LZPdU90xLWd2zPYFjju/vr9TFWUG4YkV8l1vSsG7P8MYjMKFh/YQoTyyjJUZw9YGfnRENZrdegttUxefJywpBgp0xVt5vyAMM9XjTEtwK/A5gef0fD52lKWpwD6xamPBnhb8yRPUnncapGBrQvFhB8uQnkS8BKqtiJyz8XJHg8quzUXjYd+Ywn5p+lzP5zsDXQDYOfcm4FZGYp3oFwJZz1s4YOjP8Abjd+4v0sQIcP+Kj8lBzrADQSvTIJaQZO/emnpkZYoW/ggSJLa+BrRUXK5gNHdZzUI/EDOJggrGn3ezSLd96DyDlosG+lGM/EpuiB504YNzQfrm1scVbk9R4YHNhy+sau04DJcRY0p52qeaVDswMkfjUA6JBvBFT9SqQeB7q9qRwLrk="
]
werewolf-skins:
- ==: fr.azur.tcoww.utils.skins.WerewolfSkin
value: "ewogICJ0aW1lc3RhbXAiIDogMTYxNzQ2NzI1ODQxMCwKICAicHJvZmlsZUlkIiA6ICJjMGYzYjI3YTUwMDE0YzVhYjIxZDc5ZGRlMTAxZGZlMiIsCiAgInByb2ZpbGVOYW1lIiA6ICJDVUNGTDEzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2Y5NmM4OWUwZWYwZmVlZjZkODc4ZDUzYzQ4OWIyMzliNDdlNmViODQyZDJmM2MzZTlhZDdkMjliZmYxYTM0OWUiCiAgICB9CiAgfQp9"
signature: "uWIu/2R3zImj87H7x//O/04eJ1SwsHvLSxaj1YQuI1vBndUVHhN42Ja7AOJoAieVO8yN1qn/rJj1oK5z5koQ2fZwjNCc0mKTrakCMWGqjaAImDw8RWriUQ3ClPCw6NtCmB1qVUp60kUEpGha/btUSsAEUnVZ0ghM4SXTcS2izbDe/dRfbi5EDr2Uue3sz6+med3hjecZzrKJUX19wP1WwPifEOH2X/smzzGOdqlQtylMZQh3nNeho/GBwbDVXsXsR6LcDgoInDnw63cDV7n4dTs6giGmcx0knhDWH24Z/vrgNSqzbNY9hIgVBNWUqs/1o7/xrbK1HilYBPAiVO0F6+vJOpzdFrpEZVr0M++u+X2Zkt1sfshKZqt0O07KbH8h4xj3J+6iDF5RHHFGmZy0awFGZGIi8wqlffwQfZ+l7nPU1hpMD5DkArxBWJWgnquyCNpQ44OlmTpPRa/gopRpuMwJ1r6vDPhgIavJX6cKQ2lKTYKG2/HB+ZEl/IUtuXxKO7bFe4KOvjJpMwBjOmzqbn2sk3GqbSg8NfNMFA4FjRBvHXOaMsDCqz0MJxLFKvIfYRVWG8XAU6BU82RoS3qGFSOhI6N/DvD7dx1V40q52iWv2j4/S41P5zrtMqQm7wofocptz6sS7GLE1u9rPCBLRIck0wzi3OF7MV2p4wlw6lw="
- ==: fr.azur.tcoww.utils.skins.WerewolfSkin
value: "ewogICJ0aW1lc3RhbXAiIDogMTYxNTYxMTkwOTY4MCwKICAicHJvZmlsZUlkIiA6ICIwMGM2Yjk0YTY5YmU0MzY3OTkwOTQxNjFjMjAxOWI3ZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJLQUVWRVJZIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2I0MGRjNTcxZGUwZGQ4ZmQ5NzBjNzNmOTE2NGJjMTQ0Njk1MWFlNDhkNWFiZTQyMzI5MGVlYjFhODc4NDU1ZmUiCiAgICB9CiAgfQp9"
signature: "kVGwx98YPc6tIZ306TiGI5eoK4v7oEjIvCaOPS03jq4LjBWV12NBTWW67u+HrI2/2m3r8pvAyx1Ddpw4YWyaDbaG0FvLuurWbETqtwvyvVx7R3BZHgw2EnVOYk4+WPmA8xUgzoIbZF/Ch4bSqP7UlZy7/fz4AQwFK/qLMzApOAeV4Kzn4p6+MK2uOkfSjGgwxqAO/+TZj6u01mk1NKNh4ors9RGDG3DBHZKSxTG/PzY5ErKABRXkB/RBMA5IvU5Rzn5cH+Sfkus/7LtYp5u+5i+HcpN/klyBBw4D5di3WzFOAIX78ZiyKzKzWpSN0z6Cb2DZdo1ufeZH9mw8nv+A8VfpGXwphwde+oGBP4l4OffE7YvbnWyVHmlG0L36iI2/FkWQl91GIT15YBHrjeQ9pmvbzCQUMyAHi2jZePTTcT1rDnSzBk7fc9IwfcBRNyqbveTNu9zikfx0W6GnXAbV4qOTIIVEHlpxQ9KW017sqmgiF5l/3ZVwqbEQzglNehYHA55vRfKY0HKbGB54k1oTnM3X7mNXIu6fk8dVgCvT8lVdDm3LWcE6aWF8Yp6QrXNvTNW1yxSRAhigsHrvbajp2Odg18lMTng0oh/4JiYOQAgb6tFdRtQ4nrSVHRBOEvY09IRFzVTTd7n2JaYZ6QcCMOiKOX25fv22rbjSeAZ/Whk="
- ==: fr.azur.tcoww.utils.skins.WerewolfSkin
value: "ewogICJ0aW1lc3RhbXAiIDogMTc0Nzk4MDc5ODQyMywKICAicHJvZmlsZUlkIiA6ICI2NWI5NTU1YWQyMGU0NWM5YjFkNmU3MjQwNjU0NTBkNCIsCiAgInByb2ZpbGVOYW1lIiA6ICJKYXZhUHJvZmlsZU5hbWVYIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzJlZTlmNjcxYzFmNjY4Y2IwYWUwNGE5YzhiZWM0OTVlYTVmNzNmNTNmYmY3ZjM5MmNkYjdlMTc1ZmVjZDFjYmUiCiAgICB9CiAgfQp9"
signature: "wGc2rNbc7+nziEDMKFoBIltHZwx5PQfsfMGyRTfTUdtVi3O4iu0idcpTsTVJ80njIFZvmLpzlwjPu6ULrXJwgQQyGCNdp60jQMl9vGjzgJzs6uqhSCke3EvjOCuAGkv2ScRY5hDGQOnIruxwmgYgDeXHnujt+zTBvW+imxLVaZhXoKVPyqlvm6njWp0XECyAi8DFKbzIdDVp+NsRzIGjKeVoMgWd4xlNazUZGgTsXNCD/+aZOJ1LxA51I8Wgsm888MUXnaGhNI3U4jJNKHAPZMS4++JEuwWzlptJdZIa1jOfwMD6HvIPKEmC5+eDCRFvPIEh84VqErtNTPEUAz7wZAJvO+j+x5A/9VnQmKJhEBxBjlw1Ky+mQcLJnRX/x9jc4EvEd9gWGIUj46y3IF8jWn71eXvinSJ4Op4LQEiv5BfuyMTfY2xwFTlGWjdYF6Xnac+Gz6+E1gjCBSuqKWd56QFDcrqCcvbG50ERhenCUEhUgI33rXNxS/hwz2iClVM26GFJlUtZkZ7/021FjEQNLEYivI1ZkHe9ip8sxx2QVKd5k7Ui/hXkP4xSal3okL4Ar/cThMh0aoWzziYsoAahtoxFWEags7lNOffMoLGmPbgGVRUGHogNu2CES0SNs4AhHVUVERo8OTJHx46XJxXqvLzeEn6zXFD63XXQR8AyXTQ="
- ==: fr.azur.tcoww.utils.skins.WerewolfSkin
value: "ewogICJ0aW1lc3RhbXAiIDogMTY0Mjc4ODk3NDg1NSwKICAicHJvZmlsZUlkIiA6ICIzYzE0YmVkNDFiOGE0MDIzOGM3MDgzMTA1NzEwMTZmYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJOb2Jpa28iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWZlNzIzMDdjZTEyNmI1Y2I0ZDQ0ZGFlZTcwZDI1ZmQwNDMwOGJjN2M0MzI4OWM3ZDk3MDY4NDY5NWI2MGJhIgogICAgfQogIH0KfQ=="
signature: "hJ/FhIMQY9G/BqOvGJ0nhuJk/7Y/q/unCpeC7LxGvcYjXzsY/kcInwUzgfjRqNI0oI5ggOAdxIiaGvBRqWCshjxcx6iRyJZBMKwFchM1baHntRl22yxvBA16gpMxbWTihQ0Jtz9m/Dj8clYgyEkMOKFEOya5YiyyqjK+YOl5xWiM1pGAomFixqr7VBLk/NE7e+qYXuFjDl3IZ1OP+tgdgyjkqUZE3OBGQfXP6wvTFjgk/maCPUmzffnbw/yH4JZIa3N/vhK+jzowcV+ymZK/9+10ge8hIZ5LZPdU90xLWd2zPYFjju/vr9TFWUG4YkV8l1vSsG7P8MYjMKFh/YQoTyyjJUZw9YGfnRENZrdegttUxefJywpBgp0xVt5vyAMM9XjTEtwK/A5gef0fD52lKWpwD6xamPBnhb8yRPUnncapGBrQvFhB8uQnkS8BKqtiJyz8XJHg8quzUXjYd+Ywn5p+lzP5zsDXQDYOfcm4FZGYp3oFwJZz1s4YOjP8Abjd+4v0sQIcP+Kj8lBzrADQSvTIJaQZO/emnpkZYoW/ggSJLa+BrRUXK5gNHdZzUI/EDOJggrGn3ezSLd96DyDlosG+lGM/EpuiB504YNzQfrm1scVbk9R4YHNhy+sau04DJcRY0p52qeaVDswMkfjUA6JBvBFT9SqQeB7q9qRwLrk="
# The time in second before teleport no sleeping player
playerSleep: 20
player-sleep: 30

View File

@@ -1,5 +1,5 @@
name: tcoww
version: '1.0-SNAPSHOT'
version: '1.2.0'
main: fr.azur.tcoww.Tcoww
api-version: '1.21'
load: POSTWORLD
@@ -9,8 +9,9 @@ dependencies:
server:
SkinsRestorer:
load: BEFORE
required: true
required: false
join-classpath: true
PlasmoVoice:
voicechat:
load: BEFORE
required: true
required: false
join-classpath: true