commit 9ab31b2fe1ac260088739c6700061b292eddcd08 Author: Azur Date: Sat Jun 28 14:53:52 2025 +0200 đŸ–ïžMain commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da2cfa1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,120 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.kotlin/ +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ +runs/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/README.md b/README.md new file mode 100644 index 0000000..e125755 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# TCOWW +TCOWW 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. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..56eedf9 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,69 @@ +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" +} + +group = "fr.azur" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() + gradlePluginPortal() + maven("https://repo.papermc.io/repository/maven-public/") { + name = "papermc-repo" + } + maven("https://oss.sonatype.org/content/groups/public/") { + name = "sonatype" + } + maven("https://repo.codemc.org/repository/maven-public/") { + name = "codemc" + } + maven("https://repo.plasmoverse.com/releases") + maven("https://repo.plasmoverse.com/snapshots") +} + +dependencies { + paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") + + + compileOnly("net.skinsrestorer:skinsrestorer-api:15.6.3") + compileOnly("su.plo.voice.api:server:2.1.4") + + implementation(kotlin("stdlib-jdk8")) +} + +val targetJavaVersion = 21 +kotlin { + jvmToolchain(targetJavaVersion) +} + +tasks { + runServer { + downloadPlugins { + modrinth("plasmo-voice", "spigot-2.1.4") + modrinth("skinsrestorer", "15.6.3") + } + minecraftVersion("1.21.4") + } + build { + dependsOn("shadowJar") + } + processResources { + val props = mapOf("version" to version) + inputs.properties(props) + filteringCharset = "UTF-8" + filesMatching("paper-plugin.yml") { + expand(props) + } + } + shadowJar { + archiveBaseName.set("tcoww") + + minimize() + +// exclude("META-INF/*.kotlin_module") +// exclude("META-INF/*.version") + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e69de29 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..f57a814 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..84e5d91 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "Werewolf Role Play" diff --git a/src/main/kotlin/fr/azur/tcoww/Tcoww.kt b/src/main/kotlin/fr/azur/tcoww/Tcoww.kt new file mode 100644 index 0000000..474ce87 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/Tcoww.kt @@ -0,0 +1,51 @@ +package fr.azur.tcoww + +import fr.azur.tcoww.events.GameEvent +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 org.bukkit.plugin.java.JavaPlugin +import su.plo.voice.api.server.PlasmoVoiceServer + +class Tcoww : JavaPlugin() { + private lateinit var skinsRestorer: SkinsRestorer + private lateinit var skinManager: Skin + val plasmoAddon = PlasmoAddon() + + override fun onEnable() { + saveResource("config.yml", false) + saveDefaultConfig() + + PlasmoVoiceServer.getAddonsLoader().load(plasmoAddon) + + skinsRestorer = SkinsRestorerProvider.get() + skinManager = Skin(this, skinsRestorer) + + server.pluginManager.registerEvents(ToolsEvents(this), this) + server.pluginManager.registerEvents(GameEvent(this, skinManager), this) + + registerRoles() + } + + override fun onDisable() { + + } + + fun reload(withPlasmoAddon: Boolean) { + this.reloadConfig() + skinManager.reloadSkin() + if (withPlasmoAddon) plasmoAddon.onConfigLoaded() + } + + private fun registerRoles() { + Role.registerRole(Villager()) + Role.registerRole(Werewolf()) + Role.registerRole(Child()) + Role.registerRole(FortuneTeller()) + Role.registerRole(Hunter()) + Role.registerRole(Witch()) + } +} diff --git a/src/main/kotlin/fr/azur/tcoww/TcowwBootstrap.kt b/src/main/kotlin/fr/azur/tcoww/TcowwBootstrap.kt new file mode 100644 index 0000000..1294470 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/TcowwBootstrap.kt @@ -0,0 +1,24 @@ +package fr.azur.tcoww + +import fr.azur.tcoww.commands.Power +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 { + 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()) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/commands/GameCommand.kt b/src/main/kotlin/fr/azur/tcoww/commands/GameCommand.kt new file mode 100644 index 0000000..c68ba3a --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/commands/GameCommand.kt @@ -0,0 +1,67 @@ +package fr.azur.tcoww.commands + +import com.mojang.brigadier.Command +import com.mojang.brigadier.arguments.StringArgumentType +import com.mojang.brigadier.builder.LiteralArgumentBuilder +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 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.Bukkit +import org.bukkit.NamespacedKey + + +class GameCommand { + val root: LiteralArgumentBuilder = 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 + } + ) + ) + ).then( + Commands.literal("stop").executes { ctx -> + Game.current?.stopGame(true) + Command.SINGLE_SUCCESS + } + ) +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/commands/Power.kt b/src/main/kotlin/fr/azur/tcoww/commands/Power.kt new file mode 100644 index 0000000..dc7242c --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/commands/Power.kt @@ -0,0 +1,156 @@ +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 = 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.getTargetPlayer(name: String): Player? { + val resolver = this.getArgument(name, PlayerSelectorArgumentResolver::class.java) + return resolver.resolve(this.source).firstOrNull() + } +} diff --git a/src/main/kotlin/fr/azur/tcoww/commands/ReloadCommands.kt b/src/main/kotlin/fr/azur/tcoww/commands/ReloadCommands.kt new file mode 100644 index 0000000..827202d --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/commands/ReloadCommands.kt @@ -0,0 +1,18 @@ +package fr.azur.tcoww.commands + +import com.mojang.brigadier.Command +import com.mojang.brigadier.builder.LiteralArgumentBuilder +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 = 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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/commands/TimeGest.kt b/src/main/kotlin/fr/azur/tcoww/commands/TimeGest.kt new file mode 100644 index 0000000..0dd1b52 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/commands/TimeGest.kt @@ -0,0 +1,44 @@ +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 = 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 + } + ) +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/commands/Tools.kt b/src/main/kotlin/fr/azur/tcoww/commands/Tools.kt new file mode 100644 index 0000000..fc69156 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/commands/Tools.kt @@ -0,0 +1,146 @@ +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 fr.azur.tcoww.Tcoww +import fr.azur.tcoww.items.CustomItems +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.event.ClickEvent +import net.kyori.adventure.text.format.NamedTextColor +import net.kyori.adventure.text.format.TextDecoration +import org.bukkit.Bukkit +import org.bukkit.Location +import org.bukkit.entity.Player + +class Tools { + val root: LiteralArgumentBuilder = 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() + + 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 + } + ) + ) + + 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 != null) { + val loc = Location.deserialize(it as Map) + 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 + ) + }" + ) + ) + .color(NamedTextColor.LIGHT_PURPLE) + .decorate(TextDecoration.UNDERLINED) + ) + .appendNewline() + } + return Component.text("Current bed locations :") + .color(NamedTextColor.DARK_PURPLE) + .appendNewline() + .append(bedLocationComponent) + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/commands/Vote.kt b/src/main/kotlin/fr/azur/tcoww/commands/Vote.kt new file mode 100644 index 0000000..730e2da --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/commands/Vote.kt @@ -0,0 +1,32 @@ +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.game.Phase +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 org.bukkit.entity.Player + +class Vote { + val root: LiteralArgumentBuilder = 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() + 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 + } + Command.SINGLE_SUCCESS + } + ) +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/events/GameEvent.kt b/src/main/kotlin/fr/azur/tcoww/events/GameEvent.kt new file mode 100644 index 0000000..a5d4606 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/events/GameEvent.kt @@ -0,0 +1,432 @@ +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 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 != null) Location.deserialize(it as Map) 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) +} diff --git a/src/main/kotlin/fr/azur/tcoww/events/TimePhaseChangeEvent.kt b/src/main/kotlin/fr/azur/tcoww/events/TimePhaseChangeEvent.kt new file mode 100644 index 0000000..18ba92a --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/events/TimePhaseChangeEvent.kt @@ -0,0 +1,20 @@ +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() + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/events/ToolsEvents.kt b/src/main/kotlin/fr/azur/tcoww/events/ToolsEvents.kt new file mode 100644 index 0000000..ab4d353 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/events/ToolsEvents.kt @@ -0,0 +1,82 @@ +package fr.azur.tcoww.events + +import fr.azur.tcoww.utils.BedGestion +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.NamespacedKey +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 { + @EventHandler + fun clickEvent(event: PlayerInteractEvent) { + val tool = event.player.inventory.itemInMainHand + + if (!tool.persistentDataContainer.has(NamespacedKey("tcoww", "tools"))) return + + when (tool.persistentDataContainer.get(NamespacedKey("tcoww", "tools"), 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 block = BedGestion.getHeadBed(interactloc.block) + + if (!list.contains(block.location.serialize())) { + list.add(block.location.serialize()) + plugin.config.set("bedLocation", list) + plugin.saveConfig() + event.player.sendMessage( + Component.text("Bed location add.").color(NamedTextColor.LIGHT_PURPLE) + ) + } else { + event.player.sendMessage( + Component.text("Bed location already exists.").color(NamedTextColor.DARK_RED) + ) + } + } else { + event.player.sendMessage( + Component.text("It's not a bed...").color(NamedTextColor.DARK_RED) + ) + } + } + } + + 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)) + } + } + + 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)) + } + } + } + } + +// @EventHandler +// fun reloadAfterWorld(worldInitEvent: WorldInitEvent) { +// +// } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/game/Game.kt b/src/main/kotlin/fr/azur/tcoww/game/Game.kt new file mode 100644 index 0000000..9400407 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/game/Game.kt @@ -0,0 +1,129 @@ +package fr.azur.tcoww.game + +import fr.azur.tcoww.roles.Role +import fr.azur.tcoww.roles.Villager +import fr.azur.tcoww.roles.Werewolf +import net.kyori.adventure.text.Component +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.persistence.PersistentDataType +import org.bukkit.plugin.Plugin +import org.bukkit.scheduler.BukkitTask +import kotlin.math.ceil + +class Game(var plugin: Plugin, val world: World, val players: Iterable, var roles: Iterable) { + val werewolfPercentage: Float = plugin.config.getInt("wwcount").toFloat() + val timeGestion: TimeGestion + val playersMutable = players.toMutableList() + val timeGestShedule: BukkitTask + var wwcankill = false + + 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.inventory.clear() + player.health = 20.0 + } + + assignRoles() + + timeGestion = TimeGestion(plugin, world) + + timeGestShedule = Bukkit.getScheduler().runTaskTimer(plugin, Runnable { + timeGestion.tick() + }, 0L, 1L) + + 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()) + } + } + + companion object { + 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 stopGame(withoutResult: Boolean = false) { + current ?: return + current = null + timeGestShedule.cancel() + world.time = 18000 + 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 + } + }, 200) + } +} diff --git a/src/main/kotlin/fr/azur/tcoww/game/Phase.kt b/src/main/kotlin/fr/azur/tcoww/game/Phase.kt new file mode 100644 index 0000000..7c067a3 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/game/Phase.kt @@ -0,0 +1,8 @@ +package fr.azur.tcoww.game + +enum class Phase { + DAY, + VOTE, + NIGHT, + CREPUSCULAR +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/game/TimeGestion.kt b/src/main/kotlin/fr/azur/tcoww/game/TimeGestion.kt new file mode 100644 index 0000000..985956d --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/game/TimeGestion.kt @@ -0,0 +1,81 @@ +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() + } +} diff --git a/src/main/kotlin/fr/azur/tcoww/items/CustomItems.kt b/src/main/kotlin/fr/azur/tcoww/items/CustomItems.kt new file mode 100644 index 0000000..de2fa5e --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/items/CustomItems.kt @@ -0,0 +1,226 @@ +package fr.azur.tcoww.items + +import fr.azur.tcoww.game.Game +import fr.azur.tcoww.roles.Role +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 +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.CrossbowMeta +import org.bukkit.inventory.meta.Damageable +import org.bukkit.inventory.meta.PotionMeta +import org.bukkit.persistence.PersistentDataType +import org.bukkit.potion.PotionEffect +import org.bukkit.potion.PotionEffectType + +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) + ) + ) + } + } + }), + 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) + } + } + } + }), + 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) + } + } + }), + 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 pour l'utiliser.").color(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 pour l'utiliser.").color(NamedTextColor.GOLD) + ) + ) + } + } + }), + 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 pour l'utiliser.").color(NamedTextColor.GOLD))) + } + } + }), + 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))) + } + } + }), + 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))) + } + } + }), + 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))) + } + } + }), + 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 players = Game.current?.playersMutable?.shuffled().orEmpty() + + val (villagers, werewolves) = players.partition { + Role.getRole(it)?.lg == false + } + + val lg = werewolves.firstOrNull() + val names = mutableListOf() + + 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) + } + } + }); + + private var cachedItem: ItemStack? = null + + val item: ItemStack + get() { + return if (cache) { + cachedItem ?: supplier().also { cachedItem = it } + } else { + supplier() + } + } + + 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 + ) + + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/plasmovoice/addon/PlasmoAddon.kt b/src/main/kotlin/fr/azur/tcoww/plasmovoice/addon/PlasmoAddon.kt new file mode 100644 index 0000000..ae8d63e --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/plasmovoice/addon/PlasmoAddon.kt @@ -0,0 +1,37 @@ +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 = "..." + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/roles/Child.kt b/src/main/kotlin/fr/azur/tcoww/roles/Child.kt new file mode 100644 index 0000000..fa0589b --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/roles/Child.kt @@ -0,0 +1,22 @@ +package fr.azur.tcoww.roles + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +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") + 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) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/roles/FortuneTeller.kt b/src/main/kotlin/fr/azur/tcoww/roles/FortuneTeller.kt new file mode 100644 index 0000000..6d9f66b --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/roles/FortuneTeller.kt @@ -0,0 +1,25 @@ +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.NamespacedKey +import org.bukkit.entity.Player + +class FortuneTeller : Role { + override fun handle(player: Player) { + player.inventory.addItem(CustomItems.CrystalBall.item) + } + + override fun onDeath(player: Player) {} + + override var lg = false + override var id = NamespacedKey("tcoww", "fortune_teller") + override val displayName = Component.text("Voyante") + 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) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/roles/Hunter.kt b/src/main/kotlin/fr/azur/tcoww/roles/Hunter.kt new file mode 100644 index 0000000..cd8db61 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/roles/Hunter.kt @@ -0,0 +1,34 @@ +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.NamespacedKey +import org.bukkit.entity.Player + +class Hunter : Role { + override fun handle(player: Player) { + player.inventory.addItem(CustomItems.BlasTechDL44.item) + } + + override fun onDeath(player: Player) { + TODO("Not yet implemented") + } + + override var lg = false + override var id = NamespacedKey("tcoww", "hunter") + override val displayName = Component.text("Chaseur") + 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) + ) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/roles/Role.kt b/src/main/kotlin/fr/azur/tcoww/roles/Role.kt new file mode 100644 index 0000000..db7e86b --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/roles/Role.kt @@ -0,0 +1,49 @@ +package fr.azur.tcoww.roles + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +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 + val id: NamespacedKey + val displayName: Component + val hasPowerCommand: Boolean + val description: Component + + companion object { + var registerRoles = hashMapOf() + private val rolekey = NamespacedKey("tcoww", "role") + fun setRole(player: Player, role: Role) { + player.persistentDataContainer.set( + rolekey, + PersistentDataType.STRING, + role.id.toString() + ) + player.sendMessage( + Component.text("Votre role est ").append(role.displayName).color(NamedTextColor.DARK_AQUA) + ) + 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) + }) + role.handle(player) + } + + fun getRole(player: Player): Role? { + val rolestr = player.persistentDataContainer.get(rolekey, PersistentDataType.STRING) + if (rolestr == null) return null + return registerRoles[NamespacedKey.fromString(rolestr)] + } + + fun registerRole(role: Role) { + registerRoles.put(role.id, role) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/roles/Villager.kt b/src/main/kotlin/fr/azur/tcoww/roles/Villager.kt new file mode 100644 index 0000000..6808fbb --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/roles/Villager.kt @@ -0,0 +1,26 @@ +package fr.azur.tcoww.roles + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +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 + override var id = NamespacedKey("tcoww", "villager") + override val displayName = Component.text("Villageois") + 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) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/roles/Werewolf.kt b/src/main/kotlin/fr/azur/tcoww/roles/Werewolf.kt new file mode 100644 index 0000000..472d148 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/roles/Werewolf.kt @@ -0,0 +1,25 @@ +package fr.azur.tcoww.roles + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +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 + override var id = NamespacedKey("tcoww", "werewolf") + override val displayName = Component.text("Loup-Garou") + 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 + ) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/roles/Witch.kt b/src/main/kotlin/fr/azur/tcoww/roles/Witch.kt new file mode 100644 index 0000000..8ae71fb --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/roles/Witch.kt @@ -0,0 +1,29 @@ +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.NamespacedKey +import org.bukkit.entity.Player + +class Witch : Role { + override fun handle(player: Player) { + player.inventory.addItem(CustomItems.DeathPotion.item) + player.inventory.addItem(CustomItems.HealPotion.item) + } + + override fun onDeath(player: Player) {} + + override var lg = false + override var id = NamespacedKey("tcoww", "witch") + override val displayName = Component.text("SorciĂšre") + 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() +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/utils/BedGestion.kt b/src/main/kotlin/fr/azur/tcoww/utils/BedGestion.kt new file mode 100644 index 0000000..fe53937 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/utils/BedGestion.kt @@ -0,0 +1,24 @@ +package fr.azur.tcoww.utils + +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 getHeadBed(block: Block): Block { + val bedData = block.blockData as Bed + return if (bedData.part == Bed.Part.FOOT) { + block.getRelative(bedData.facing) + } else { + block + } + } + + fun isOcupied(block: Block): Boolean { + val bedData = block.blockData as Bed + return bedData.isOccupied + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/utils/Skin.kt b/src/main/kotlin/fr/azur/tcoww/utils/Skin.kt new file mode 100644 index 0000000..28d6093 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/utils/Skin.kt @@ -0,0 +1,65 @@ +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 + } + +} \ No newline at end of file diff --git a/src/main/kotlin/fr/azur/tcoww/utils/Stockage.kt b/src/main/kotlin/fr/azur/tcoww/utils/Stockage.kt new file mode 100644 index 0000000..5e2a562 --- /dev/null +++ b/src/main/kotlin/fr/azur/tcoww/utils/Stockage.kt @@ -0,0 +1,13 @@ +package fr.azur.tcoww.utils + +import net.skinsrestorer.api.property.SkinIdentifier +import org.bukkit.Location +import java.util.* + + +object Stockage { + var backLocation = mutableMapOf() + var vote = mutableMapOf() + var oldPlayerSkin = mutableMapOf() + var wwPlayerFur = mutableMapOf() +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..94dd547 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,38 @@ +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 +duration: # In seconds + day: 600 + vote: 120 + night: 300 + crepuscular: 20 +wwcount: 10 +# 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=" +] +# The time in second before teleport no sleeping player +playerSleep: 20 \ No newline at end of file diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml new file mode 100644 index 0000000..ca97b60 --- /dev/null +++ b/src/main/resources/paper-plugin.yml @@ -0,0 +1,16 @@ +name: tcoww +version: '1.0-SNAPSHOT' +main: fr.azur.tcoww.Tcoww +api-version: '1.21' +load: POSTWORLD +authors: [ Azur84 ] +bootstrapper: fr.azur.tcoww.TcowwBootstrap +dependencies: + server: + SkinsRestorer: + load: BEFORE + required: true + join-classpath: true + PlasmoVoice: + load: BEFORE + required: true \ No newline at end of file