✔️ Prepare plugin 1.2 version.

This commit is contained in:
2025-11-30 17:22:48 +01:00
parent f0d1e27085
commit fa5a54c8b9
44 changed files with 2031 additions and 1525 deletions

View File

@@ -3,16 +3,18 @@ plugins {
id("com.gradleup.shadow") version "9.2.2" id("com.gradleup.shadow") version "9.2.2"
id("xyz.jpenilla.run-paper") version "3.0.2" id("xyz.jpenilla.run-paper") version "3.0.2"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19" id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
id("org.jlleitschuh.gradle.ktlint") version "14.0.1"
} }
group = "fr.azur" group = "fr.azur"
version = "1.1-SNAPSHOT"
val version: String by project
val minecraftVersion: String by project val minecraftVersion: String by project
val paperVersion: String by project val paperVersion: String by project
val skinRestorerVersion: String by project val skinRestorerVersion: String by project
val voiceChatVersion: String by project val voiceChatVersion: String by project
val voiceChatApiVersion: String by project val voiceChatApiVersion: String by project
val luckPermsVersion: String by project
repositories { repositories {
mavenCentral() mavenCentral()
@@ -34,11 +36,11 @@ repositories {
dependencies { dependencies {
paperweight.paperDevBundle("$minecraftVersion-$paperVersion") paperweight.paperDevBundle("$minecraftVersion-$paperVersion")
compileOnly("net.skinsrestorer:skinsrestorer-api:$skinRestorerVersion")
implementation("net.skinsrestorer:skinsrestorer-api:$skinRestorerVersion") compileOnly("de.maxhenkel.voicechat:voicechat-api:$voiceChatApiVersion")
implementation("de.maxhenkel.voicechat:voicechat-api:$voiceChatApiVersion")
implementation(kotlin("stdlib-jdk8")) implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
} }
val targetJavaVersion = 21 val targetJavaVersion = 21
@@ -49,8 +51,9 @@ kotlin {
tasks { tasks {
runServer { runServer {
downloadPlugins { downloadPlugins {
modrinth("plasmo-voice", "bukkit-$voiceChatVersion")
modrinth("skinsrestorer", skinRestorerVersion) modrinth("skinsrestorer", skinRestorerVersion)
modrinth("luckperms", luckPermsVersion)
modrinth("simple-voice-chat", voiceChatVersion)
} }
minecraftVersion(minecraftVersion) minecraftVersion(minecraftVersion)
} }
@@ -69,8 +72,5 @@ tasks {
archiveBaseName.set("tcoww") archiveBaseName.set("tcoww")
minimize() minimize()
// exclude("META-INF/*.kotlin_module")
// exclude("META-INF/*.version")
} }
} }

View File

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

View File

@@ -1,50 +1,64 @@
package fr.azur.tcoww package fr.azur.tcoww
import de.maxhenkel.voicechat.api.BukkitVoicechatService import de.maxhenkel.voicechat.api.BukkitVoicechatService
import fr.azur.tcoww.events.GameEvent import fr.azur.tcoww.events.GameEvents
import fr.azur.tcoww.events.PowerEvents
import fr.azur.tcoww.events.ToolsEvents import fr.azur.tcoww.events.ToolsEvents
import fr.azur.tcoww.roles.* import fr.azur.tcoww.events.UIEvents
import fr.azur.tcoww.utils.Skin import fr.azur.tcoww.roles.Child
import fr.azur.tcoww.voicechat.VoiceChatPlugin import fr.azur.tcoww.roles.FortuneTeller
import net.skinsrestorer.api.SkinsRestorer import fr.azur.tcoww.roles.Hunter
import net.skinsrestorer.api.SkinsRestorerProvider 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 org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scoreboard.Team
class Tcoww : JavaPlugin() { object Tcoww : JavaPlugin() {
private lateinit var skinsRestorer: SkinsRestorer
private lateinit var skinManager: Skin
override fun onEnable() { override fun onEnable() {
saveResource("config.yml", false) ConfigurationSerialization.registerClass(WerewolfSkin::class.java)
saveDefaultConfig() saveDefaultConfig()
skinsRestorer = SkinsRestorerProvider.get() Manager.handle()
skinManager = Skin(this, skinsRestorer)
server.pluginManager.registerEvents(ToolsEvents(this), this) server.pluginManager.apply {
server.pluginManager.registerEvents(GameEvent(this, skinManager), this) registerEvents(UIEvents, this@Tcoww)
registerEvents(GameEvents, this@Tcoww)
registerEvents(PowerEvents, this@Tcoww)
registerEvents(ToolsEvents, this@Tcoww)
}
val vcservice = server.servicesManager.load(BukkitVoicechatService::class.java) val service = server.servicesManager.load(BukkitVoicechatService::class.java)
vcservice?.registerPlugin(VoiceChatPlugin()) service?.registerPlugin(VoiceChatPlugin)
registerRoles() registerRoles()
val mainScoreboard = server.scoreboardManager.mainScoreboard
if (mainScoreboard.getTeam("NoCollide") == null) {
val team = server.scoreboardManager.mainScoreboard.registerNewTeam("NoCollision")
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER)
}
} }
override fun onDisable() { override fun onDisable() {
} }
fun reload() { fun reload() {
this.reloadConfig() this.reloadConfig()
skinManager.reloadSkin()
} }
private fun registerRoles() { private fun registerRoles() {
Role.registerRole(Villager()) Role.registerRole(Villager)
Role.registerRole(Werewolf()) Role.registerRole(Werewolf)
Role.registerRole(Child()) Role.registerRole(Child)
Role.registerRole(FortuneTeller()) Role.registerRole(FortuneTeller)
Role.registerRole(Hunter()) Role.registerRole(Hunter)
Role.registerRole(Witch()) Role.registerRole(Witch)
} }
} }

View File

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

View File

@@ -1,22 +1,20 @@
package fr.azur.tcoww.commands package fr.azur.tcoww.commands
import com.mojang.brigadier.Command import com.mojang.brigadier.Command
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import fr.azur.tcoww.game.Game import fr.azur.tcoww.game.Game
import io.papermc.paper.command.brigadier.CommandSourceStack import fr.azur.tcoww.utils.Players.kill
import io.papermc.paper.command.brigadier.Commands import io.papermc.paper.command.brigadier.Commands
import io.papermc.paper.command.brigadier.argument.ArgumentTypes import io.papermc.paper.command.brigadier.argument.ArgumentTypes
import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.GameMode
class Exclude { object Exclude {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("exclude") val root =
Commands
.literal("exclude")
.requires { sender -> .requires { sender ->
sender.sender.isOp sender.sender.isOp
}.then( }.then(
Commands.literal("start")
.then(
Commands.argument("players", ArgumentTypes.playerProfiles()).executes { ctx -> Commands.argument("players", ArgumentTypes.playerProfiles()).executes { ctx ->
val targetResolver = ctx.getArgument("players", PlayerProfileListResolver::class.java) val targetResolver = ctx.getArgument("players", PlayerProfileListResolver::class.java)
val target = targetResolver.resolve(ctx.source).first() val target = targetResolver.resolve(ctx.source).first()
@@ -25,16 +23,10 @@ class Exclude {
val player = Bukkit.getPlayer(uuid) val player = Bukkit.getPlayer(uuid)
if (player != null) { if (player != null) {
val game = Game.current ?: return@executes Command.SINGLE_SUCCESS val game = Game.current ?: return@executes Command.SINGLE_SUCCESS
player.gameMode = GameMode.SPECTATOR player.kill()
player.isInvulnerable = false
game.playersMutable.remove(player)
} }
} }
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} },
).build()
)
)
} }

View File

@@ -1,67 +1,90 @@
package fr.azur.tcoww.commands package fr.azur.tcoww.commands
import com.mojang.brigadier.Command import com.mojang.brigadier.Command
import com.mojang.brigadier.arguments.StringArgumentType import com.mojang.brigadier.tree.LiteralCommandNode
import com.mojang.brigadier.builder.LiteralArgumentBuilder import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.game.Game import fr.azur.tcoww.game.Game
import fr.azur.tcoww.roles.Role import fr.azur.tcoww.ui.GameConfig
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.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands import io.papermc.paper.command.brigadier.Commands
import io.papermc.paper.command.brigadier.argument.ArgumentTypes import io.papermc.paper.dialog.Dialog
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver 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.Bukkit
import org.bukkit.GameMode
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.World
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
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(),
),
)
}
class GameCommand { val root: LiteralCommandNode<CommandSourceStack> =
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("game") Commands
.literal("game")
.requires { sender -> .requires { sender ->
sender.sender.isOp sender.sender.isOp
}.then( }.then(
Commands.literal("start") Commands
.then( .literal("start")
Commands.argument("players", ArgumentTypes.players()).then( .executes { ctx ->
Commands.argument("roles", StringArgumentType.greedyString()).suggests { ctx, builder -> val executor = ctx.source.executor ?: ctx.source.sender
val roleswithoutauto = if (executor is Player) {
Role.registerRoles.filter { role -> role.value !is Villager && role.value !is Werewolf } val players =
val rolessplit = builder.remaining.split(" ") Bukkit
val startstr = rolessplit.dropLast(1).joinToString(separator = " ") .getOnlinePlayers()
.filter { it.gameMode == GameMode.ADVENTURE || it.gameMode == GameMode.SURVIVAL }
roleswithoutauto.forEach { role -> val menu = GameConfig(players.size)
if (role.key.toString().startsWith(rolessplit.last())) { executor.openInventory(menu.inventory)
builder.suggest("$startstr${if (startstr.isEmpty()) "" else " "}${role.key}") menu.future.thenAccept {
val dialog = createDialog(Tcoww, executor.world, players, it)
executor.showDialog(dialog)
} }
} }
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 Command.SINGLE_SUCCESS
} },
)
)
).then( ).then(
Commands.literal("stop").executes { ctx -> Commands.literal("stop").executes { ctx ->
Game.current?.stopGame(true) Game.current?.stopGame(true)
Command.SINGLE_SUCCESS 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 package fr.azur.tcoww.commands
import com.mojang.brigadier.Command 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.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands import io.papermc.paper.command.brigadier.Commands
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.entity.Player import org.bukkit.entity.Player
class ReloadCommands { object ReloadCommands {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("reloadcommands").executes { ctx -> val root: LiteralCommandNode<CommandSourceStack> =
Commands
.literal("reload-commands")
.executes { ctx ->
val sender = ctx.source.sender as? Player val sender = ctx.source.sender as? Player
sender?.updateCommands() sender?.updateCommands()
sender?.sendMessage(Component.text("Commands rechargés !").color(NamedTextColor.GOLD)) sender?.sendMessage(Component.text("Commands rechargés !", NamedTextColor.GOLD))
Command.SINGLE_SUCCESS 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.Command
import com.mojang.brigadier.arguments.IntegerArgumentType import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.arguments.StringArgumentType 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.Tcoww
import fr.azur.tcoww.items.CustomItems import fr.azur.tcoww.items.CustomItems
import io.papermc.paper.command.brigadier.CommandSourceStack import io.papermc.paper.command.brigadier.CommandSourceStack
@@ -16,14 +16,19 @@ import org.bukkit.Bukkit
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.entity.Player import org.bukkit.entity.Player
class Tools { object Tools {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("tools") val root: LiteralCommandNode<CommandSourceStack> =
Commands
.literal("tools")
.requires { sender -> .requires { sender ->
sender.sender.isOp sender.sender.isOp
}.then( }.then(
Commands.literal("location") Commands
.literal("location")
.then( .then(
Commands.literal("bed").executes { ctx -> Commands
.literal("bed")
.executes { ctx ->
val player = ctx.source.executor as Player val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.BedLocTool.item) player.inventory.addItem(CustomItems.BedLocTool.item)
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
@@ -31,52 +36,58 @@ class Tools {
Commands.literal("get").executes { ctx -> Commands.literal("get").executes { ctx ->
ctx.source.sender.sendMessage(sendBed()) ctx.source.sender.sendMessage(sendBed())
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} },
).then( ).then(
Commands.literal("remove").then( Commands.literal("remove").then(
Commands.argument("index", IntegerArgumentType.integer(0)) Commands
.argument("index", IntegerArgumentType.integer(0))
.executes { ctx -> .executes { ctx ->
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") ?: return@executes 0 val plugin = Bukkit.getPluginManager().getPlugin("tcoww") ?: return@executes 0
val index = IntegerArgumentType.getInteger(ctx, "index") val index = IntegerArgumentType.getInteger(ctx, "index")
val list = plugin.config.getMapList("bedLocation") val list =
plugin.config
.getList("bed-locations")
.orEmpty()
.mapNotNull { it as? Location }
plugin.config.set("bedLocation", list.minus(list[index])) plugin.config.set("bed-locations", list.drop(index))
plugin.saveConfig() plugin.saveConfig()
ctx.source.sender.sendMessage(sendBed()) ctx.source.sender.sendMessage(sendBed())
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} },
)) ),
),
).then( ).then(
Commands.literal("spawn").executes { ctx -> Commands.literal("spawn").executes { ctx ->
val player = ctx.source.executor as Player val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.SpawnLocTool.item) player.inventory.addItem(CustomItems.SpawnLocTool.item)
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} },
).then( ).then(
Commands.literal("lobby").executes { ctx -> Commands.literal("lobby").executes { ctx ->
val player = ctx.source.executor as Player val player = ctx.source.executor as Player
player.inventory.addItem(CustomItems.LobbyLocTool.item) player.inventory.addItem(CustomItems.LobbyLocTool.item)
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} },
) ),
).then( ).then(
Commands.literal("reload").executes { Commands.literal("reload").executes {
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") as Tcoww val plugin = Bukkit.getPluginManager().getPlugin("tcoww") as Tcoww
plugin.reload() plugin.reload()
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} },
).then( ).then(
Commands.literal("customitem").then( Commands.literal("customitem").then(
Commands.argument("item", StringArgumentType.greedyString()) Commands
.argument("item", StringArgumentType.greedyString())
.suggests { _, builder -> .suggests { _, builder ->
CustomItems.entries.forEach { CustomItems.entries.forEach {
builder.suggest(it.name) builder.suggest(it.name)
} }
builder.buildFuture() builder.buildFuture()
} }.executes { ctx ->
.executes { ctx ->
val itemName = StringArgumentType.getString(ctx, "item") val itemName = StringArgumentType.getString(ctx, "item")
val item = CustomItems.valueOf(itemName).item val item = CustomItems.valueOf(itemName).item
val exe = ctx.source.executor val exe = ctx.source.executor
@@ -84,61 +95,58 @@ class Tools {
exe.inventory.addItem(item) exe.inventory.addItem(item)
} }
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} },
) ),
) ).build()
fun sendBed(): Component { fun sendBed(): Component {
val plugin = Bukkit.getPluginManager().getPlugin("tcoww") ?: return Component.empty() val plugin = Bukkit.getPluginManager().getPlugin("tcoww") ?: return Component.empty()
val bedlist = plugin.config.getMapList("bedLocation") val bedList =
val bedLocationComponent = bedlist.map { plugin.config
if (it as? Map<String, Object> != null) { .getList("bed-locations")
val loc = Location.deserialize(it as Map<String, Object>) .orEmpty()
Component.text("[${loc.x}, ${loc.y}, ${loc.z}] ") val bedLocationComponent =
bedList.mapIndexed { index, element ->
if (element is Location) {
Component
.text("[${element.x}, ${element.y}, ${element.z}] ")
.color(NamedTextColor.LIGHT_PURPLE) .color(NamedTextColor.LIGHT_PURPLE)
.append( .append(
Component.text("teleport") Component
.text("teleport")
.clickEvent( .clickEvent(
ClickEvent.runCommand("/minecraft:tp ${loc.x.toInt()} ${loc.y.toInt()} ${loc.z.toInt()}") ClickEvent.runCommand("/minecraft:tp ${element.x} ${element.y} ${element.z}"),
) ).color(NamedTextColor.LIGHT_PURPLE)
.color(NamedTextColor.LIGHT_PURPLE) .decorate(TextDecoration.UNDERLINED),
.decorate(TextDecoration.UNDERLINED) ).appendSpace()
)
.appendSpace()
.append( .append(
Component.text("remove") Component
.text("remove")
.clickEvent( .clickEvent(
ClickEvent.runCommand( ClickEvent.runCommand(
"/tcoww:tools location bed remove ${ "/tcoww:tools location bed remove $index",
bedlist.indexOf( ),
it ).color(NamedTextColor.LIGHT_PURPLE)
) .decorate(TextDecoration.UNDERLINED),
}" ).appendNewline()
) } else {
) Component
.color(NamedTextColor.LIGHT_PURPLE) .text("[unkown location] ")
.decorate(TextDecoration.UNDERLINED)
)
.appendNewline()
} else Component.text("[unkown location] ")
.color(NamedTextColor.LIGHT_PURPLE) .color(NamedTextColor.LIGHT_PURPLE)
.append( .append(
Component.text("remove") Component
.text("remove")
.clickEvent( .clickEvent(
ClickEvent.runCommand( ClickEvent.runCommand(
"/tcoww:tools location bed remove ${ "/tcoww:tools location bed remove $index",
bedlist.indexOf( ),
it ).color(NamedTextColor.LIGHT_PURPLE)
) .decorate(TextDecoration.UNDERLINED),
}" ).appendNewline()
)
)
.color(NamedTextColor.LIGHT_PURPLE)
.decorate(TextDecoration.UNDERLINED)
)
.appendNewline()
} }
return Component.text("Current bed locations :") }
return Component
.text("Current bed locations :")
.color(NamedTextColor.DARK_PURPLE) .color(NamedTextColor.DARK_PURPLE)
.appendNewline() .appendNewline()
.append(bedLocationComponent) .append(bedLocationComponent)

View File

@@ -1,32 +1,53 @@
package fr.azur.tcoww.commands package fr.azur.tcoww.commands
import com.mojang.brigadier.Command 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.Game
import fr.azur.tcoww.game.Phase import fr.azur.tcoww.game.NightLightCycle
import fr.azur.tcoww.utils.Stockage import fr.azur.tcoww.ui.PlayerSelectMenu
import io.papermc.paper.command.brigadier.CommandSourceStack import io.papermc.paper.command.brigadier.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands import io.papermc.paper.command.brigadier.Commands
import io.papermc.paper.command.brigadier.argument.ArgumentTypes import io.papermc.paper.command.brigadier.argument.ArgumentTypes
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver
import org.bukkit.entity.Player import org.bukkit.entity.Player
import java.util.UUID
class Vote { object Vote {
val root: LiteralArgumentBuilder<CommandSourceStack> = Commands.literal("vote").then( var vote = mutableMapOf<UUID, UUID>()
Commands.argument("target", ArgumentTypes.player())
val root: LiteralCommandNode<CommandSourceStack> =
Commands
.literal("vote")
.then(
Commands
.argument("target", ArgumentTypes.player())
.requires { ctx -> .requires { ctx ->
Game.current?.timeGestion?.phase == Phase.VOTE && Game.current!!.playersMutable.contains(ctx.sender) Game.current?.timeGestion?.phase == NightLightCycle.Phase.VOTE &&
}.suggests { ctx, builder -> Game.current!!.remainingPlayer.contains(ctx.sender)
Game.current?.playersMutable?.forEach { player -> builder.suggest(player.name) } }.suggests { _, builder ->
Game.current?.remainingPlayer?.forEach { player -> builder.suggest(player.name) }
builder.buildFuture() builder.buildFuture()
}.executes { ctx -> }.executes { ctx ->
val targetResolver = ctx.getArgument("target", PlayerSelectorArgumentResolver::class.java) val targetResolver = ctx.getArgument("target", PlayerSelectorArgumentResolver::class.java)
val target = targetResolver.resolve(ctx.source).first() val target = targetResolver.resolve(ctx.source).first()
val player = (ctx.source.executor ?: ctx.source.sender as Player) as Player val player = (ctx.source.executor ?: ctx.source.sender as Player) as Player
if (Game.current?.playersMutable?.contains(target) ?: false) { if (Game.current?.remainingPlayer?.contains(target) ?: false) {
Stockage.vote[player.uniqueId] = target.uniqueId vote[player.uniqueId] = target.uniqueId
} }
Command.SINGLE_SUCCESS 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
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,421 +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.health = 0.0
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
)
)
}
@EventHandler(
priority = EventPriority.MONITOR,
ignoreCancelled = true
)
fun endSleep(event: PlayerBedLeaveEvent) {
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.05,
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,247 @@
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.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.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.config.getLocation("lobby-location")
Bukkit.getScheduler().runTaskLater(
Tcoww,
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
}
if (player.persistentDataContainer.get(
PlayerDead,
PersistentDataType.BOOLEAN,
) == true
) {
return@callback
}
if (item == null) return@callback
(item.itemMeta as Damageable).damage = 1
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) 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)
}
}
@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))
}
}
}
}

View File

@@ -0,0 +1,178 @@
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.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.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.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),
),
)
}
}
NightLightCycle.Phase.NIGHT -> {
if (Vote.vote.isNotEmpty()) {
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,
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()
}
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,
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.config.getLong("player-sleep"),
)
}
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,157 @@
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.inventory.meta.Damageable
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 (player.hasCooldown(item)) return
when (item.persistentDataContainer.get(NamespacedKey("tcoww", "power"), PersistentDataType.INTEGER)) {
1 -> {
if ((item.itemMeta as Damageable).damage == 1) return
val current = Game.current ?: return
val menu = PlayerSelectMenu(current.remainingPlayer)
player.openInventory(menu.inventory)
menu.future.whenComplete { selectedPlayer, _ ->
val target = selectedPlayer ?: return@whenComplete
(item.itemMeta as Damageable).damage = 1
target.addPotionEffect(PotionEffect(PotionEffectType.WITHER, 600, 4))
}
}
2 -> {
if ((item.itemMeta as Damageable).damage == 1) return
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
(item.itemMeta as Damageable).damage = 1
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 -> {
player.setCooldown(item, 600)
if (player.isTransformed()) {
player.tfHuman()
} else {
player.tfWerewolf()
}
}
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 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.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.NamespacedKey import org.bukkit.Location
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
import org.bukkit.plugin.java.JavaPlugin
class ToolsEvents(val plugin: JavaPlugin) : Listener { object ToolsEvents : Listener {
@EventHandler @EventHandler
fun clickEvent(event: PlayerInteractEvent) { fun clickEvent(event: PlayerInteractEvent) {
val tool = event.player.inventory.itemInMainHand 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 -> { 1 -> {
event.isCancelled = true event.isCancelled = true
val interactloc = event.interactionPoint val interactLoc = event.interactionPoint
if (interactloc != null) { if (interactLoc != null) {
if (BedGestion.isBed(interactloc.block)) { if (interactLoc.block.isBed()) {
val list = plugin.config.getMapList("bedLocation") val list =
Tcoww.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())) { if (!list.contains(block.location)) {
list.add(block.location.serialize()) list.add(block.location)
plugin.config.set("bedLocation", list) Tcoww.config.set("bed-locations", list)
plugin.saveConfig() Tcoww.saveConfig()
event.player.sendMessage( event.player.sendMessage(
Component.text("Bed location add.").color(NamedTextColor.LIGHT_PURPLE) Component.text("Bed location add.", NamedTextColor.LIGHT_PURPLE),
) )
} else { } else {
event.player.sendMessage( event.player.sendMessage(
Component.text("Bed location already exists.").color(NamedTextColor.DARK_RED) Component.text("Bed location already exists.", NamedTextColor.DARK_RED),
) )
} }
} else { } else {
event.player.sendMessage( 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 -> { 2 -> {
event.isCancelled = true event.isCancelled = true
val interactloc = event.interactionPoint val interactLoc = event.interactionPoint
if (interactloc != null) { if (interactLoc != null) {
interactloc.x = interactloc.blockX.toDouble() interactLoc.x = interactLoc.blockX.toDouble()
interactloc.y = interactloc.blockY.toDouble() + 1 interactLoc.y = interactLoc.blockY.toDouble() + 1
interactloc.z = interactloc.blockZ.toDouble() interactLoc.z = interactLoc.blockZ.toDouble()
plugin.config.set("spawnLocation", interactloc) Tcoww.config.set("spawn-location", interactLoc)
plugin.saveConfig() Tcoww.saveConfig()
event.player.sendMessage(Component.text("Spawn location set.").color(NamedTextColor.LIGHT_PURPLE)) event.player.sendMessage(Component.text("Spawn location set.", NamedTextColor.LIGHT_PURPLE))
} }
} }
3 -> { 3 -> {
event.isCancelled = true event.isCancelled = true
val interactloc = event.interactionPoint val interactLoc = event.interactionPoint
if (interactloc != null) { if (interactLoc != null) {
interactloc.x = interactloc.blockX.toDouble() interactLoc.x = interactLoc.blockX.toDouble()
interactloc.y = interactloc.blockY.toDouble() + 1 interactLoc.y = interactLoc.blockY.toDouble() + 1
interactloc.z = interactloc.blockZ.toDouble() interactLoc.z = interactLoc.blockZ.toDouble()
plugin.config.set("lobbyLocation", interactloc) Tcoww.config.set("lobby-location", interactLoc)
plugin.saveConfig() Tcoww.saveConfig()
event.player.sendMessage(Component.text("Lobby location set.").color(NamedTextColor.LIGHT_PURPLE)) 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,35 +1,42 @@
package fr.azur.tcoww.game package fr.azur.tcoww.game
import fr.azur.tcoww.Tcoww
import fr.azur.tcoww.roles.Role import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.roles.Villager import fr.azur.tcoww.roles.Role.Companion.werewolfRole
import fr.azur.tcoww.roles.Werewolf import fr.azur.tcoww.utils.Players.tfHuman
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.GameMode import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.World import org.bukkit.World
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.persistence.PersistentDataType
import org.bukkit.plugin.Plugin import org.bukkit.plugin.Plugin
import org.bukkit.potion.PotionEffect import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType import org.bukkit.potion.PotionEffectType
import org.bukkit.scheduler.BukkitTask
import kotlin.math.ceil
class Game(var plugin: Plugin, val world: World, val players: Iterable<Player>, var roles: Iterable<Role>) { class Game(
val werewolfPercentage: Float = plugin.config.getInt("wwcount").toFloat() var plugin: Plugin,
val timeGestion: TimeGestion val world: World,
val playersMutable = players.toMutableList() val players: Iterable<Player>,
val timeGestShedule: BukkitTask var roles: Map<NamespacedKey, Int>,
var wwcankill = false ) {
val timeGestion: NightLightCycle
val remainingPlayer = players.toMutableList()
val beds =
Tcoww.config
.getList("bed-locations")
.orEmpty()
.mapNotNull { it as? Location }
init { init {
players.forEach { player -> players.forEach { player ->
player.persistentDataContainer.set(NamespacedKey("tcoww", "dead"), PersistentDataType.BOOLEAN, false) player.tfHuman()
player.persistentDataContainer.set(NamespacedKey("tcoww", "wwtranform"), PersistentDataType.BOOLEAN, false) val spawnLoc = plugin.config.getLocation("spawn-location")
player.teleport(plugin.config.getLocation("spawnLocation")!!) if (spawnLoc != null) player.teleport(spawnLoc)
player.inventory.clear() player.inventory.clear()
player.gameMode = GameMode.ADVENTURE
player.health = 20.0 player.health = 20.0
player.addPotionEffect( player.addPotionEffect(
PotionEffect( PotionEffect(
@@ -37,47 +44,27 @@ class Game(var plugin: Plugin, val world: World, val players: Iterable<Player>,
PotionEffect.INFINITE_DURATION, PotionEffect.INFINITE_DURATION,
1, 1,
false, false,
false false,
) ),
) )
} }
assignRoles() assignRoles()
timeGestion = TimeGestion(plugin, world) timeGestion = NightLightCycle(plugin, world)
timeGestShedule = Bukkit.getScheduler().runTaskTimer(plugin, Runnable {
timeGestion.tick()
}, 0L, 1L)
current = this current = this
} }
fun getWerewolfInt(): Int {
val playerCount = players.count()
return ceil(playerCount * (werewolfPercentage / 100f)).toInt()
}
private fun assignRoles() { private fun assignRoles() {
val playerList = players.shuffled().toMutableList() val playersShuffled = players.shuffled()
var playerIndex = 0
val werewolfCount = getWerewolfInt() roles.forEach {
val specialRolesCount = roles.count() val role = Role.registerRoles.getValue(it.key)
for (i in 0..<it.value) {
if (werewolfCount + specialRolesCount > playerList.size) { playersShuffled[playerIndex].werewolfRole = role
throw IllegalArgumentException("Not enough players for the given number of werewolves and special roles.") playerIndex++
} }
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())
} }
} }
@@ -85,56 +72,54 @@ class Game(var plugin: Plugin, val world: World, val players: Iterable<Player>,
var current: Game? = null var current: Game? = null
} }
fun gameEnded(): Boolean { fun checkGameEnd(): Boolean {
var hasWerewolf = false val team = players.map { player -> player.werewolfRole.team }.toSet()
var hasVillager = false return team.count() <= 1
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) { fun stopGame(withoutResult: Boolean = false) {
current ?: return current ?: return
current = null current = null
timeGestShedule.cancel() timeGestion.stop()
world.time = 18000 world.time = 18000
players.forEach { player -> players.forEach { player ->
player.gameMode = GameMode.SPECTATOR player.gameMode = GameMode.SPECTATOR
player.sendMessage( player.sendMessage(
Component.text("La partie est terminée !", NamedTextColor.DARK_GREEN) Component
.text("La partie est terminée !", NamedTextColor.DARK_GREEN)
.appendNewline() .appendNewline()
.append { .apply {
if (withoutResult) if (!withoutResult) return@apply
Component.empty() remainingPlayer.forEachIndexed { index, player ->
else { append(player.displayName())
val villageHasWolf = playersMutable.any {
Role.getRole(it)?.lg ?: false if (index != remainingPlayer.lastIndex) {
} append(
if (villageHasWolf) {
Component.text( Component.text(
"Les loups-garous ont finalement dévoré le village.", when (index) {
NamedTextColor.DARK_RED remainingPlayer.lastIndex - 1 -> " et "
) else -> ", "
} else { },
Component.text( NamedTextColor.DARK_GREEN,
"Les villageois ont finalement sauvé le village.", ),
NamedTextColor.DARK_BLUE
) )
} }
append(Component.text(" ont gagné !", NamedTextColor.DARK_GREEN))
} }
} },
) )
} }
Bukkit.getScheduler().runTaskLater(plugin, Runnable { val lobbyLoc = plugin.config.getLocation("lobby-location")
Bukkit.getScheduler().runTaskLater(
plugin,
Runnable {
players.forEach { player -> players.forEach { player ->
player.teleport(plugin.config.getLocation("lobbyLocation")!!) if (lobbyLoc != null) player.teleport(lobbyLoc)
player.gameMode = GameMode.ADVENTURE player.gameMode = GameMode.ADVENTURE
} }
}, 200) },
200,
)
} }
} }

View File

@@ -0,0 +1,127 @@
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,
) : 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
var tickOrigin: Int = 0
var realDifference: Int
var tickDifference: Int
var dayCount: Int = 0
init {
world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
realDifference = duration.day * 1000
targetInstant = System.currentTimeMillis() + realDifference.toLong()
tickDifference = 12_000
Bukkit.getPluginManager().callEvent(NightLightCyclePhaseChangeEvent(world, phase, duration))
process =
Bukkit.getScheduler().runTaskTimer(
plugin,
Runnable {
this.tick()
},
0L,
1L,
)
}
fun changePhase(newPhase: Phase) {
when (newPhase) {
Phase.DAY -> {
tickDifference = 12_000
tickOrigin = 0
realDifference = duration.day * 1000
}
Phase.VOTE -> {
tickDifference = 1_000
tickOrigin = 12_000
realDifference = duration.vote * 1000
}
Phase.NIGHT -> {
tickDifference = 11_000
tickOrigin = 13_000
realDifference = duration.night * 1000
}
Phase.CREPUSCULAR -> {
tickDifference = 1_000
tickOrigin = 23_000
realDifference = duration.crepuscular * 1000
}
}
targetInstant = System.currentTimeMillis() + realDifference
phase = newPhase
Bukkit.getPluginManager().callEvent(NightLightCyclePhaseChangeEvent(world, newPhase, duration))
}
fun tick() {
if (System.currentTimeMillis() >= targetInstant) {
val phase = phases[(phases.indexOf(phase) + 1) % phases.count()]
if (phase == Phase.DAY) {
dayCount += 1
}
changePhase(phase)
}
}
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.game.Game
import fr.azur.tcoww.roles.Role import fr.azur.tcoww.roles.Role
import fr.azur.tcoww.utils.DataKeys
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration import net.kyori.adventure.text.format.TextDecoration
import org.bukkit.Color import org.bukkit.Color
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.enchantments.Enchantment import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.inventory.ItemRarity import org.bukkit.inventory.ItemRarity
@@ -19,31 +19,38 @@ import org.bukkit.persistence.PersistentDataType
import org.bukkit.potion.PotionEffect import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType 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, { WereWolfTransformItem(true, {
ItemStack.of(Material.BONE).apply { ItemStack.of(Material.BONE).apply {
itemMeta = itemMeta.apply { itemMeta =
customName(Component.text("Os de transformation").color(NamedTextColor.DARK_RED)) itemMeta.apply {
customName(Component.text("Os de transformation", NamedTextColor.DARK_RED))
persistentDataContainer.set( persistentDataContainer.set(
NamespacedKey("tcoww", "power"), DataKeys.PowerItems,
PersistentDataType.INTEGER, PersistentDataType.INTEGER,
3 3,
) )
lore( lore(
listOf( listOf(
Component.text("Clique pour basculer en forme loup-garou.") Component
.color(NamedTextColor.GOLD) .text("Clique pour basculer en forme loup-garou.")
) .color(NamedTextColor.GOLD),
),
) )
} }
} }
}), }),
GunBullet(true, { GunBullet(true, {
ItemStack(Material.TIPPED_ARROW).apply { ItemStack(Material.TIPPED_ARROW).apply {
itemMeta = (itemMeta as PotionMeta).apply { itemMeta =
(itemMeta as PotionMeta).apply {
color = Color.ORANGE color = Color.ORANGE
displayName(Component.text("Balle de fusil.", NamedTextColor.YELLOW)) displayName(Component.text("Balle de fusil.", NamedTextColor.YELLOW))
basePotionType = basePotionType.apply { basePotionType =
basePotionType.apply {
addCustomEffect(PotionEffect(PotionEffectType.INSTANT_DAMAGE, 20, 99), true) addCustomEffect(PotionEffect(PotionEffectType.INSTANT_DAMAGE, 20, 99), true)
} }
} }
@@ -51,123 +58,138 @@ enum class CustomItems(val cache: Boolean, val supplier: () -> ItemStack) {
}), }),
BlasTechDL44(true, { BlasTechDL44(true, {
ItemStack.of(Material.CROSSBOW).apply { ItemStack.of(Material.CROSSBOW).apply {
itemMeta = (itemMeta as CrossbowMeta).apply { itemMeta =
(itemMeta as DamageableCrossbow).apply {
addChargedProjectile(GunBullet.item) addChargedProjectile(GunBullet.item)
displayName(Component.text("BlasTech DL-44", NamedTextColor.GOLD)) displayName(Component.text("BlasTech DL-44", NamedTextColor.GOLD))
addEnchant(Enchantment.INFINITY, 1, true) addEnchant(Enchantment.INFINITY, 1, true)
}
itemMeta = (itemMeta as Damageable).apply {
setMaxDamage(1) setMaxDamage(1)
} }
} }
}), }),
CrystalBall(true, { CrystalBall(true, {
ItemStack.of(Material.GLASS).apply { ItemStack.of(Material.GLASS).apply {
itemMeta = itemMeta.apply { itemMeta =
itemMeta.apply {
customName( customName(
Component.text("Boule de cristal").color(NamedTextColor.AQUA) Component.text("Boule de cristal", NamedTextColor.AQUA),
) )
persistentDataContainer.set( persistentDataContainer.set(
NamespacedKey("tcoww", "power"), DataKeys.PowerItems,
PersistentDataType.INTEGER, PersistentDataType.INTEGER,
4 4,
) )
lore(listOf(Component.text("/power view <pseudo> pour l'utiliser.").color(NamedTextColor.GOLD))) setMaxStackSize(1)
lore(listOf(Component.text("Click droit pour utiliser.", NamedTextColor.GOLD)))
} }
} }
} }),
),
DeathPotion(true, { DeathPotion(true, {
ItemStack.of(Material.POTION).apply { ItemStack.of(Material.POTION).apply {
itemMeta = (itemMeta as PotionMeta).apply { itemMeta =
(itemMeta as DamageablePotion).apply {
setMaxStackSize(1)
setMaxDamage(1)
customName( customName(
Component.text("Potion de poison").color(NamedTextColor.DARK_PURPLE) Component.text("Potion de poison", NamedTextColor.DARK_PURPLE),
) )
persistentDataContainer.set( persistentDataContainer.set(
NamespacedKey("tcoww", "power"), DataKeys.PowerItems,
PersistentDataType.INTEGER, PersistentDataType.INTEGER,
1 1,
) )
color = Color.BLACK color = Color.BLACK
lore( lore(
listOf( listOf(
Component.text("/power kill <pseudo> pour l'utiliser.").color(NamedTextColor.GOLD) Component.text("Click droit pour utiliser.", NamedTextColor.GOLD),
) ),
) )
} }
} }
}), }),
HealPotion(true, { HealPotion(true, {
ItemStack.of(Material.POTION).apply { ItemStack.of(Material.POTION).apply {
itemMeta = (itemMeta as PotionMeta).apply { itemMeta =
(itemMeta as DamageablePotion).apply {
setMaxStackSize(1)
setMaxDamage(1)
customName( customName(
Component.text("Potion de vie").color(NamedTextColor.DARK_PURPLE) Component.text("Potion de vie", NamedTextColor.DARK_PURPLE),
) )
persistentDataContainer.set( persistentDataContainer.set(
NamespacedKey("tcoww", "power"), DataKeys.PowerItems,
PersistentDataType.INTEGER, PersistentDataType.INTEGER,
2 2,
) )
color = Color.RED color = Color.RED
lore(listOf(Component.text("/power save <pseudo> pour l'utiliser.").color(NamedTextColor.GOLD))) lore(listOf(Component.text("Click droit pour utiliser.", NamedTextColor.GOLD)))
} }
} }
}), }),
SpawnLocTool(true, { SpawnLocTool(true, {
ItemStack.of(Material.SUGAR_CANE).apply { ItemStack.of(Material.SUGAR_CANE).apply {
itemMeta = itemMeta.apply { itemMeta =
itemMeta.apply {
setRarity(ItemRarity.EPIC) setRarity(ItemRarity.EPIC)
persistentDataContainer.set( persistentDataContainer.set(
NamespacedKey("tcoww", "tools"), DataKeys.ToolsItems,
PersistentDataType.INTEGER, PersistentDataType.INTEGER,
2 2,
) )
lore(listOf(Component.text("Spawn location tool").color(NamedTextColor.GOLD))) lore(listOf(Component.text("Spawn location tool", NamedTextColor.GOLD)))
} }
} }
}), }),
BedLocTool(true, { BedLocTool(true, {
ItemStack.of(Material.WOODEN_HOE).apply { ItemStack.of(Material.WOODEN_HOE).apply {
itemMeta = itemMeta.apply { itemMeta =
itemMeta.apply {
setRarity(ItemRarity.EPIC) setRarity(ItemRarity.EPIC)
persistentDataContainer.set( persistentDataContainer.set(
NamespacedKey("tcoww", "tools"), DataKeys.ToolsItems,
PersistentDataType.INTEGER, PersistentDataType.INTEGER,
1 1,
) )
lore(listOf(Component.text("bed location tool").color(NamedTextColor.GOLD))) lore(listOf(Component.text("bed location tool", NamedTextColor.GOLD)))
} }
} }
}), }),
LobbyLocTool(true, { LobbyLocTool(true, {
ItemStack.of(Material.POPPED_CHORUS_FRUIT).apply { ItemStack.of(Material.POPPED_CHORUS_FRUIT).apply {
itemMeta = itemMeta.apply { itemMeta =
itemMeta.apply {
setRarity(ItemRarity.EPIC) setRarity(ItemRarity.EPIC)
persistentDataContainer.set( persistentDataContainer.set(
NamespacedKey("tcoww", "tools"), DataKeys.ToolsItems,
PersistentDataType.INTEGER, PersistentDataType.INTEGER,
3 3,
) )
lore(listOf(Component.text("Lobby location tool").color(NamedTextColor.GOLD))) lore(listOf(Component.text("Lobby location tool", NamedTextColor.GOLD)))
} }
} }
}), }),
ListHint(false, { ListHint(false, {
val finalLore = buildList { val finalLore =
buildList {
add( add(
Component.text("C'est une liste de nom.", NamedTextColor.RED) Component.text("C'est une liste de nom.", NamedTextColor.RED),
) )
add( add(
Component.text("Il y a écrit que un de ces 3 citoyens", NamedTextColor.DARK_RED) Component.text("Il y a écrit que un de ces 3 citoyens", NamedTextColor.DARK_RED),
) )
add( add(
Component.text("est un loup-garou.", NamedTextColor.DARK_RED) 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 { val (villagers, werewolves) =
Role.getRole(it)?.lg == false players.partition {
Role.getRole(it)?.team != Role.Team.LG
} }
val lg = werewolves.firstOrNull() val lg = werewolves.firstOrNull()
@@ -188,12 +210,14 @@ enum class CustomItems(val cache: Boolean, val supplier: () -> ItemStack) {
} }
} }
ItemStack(Material.PAPER).apply { ItemStack(Material.PAPER).apply {
itemMeta = itemMeta.apply { itemMeta =
itemMeta.apply {
displayName(Component.text("Liste de nom", NamedTextColor.GOLD)) displayName(Component.text("Liste de nom", NamedTextColor.GOLD))
lore(finalLore) lore(finalLore)
} }
} }
}); }),
;
private var cachedItem: ItemStack? = null private var cachedItem: ItemStack? = null
@@ -206,8 +230,19 @@ enum class CustomItems(val cache: Boolean, val supplier: () -> ItemStack) {
} }
} }
interface DamageableCrossbow :
Damageable,
CrossbowMeta
interface DamageablePotion :
Damageable,
PotionMeta {
override fun clone(): DamageablePotion
}
data object NamedColors { data object NamedColors {
val namedColors = listOf( val namedColors =
listOf(
NamedTextColor.DARK_RED, NamedTextColor.DARK_RED,
NamedTextColor.RED, NamedTextColor.RED,
NamedTextColor.GOLD, NamedTextColor.GOLD,
@@ -219,8 +254,7 @@ enum class CustomItems(val cache: Boolean, val supplier: () -> ItemStack) {
NamedTextColor.DARK_BLUE, NamedTextColor.DARK_BLUE,
NamedTextColor.BLUE, NamedTextColor.BLUE,
NamedTextColor.LIGHT_PURPLE, NamedTextColor.LIGHT_PURPLE,
NamedTextColor.DARK_PURPLE NamedTextColor.DARK_PURPLE,
) )
} }
} }

View File

@@ -2,21 +2,22 @@ package fr.azur.tcoww.roles
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
class Child : Role { object Child : Role {
override fun handle(player: Player) {} override val id = NamespacedKey("tcoww", "child")
override val team = Role.Team.Villager
override fun onDeath(player: Player) {} override val displayName = Component.text("Petite Fille", NamedTextColor.LIGHT_PURPLE)
override val icon = Material.MAGENTA_WOOL
override var lg = false override val order: Int = 2
override var id = NamespacedKey("tcoww", "child")
override val displayName = Component.text("Petite Fille")
override val hasPowerCommand = false override val hasPowerCommand = false
override val description = override val description =
Component.text("Extrêmement discrette et au coeur du risque.", NamedTextColor.BLUE).appendNewline() listOf(
.append( 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) 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 fr.azur.tcoww.items.CustomItems
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.entity.Player import org.bukkit.entity.Player
class FortuneTeller : Role { object FortuneTeller : Role {
override fun handle(player: Player) { override fun handle(player: Player) {
player.inventory.addItem(CustomItems.CrystalBall.item) player.inventory.addItem(CustomItems.CrystalBall.item)
} }
override fun onDeath(player: Player) {} override val team = Role.Team.Villager
override var lg = false
override var id = NamespacedKey("tcoww", "fortune_teller") 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 hasPowerCommand = true
override val description = override val description =
Component.text("Vous voyez des choses mais quoi ?.", NamedTextColor.BLUE).appendNewline() listOf(
.append( Component.text("Vous voyez des choses mais quoi ?.", NamedTextColor.BLUE),
Component.text("Tout les 2 jours vous pouvez regarder le role d'un joueur.", 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 fr.azur.tcoww.items.CustomItems
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.entity.Player import org.bukkit.entity.Player
class Hunter : Role { object Hunter : Role {
override fun handle(player: Player) { override fun handle(player: Player) {
player.inventory.addItem(CustomItems.BlasTechDL44.item) player.inventory.addItem(CustomItems.BlasTechDL44.item)
} }
override fun onDeath(player: Player) { override val team = Role.Team.Villager
TODO("Not yet implemented")
}
override var lg = false
override var id = NamespacedKey("tcoww", "hunter") 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 hasPowerCommand = false
override val description = override val description =
listOf(
Component.text( Component.text(
"Vous êtes la personne, la plus redouter des animaux (et en particulier des loups).", "Vous êtes la personne, la plus redouter des animaux (et en particulier des loups).",
NamedTextColor.BLUE NamedTextColor.BLUE,
).appendNewline() ),
.append( Component
Component.text("Vous possedez un ", NamedTextColor.BLUE) .text("Vous possedez un ", NamedTextColor.BLUE)
.append(CustomItems.BlasTechDL44.item.displayName()) .append(CustomItems.BlasTechDL44.item.displayName())
.append( .append(
Component.text(" qui permet de one shot n'importe quoi... (Usage Unique.)", NamedTextColor.BLUE) Component.text(" qui permet de one shot n'importe quoi... (Usage Unique)", NamedTextColor.BLUE),
) ),
) )
} }

View File

@@ -2,48 +2,87 @@ package fr.azur.tcoww.roles
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
interface Role { interface Role {
fun handle(player: Player) enum class Team {
fun onDeath(player: Player) LG {
val lg: Boolean 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 id: NamespacedKey
val icon: Material
val order: Int
val team: Team
val displayName: Component val displayName: Component
val hasPowerCommand: Boolean val hasPowerCommand: Boolean
val description: Component val description: List<Component>
val lineDescription: Component
get() {
return Component.empty().apply {
description.forEach {
append(it)
appendNewline()
}
}
}
companion object { companion object {
var registerRoles = hashMapOf<NamespacedKey, Role>() var registerRoles = hashMapOf<NamespacedKey, Role>()
private val rolekey = NamespacedKey("tcoww", "role") private val roleKey = NamespacedKey("tcoww", "role")
fun setRole(player: Player, role: Role) {
player.persistentDataContainer.set( fun setRole(
rolekey, player: Player,
PersistentDataType.STRING, role: Role,
role.id.toString() ) {
player.sendMessage(
Component.text("Votre role est ", NamedTextColor.DARK_AQUA).append(role.displayName),
) )
player.sendMessage( player.sendMessage(
Component.text("Votre role est ").append(role.displayName).color(NamedTextColor.DARK_AQUA) role.lineDescription.append {
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))
}
},
) )
player.sendMessage(role.description.appendNewline().append {
if (role.lg) player.persistentDataContainer.set(roleKey, PersistentDataType.STRING, role.id.toString())
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? { fun getRole(player: Player): Role? {
val rolestr = player.persistentDataContainer.get(rolekey, PersistentDataType.STRING) val roleKey = player.persistentDataContainer.get(roleKey, PersistentDataType.STRING) ?: return null
if (rolestr == null) return null return registerRoles[NamespacedKey.fromString(roleKey)]
return registerRoles[NamespacedKey.fromString(rolestr)]
} }
var Player.werewolfRole: Role
get() = getRole(this)!!
set(role) = setRole(this, role)
fun registerRole(role: 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.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
class Villager : Role { object Villager : Role {
override fun handle(player: Player) { override val team = Role.Team.Villager
}
override fun onDeath(player: Player) {
}
override var lg: Boolean = false
override var id = NamespacedKey("tcoww", "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 hasPowerCommand = false
override val description = override val description =
Component.text("En tant que villageois, vous n'avez aucun pouvoir.", NamedTextColor.BLUE).appendNewline() listOf(
.append( 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) Component.text("Profiter en pour risquer pour trouver des indices ! ^^", NamedTextColor.BLUE),
) )
} }

View File

@@ -1,25 +1,32 @@
package fr.azur.tcoww.roles 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.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.entity.Player import org.bukkit.entity.Player
class Werewolf : Role { object Werewolf : Role {
override fun handle(player: Player) {} override val team = Role.Team.LG
override fun onDeath(player: Player) {}
override var lg = true
override var id = NamespacedKey("tcoww", "werewolf") 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 hasPowerCommand = false
override val description = override val description =
Component.text("Pour la faire courte, vous avez faim, très faim.", NamedTextColor.BLUE).appendNewline() listOf(
.append( Component.text("Pour la faire courte, vous avez faim, très faim.", NamedTextColor.BLUE),
Component.text( 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.", "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 NamedTextColor.BLUE,
) ),
) )
override fun handle(player: Player) {
val tfItem = CustomItems.WereWolfTransformItem.item
player.inventory.addItem(tfItem)
player.setCooldown(tfItem, ((Tcoww.config.getInt("duration.day") + Tcoww.config.getInt("duration.day")) * 20))
}
} }

View File

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

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.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.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,46 @@
package fr.azur.tcoww.ui
import fr.azur.tcoww.Tcoww
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.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 = player.playerProfile
}
}
setItem(index, item)
}
}
override fun getInventory(): Inventory = inventory
override fun onClick(event: InventoryClickEvent) {
val index = event.slot
if (index !in 0..<players.count() || event.clickedInventory != inventory) return
future.complete(players.elementAt(index))
}
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 import org.bukkit.block.data.type.Bed
object BedGestion { object BedGestion {
fun isBed(block: Block): Boolean { fun Block.isBed() = blockData is Bed
return block.blockData is Bed
}
fun getHeadBed(block: Block): Block { fun Block.getHeadBed(): Block {
val bedData = block.blockData as Bed val bedData = blockData as Bed
return if (bedData.part == Bed.Part.FOOT) { return if (bedData.part == Bed.Part.FOOT) {
block.getRelative(bedData.facing) getRelative(bedData.facing)
} else { } else {
block this
} }
} }
fun isOcupied(block: Block): Boolean { fun Block.isOccupied(): Boolean {
val bedData = block.blockData as Bed val bedData = blockData as Bed
return bedData.isOccupied 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, "dead")
val PowerItems = NamespacedKey(Tcoww, "power")
val Insomnia = NamespacedKey(Tcoww, "insomnia")
val ToolsItems = NamespacedKey(Tcoww, "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("NoCollide")
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,58 @@
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.VoicechatServerStartedEvent
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)
}
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()
}
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,77 @@
package fr.azur.tcoww.utils.skins
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 wwPlayerFur = mutableMapOf<UUID, Int>()
fun handle() {
skinsRestorer = SkinsRestorerProvider.get()
reloadSkin()
}
fun reloadSkin() {
val skinsValue =
Tcoww.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()
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,14 +0,0 @@
package fr.azur.tcoww.voicechat
import de.maxhenkel.voicechat.api.VoicechatPlugin
import de.maxhenkel.voicechat.api.events.EventRegistration
class VoiceChatPlugin: VoicechatPlugin {
override fun getPluginId(): String {
return "tcow"
}
override fun registerEvents(registration: EventRegistration) {
}
}

View File

@@ -1,38 +1,35 @@
spawnLocation: # Bukkit Location
==: org.bukkit.Location spawn-location: null
world: world
x: 0.0 # Bukkit Location
y: 0.0 lobby-location: null
z: 0.0
pitch: 0.0 # List of Bukkit Location
yaw: 0.0 bed-locations: null
lobbyLocation:
==: org.bukkit.Location
world: world
x: 0.0
y: 0.0
z: 0.0
pitch: 0.0
yaw: 0.0
duration: # In seconds duration: # In seconds
day: 600 day: 600
vote: 120 vote: 120
night: 300 night: 300
crepuscular: 20 crepuscular: 20
wwcount: 10
werewolf-default-percentage: 20
# The skin value # The skin value
wwskinsvalue: [
"ewogICJ0aW1lc3RhbXAiIDogMTYxNzQ2NzI1ODQxMCwKICAicHJvZmlsZUlkIiA6ICJjMGYzYjI3YTUwMDE0YzVhYjIxZDc5ZGRlMTAxZGZlMiIsCiAgInByb2ZpbGVOYW1lIiA6ICJDVUNGTDEzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2Y5NmM4OWUwZWYwZmVlZjZkODc4ZDUzYzQ4OWIyMzliNDdlNmViODQyZDJmM2MzZTlhZDdkMjliZmYxYTM0OWUiCiAgICB9CiAgfQp9",
"ewogICJ0aW1lc3RhbXAiIDogMTYxNTYxMTkwOTY4MCwKICAicHJvZmlsZUlkIiA6ICIwMGM2Yjk0YTY5YmU0MzY3OTkwOTQxNjFjMjAxOWI3ZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJLQUVWRVJZIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2I0MGRjNTcxZGUwZGQ4ZmQ5NzBjNzNmOTE2NGJjMTQ0Njk1MWFlNDhkNWFiZTQyMzI5MGVlYjFhODc4NDU1ZmUiCiAgICB9CiAgfQp9",
"ewogICJ0aW1lc3RhbXAiIDogMTc0Nzk4MDc5ODQyMywKICAicHJvZmlsZUlkIiA6ICI2NWI5NTU1YWQyMGU0NWM5YjFkNmU3MjQwNjU0NTBkNCIsCiAgInByb2ZpbGVOYW1lIiA6ICJKYXZhUHJvZmlsZU5hbWVYIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzJlZTlmNjcxYzFmNjY4Y2IwYWUwNGE5YzhiZWM0OTVlYTVmNzNmNTNmYmY3ZjM5MmNkYjdlMTc1ZmVjZDFjYmUiCiAgICB9CiAgfQp9",
"ewogICJ0aW1lc3RhbXAiIDogMTY0Mjc4ODk3NDg1NSwKICAicHJvZmlsZUlkIiA6ICIzYzE0YmVkNDFiOGE0MDIzOGM3MDgzMTA1NzEwMTZmYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJOb2Jpa28iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWZlNzIzMDdjZTEyNmI1Y2I0ZDQ0ZGFlZTcwZDI1ZmQwNDMwOGJjN2M0MzI4OWM3ZDk3MDY4NDY5NWI2MGJhIgogICAgfQogIH0KfQ=="
]
# The skin signature (see mineskin.org) # The skin signature (see mineskin.org)
wwskinssignature: [ werewolf-skins:
"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
"kVGwx98YPc6tIZ306TiGI5eoK4v7oEjIvCaOPS03jq4LjBWV12NBTWW67u+HrI2/2m3r8pvAyx1Ddpw4YWyaDbaG0FvLuurWbETqtwvyvVx7R3BZHgw2EnVOYk4+WPmA8xUgzoIbZF/Ch4bSqP7UlZy7/fz4AQwFK/qLMzApOAeV4Kzn4p6+MK2uOkfSjGgwxqAO/+TZj6u01mk1NKNh4ors9RGDG3DBHZKSxTG/PzY5ErKABRXkB/RBMA5IvU5Rzn5cH+Sfkus/7LtYp5u+5i+HcpN/klyBBw4D5di3WzFOAIX78ZiyKzKzWpSN0z6Cb2DZdo1ufeZH9mw8nv+A8VfpGXwphwde+oGBP4l4OffE7YvbnWyVHmlG0L36iI2/FkWQl91GIT15YBHrjeQ9pmvbzCQUMyAHi2jZePTTcT1rDnSzBk7fc9IwfcBRNyqbveTNu9zikfx0W6GnXAbV4qOTIIVEHlpxQ9KW017sqmgiF5l/3ZVwqbEQzglNehYHA55vRfKY0HKbGB54k1oTnM3X7mNXIu6fk8dVgCvT8lVdDm3LWcE6aWF8Yp6QrXNvTNW1yxSRAhigsHrvbajp2Odg18lMTng0oh/4JiYOQAgb6tFdRtQ4nrSVHRBOEvY09IRFzVTTd7n2JaYZ6QcCMOiKOX25fv22rbjSeAZ/Whk=", value: "ewogICJ0aW1lc3RhbXAiIDogMTYxNzQ2NzI1ODQxMCwKICAicHJvZmlsZUlkIiA6ICJjMGYzYjI3YTUwMDE0YzVhYjIxZDc5ZGRlMTAxZGZlMiIsCiAgInByb2ZpbGVOYW1lIiA6ICJDVUNGTDEzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2Y5NmM4OWUwZWYwZmVlZjZkODc4ZDUzYzQ4OWIyMzliNDdlNmViODQyZDJmM2MzZTlhZDdkMjliZmYxYTM0OWUiCiAgICB9CiAgfQp9"
"wGc2rNbc7+nziEDMKFoBIltHZwx5PQfsfMGyRTfTUdtVi3O4iu0idcpTsTVJ80njIFZvmLpzlwjPu6ULrXJwgQQyGCNdp60jQMl9vGjzgJzs6uqhSCke3EvjOCuAGkv2ScRY5hDGQOnIruxwmgYgDeXHnujt+zTBvW+imxLVaZhXoKVPyqlvm6njWp0XECyAi8DFKbzIdDVp+NsRzIGjKeVoMgWd4xlNazUZGgTsXNCD/+aZOJ1LxA51I8Wgsm888MUXnaGhNI3U4jJNKHAPZMS4++JEuwWzlptJdZIa1jOfwMD6HvIPKEmC5+eDCRFvPIEh84VqErtNTPEUAz7wZAJvO+j+x5A/9VnQmKJhEBxBjlw1Ky+mQcLJnRX/x9jc4EvEd9gWGIUj46y3IF8jWn71eXvinSJ4Op4LQEiv5BfuyMTfY2xwFTlGWjdYF6Xnac+Gz6+E1gjCBSuqKWd56QFDcrqCcvbG50ERhenCUEhUgI33rXNxS/hwz2iClVM26GFJlUtZkZ7/021FjEQNLEYivI1ZkHe9ip8sxx2QVKd5k7Ui/hXkP4xSal3okL4Ar/cThMh0aoWzziYsoAahtoxFWEags7lNOffMoLGmPbgGVRUGHogNu2CES0SNs4AhHVUVERo8OTJHx46XJxXqvLzeEn6zXFD63XXQR8AyXTQ=", 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="
"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=" - ==: 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 # The time in second before teleport no sleeping player
playerSleep: 20 player-sleep: 30

View File

@@ -9,8 +9,9 @@ dependencies:
server: server:
SkinsRestorer: SkinsRestorer:
load: BEFORE load: BEFORE
required: true required: false
join-classpath: true join-classpath: true
voicechat: voicechat:
load: BEFORE load: BEFORE
required: true required: false
join-classpath: true