diff --git a/src/main/kotlin/xyz/fortern/minehunt/Console.kt b/src/main/kotlin/xyz/fortern/minehunt/Console.kt index a61c238..751c80b 100644 --- a/src/main/kotlin/xyz/fortern/minehunt/Console.kt +++ b/src/main/kotlin/xyz/fortern/minehunt/Console.kt @@ -398,6 +398,9 @@ class Console { scoreboard.getObjective("rule-list")!!.displaySlot = null stage = GameStage.PROCESSING + Bukkit.getOnlinePlayers().forEach { + it.updateCommands() + } } /** @@ -476,6 +479,7 @@ class Console { it.sendMessage(Component.text("没有赢家", NamedTextColor.GOLD)) } it.gameMode = GameMode.SURVIVAL + it.updateCommands() } // 取消剩余的复活任务 hunterRespawnTasks.forEach { @@ -578,6 +582,7 @@ class Console { hunterRespawnTasks.remove(uuid) }, gameRules.getRuleValue(RuleKey.HUNTER_RESPAWN_CD) * 20L) } + player.updateCommands() } private fun initScoreboard() { @@ -657,6 +662,11 @@ class Console { } } + /** + * 判断是否处于倒计时前的开始阶段 + */ + fun preparingAndNoCountdown(): Boolean = stage == GameStage.PREPARING && beginningCountdown == null + /** * 游戏阶段 */ diff --git a/src/main/kotlin/xyz/fortern/minehunt/Minehunt.kt b/src/main/kotlin/xyz/fortern/minehunt/Minehunt.kt index aefb94c..d36b8f7 100644 --- a/src/main/kotlin/xyz/fortern/minehunt/Minehunt.kt +++ b/src/main/kotlin/xyz/fortern/minehunt/Minehunt.kt @@ -1,7 +1,9 @@ package xyz.fortern.minehunt +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents import org.bukkit.Bukkit import org.bukkit.plugin.java.JavaPlugin +import xyz.fortern.minehunt.command.MhCommand import xyz.fortern.minehunt.command.MinehuntCommand import xyz.fortern.minehunt.command.TestCommand import xyz.fortern.minehunt.listener.PlayerListener @@ -30,6 +32,10 @@ class Minehunt : JavaPlugin() { Bukkit.getPluginCommand("test")!!.setExecutor(TestCommand()) Bukkit.getPluginCommand("minehunt")!!.setExecutor(MinehuntCommand(console)) + this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS) { + it.registrar().register(MhCommand(console).createCommand().build()) + } + } override fun onDisable() { diff --git a/src/main/kotlin/xyz/fortern/minehunt/command/MhCommand.kt b/src/main/kotlin/xyz/fortern/minehunt/command/MhCommand.kt new file mode 100644 index 0000000..4dc783a --- /dev/null +++ b/src/main/kotlin/xyz/fortern/minehunt/command/MhCommand.kt @@ -0,0 +1,212 @@ +package xyz.fortern.minehunt.command + +import com.mojang.brigadier.Command +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import io.papermc.paper.command.brigadier.CommandSourceStack +import io.papermc.paper.command.brigadier.Commands +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import xyz.fortern.minehunt.Console +import xyz.fortern.minehunt.Console.GameStage +import xyz.fortern.minehunt.Minehunt +import xyz.fortern.minehunt.rule.RuleKey + +class MhCommand( + private val console: Console +) { + + private val helpMessages = listOf( + Component.text("Minehunt v${Minehunt.instance().pluginMeta.version}", NamedTextColor.GREEN), + Component.text("/minehunt help ", NamedTextColor.GOLD) + .append(Component.text("帮助信息", NamedTextColor.WHITE)), + Component.text("/minehunt join (hunter|speedrunner|audience) ", NamedTextColor.GOLD) + .append(Component.text("加入一个阵营", NamedTextColor.WHITE)), + Component.text("/minehunt leave ", NamedTextColor.GOLD) + .append(Component.text("加入观察者阵营", NamedTextColor.WHITE)), + Component.text("/minehunt rule ", NamedTextColor.GOLD) + .append(Component.text("查看或修改游戏规则", NamedTextColor.WHITE)), + Component.text("/minehunt start ", NamedTextColor.GOLD) + .append(Component.text("开始游戏", NamedTextColor.WHITE)), + Component.text("/minehunt stop ", NamedTextColor.GOLD) + .append(Component.text("进行中止游戏的投票", NamedTextColor.WHITE)), + Component.text("/minehunt give ", NamedTextColor.GOLD) + .append(Component.text("给予游戏中所需的特殊物品", NamedTextColor.WHITE)), + ) + private val ruleHelpMessages = listOf( + Component.text("/minehunt rule ", NamedTextColor.GREEN) + .append(Component.text("查看一项规则的详情", NamedTextColor.WHITE)), + Component.text("/minehunt rule ", NamedTextColor.GREEN) + .append(Component.text("为一项规则设置新的值", NamedTextColor.WHITE)), + ) + + fun createCommand(): LiteralArgumentBuilder = + Commands.literal("hunt") + .then(help()) + .then(join()) + .then(leave()) + .then(rule()) + .then(start()) + .then(stop()) + .then(give()) + + private fun help(): LiteralArgumentBuilder { + return Commands.literal("help").executes { sendHelp(it.source.sender) } + } + + private fun join(): LiteralArgumentBuilder { + return Commands.literal("join") + .requires { + it.sender is Player && console.preparingAndNoCountdown() + }.then( + Commands.literal("hunter").executes { + console.joinHunter(it.source.sender as Player) + Command.SINGLE_SUCCESS + } + ).then( + Commands.literal("speedrunner").executes { + console.joinSpeedrunner(it.source.sender as Player) + Command.SINGLE_SUCCESS + } + ).then( + Commands.literal("audience").executes { + console.joinAudience(it.source.sender as Player) + Command.SINGLE_SUCCESS + } + ) + } + + private fun leave(): LiteralArgumentBuilder { + return Commands.literal("leave") + .requires { it.sender is Player && console.preparingAndNoCountdown() } + .executes { + console.joinAudience(it.source.sender as Player) + Command.SINGLE_SUCCESS + } + } + + private fun rule(): LiteralArgumentBuilder { + val ruleNode = Commands.literal("rule").requires { + console.preparingAndNoCountdown() + } + console.gameRules.getAllRules().forEach { (k, v) -> + ruleNode.then( + // 定义ruleKey节点 在 minehunt -> rule -> ruleKey + Commands.literal(k.name) + .then( + // 定义value节点 在 minehunt -> rule -> ruleKey -> value + Commands.argument("value", k.type) + // 执行修改规则的命令 + .executes { + modifyGameRule(k, it.getArgument("value", k.clazz)!!, it.source.sender) + } + ) + // 执行查询规则的命令 + .executes { sendRuleInfo(it.source.sender, k) } + ) + } + ruleNode.executes { + sendHelpRule(it.source.sender) + } + return ruleNode + } + + /** + * 游戏开始 + */ + private fun start(): LiteralArgumentBuilder { + return Commands.literal("start") + .requires { console.preparingAndNoCountdown() } + .executes { + val result = console.tryStart() + if (result.isNotEmpty()) { + it.source.sender.sendMessage(Component.text("游戏开始失败,原因:${result}", NamedTextColor.RED)) + } + Command.SINGLE_SUCCESS + } + } + + /** + * 投票结束游戏 + */ + private fun stop(): LiteralArgumentBuilder { + return Commands.literal("stop") + .requires { + if (console.stage != GameStage.PROCESSING) return@requires false + val sender = it.sender + if (sender !is Player) return@requires false + console.isHunter(sender) || console.isSpeedrunner(sender) + } + .executes { + console.voteForStop(it.source.sender as Player) + Command.SINGLE_SUCCESS + } + } + + /** + * 给予玩家特殊物品 + */ + private fun give(): LiteralArgumentBuilder { + return Commands.literal("give") + .requires { it.sender is Player && console.stage == GameStage.PROCESSING } + .then( + Commands.literal("compass") + .requires { + val sender = it.sender + sender is Player && console.isHunter(sender) + } + ) + } + + /** + * 修改某项游戏规则 + */ + private fun modifyGameRule(ruleKey: RuleKey<*>, value: Any, sender: CommandSender): Int { + if (console.gameRules.setGameRuleValue(ruleKey, value)) { + sender.sendMessage( + Component.text(sender.name, NamedTextColor.YELLOW) + .append(Component.text("修改规则项", NamedTextColor.WHITE)) + .append(Component.text(ruleKey.name, NamedTextColor.GOLD)) + .append(Component.text("值为", NamedTextColor.WHITE)) + .append( + Component.text( + console.gameRules.getRuleValue(ruleKey).toString(), + NamedTextColor.GREEN + ) + ) + ) + console.refreshEntry(ruleKey) + } else { + sender.sendMessage(Component.text("不合适的值", NamedTextColor.RED)) + } + return Command.SINGLE_SUCCESS + } + + /** + * 发送规则详情 + */ + private fun sendRuleInfo(sender: CommandSender, ruleKey: RuleKey<*>): Int { + sender.sendMessage(Component.text("游戏规则: ${ruleKey.name}")) + sender.sendMessage(Component.text("描述: ${ruleKey.info}")) + sender.sendMessage(Component.text("值类型: ${ruleKey.typeInfo}")) + sender.sendMessage(Component.text("数值: ${console.gameRules.getRuleValue(ruleKey)}")) + return Command.SINGLE_SUCCESS + } + + /** + * 发送帮助信息 + */ + private fun sendHelp(sender: CommandSender): Int { + helpMessages.forEach { sender.sendMessage(it) } + return Command.SINGLE_SUCCESS + } + + /** + * 发送rule子命令的帮助信息 + */ + private fun sendHelpRule(sender: CommandSender): Int { + ruleHelpMessages.forEach { sender.sendMessage(it) } + return Command.SINGLE_SUCCESS + } +} diff --git a/src/main/kotlin/xyz/fortern/minehunt/rule/GameRules.kt b/src/main/kotlin/xyz/fortern/minehunt/rule/GameRules.kt index 16d0f07..bfc4884 100644 --- a/src/main/kotlin/xyz/fortern/minehunt/rule/GameRules.kt +++ b/src/main/kotlin/xyz/fortern/minehunt/rule/GameRules.kt @@ -25,6 +25,19 @@ class GameRules internal constructor() { return true } + /** + * 设置一项游戏规则 + */ + @Suppress("UNCHECKED_CAST") + fun setGameRuleValue(rule: RuleKey, value: Any): Boolean { + return try { + setRuleValue(rule, value as T) + true + } catch (e: ClassCastException) { + false + } + } + private fun setRuleValue(rule: RuleKey, value: T) { map[rule] = value!! } diff --git a/src/main/kotlin/xyz/fortern/minehunt/rule/RuleKey.kt b/src/main/kotlin/xyz/fortern/minehunt/rule/RuleKey.kt index f5a3e0b..fa23996 100644 --- a/src/main/kotlin/xyz/fortern/minehunt/rule/RuleKey.kt +++ b/src/main/kotlin/xyz/fortern/minehunt/rule/RuleKey.kt @@ -1,5 +1,9 @@ package xyz.fortern.minehunt.rule +import com.mojang.brigadier.arguments.ArgumentType +import com.mojang.brigadier.arguments.BoolArgumentType +import com.mojang.brigadier.arguments.IntegerArgumentType + /** * 描述每一个规则项的类 */ @@ -14,10 +18,15 @@ class RuleKey private constructor( */ val info: String, + /** + * 这项规则值的参数类型 + */ + val type: ArgumentType<*>, + /** * 这项规则值的类型 */ - val type: Class, + val clazz: Class, /** * 值类型的描述信息 @@ -47,7 +56,14 @@ class RuleKey private constructor( } val HUNTER_READY_CD = - RuleKey("hunter_ready_cd", "猎人出生倒计时(秒)", Int::class.java, "Integer", listOf("0", "15", "30")) { + RuleKey( + "hunter_ready_cd", + "猎人出生倒计时(秒)", + IntegerArgumentType.integer(0, 120), + Int::class.java, + "Integer", + listOf("0", "15", "30") + ) { try { val i = it.toInt() if (i in 0..120) i else null @@ -56,7 +72,14 @@ class RuleKey private constructor( } } val HUNTER_RESPAWN_CD = - RuleKey("hunter_respawn_cd", "猎人重生倒计时(秒)", Int::class.java, "Integer", listOf("0, 15, 30")) { + RuleKey( + "hunter_respawn_cd", + "猎人重生倒计时(秒)", + IntegerArgumentType.integer(0, 120), + Int::class.java, + "Integer", + listOf("0, 15, 30") + ) { try { val i = it.toInt() if (i in 0..120) i else null @@ -67,10 +90,17 @@ class RuleKey private constructor( val FRIENDLY_FIRE = RuleKey( "friendly_fire", "队友之间是否有伤害", + BoolArgumentType.bool(), Boolean::class.java, "Boolean", listOf("true", "false"), boolValidate ) + + val map: Map> = mapOf( + "hunter_ready_cd" to HUNTER_READY_CD, + "hunter_respawn_cd" to HUNTER_RESPAWN_CD, + "friendly_fire" to FRIENDLY_FIRE + ) } }