Add map voting
This commit is contained in:
parent
98ff252eba
commit
1b2fc74c65
22 changed files with 353 additions and 177 deletions
|
@ -21,7 +21,6 @@ repositories {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
implementation(kotlin("reflect"))
|
implementation(kotlin("reflect"))
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7")
|
|
||||||
implementation("commons-codec:commons-codec:1.14")
|
implementation("commons-codec:commons-codec:1.14")
|
||||||
compileOnly("com.comphenix.protocol", "ProtocolLib", "4.5.0")
|
compileOnly("com.comphenix.protocol", "ProtocolLib", "4.5.0")
|
||||||
compileOnly("org.spigotmc", "spigot-api", "1.15.2-R0.1-SNAPSHOT")
|
compileOnly("org.spigotmc", "spigot-api", "1.15.2-R0.1-SNAPSHOT")
|
||||||
|
|
|
@ -5,7 +5,7 @@ import de.moritzruth.spigot_ttt.game.InfoCommand
|
||||||
import de.moritzruth.spigot_ttt.game.ReviveCommand
|
import de.moritzruth.spigot_ttt.game.ReviveCommand
|
||||||
import de.moritzruth.spigot_ttt.game.StartCommand
|
import de.moritzruth.spigot_ttt.game.StartCommand
|
||||||
import de.moritzruth.spigot_ttt.game.items.AddItemSpawnCommand
|
import de.moritzruth.spigot_ttt.game.items.AddItemSpawnCommand
|
||||||
import de.moritzruth.spigot_ttt.worlds.WorldCommand
|
import de.moritzruth.spigot_ttt.game.worlds.VotingCommand
|
||||||
|
|
||||||
object CommandManager {
|
object CommandManager {
|
||||||
fun initializeCommands() {
|
fun initializeCommands() {
|
||||||
|
@ -16,6 +16,6 @@ object CommandManager {
|
||||||
ResourcepackCommand()
|
ResourcepackCommand()
|
||||||
ReloadTTTConfigCommand()
|
ReloadTTTConfigCommand()
|
||||||
InfoCommand()
|
InfoCommand()
|
||||||
WorldCommand()
|
VotingCommand()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ object Settings {
|
||||||
val preparingPhaseDuration get() = plugin.config.getInt("duration.preparing", 20)
|
val preparingPhaseDuration get() = plugin.config.getInt("duration.preparing", 20)
|
||||||
val combatPhaseDuration get() = plugin.config.getInt("duration.combat", 480) // 8 minutes
|
val combatPhaseDuration get() = plugin.config.getInt("duration.combat", 480) // 8 minutes
|
||||||
val overPhaseDuration get() = plugin.config.getInt("duration.over", 10)
|
val overPhaseDuration get() = plugin.config.getInt("duration.over", 10)
|
||||||
|
val mapVotingDuration get() = plugin.config.getInt("duration.map-voting", 10)
|
||||||
val initialCredits get() = plugin.config.getInt("initial-credits", 2)
|
val initialCredits get() = plugin.config.getInt("initial-credits", 2)
|
||||||
val creditsPerKill get() = plugin.config.getInt("credits-per-kill", 1)
|
val creditsPerKill get() = plugin.config.getInt("credits-per-kill", 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package de.moritzruth.spigot_ttt
|
package de.moritzruth.spigot_ttt
|
||||||
|
|
||||||
import de.moritzruth.spigot_ttt.game.GameManager
|
import de.moritzruth.spigot_ttt.game.GameManager
|
||||||
import de.moritzruth.spigot_ttt.worlds.WorldManager
|
import de.moritzruth.spigot_ttt.game.worlds.MapVoting
|
||||||
|
import de.moritzruth.spigot_ttt.game.worlds.WorldManager
|
||||||
import org.bukkit.ChatColor
|
import org.bukkit.ChatColor
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
@ -13,8 +14,8 @@ class TTTPlugin: JavaPlugin() {
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
saveDefaultConfig()
|
saveDefaultConfig()
|
||||||
|
|
||||||
WorldManager.removeNeglectedWorlds()
|
WorldManager.initialize()
|
||||||
|
MapVoting.registerListener()
|
||||||
CommandManager.initializeCommands()
|
CommandManager.initializeCommands()
|
||||||
GameManager.initialize()
|
GameManager.initialize()
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ class TTTPlugin: JavaPlugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
GameManager.resetWorld()
|
GameManager.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun broadcast(message: String, withPrefix: Boolean = true) {
|
fun broadcast(message: String, withPrefix: Boolean = true) {
|
||||||
|
|
|
@ -18,10 +18,11 @@ class AbortCommand: CommandExecutor {
|
||||||
|
|
||||||
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
||||||
if (GameManager.phase === null) {
|
if (GameManager.phase === null) {
|
||||||
|
val tttWorld = GameManager.tttWorld
|
||||||
|
if (tttWorld == null)
|
||||||
sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Zurzeit läuft kein Spiel.")
|
sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Zurzeit läuft kein Spiel.")
|
||||||
} else {
|
else tttWorld.unload()
|
||||||
GameManager.abortGame(true)
|
} else GameManager.abortGame(true)
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,15 @@ import de.moritzruth.spigot_ttt.game.items.shop.Shop
|
||||||
import de.moritzruth.spigot_ttt.game.items.shop.ShopListener
|
import de.moritzruth.spigot_ttt.game.items.shop.ShopListener
|
||||||
import de.moritzruth.spigot_ttt.game.players.PlayerManager
|
import de.moritzruth.spigot_ttt.game.players.PlayerManager
|
||||||
import de.moritzruth.spigot_ttt.game.players.Role
|
import de.moritzruth.spigot_ttt.game.players.Role
|
||||||
|
import de.moritzruth.spigot_ttt.game.worlds.TTTWorld
|
||||||
import de.moritzruth.spigot_ttt.plugin
|
import de.moritzruth.spigot_ttt.plugin
|
||||||
import de.moritzruth.spigot_ttt.utils.call
|
import de.moritzruth.spigot_ttt.utils.call
|
||||||
import de.moritzruth.spigot_ttt.utils.heartsToHealth
|
import de.moritzruth.spigot_ttt.utils.heartsToHealth
|
||||||
import de.moritzruth.spigot_ttt.utils.teleportToWorldSpawn
|
import de.moritzruth.spigot_ttt.utils.teleportToWorldSpawn
|
||||||
import org.bukkit.*
|
import org.bukkit.GameRule
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.Sound
|
||||||
|
import org.bukkit.SoundCategory
|
||||||
import org.bukkit.block.Block
|
import org.bukkit.block.Block
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
@ -29,9 +33,13 @@ object GameManager {
|
||||||
ScoreboardHelper.forEveryScoreboard { it.updateEverything(); it.showCorrectSidebarScoreboard() }
|
ScoreboardHelper.forEveryScoreboard { it.updateEverything(); it.showCorrectSidebarScoreboard() }
|
||||||
}
|
}
|
||||||
|
|
||||||
val world = plugin.server.getWorld("world")!!
|
var tttWorld: TTTWorld? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
if (value != null) adjustWorld()
|
||||||
|
}
|
||||||
|
|
||||||
val destroyedBlocks = mutableMapOf<Location, Material>()
|
val world get() = tttWorld?.world ?: throw IllegalStateException("The world was not set or is not loaded")
|
||||||
|
|
||||||
private val listeners = ItemManager.listeners
|
private val listeners = ItemManager.listeners
|
||||||
.plus(GeneralGameListener)
|
.plus(GeneralGameListener)
|
||||||
|
@ -43,8 +51,6 @@ object GameManager {
|
||||||
.plus(GeneralGameListener.packetListener)
|
.plus(GeneralGameListener.packetListener)
|
||||||
|
|
||||||
fun initialize() {
|
fun initialize() {
|
||||||
adjustWorld()
|
|
||||||
|
|
||||||
listeners.forEach { plugin.server.pluginManager.registerEvents(it, plugin) }
|
listeners.forEach { plugin.server.pluginManager.registerEvents(it, plugin) }
|
||||||
packetListeners.forEach { ProtocolLibrary.getProtocolManager().addPacketListener(it) }
|
packetListeners.forEach { ProtocolLibrary.getProtocolManager().addPacketListener(it) }
|
||||||
}
|
}
|
||||||
|
@ -64,7 +70,7 @@ object GameManager {
|
||||||
GameEndEvent(false).call()
|
GameEndEvent(false).call()
|
||||||
|
|
||||||
phase = null
|
phase = null
|
||||||
resetWorld()
|
reset()
|
||||||
PlayerManager.resetAfterGame()
|
PlayerManager.resetAfterGame()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,21 +98,11 @@ object GameManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetWorld() {
|
fun reset() {
|
||||||
CorpseManager.destroyAll()
|
CorpseManager.destroyAll()
|
||||||
ItemManager.reset()
|
ItemManager.reset()
|
||||||
|
tttWorld?.unload()
|
||||||
destroyedBlocks.forEach { (location, material) ->
|
tttWorld = null
|
||||||
world.getBlockAt(location).type = material
|
|
||||||
}
|
|
||||||
destroyedBlocks.clear()
|
|
||||||
|
|
||||||
world.run {
|
|
||||||
setStorm(false)
|
|
||||||
time = 0
|
|
||||||
setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
|
|
||||||
setGameRule(GameRule.DO_WEATHER_CYCLE, false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun abortGame(broadcast: Boolean = false) {
|
fun abortGame(broadcast: Boolean = false) {
|
||||||
|
@ -115,8 +111,8 @@ object GameManager {
|
||||||
GameEndEvent(true).call()
|
GameEndEvent(true).call()
|
||||||
phase = null
|
phase = null
|
||||||
Timers.cancelCurrentTask()
|
Timers.cancelCurrentTask()
|
||||||
resetWorld()
|
|
||||||
PlayerManager.resetAfterGame()
|
PlayerManager.resetAfterGame()
|
||||||
|
reset()
|
||||||
|
|
||||||
if (broadcast) {
|
if (broadcast) {
|
||||||
GameMessenger.aborted()
|
GameMessenger.aborted()
|
||||||
|
@ -179,9 +175,8 @@ object GameManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroyBlock(block: Block) {
|
fun destroyBlockIfAllowed(block: Block) {
|
||||||
if (phase != null && block.type.toString().contains("glass_pane", true)) {
|
if (phase != null && block.type.toString().contains("glass_pane", true)) {
|
||||||
destroyedBlocks[block.location] = block.type
|
|
||||||
block.type = Material.AIR
|
block.type = Material.AIR
|
||||||
world.playSound(
|
world.playSound(
|
||||||
block.location,
|
block.location,
|
||||||
|
|
|
@ -122,7 +122,7 @@ object GeneralGameListener : Listener {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
fun onPlayerInteract(event: PlayerInteractEvent) {
|
fun onPlayerInteract(event: PlayerInteractEvent) {
|
||||||
if (event.player.inventory.itemInMainHand.type == Material.AIR && event.action == Action.LEFT_CLICK_BLOCK) {
|
if (event.player.inventory.itemInMainHand.type == Material.AIR && event.action == Action.LEFT_CLICK_BLOCK) {
|
||||||
GameManager.destroyBlock(event.clickedBlock!!)
|
GameManager.destroyBlockIfAllowed(event.clickedBlock!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class InfoCommand: CommandExecutor {
|
||||||
init {
|
init {
|
||||||
val command = plugin.getCommand("info")!!
|
val command = plugin.getCommand("info")!!
|
||||||
command.tabCompleter = createTabCompleter { _, index ->
|
command.tabCompleter = createTabCompleter { _, index ->
|
||||||
if (index == 1) PlayerManager.tttPlayers.map { it.player.name }
|
if (index == 0) PlayerManager.tttPlayers.map { it.player.name }
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
command.setExecutor(this)
|
command.setExecutor(this)
|
||||||
|
|
|
@ -23,14 +23,14 @@ class ReviveCommand: CommandExecutor {
|
||||||
.map { it.player.name }
|
.map { it.player.name }
|
||||||
|
|
||||||
if (sender is Player) when (index) {
|
if (sender is Player) when (index) {
|
||||||
1 -> getPlayers().filter { it != sender.name }.run {
|
0 -> getPlayers().filter { it != sender.name }.run {
|
||||||
if (TTTPlayer.of(sender)?.alive == false) plus("here")
|
if (TTTPlayer.of(sender)?.alive == false) plus("here")
|
||||||
else this
|
else this
|
||||||
}
|
}
|
||||||
2 -> listOf("here")
|
1 -> listOf("here")
|
||||||
else -> null
|
else -> null
|
||||||
} else when (index) {
|
} else when (index) {
|
||||||
1 -> getPlayers()
|
0 -> getPlayers()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,13 @@ class StartCommand: CommandExecutor {
|
||||||
|
|
||||||
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
||||||
if (GameManager.phase === null) {
|
if (GameManager.phase === null) {
|
||||||
|
if (GameManager.tttWorld == null) {
|
||||||
|
sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Bitte starte zuerst das Map-Voting mit " +
|
||||||
|
"${ChatColor.WHITE}/voting")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
GameManager.startPreparingPhase()
|
GameManager.startPreparingPhase()
|
||||||
} catch (e: PlayerManager.NotEnoughPlayersException) {
|
} catch (e: PlayerManager.NotEnoughPlayersException) {
|
||||||
|
|
|
@ -98,7 +98,7 @@ abstract class Gun(
|
||||||
|
|
||||||
if (rayTraceResult !== null) {
|
if (rayTraceResult !== null) {
|
||||||
val hitBlock = rayTraceResult.hitBlock
|
val hitBlock = rayTraceResult.hitBlock
|
||||||
if (hitBlock != null) GameManager.destroyBlock(hitBlock)
|
if (hitBlock != null) GameManager.destroyBlockIfAllowed(hitBlock)
|
||||||
|
|
||||||
val entity = rayTraceResult.hitEntity
|
val entity = rayTraceResult.hitEntity
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,11 @@ import kotlin.random.Random
|
||||||
object PlayerManager {
|
object PlayerManager {
|
||||||
val tttPlayers = mutableListOf<TTTPlayer>()
|
val tttPlayers = mutableListOf<TTTPlayer>()
|
||||||
|
|
||||||
private fun getAvailablePlayers() = plugin.server.onlinePlayers.filter { it.gameMode === GameMode.SURVIVAL }
|
fun isAvailable(player: Player): Boolean {
|
||||||
|
return player.gameMode === GameMode.SURVIVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAvailablePlayers() = plugin.server.onlinePlayers.filter { isAvailable(it) }
|
||||||
private fun getStillLivingRoleGroups() = getStillLivingRoles().map { it.group }.toSet()
|
private fun getStillLivingRoleGroups() = getStillLivingRoles().map { it.group }.toSet()
|
||||||
private fun getStillLivingRoles() = tttPlayers.filter {
|
private fun getStillLivingRoles() = tttPlayers.filter {
|
||||||
it.alive || SecondChance.getInstance(it)?.preventRoundEnd == true
|
it.alive || SecondChance.getInstance(it)?.preventRoundEnd == true
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
package de.moritzruth.spigot_ttt.game.worlds
|
||||||
|
|
||||||
|
import de.moritzruth.spigot_ttt.Settings
|
||||||
|
import de.moritzruth.spigot_ttt.TTTPlugin
|
||||||
|
import de.moritzruth.spigot_ttt.game.GameManager
|
||||||
|
import de.moritzruth.spigot_ttt.game.players.PlayerManager
|
||||||
|
import de.moritzruth.spigot_ttt.plugin
|
||||||
|
import de.moritzruth.spigot_ttt.utils.*
|
||||||
|
import org.bukkit.ChatColor
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent
|
||||||
|
import org.bukkit.event.inventory.InventoryType
|
||||||
|
import org.bukkit.event.player.PlayerDropItemEvent
|
||||||
|
import org.bukkit.event.player.PlayerGameModeChangeEvent
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
class MapVoting private constructor() {
|
||||||
|
private var secondsRemaining = Settings.mapVotingDuration
|
||||||
|
private val maps = WorldManager.tttWorlds.toList()
|
||||||
|
private var timerTask: BukkitTask
|
||||||
|
|
||||||
|
private val inventory = plugin.server.createInventory(
|
||||||
|
null,
|
||||||
|
InventoryType.CHEST,
|
||||||
|
"${ChatColor.BLUE}${ChatColor.BOLD}Map-Voting"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val votes = mutableMapOf<UUID, TTTWorld>()
|
||||||
|
|
||||||
|
private fun createMapItemStack(map: TTTWorld): ItemStack {
|
||||||
|
val config = map.config
|
||||||
|
val iconMaterialString = config.getString("icon") ?: "GRASS_BLOCK"
|
||||||
|
val votesForThisMap = votes.values.count { it === map }
|
||||||
|
|
||||||
|
return ItemStack(Material.valueOf(iconMaterialString), max(1, votesForThisMap)).applyMeta {
|
||||||
|
setDisplayName("${config.getString("title")}${ChatColor.RESET} ${ChatColor.GRAY}($votesForThisMap)")
|
||||||
|
lore = listOf("").plus(config.getStringList("description").map { "${ChatColor.RESET}$it" })
|
||||||
|
hideInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
maps.forEachIndexed { index, map -> inventory.setItem(index, createMapItemStack(map)) }
|
||||||
|
|
||||||
|
timerTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||||
|
if (secondsRemaining == 0) {
|
||||||
|
stop()
|
||||||
|
val votedMaps = votes.values
|
||||||
|
val winnerMap =
|
||||||
|
if (votedMaps.count() == 0) maps.random()
|
||||||
|
else {
|
||||||
|
val mapsSortedByVotes = votedMaps.sortedBy { votedMap -> votedMaps.count { it === votedMap } }
|
||||||
|
mapsSortedByVotes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.broadcast("${ChatColor.GREEN}Ausgewählte Map: " +
|
||||||
|
winnerMap.config.getString("title"))
|
||||||
|
|
||||||
|
if (winnerMap.world != null) winnerMap.unload()
|
||||||
|
winnerMap.load()
|
||||||
|
GameManager.tttWorld = winnerMap
|
||||||
|
plugin.server.onlinePlayers.forEach {
|
||||||
|
it.teleport(winnerMap.world!!.spawnLocation)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inventory.setItem(26, ItemStack(Material.CLOCK, secondsRemaining).applyMeta {
|
||||||
|
setDisplayName("${ChatColor.GREEN}Verbleibende Zeit: ${ChatColor.WHITE}${secondsRemaining}s")
|
||||||
|
})
|
||||||
|
secondsRemaining -= 1
|
||||||
|
}
|
||||||
|
}, 0, secondsToTicks(1).toLong())
|
||||||
|
|
||||||
|
PlayerManager.getAvailablePlayers().forEach {
|
||||||
|
giveVoteItem(it)
|
||||||
|
it.sendMessage("${TTTPlugin.prefix}${ChatColor.GREEN}Das Map-Voting wurde gestartet.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun vote(player: Player, map: TTTWorld) {
|
||||||
|
votes[player.uniqueId] = map
|
||||||
|
inventory.setItem(maps.indexOf(map), createMapItemStack(map))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
PlayerManager.getAvailablePlayers().forEach {
|
||||||
|
it.sendMessage("${TTTPlugin.prefix}${ChatColor.RED}Das Map-Voting wurde abgebrochen.")
|
||||||
|
}
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stop() {
|
||||||
|
timerTask.cancel()
|
||||||
|
current = null
|
||||||
|
PlayerManager.getAvailablePlayers().forEach { removeVoteItem(it) }
|
||||||
|
plugin.server.onlinePlayers.forEach { if (it.openInventory.topInventory === inventory) it.closeInventory() }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var current: MapVoting? = null; private set
|
||||||
|
|
||||||
|
private val voteItem = ItemStack(Material.PAPER).applyMeta {
|
||||||
|
setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Map-Voting")
|
||||||
|
hideInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun giveVoteItem(player: Player) {
|
||||||
|
player.inventory.setItem(8, voteItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeVoteItem(player: Player) {
|
||||||
|
player.inventory.clear(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start(): MapVoting? {
|
||||||
|
if (current != null) throw IllegalStateException("There is already a map voting in progress")
|
||||||
|
return MapVoting().also { current = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val listener = object : Listener {
|
||||||
|
@EventHandler
|
||||||
|
fun onPlayerInteract(event: PlayerInteractEvent) {
|
||||||
|
if (event.item?.type == Material.PAPER && event.action.isRightClick) {
|
||||||
|
val voting = current ?: return
|
||||||
|
event.player.openInventory(voting.inventory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onInventoryClick(event: InventoryClickEvent) {
|
||||||
|
val whoClicked = event.whoClicked
|
||||||
|
if (whoClicked !is Player) return
|
||||||
|
|
||||||
|
val voting = current ?: return
|
||||||
|
if (event.clickedInventory != voting.inventory) return
|
||||||
|
event.isCancelled = true
|
||||||
|
|
||||||
|
if (event.click.isLeftClick) {
|
||||||
|
val map = voting.maps.getOrNull(event.slot) ?: return
|
||||||
|
voting.vote(whoClicked, map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onPlayerDropItem(event: PlayerDropItemEvent) {
|
||||||
|
if (current != null && event.itemDrop.itemStack.type == Material.PAPER) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onPlayerGameModeChange(event: PlayerGameModeChangeEvent) {
|
||||||
|
nextTick {
|
||||||
|
if (current != null) {
|
||||||
|
if (PlayerManager.isAvailable(event.player)) giveVoteItem(event.player)
|
||||||
|
else removeVoteItem(event.player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onPlayerJoinEvent(event: PlayerJoinEvent) {
|
||||||
|
if (PlayerManager.isAvailable(event.player)) {
|
||||||
|
if (current == null) removeVoteItem(event.player)
|
||||||
|
else giveVoteItem(event.player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerListener() {
|
||||||
|
plugin.server.pluginManager.registerEvents(listener, plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package de.moritzruth.spigot_ttt.game.worlds
|
||||||
|
|
||||||
|
import de.moritzruth.spigot_ttt.plugin
|
||||||
|
import de.moritzruth.spigot_ttt.utils.ConfigurationFile
|
||||||
|
import org.bukkit.World
|
||||||
|
import org.bukkit.WorldCreator
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
|
||||||
|
class TTTWorld(private val sourceWorldDir: File) {
|
||||||
|
init {
|
||||||
|
if (!sourceWorldDir.exists()) throw FileNotFoundException()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val name: String = sourceWorldDir.name
|
||||||
|
val config = ConfigurationFile(sourceWorldDir.resolve("config.yml"))
|
||||||
|
|
||||||
|
private val actualWorldName = "${WORLD_PREFIX}${name}"
|
||||||
|
private val worldDir = plugin.server.worldContainer.resolve("./$actualWorldName")
|
||||||
|
var world: World? = plugin.server.getWorld(actualWorldName); private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (world != null) unloadWorld()
|
||||||
|
if (worldDir.exists()) worldDir.deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
if (world != null) throw IllegalStateException("The world is already loaded")
|
||||||
|
|
||||||
|
sourceWorldDir.copyRecursively(worldDir)
|
||||||
|
loadWorld()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadWorld() {
|
||||||
|
world = plugin.server.getWorld(actualWorldName)
|
||||||
|
?: plugin.server.createWorld(WorldCreator.name(actualWorldName))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unload() {
|
||||||
|
if (world == null) throw IllegalStateException("The world is not loaded")
|
||||||
|
unloadWorld()
|
||||||
|
worldDir.deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unloadWorld() {
|
||||||
|
world!!.players.forEach { it.teleport(plugin.server.getWorld("world")!!.spawnLocation) }
|
||||||
|
plugin.server.unloadWorld(actualWorldName, false)
|
||||||
|
world = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createForSourceWorlds(): Set<TTTWorld> =
|
||||||
|
WORLDS_DIR.listFiles(File::isDirectory)!!.map { TTTWorld(it) }.toSet()
|
||||||
|
|
||||||
|
const val WORLD_PREFIX = "tempworld_"
|
||||||
|
private val WORLDS_DIR = plugin.dataFolder.resolve("./worlds").also { it.mkdirs() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package de.moritzruth.spigot_ttt.game.worlds
|
||||||
|
|
||||||
|
import de.moritzruth.spigot_ttt.COMMAND_RESPONSE_PREFIX
|
||||||
|
import de.moritzruth.spigot_ttt.game.GameManager
|
||||||
|
import de.moritzruth.spigot_ttt.game.GamePhase
|
||||||
|
import de.moritzruth.spigot_ttt.plugin
|
||||||
|
import de.moritzruth.spigot_ttt.utils.EmptyTabCompleter
|
||||||
|
import org.bukkit.ChatColor
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandExecutor
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
|
||||||
|
class VotingCommand: CommandExecutor {
|
||||||
|
init {
|
||||||
|
val command = plugin.getCommand("voting")!!
|
||||||
|
command.setExecutor(this)
|
||||||
|
command.tabCompleter = EmptyTabCompleter
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
||||||
|
if (args.count() == 1 && args[0].equals("cancel", true)) {
|
||||||
|
val voting = MapVoting.current
|
||||||
|
if (voting == null) {
|
||||||
|
sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Zurzeit läuft kein Map-Voting.")
|
||||||
|
} else {
|
||||||
|
voting.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MapVoting.current == null) {
|
||||||
|
if (GameManager.phase == null || GameManager.phase == GamePhase.OVER) {
|
||||||
|
MapVoting.start()
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Du kannst das Map-Voting nicht starten, " +
|
||||||
|
"während das Spiel läuft.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Es läuft bereits ein Map-Voting.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package de.moritzruth.spigot_ttt.game.worlds
|
||||||
|
|
||||||
|
object WorldManager {
|
||||||
|
lateinit var tttWorlds: Set<TTTWorld>
|
||||||
|
|
||||||
|
fun initialize() {
|
||||||
|
tttWorlds = TTTWorld.createForSourceWorlds()
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,10 +8,10 @@ fun createTabCompleter(fn: (sender: CommandSender, index: Int) -> List<String>?)
|
||||||
|
|
||||||
fun createTabCompleter(fn: (sender: CommandSender, index: Int, args: List<String>) -> List<String>?) =
|
fun createTabCompleter(fn: (sender: CommandSender, index: Int, args: List<String>) -> List<String>?) =
|
||||||
TabCompleter { sender, _, _, args ->
|
TabCompleter { sender, _, _, args ->
|
||||||
val index = args.count()
|
val index = args.count() - 1
|
||||||
|
|
||||||
val completions =
|
val completions =
|
||||||
if (index == 0) emptyList()
|
if (index < 0) emptyList()
|
||||||
else fn(sender, index, args.toList()) ?: emptyList()
|
else fn(sender, index, args.toList()) ?: emptyList()
|
||||||
|
|
||||||
completions.filter { it.startsWith(args.last(), true) }
|
completions.filter { it.startsWith(args.last(), true) }
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
package de.moritzruth.spigot_ttt.worlds
|
|
||||||
|
|
||||||
import de.moritzruth.spigot_ttt.plugin
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.bukkit.WorldCreator
|
|
||||||
|
|
||||||
class TTTWorld(val sourceWorld: WorldManager.SourceWorld) {
|
|
||||||
var state: State = State.NOT_COPIED; private set
|
|
||||||
enum class State {
|
|
||||||
NOT_COPIED,
|
|
||||||
COPYING,
|
|
||||||
COPIED,
|
|
||||||
LOADED,
|
|
||||||
UNLOADING
|
|
||||||
}
|
|
||||||
|
|
||||||
val id = WorldManager.tttWorlds.count()
|
|
||||||
val actualWorldName = "${WORLD_PREFIX}${id}"
|
|
||||||
private val worldDir = plugin.server.worldContainer.resolve("./$actualWorldName")
|
|
||||||
|
|
||||||
init {
|
|
||||||
WorldManager.tttWorlds.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun copy() {
|
|
||||||
if (state != State.NOT_COPIED) throw IllegalStateException("The world was already copied")
|
|
||||||
state = State.COPYING
|
|
||||||
|
|
||||||
coroutineScope {
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
sourceWorld.dir.copyRecursively(worldDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun load() {
|
|
||||||
if (state != State.COPIED) throw IllegalStateException("The world was not copied yet or already loaded")
|
|
||||||
plugin.server.createWorld(WorldCreator.name(actualWorldName))
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun save() {
|
|
||||||
if (state != State.LOADED) throw IllegalStateException("The world must be loaded")
|
|
||||||
|
|
||||||
coroutineScope {
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
val tempWorldDir = WorldManager.worldsDir.resolve("./${sourceWorld.name}_$id")
|
|
||||||
worldDir.copyRecursively(tempWorldDir)
|
|
||||||
sourceWorld.dir.deleteRecursively()
|
|
||||||
tempWorldDir.renameTo(sourceWorld.dir)
|
|
||||||
state = State.NOT_COPIED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun unloadAndRemove() {
|
|
||||||
if (state != State.LOADED) throw IllegalStateException("The world must be loaded")
|
|
||||||
state = State.UNLOADING
|
|
||||||
plugin.server.unloadWorld(actualWorldName, false)
|
|
||||||
|
|
||||||
coroutineScope {
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
worldDir.deleteRecursively()
|
|
||||||
state = State.NOT_COPIED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val WORLD_PREFIX = "tempworld_"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package de.moritzruth.spigot_ttt.worlds
|
|
||||||
|
|
||||||
import de.moritzruth.spigot_ttt.plugin
|
|
||||||
import de.moritzruth.spigot_ttt.utils.createTabCompleter
|
|
||||||
import org.bukkit.command.Command
|
|
||||||
import org.bukkit.command.CommandExecutor
|
|
||||||
import org.bukkit.command.CommandSender
|
|
||||||
|
|
||||||
class WorldCommand: CommandExecutor {
|
|
||||||
init {
|
|
||||||
val command = plugin.getCommand("world")!!
|
|
||||||
command.setExecutor(this)
|
|
||||||
command.tabCompleter = createTabCompleter { _, index, args ->
|
|
||||||
return@createTabCompleter when(index) {
|
|
||||||
0 -> listOf("load", "save", "join", "list")
|
|
||||||
1 -> when(args[0].toLowerCase()) {
|
|
||||||
"load" -> WorldManager.sourceWorlds.map { it.name }
|
|
||||||
"save", "join" -> WorldManager.tttWorlds.map { it.id.toString() }
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package de.moritzruth.spigot_ttt.worlds
|
|
||||||
|
|
||||||
import de.moritzruth.spigot_ttt.plugin
|
|
||||||
import de.moritzruth.spigot_ttt.utils.ConfigurationFile
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
object WorldManager {
|
|
||||||
data class SourceWorld(val dir: File) {
|
|
||||||
val name: String = dir.name
|
|
||||||
val config = ConfigurationFile(dir.resolve("config.yml"))
|
|
||||||
}
|
|
||||||
|
|
||||||
val worldsDir = plugin.dataFolder.resolve("./worlds").also { it.mkdirs() }
|
|
||||||
val sourceWorlds = worldsDir.listFiles(File::isDirectory)!!.map { SourceWorld(it) }
|
|
||||||
|
|
||||||
val tttWorlds = mutableSetOf<TTTWorld>()
|
|
||||||
|
|
||||||
fun removeNeglectedWorlds() {
|
|
||||||
plugin.server.worldContainer.listFiles { file ->
|
|
||||||
file.isDirectory && file.name.startsWith(TTTWorld.WORLD_PREFIX) &&
|
|
||||||
tttWorlds.find { it.actualWorldName == file.name } != null
|
|
||||||
}!!.forEach { it.deleteRecursively() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ duration:
|
||||||
preparing: 20
|
preparing: 20
|
||||||
combat: 480
|
combat: 480
|
||||||
over: 10
|
over: 10
|
||||||
|
map-voting: 10
|
||||||
|
|
||||||
initial-credits: 2
|
initial-credits: 2
|
||||||
credits-per-kill: 1
|
credits-per-kill: 1
|
||||||
|
|
|
@ -7,15 +7,20 @@ depend:
|
||||||
- ProtocolLib
|
- ProtocolLib
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
|
voting:
|
||||||
|
usage: /voting ['cancel']
|
||||||
|
permission: ttt.voting
|
||||||
|
description: Start (or cancel) the map voting
|
||||||
|
|
||||||
start:
|
start:
|
||||||
usage: /start
|
usage: /start
|
||||||
permission: ttt.start
|
permission: ttt.start
|
||||||
description: Starts the TTT game
|
description: Start the TTT game
|
||||||
|
|
||||||
abort:
|
abort:
|
||||||
usage: /abort
|
usage: /abort
|
||||||
permission: ttt.abort
|
permission: ttt.abort
|
||||||
description: Aborts the TTT game
|
description: Go back to the lobby world and abort the TTT game, if it is running
|
||||||
|
|
||||||
additemspawn:
|
additemspawn:
|
||||||
usage: /additemspawn
|
usage: /additemspawn
|
||||||
|
@ -23,12 +28,12 @@ commands:
|
||||||
description: Add an item spawn
|
description: Add an item spawn
|
||||||
|
|
||||||
revive:
|
revive:
|
||||||
usage: /revive [Player] ['here']
|
usage: /revive [player] ['here']
|
||||||
permission: ttt.revive
|
permission: ttt.revive
|
||||||
description: Revive yourself or another player at the world spawn or at your location
|
description: Revive yourself or another player at the world spawn or at your location
|
||||||
|
|
||||||
info:
|
info:
|
||||||
usage: /info [Player]
|
usage: /info [player]
|
||||||
permission: ttt.info
|
permission: ttt.info
|
||||||
description: Show information about all players or a specific player
|
description: Show information about all players or a specific player
|
||||||
|
|
||||||
|
@ -45,8 +50,3 @@ commands:
|
||||||
permission: ttt.reload
|
permission: ttt.reload
|
||||||
aliases:
|
aliases:
|
||||||
- rt
|
- rt
|
||||||
|
|
||||||
world:
|
|
||||||
usage: /world load <Name> OR /world <'join'|'save'> <World ID> OR /world list
|
|
||||||
description: Perform world operations
|
|
||||||
permission: ttt.world
|
|
||||||
|
|
Reference in a new issue