package de.steamwar.tntleague.game import de.steamwar.scoreboard.SWScoreboard import de.steamwar.sql.Fight import de.steamwar.sql.FightPlayer import de.steamwar.sql.SteamwarUser import de.steamwar.tntleague.config.TNTLeagueConfig import de.steamwar.tntleague.config.TNTLeagueWorldConfig import de.steamwar.tntleague.config.world import de.steamwar.tntleague.events.DummyListener import de.steamwar.tntleague.events.IngameListener import de.steamwar.tntleague.events.LobbyListener import de.steamwar.tntleague.inventory.DealerInventory import de.steamwar.tntleague.plugin import de.steamwar.tntleague.util.* import net.kyori.adventure.bossbar.BossBar import net.kyori.adventure.sound.Sound import org.bukkit.GameMode import org.bukkit.Location import org.bukkit.Material import org.bukkit.block.data.type.TNT import org.bukkit.entity.Entity import org.bukkit.entity.Item import org.bukkit.entity.Player import org.bukkit.entity.TNTPrimed import org.bukkit.entity.Villager import org.bukkit.event.HandlerList import org.bukkit.event.Listener import org.bukkit.inventory.ItemStack import org.bukkit.scheduler.BukkitTask import java.sql.Timestamp import java.time.Instant object TNTLeagueGame { var state: GameState = GameState.LOBBY set(value) { if (field.listener != value.listener) { HandlerList.unregisterAll(field.listener) plugin.server.pluginManager.registerEvents(value.listener, plugin) } field = value } var gameTimeRemaining: Int = TNTLeagueConfig.config.gameTime val blueTeam = TNTLeagueTeam(TNTLeagueWorldConfig.blueTeam, TNTLeagueTeam.Team.BLUE) val redTeam = TNTLeagueTeam(TNTLeagueWorldConfig.redTeam, TNTLeagueTeam.Team.RED) private lateinit var start: Timestamp private var task: Int? = null private lateinit var spawnerTask: BukkitTask private lateinit var timerTask: BukkitTask private fun setup() { assert(state == GameState.STARTING) { "Game is already running" } state = GameState.RUNNING plugin.server.onlinePlayers.forEach { SWScoreboard.impl.createScoreboard(it, TNTLeagueScoreboard(it)) } blueTeam.start() redTeam.start() plugin.server.broadcast(translate("gameStarted").success()) val tnt = ItemStack(Material.TNT) start = Timestamp.from(Instant.now()) spawnerTask = plugin.server.scheduler.runTaskTimer(plugin, bukkit { if (world.getNearbyEntitiesByType(Item::class.java, TNTLeagueWorldConfig.blueTeam.itemSpawn, 3.0).sumOf { it.itemStack.amount } <= 256) { spawnItems(TNTLeagueWorldConfig.blueTeam.itemSpawn, tnt) spawnItems(TNTLeagueWorldConfig.blueTeam.itemSpawn, DealerInventory.coins) } if (world.getNearbyEntitiesByType(Item::class.java, TNTLeagueWorldConfig.redTeam.itemSpawn, 3.0).sumOf { it.itemStack.amount } <= 256) { spawnItems(TNTLeagueWorldConfig.redTeam.itemSpawn, tnt) spawnItems(TNTLeagueWorldConfig.redTeam.itemSpawn, DealerInventory.coins) } }, 5, 10) timerTask = plugin.server.scheduler.runTaskTimer(plugin, bukkit { gameTimeRemaining-- if (gameTimeRemaining == 0) { draw(WinReason.TIMEOUT) return@bukkit } if (gameTimeRemaining % 300 == 0) { plugin.server.broadcast(translate("timeRemaining", (gameTimeRemaining / 60).toString().yellow()).basic()) plugin.server.onlinePlayers.forEach { it.playSound(Sound.sound(org.bukkit.Sound.BLOCK_NOTE_BLOCK_PLING.key, Sound.Source.MASTER, 1f, 1f)) } } }, 20, 20) } private fun bukkit(f: () -> Unit): () -> Unit = f private fun end() { if(state != GameState.RUNNING) return state = GameState.END plugin.server.onlinePlayers.forEach { it.gameMode = GameMode.SPECTATOR SWScoreboard.impl.removeScoreboard(it) it.playSound(Sound.sound(org.bukkit.Sound.ENTITY_ENDER_DRAGON_DEATH.key, Sound.Source.MASTER, 1f, 1f)) } plugin.server.broadcast(translate("gameEnded").success()) spawnerTask.cancel() var shutdown = 10 plugin.server.scheduler.runTaskTimer(plugin, bukkit { if (shutdown == 0) { plugin.server.shutdown() } plugin.server.broadcast(translate("shutdown", shutdown.toString().yellow()).basic()) shutdown-- }, 20, 20) } private fun spawnItems(loc: Location, item: ItemStack) = plugin.server.worlds.first().dropItem(loc, item) fun getTeam(player: Player) = if (player in blueTeam.members) blueTeam else if (player in redTeam.members) redTeam else null fun getFreeTeam() = if (blueTeam.leader == null) blueTeam else if (redTeam.leader == null) redTeam else null fun checkStart() { if (blueTeam.isReady && redTeam.isReady) { blueTeam.leader?.inventory?.clear() redTeam.leader?.inventory?.clear() state = GameState.STARTING var countdown = TNTLeagueConfig.config.startDelay plugin.server.broadcast(translate("gameStarting", countdown.toString().yellow()).basic()) val bar = BossBar.bossBar(translate("gameStart", countdown.toString().yellow()).gray(), (TNTLeagueConfig.config.startDelay - countdown) / TNTLeagueConfig.config.startDelay.toFloat(), BossBar.Color.GREEN, BossBar.Overlay.NOTCHED_10) plugin.server.onlinePlayers.forEach { bar.addViewer(it) } task = plugin.server.scheduler.scheduleSyncRepeatingTask(plugin, { plugin.server.onlinePlayers.forEach { it.playSound(Sound.sound(org.bukkit.Sound.ENTITY_EXPERIENCE_ORB_PICKUP.key, Sound.Source.MASTER, 1f, 1f)) } if (countdown-- == 0) { plugin.server.onlinePlayers.forEach { it.hideBossBar(bar) } task = task?.also { plugin.server.scheduler.cancelTask(it) }.let { null } setup() } else { bar.name(translate("gameStart", countdown.toString().yellow()).gray()) bar.progress((TNTLeagueConfig.config.startDelay - countdown) / TNTLeagueConfig.config.startDelay.toFloat()) plugin.server.onlinePlayers.filter { !it.activeBossBars().contains(bar) }.forEach { bar.addViewer(it) } } }, 20, 20) if (task == -1) { error("Failed to start countdown task") } } } fun playerLeave(player: Player) { blueTeam.invites.remove(player) redTeam.invites.remove(player) getTeam(player)?.apply { members.remove(player) if (leader == player) { win(this.opposite, WinReason.LEAVE) } } } fun reset() { assert(state == GameState.LOBBY || state == GameState.STARTING) { "Game is not in lobby or starting state" } if (state == GameState.STARTING) { task = task?.also { plugin.server.scheduler.cancelTask(it) }.let { null } plugin.server.onlinePlayers.forEach { p -> p.activeBossBars().forEach { it.removeViewer(p) } } state = GameState.LOBBY } } fun win(tntLeagueTeam: TNTLeagueTeam, reason: WinReason) { if (state != GameState.RUNNING) return end() plugin.server.broadcast(translate("teamWin", translate(tntLeagueTeam.name).color(tntLeagueTeam.color)).success()) statistic(tntLeagueTeam, reason) explode(tntLeagueTeam.opposite) } fun draw(reason: WinReason) { if (state != GameState.RUNNING) return end() plugin.server.broadcast(translate("draw").success()) statistic(null, reason) } fun explode(team: TNTLeagueTeam) { Area(team.config.spawnLocation.clone().add(20.0, 30.0, 20.0), team.config.spawnLocation.clone().subtract(20.0, 0.0, 20.0).add(0.0, 30.0, 0.0)) .locations .filterIndexed { index, _ -> index % 7 == 0 } .forEachIndexed { index, location -> world.spawn(location, TNTPrimed::class.java).apply { fuseTicks = index + 40 } } } private fun statistic(winTeam: TNTLeagueTeam?, reason: WinReason) { val fightId = Fight.create( "TNTLeague", world.name, start, TNTLeagueConfig.config.gameTime - gameTimeRemaining, SteamwarUser.get(blueTeam.leader!!.uniqueId).id, SteamwarUser.get(redTeam.leader!!.uniqueId).id, null, null, when (winTeam) { blueTeam -> 1 redTeam -> 2 else -> 0 }, when (reason) { WinReason.TIMEOUT -> "TIMEOUT" WinReason.DESTROYED -> "DESTROYED" WinReason.LEAVE -> "LEAVE" } ) addTeamMember(blueTeam, fightId) addTeamMember(redTeam, fightId) } private fun addTeamMember(team: TNTLeagueTeam, fightId: Int) { team.members.filter { team.leader != it } .forEach { FightPlayer.create( fightId, SteamwarUser.get(it.uniqueId).id, team == blueTeam, "TNTLeague", 0, false ) } } enum class GameState(val listener: Listener) { LOBBY(LobbyListener), STARTING(LobbyListener), RUNNING(IngameListener), END(DummyListener); } enum class WinReason { TIMEOUT, DESTROYED, LEAVE } }