diff options
| author | Freya Murphy <freya@freyacat.org> | 2026-03-26 23:15:33 -0400 |
|---|---|---|
| committer | Freya Murphy <freya@freyacat.org> | 2026-03-27 23:09:23 -0400 |
| commit | f8322cd21cde68a72b05efbad3a05b8e67c0bdd0 (patch) | |
| tree | d7e60bc8fedadc8fa7ae725571cad1f398eaf6dc /core/src/command | |
| download | kenshinshideandseek2-f8322cd21cde68a72b05efbad3a05b8e67c0bdd0.tar.gz kenshinshideandseek2-f8322cd21cde68a72b05efbad3a05b8e67c0bdd0.tar.bz2 kenshinshideandseek2-f8322cd21cde68a72b05efbad3a05b8e67c0bdd0.zip | |
initial
Diffstat (limited to 'core/src/command')
35 files changed, 1427 insertions, 0 deletions
diff --git a/core/src/command/Confirm.kt b/core/src/command/Confirm.kt new file mode 100644 index 0000000..359f490 --- /dev/null +++ b/core/src/command/Confirm.kt @@ -0,0 +1,32 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.REQUESTS +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player + +class KhsConfirm : Command { + override val label = "confirm" + override val usage = listOf<String>() + override val description = "Confirm a request of a previously run command" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val request = REQUESTS.remove(player.uuid) + + if (request == null) { + player.message(plugin.locale.prefix.error + plugin.locale.confirm.none) + return + } + + if (request.expired) { + player.message(plugin.locale.prefix.error + plugin.locale.confirm.timedOut) + return + } + + request.fn() + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf() + } +} diff --git a/core/src/command/Debug.kt b/core/src/command/Debug.kt new file mode 100644 index 0000000..d000d60 --- /dev/null +++ b/core/src/command/Debug.kt @@ -0,0 +1,24 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.inv.createDebugMenu +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsDebug : Command { + override val label = "debug" + override val usage = listOf<String>() + override val description = "Mess with/debug the current game" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + runChecks(plugin, player) { gameMapExists() } + + val inv = createDebugMenu(plugin) ?: return + player.showInventory(inv) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf() + } +} diff --git a/core/src/command/Help.kt b/core/src/command/Help.kt new file mode 100644 index 0000000..168b69c --- /dev/null +++ b/core/src/command/Help.kt @@ -0,0 +1,39 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import kotlin.text.toUInt + +class KhsHelp : Command { + override val label = "help" + override val usage = listOf("*page") + override val description = "Lists the commands you can use" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val commands = plugin.commandGroup.commandsFor(player) + val pageSize = 4u + val pages = (commands.size.toUInt() + pageSize - 1u) / pageSize + val page = maxOf(minOf(args.firstOrNull().let { it?.toUIntOrNull() } ?: 0u, pages), 1u) + + player.message( + buildString { + appendLine( + "&b=================== &fHelp: Page ($page/$pages) &b===================" + ) + for ((label, command) in commands.chunked(pageSize.toInt()).get(page.toInt() - 1)) { + val cmd = label.substring(3) + val usage = command.usage.joinToString(" ") + val description = command.description + appendLine("&7?&f &b/hs &f$cmd &9$usage") + appendLine("&7?&f &7&o$description") + } + appendLine("&b=====================================================") + } + ) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf(parameter) + } +} diff --git a/core/src/command/Join.kt b/core/src/command/Join.kt new file mode 100644 index 0000000..88bb94c --- /dev/null +++ b/core/src/command/Join.kt @@ -0,0 +1,33 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsJoin : Command { + override val label = "join" + override val usage = listOf<String>("*map") + override val description = "Joins the game, and can set a map if the lobby is empty" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val mapName = args.firstOrNull() + val map = mapName?.let { plugin.maps.get(it) } + + runChecks(plugin, player) { + gameMapExists() + playerNotInGame() + if (mapName != null) mapSetup(map) + } + + if (plugin.game.size == 0u) plugin.game.setMap(map) + + plugin.game.join(player.uuid) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "*map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/Leave.kt b/core/src/command/Leave.kt new file mode 100644 index 0000000..752fae4 --- /dev/null +++ b/core/src/command/Leave.kt @@ -0,0 +1,22 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsLeave : Command { + override val label = "leave" + override val usage = listOf<String>() + override val description = "Leaves the game lobby" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + runChecks(plugin, player) { playerInGame() } + + plugin.game.leave(player.uuid) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf() + } +} diff --git a/core/src/command/Reload.kt b/core/src/command/Reload.kt new file mode 100644 index 0000000..eee5231 --- /dev/null +++ b/core/src/command/Reload.kt @@ -0,0 +1,33 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsReload : Command { + override val label = "reload" + override val usage = listOf<String>() + override val description = "Reload's the plugin config" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + runChecks(plugin, player) { + gameNotInProgress() + lobbyEmpty() + } + + player.message(plugin.locale.prefix.default + plugin.locale.command.reloading) + plugin + .reloadConfig() + .onSuccess { + player.message(plugin.locale.prefix.default + plugin.locale.command.reloaded) + } + .onFailure { + player.message(plugin.locale.prefix.default + plugin.locale.command.errorReloading) + } + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf() + } +} diff --git a/core/src/command/Send.kt b/core/src/command/Send.kt new file mode 100644 index 0000000..bca4467 --- /dev/null +++ b/core/src/command/Send.kt @@ -0,0 +1,30 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsSend : Command { + override val label = "send" + override val usage = listOf("map") + override val description = "Send the current lobby to another map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val map = plugin.maps.get(args.first()) + + runChecks(plugin, player) { + gameNotInProgress() + playerInGame() + mapSetup(map) + } + + plugin.game.setMap(map) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/SetExit.kt b/core/src/command/SetExit.kt new file mode 100644 index 0000000..d3feb2c --- /dev/null +++ b/core/src/command/SetExit.kt @@ -0,0 +1,25 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsSetExit : Command { + override val label = "setexit" + override val usage = listOf<String>() + override val description = "Sets the plugins's exit location" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + runChecks(plugin, player) { gameNotInProgress() } + + plugin.config.exit = player.location + plugin.saveConfig() + + player.message(plugin.locale.prefix.default + plugin.locale.map.set.exit) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf() + } +} diff --git a/core/src/command/Start.kt b/core/src/command/Start.kt new file mode 100644 index 0000000..e24c505 --- /dev/null +++ b/core/src/command/Start.kt @@ -0,0 +1,34 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsStart : Command { + override val label = "start" + override val usage = listOf("*seekers...") + override val description = + "Starts the game either with a random set of seekers or a chosen list" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + runChecks(plugin, player) { + gameMapExists() + gameNotInProgress() + playerInGame() + lobbyHasEnoughPlayers() + } + + val pool = + args + .map { plugin.shim.getPlayer(it)?.uuid } + .filterNotNull() + .filter { plugin.game.hasPlayer(it) } + + plugin.game.start(pool) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return plugin.game.players.map(Player::name) + } +} diff --git a/core/src/command/Stop.kt b/core/src/command/Stop.kt new file mode 100644 index 0000000..c0ca401 --- /dev/null +++ b/core/src/command/Stop.kt @@ -0,0 +1,27 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.game.Game +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsStop : Command { + override val label = "stop" + override val usage = listOf<String>() + override val description = "Stops the game" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + runChecks(plugin, player) { + gameMapExists() + gameInProgress() + } + + plugin.game.broadcast(plugin.locale.prefix.abort + plugin.locale.game.stop) + plugin.game.stop(Game.WinType.NONE) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf() + } +} diff --git a/core/src/command/Top.kt b/core/src/command/Top.kt new file mode 100644 index 0000000..900f52f --- /dev/null +++ b/core/src/command/Top.kt @@ -0,0 +1,49 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player + +class KhsTop : Command { + override val label = "top" + override val usage = listOf("*page") + override val description = "Shows the game leaderboard" + + private fun getColor(index: UInt): Char { + return when (index) { + 0u -> 'e' + 1u -> '7' + 2u -> '6' + else -> 'f' + } + } + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + var page = args.firstOrNull()?.toUIntOrNull() ?: 0u + page = maxOf(page, 1u) - 1u + + var pageSize = 5u + val entires = plugin.database?.getPlayers(page, pageSize) + if (entires == null || entires.isEmpty()) { + player.message(plugin.locale.prefix.default + plugin.locale.database.noInfo) + return + } + + val message = buildString { + appendLine("&f------- &lLEADERBOARD &7(Page ${page + 1u}) &f-------") + for ((i, entry) in entires.withIndex()) { + val wins = entry.hiderWins + entry.seekerWins + val idx = (pageSize * page) + i.toUInt() + val color = getColor(idx) + val name = entry.name ?: continue + appendLine("&$color${idx + 1u}. &c$wins &f$name") + } + } + + player.message(message) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf(parameter) + } +} diff --git a/core/src/command/Wins.kt b/core/src/command/Wins.kt new file mode 100644 index 0000000..02faf04 --- /dev/null +++ b/core/src/command/Wins.kt @@ -0,0 +1,41 @@ +package cat.freya.khs.command + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player + +class KhsWins : Command { + override val label = "wins" + override val usage = listOf("player") + override val description = "Shows stats for a given player" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + val data = plugin.database?.getPlayer(name) + if (data == null) { + player.message(plugin.locale.prefix.default + plugin.locale.database.noInfo) + return + } + + val message = buildString { + val wins = data.seekerWins + data.hiderWins + val games = wins + data.seekerLosses + data.hiderLosses + appendLine("&f&l" + "=".repeat(30)) + appendLine(plugin.locale.database.infoFor.with(name)) + appendLine("&bTOTAL WINS: &f$wins") + appendLine("&6HIDER WINS: &f${data.hiderWins}") + appendLine("&cSEEKER WINS: &f${data.seekerWins}") + appendLine("GAMES PLAYED: ${games}") + append("&f&l" + "=".repeat(30)) + } + + player.message(message) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return when (parameter) { + "player" -> plugin.database?.getPlayerNames(10u, typed) ?: listOf() + else -> listOf() + } + } +} diff --git a/core/src/command/map/Add.kt b/core/src/command/map/Add.kt new file mode 100644 index 0000000..8505849 --- /dev/null +++ b/core/src/command/map/Add.kt @@ -0,0 +1,35 @@ +package cat.freya.khs.command.map + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.config.MapConfig +import cat.freya.khs.game.KhsMap +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapAdd : Command { + override val label = "add" + override val usage = listOf("name", "world") + override val description = "Add a map to the plugin" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name, world) = args + runChecks(plugin, player) { + mapDoesNotExist(name) + mapNameValid(name) + worldValid(world) + } + + plugin.maps[name] = KhsMap(name, MapConfig(world), plugin) + plugin.saveConfig() + + player.message(plugin.locale.prefix.default + plugin.locale.map.created.with(name)) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "name" -> listOf("name") + "world" -> plugin.shim.worlds.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/GoTo.kt b/core/src/command/map/GoTo.kt new file mode 100644 index 0000000..20444cc --- /dev/null +++ b/core/src/command/map/GoTo.kt @@ -0,0 +1,40 @@ +package cat.freya.khs.command.map + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapGoTo : Command { + override val label = "goto" + override val usage = listOf("map", "spawn") + override val description = "Goes to a spawn location for a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name, spawn) = args + runChecks(plugin, player) { mapExists(name) } + + var map = plugin.maps.get(name) ?: return + val loc = + when (spawn) { + "spawn" -> map.gameSpawn + "lobby" -> map.lobbySpawn + "seekerlobby" -> map.seekerLobbySpawn + else -> null + } + + if (loc == null) { + player.message(plugin.locale.prefix.error + plugin.locale.map.error.locationNotSet) + return + } + + loc.teleport(player) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + "spawn" -> listOf("spawn", "lobby", "seekerlobby").filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/List.kt b/core/src/command/map/List.kt new file mode 100644 index 0000000..8bc7a81 --- /dev/null +++ b/core/src/command/map/List.kt @@ -0,0 +1,32 @@ +package cat.freya.khs.command.map + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player + +class KhsMapList : Command { + override val label = "list" + override val usage = listOf<String>() + override val description = "List maps known to the plugin" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + if (plugin.maps.isEmpty()) { + player.message(plugin.locale.prefix.default + plugin.locale.map.none) + return + } + + player.message( + buildString { + appendLine(plugin.locale.prefix.default + plugin.locale.map.list) + for ((name, map) in plugin.maps) { + append("&e- &f$name: ") + appendLine(if (map.setup) "&aSETUP" else "&cNOT SETUP") + } + } + ) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf() + } +} diff --git a/core/src/command/map/Remove.kt b/core/src/command/map/Remove.kt new file mode 100644 index 0000000..f8aab4f --- /dev/null +++ b/core/src/command/map/Remove.kt @@ -0,0 +1,32 @@ +package cat.freya.khs.command.map + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapRemove : Command { + override val label = "remove" + override val usage = listOf("map") + override val description = "Remove a map from the plugin" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + mapExists(name) + gameNotInProgress() + lobbyEmpty() + } + + plugin.maps.remove(name) + plugin.saveConfig() + + player.message(plugin.locale.prefix.default + plugin.locale.map.deleted.with(name)) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/Save.kt b/core/src/command/map/Save.kt new file mode 100644 index 0000000..a68b6cc --- /dev/null +++ b/core/src/command/map/Save.kt @@ -0,0 +1,31 @@ +package cat.freya.khs.command.map + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.game.mapSave +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapSave : Command { + override val label = "save" + override val usage = listOf("map") + override val description = "Save the map backup used for gameplay" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + mapExists(name) + gameNotInProgress() + lobbyEmpty() + } + + var map = plugin.maps.get(name) ?: return + mapSave(plugin, map) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/Status.kt b/core/src/command/map/Status.kt new file mode 100644 index 0000000..596f306 --- /dev/null +++ b/core/src/command/map/Status.kt @@ -0,0 +1,45 @@ +package cat.freya.khs.command.map + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapStatus : Command { + override val label = "status" + override val usage = listOf("map") + override val description = "Says what is needed to fully setup the map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { mapExists(name) } + + val map = plugin.maps.get(name) ?: return + + if (map.setup) { + player.message(plugin.locale.prefix.default + plugin.locale.map.setup.complete) + return + } + + player.message( + buildString { + appendLine(plugin.locale.map.setup.header) + if (map.gameSpawn == null) appendLine(plugin.locale.map.setup.game) + if (map.lobbySpawn == null) appendLine(plugin.locale.map.setup.lobby) + if (map.seekerLobbySpawn == null) appendLine(plugin.locale.map.setup.seekerLobby) + if (plugin.config.exit == null) appendLine(plugin.locale.map.setup.exit) + if (map.bounds() == null) appendLine(plugin.locale.map.setup.bounds) + if (plugin.config.mapSaveEnabled && !map.hasMapSave()) + appendLine(plugin.locale.map.setup.saveMap) + if (map.config.blockHunt.enabled && map.config.blockHunt.blocks.isEmpty()) + appendLine(plugin.locale.map.setup.blockHunt) + } + ) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/blockhunt/Debug.kt b/core/src/command/map/blockhunt/Debug.kt new file mode 100644 index 0000000..0620e3d --- /dev/null +++ b/core/src/command/map/blockhunt/Debug.kt @@ -0,0 +1,35 @@ +package cat.freya.khs.command.map.blockhunt + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.inv.createBlockHuntPicker +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapBlockHuntDebug : Command { + override val label = "debug" + override val usage = listOf("map") + override val description = "Manually open the blockhunt picker for a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + blockHuntSupported() + blockHuntEnabled(name) + } + + val map = plugin.maps.get(name) ?: return + val inv = createBlockHuntPicker(plugin, map) ?: return + player.showInventory(inv) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> + plugin.maps + .filter { it.value.config.blockHunt.enabled } + .map { it.key } + .filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/blockhunt/Enabled.kt b/core/src/command/map/blockhunt/Enabled.kt new file mode 100644 index 0000000..a2ccdb7 --- /dev/null +++ b/core/src/command/map/blockhunt/Enabled.kt @@ -0,0 +1,39 @@ +package cat.freya.khs.command.map.blockhunt + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapBlockHuntEnabled : Command { + override val label = "enabled" + override val usage = listOf("map", "bool") + override val description = "Enable/disable blockhunt on a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name, enabled) = args + runChecks(plugin, player) { + blockHuntSupported() + mapExists(name) + gameNotInProgress() + } + + val map = plugin.maps.get(name) ?: return + map.config.blockHunt.enabled = (enabled.lowercase() == "true") + map.reloadConfig() + + val msg = + if (map.config.blockHunt.enabled) plugin.locale.blockHunt.enabled + else plugin.locale.blockHunt.disabled + + plugin.saveConfig() + player.message(plugin.locale.prefix.default + msg) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + "bool" -> listOf("true", "false").filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/blockhunt/block/Add.kt b/core/src/command/map/blockhunt/block/Add.kt new file mode 100644 index 0000000..6ed17be --- /dev/null +++ b/core/src/command/map/blockhunt/block/Add.kt @@ -0,0 +1,55 @@ +package cat.freya.khs.command.map.blockhunt.block + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapBlockHuntBlockAdd : Command { + override val label = "add" + override val usage = listOf("map", "block") + override val description = "Add a block to a block hunt map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name, blockName) = args + runChecks(plugin, player) { + blockHuntSupported() + blockHuntEnabled(name) + gameNotInProgress() + lobbyEmpty() + } + + val material = plugin.shim.parseMaterial(blockName) + if (material == null) { + player.message(plugin.locale.prefix.error + plugin.locale.blockHunt.block.unknown) + return + } + + val map = plugin.maps.get(name) ?: return + if (map.config.blockHunt.blocks.contains(material)) { + player.message( + plugin.locale.prefix.error + plugin.locale.blockHunt.block.exists.with(material) + ) + return + } + + map.config.blockHunt.blocks += material + map.reloadConfig() + + plugin.saveConfig() + player.message( + plugin.locale.prefix.default + plugin.locale.blockHunt.block.added.with(material) + ) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> + plugin.maps + .filter { it.value.config.blockHunt.enabled } + .map { it.key } + .filter { it.startsWith(typed) } + "block" -> listOf(parameter) + else -> listOf() + } +} diff --git a/core/src/command/map/blockhunt/block/List.kt b/core/src/command/map/blockhunt/block/List.kt new file mode 100644 index 0000000..b7df70d --- /dev/null +++ b/core/src/command/map/blockhunt/block/List.kt @@ -0,0 +1,46 @@ +package cat.freya.khs.command.map.blockhunt.block + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapBlockHuntBlockList : Command { + override val label = "list" + override val usage = listOf("map") + override val description = "List blocks in use on a block hunt map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + blockHuntSupported() + blockHuntEnabled(name) + } + + val map = plugin.maps.get(name) ?: return + val blocks = map.config.blockHunt.blocks + if (blocks.isEmpty()) { + player.message(plugin.locale.prefix.default + plugin.locale.blockHunt.block.none) + return + } + + val message = buildString { + appendLine(plugin.locale.blockHunt.block.list) + for (block in blocks) { + appendLine("&e- &f$block") + } + } + + player.message(message) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> + plugin.maps + .filter { it.value.config.blockHunt.enabled } + .map { it.key } + .filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/blockhunt/block/Remove.kt b/core/src/command/map/blockhunt/block/Remove.kt new file mode 100644 index 0000000..3e81371 --- /dev/null +++ b/core/src/command/map/blockhunt/block/Remove.kt @@ -0,0 +1,56 @@ +package cat.freya.khs.command.map.blockhunt.block + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapBlockHuntBlockRemove : Command { + override val label = "remove" + override val usage = listOf("map", "block") + override val description = "Remove a block from a block hunt map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name, blockName) = args + runChecks(plugin, player) { + blockHuntSupported() + blockHuntEnabled(name) + gameNotInProgress() + lobbyEmpty() + } + + val material = plugin.shim.parseMaterial(blockName) + if (material == null) { + player.message(plugin.locale.prefix.error + plugin.locale.blockHunt.block.unknown) + return + } + + val map = plugin.maps.get(name) ?: return + if (!map.config.blockHunt.blocks.contains(material)) { + player.message( + plugin.locale.prefix.error + + plugin.locale.blockHunt.block.doesntExist.with(material) + ) + return + } + + map.config.blockHunt.blocks -= material + map.reloadConfig() + + plugin.saveConfig() + player.message( + plugin.locale.prefix.default + plugin.locale.blockHunt.block.removed.with(material) + ) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> + plugin.maps + .filter { it.value.config.blockHunt.enabled } + .map { it.key } + .filter { it.startsWith(typed) } + "block" -> listOf(parameter) + else -> listOf() + } +} diff --git a/core/src/command/map/set/Border.kt b/core/src/command/map/set/Border.kt new file mode 100644 index 0000000..75a3f27 --- /dev/null +++ b/core/src/command/map/set/Border.kt @@ -0,0 +1,64 @@ +package cat.freya.khs.command.map.set + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapSetBorder : Command { + override val label = "border" + override val usage = listOf("map", "size", "delay", "move") + override val description = "Enable the world border for a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name, sizeS, delayS, moveS) = args + runChecks(plugin, player) { + mapExists(name) + inMapWorld(name) + gameNotInProgress() + } + + val size = sizeS.toULong() + val delay = delayS.toULong() + val move = moveS.toULong() + + if (size < 100u) { + player.message(plugin.locale.prefix.error + plugin.locale.worldBorder.minSize) + return + } + + if (move < 1u) { + player.message(plugin.locale.prefix.error + plugin.locale.worldBorder.minChange) + return + } + + var map = plugin.maps.get(name) ?: return + val config = map.config.worldBorder + config.enabled = true + config.pos = player.location.position + config.size = size + config.delay = delay + config.move = move + + runChecks(plugin, player) { + // note this is not error, only warn + spawnsInRange(map) + } + + map.reloadConfig() + + plugin.saveConfig() + player.message( + plugin.locale.prefix.default + plugin.locale.worldBorder.enable.with(size, delay, move) + ) + + val loc = player.location.position + map.world?.border?.move(loc.x, loc.z, size, 0UL) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf(parameter) + } +} diff --git a/core/src/command/map/set/Bounds.kt b/core/src/command/map/set/Bounds.kt new file mode 100644 index 0000000..7c13802 --- /dev/null +++ b/core/src/command/map/set/Bounds.kt @@ -0,0 +1,57 @@ +package cat.freya.khs.command.map.set + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.config.BoundConfig +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapSetBounds : Command { + override val label = "bounds" + override val usage = listOf("map") + override val description = "Sets the map bounds for a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + mapExists(name) + inMapWorld(name) + gameNotInProgress() + } + + var map = plugin.maps.get(name) ?: return + val config = map.config.bounds + + val pos = player.location.position + val num: Int + + if (config.min == null || config.max != null) { + config.min = BoundConfig(pos.x, pos.z) + config.max == null + num = 1 + } else { + val minX = minOf(config.min?.x ?: 0.0, pos.x) + val minZ = minOf(config.min?.z ?: 0.0, pos.z) + val maxX = maxOf(config.min?.x ?: 0.0, pos.x) + val maxZ = maxOf(config.min?.z ?: 0.0, pos.z) + config.min = BoundConfig(minX, minZ) + config.max = BoundConfig(maxX, maxZ) + num = 2 + } + + runChecks(plugin, player) { + // note this is not error, only warn + spawnsInRange(map) + } + + map.reloadConfig() + plugin.saveConfig() + player.message(plugin.locale.prefix.default + plugin.locale.map.set.bounds.with(num)) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/set/Lobby.kt b/core/src/command/map/set/Lobby.kt new file mode 100644 index 0000000..a90259a --- /dev/null +++ b/core/src/command/map/set/Lobby.kt @@ -0,0 +1,37 @@ +package cat.freya.khs.command.map.set + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapSetLobby : Command { + override val label = "lobby" + override val usage = listOf("map") + override val description = "Sets the lobby spawn location for a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + mapExists(name) + inMapWorld(name) + gameNotInProgress() + } + + var map = plugin.maps.get(name) ?: return + val pos = player.location.position + + runChecks(plugin, player) { spawnInRange(map, pos) } + + map.config.spawns.lobby = pos + map.reloadConfig() + plugin.saveConfig() + player.message(plugin.locale.prefix.default + plugin.locale.map.set.lobby) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/set/SeekerLobby.kt b/core/src/command/map/set/SeekerLobby.kt new file mode 100644 index 0000000..71122cb --- /dev/null +++ b/core/src/command/map/set/SeekerLobby.kt @@ -0,0 +1,38 @@ +package cat.freya.khs.command.map.set + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapSetSeekerLobby : Command { + override val label = "seekerlobby" + override val usage = listOf("map") + override val description = "Sets the seeker lobby spawn location for a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + mapExists(name) + inMapWorld(name) + gameNotInProgress() + } + + var map = plugin.maps.get(name) ?: return + val pos = player.location.position + + runChecks(plugin, player) { spawnInRange(map, pos) } + + map.config.spawns.seeker = pos + map.reloadConfig() + + plugin.saveConfig() + player.message(plugin.locale.prefix.default + plugin.locale.map.set.seekerSpawn) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/set/Spawn.kt b/core/src/command/map/set/Spawn.kt new file mode 100644 index 0000000..4eff730 --- /dev/null +++ b/core/src/command/map/set/Spawn.kt @@ -0,0 +1,38 @@ +package cat.freya.khs.command.map.set + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapSetSpawn : Command { + override val label = "spawn" + override val usage = listOf("map") + override val description = "Sets the game spawn location for a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + mapExists(name) + inMapWorld(name) + gameNotInProgress() + } + + var map = plugin.maps.get(name) ?: return + val pos = player.location.position + + runChecks(plugin, player) { spawnInRange(map, pos) } + + map.config.spawns.game = pos.toLegacy() + map.reloadConfig() + + plugin.saveConfig() + player.message(plugin.locale.prefix.default + plugin.locale.map.set.gameSpawn) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/map/unset/Border.kt b/core/src/command/map/unset/Border.kt new file mode 100644 index 0000000..87e7b85 --- /dev/null +++ b/core/src/command/map/unset/Border.kt @@ -0,0 +1,39 @@ +package cat.freya.khs.command.map.unset + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsMapUnsetBorder : Command { + override val label = "border" + override val usage = listOf("map") + override val description = "Disable the world border for a map" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + mapExists(name) + gameNotInProgress() + } + + var map = plugin.maps.get(name) ?: return + val config = map.config.worldBorder + config.enabled = false + config.pos = null + config.size = null + config.delay = null + config.move = null + + plugin.saveConfig() + player.message(plugin.locale.prefix.default + plugin.locale.worldBorder.disable) + + map.world?.border?.move(0.0, 0.0, 30_000_000UL, 0UL) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> = + when (parameter) { + "map" -> plugin.maps.keys.filter { it.startsWith(typed) } + else -> listOf() + } +} diff --git a/core/src/command/util/Command.kt b/core/src/command/util/Command.kt new file mode 100644 index 0000000..734305a --- /dev/null +++ b/core/src/command/util/Command.kt @@ -0,0 +1,17 @@ +package cat.freya.khs.command.util + +import cat.freya.khs.Khs +import cat.freya.khs.player.Player + +interface CommandPart { + val label: String +} + +interface Command : CommandPart { + val usage: List<String> + val description: String + + fun execute(plugin: Khs, player: Player, args: List<String>) + + fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> +} diff --git a/core/src/command/util/CommandGroup.kt b/core/src/command/util/CommandGroup.kt new file mode 100644 index 0000000..72659d5 --- /dev/null +++ b/core/src/command/util/CommandGroup.kt @@ -0,0 +1,134 @@ +package cat.freya.khs.command.util + +import cat.freya.khs.Khs +import cat.freya.khs.player.Player + +private data class CommandData(val command: Command, val permission: String, val args: List<String>) + +class CommandGroup(val plugin: Khs, override val label: String, vararg commands: CommandPart) : + CommandPart { + // set of commands to run in this group + private final val REGISTRY: Map<String, CommandPart> = + commands.associate { it.label.lowercase() to it } + + private fun getCommand(args: List<String>, permission: String): CommandData? { + val invoke = args.firstOrNull()?.lowercase() ?: return null + val command = REGISTRY.get(invoke) ?: return null + + return when (command) { + is Command -> CommandData(command, "$permission.$invoke", args.drop(1)) + is CommandGroup -> command.getCommand(args.drop(1), "$permission.$invoke") + else -> null + } + } + + private fun messageAbout(player: Player) { + val version = plugin.shim.pluginVersion + player.message( + "&b&lKenshin's Hide and Seek &7(&f$version&7)\n" + + "&7Author: &f[KenshinEto]\n" + + "&7Help Command: &b/hs &fhelp" + ) + } + + fun handleCommand(player: Player, args: List<String>) { + val data = getCommand(args, label) ?: return messageAbout(player) + + if (plugin.saving) { + player.message(plugin.locale.prefix.error + plugin.locale.command.notAllowedTemp) + return + } + + if (plugin.config.permissionsRequired && !player.hasPermission(data.permission)) { + player.message(plugin.locale.prefix.error + plugin.locale.command.notAllowed) + return + } + + val paramCount = data.command.usage.filter { it.firstOrNull() != '*' }.count() + if (data.args.size < paramCount) { + player.message(plugin.locale.prefix.error + plugin.locale.command.notEnoughArguments) + return + } + + runCatching { data.command.execute(plugin, player, data.args) } + .onFailure { + player.message( + plugin.locale.prefix.error + (it.message ?: plugin.locale.command.unknownError) + ) + + if (plugin.config.debug) { + plugin.shim.logger.warning("=== KHS BEGIN DEBUG TRACE ===") + plugin.shim.logger.warning(it.stackTraceToString()) + plugin.shim.logger.warning("=== KHS END DEBUG TRACE ===") + } + } + } + + private fun handleTabComplete( + player: Player, + args: List<String>, + permission: String, + ): List<String> { + val invoke = args.firstOrNull()?.lowercase() ?: return listOf() + val command = REGISTRY.get(invoke) + return when { + command is Command -> { + if ( + plugin.config.permissionsRequired && + !player.hasPermission("$permission.$invoke") + ) + return listOf() + + var index = maxOf(args.size - 1, 1) + val typed = args.getOrNull(index) ?: return listOf() + + // handle last argument of usage being a varadic (...) + if ( + index >= command.usage.size && + command.usage.lastOrNull()?.endsWith("...") == true + ) + index = command.usage.size + + val parameter = command.usage.getOrNull(index - 1) ?: return listOf() + + command.autoComplete(plugin, parameter, typed) + } + command is CommandGroup -> + command.handleTabComplete(player, args.drop(1), "$permission.$invoke") + args.size == 1 -> REGISTRY.keys.filter { it.startsWith(invoke) } + else -> listOf() + } + } + + fun handleTabComplete(player: Player, args: List<String>): List<String> { + return handleTabComplete(player, args, label) + } + + private fun commandsFor( + player: Player, + label: String, + permission: String, + res: MutableList<Pair<String, Command>>, + ) { + for ((invoke, command) in REGISTRY) { + when (command) { + is Command -> { + if ( + plugin.config.permissionsRequired && + !player.hasPermission("$permission.$invoke") + ) + continue + res.add("$label $invoke" to command) + } + is CommandGroup -> + command.commandsFor(player, "$label $invoke", "$permission.$invoke", res) + } + } + } + + fun commandsFor(player: Player): List<Pair<String, Command>> { + val commands = mutableListOf<Pair<String, Command>>() + commandsFor(player, label, label, commands) + return commands + } +} diff --git a/core/src/command/world/Create.kt b/core/src/command/world/Create.kt new file mode 100644 index 0000000..2deece7 --- /dev/null +++ b/core/src/command/world/Create.kt @@ -0,0 +1,40 @@ +package cat.freya.khs.command.world + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks +import cat.freya.khs.world.World + +class KhsWorldCreate : Command { + override val label = "create" + override val usage = listOf("name", "type") + override val description = "Create a new world" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name, typeStr) = args + runChecks(plugin, player) { worldDoesNotExist(name) } + + val type = + World.Type.values().find { it.name.lowercase() == typeStr.lowercase() } + ?: World.Type.NORMAL + + val world = plugin.shim.createWorld(name, type) + if (world == null) { + player.message(plugin.locale.prefix.error + plugin.locale.world.addedFailed.with(name)) + return + } + + player.teleport(world.spawn.withWorld(name)) + player.message(plugin.locale.prefix.default + plugin.locale.world.added.with(name)) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return when (parameter) { + "name" -> listOf(parameter) + "type" -> + World.Type.values().map { it.name.lowercase() }.filter { it.startsWith(typed) } + else -> listOf() + } + } +} diff --git a/core/src/command/world/Delete.kt b/core/src/command/world/Delete.kt new file mode 100644 index 0000000..64710a6 --- /dev/null +++ b/core/src/command/world/Delete.kt @@ -0,0 +1,50 @@ +package cat.freya.khs.command.world + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks +import java.io.File + +class KhsWorldDelete : Command { + override val label = "delete" + override val usage = listOf("name") + override val description = "Delete an existing world" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { + worldExists(name) + worldNotInUse(name) + } + + val loader = plugin.shim.getWorldLoader(name) + + // sanity check + // for the love of god, make sure were rm -fr'ing a world, not like + // some ones home dir ;-; + val lock = File(loader.dir, "session.lock") + val data = File(loader.dir, "level.dat") + if (!lock.exists() || !data.exists()) { + player.message(plugin.locale.prefix.error + plugin.locale.world.doesntExist.with(name)) + return + } + + loader.unload() + if (!loader.dir.deleteRecursively()) { + player.message( + plugin.locale.prefix.error + plugin.locale.world.removedFailed.with(name) + ) + return + } + + player.message(plugin.locale.prefix.default + plugin.locale.world.removed.with(name)) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return when (parameter) { + "name" -> plugin.shim.worlds.filter { it.startsWith(typed) } + else -> listOf() + } + } +} diff --git a/core/src/command/world/List.kt b/core/src/command/world/List.kt new file mode 100644 index 0000000..af48f03 --- /dev/null +++ b/core/src/command/world/List.kt @@ -0,0 +1,42 @@ +package cat.freya.khs.command.world + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.world.World + +class KhsWorldList : Command { + override val label = "list" + override val usage = listOf<String>() + override val description = "Teleport to a world's spawn" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val worlds = plugin.shim.worlds + if (worlds.isEmpty()) { + // uhhh, we have to be in a world to call this 0_0 + player.message(plugin.locale.prefix.error + plugin.locale.world.none) + return + } + + val message = buildString { + appendLine(plugin.locale.world.list) + for (worldName in worlds) { + val world = plugin.shim.getWorld(worldName) + val status = + when (world?.type) { + World.Type.NORMAL -> "&aNORMAL" + World.Type.FLAT -> "&aFLAT" + World.Type.NETHER -> "&cNETHER" + World.Type.END -> "&eEND" + else -> "&7NOT LOADED" + } + appendLine("&e- &f$worldName: $status") + } + } + player.message(message) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return listOf() + } +} diff --git a/core/src/command/world/Tp.kt b/core/src/command/world/Tp.kt new file mode 100644 index 0000000..c103a0c --- /dev/null +++ b/core/src/command/world/Tp.kt @@ -0,0 +1,36 @@ +package cat.freya.khs.command.world + +import cat.freya.khs.Khs +import cat.freya.khs.command.util.Command +import cat.freya.khs.player.Player +import cat.freya.khs.runChecks + +class KhsWorldTp : Command { + override val label = "tp" + override val usage = listOf("name") + override val description = "Teleport to a world's spawn" + + override fun execute(plugin: Khs, player: Player, args: List<String>) { + val (name) = args + runChecks(plugin, player) { worldExists(name) } + + val loader = plugin.shim.getWorldLoader(name) + loader.load() + + val world = plugin.shim.getWorld(name) + if (world == null) { + player.message(plugin.locale.prefix.error + plugin.locale.world.loadFailed.with(name)) + return + } + + val spawn = world.spawn.withWorld(name) + player.teleport(spawn) + } + + override fun autoComplete(plugin: Khs, parameter: String, typed: String): List<String> { + return when (parameter) { + "name" -> plugin.shim.worlds.filter { it.startsWith(typed) } + else -> listOf() + } + } +} |