Files
SteamWar/TNTLeague/src/de/steamwar/tntleague/game/TNTLeagueGame.kt
T
2024-12-13 19:13:14 +01:00

246 lines
8.7 KiB
Kotlin

/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.tntleague.game
import de.steamwar.kotlin.util.Area
import de.steamwar.scoreboard.SWScoreboard
import de.steamwar.sql.Fight
import de.steamwar.sql.FightPlayer
import de.steamwar.sql.SteamwarUser
import de.steamwar.tntleague.colorByTeam
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.message
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.*
import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.Sound
import org.bukkit.entity.Item
import org.bukkit.entity.Player
import org.bukkit.entity.TNTPrimed
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()
message.broadcast("GAME_STARTED")
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) {
if (blueTeam.damagedBlocks > redTeam.damagedBlocks)
win(blueTeam, WinReason.TIMEOUT)
else if (redTeam.damagedBlocks > blueTeam.damagedBlocks)
win(redTeam, WinReason.TIMEOUT)
else
draw(WinReason.TIMEOUT)
return@bukkit
}
if (gameTimeRemaining % 300 == 0) {
message.broadcast("TIME_REMAINING", (gameTimeRemaining / 60))
plugin.server.onlinePlayers.forEach { it.playSound(it.location, Sound.BLOCK_NOTE_BLOCK_PLING, 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(it.location, Sound.ENTITY_ENDER_DRAGON_DEATH, 1f, 1f)
}
message.broadcast("GAME_ENDED")
spawnerTask.cancel()
var shutdown = 10
plugin.server.scheduler.runTaskTimer(plugin, bukkit {
if (shutdown == 0) {
plugin.server.shutdown()
}
message.broadcast("SHUTDOWN", shutdown)
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
message.broadcast("GAME_STARTING", countdown.toString())
task = plugin.server.scheduler.scheduleSyncRepeatingTask(plugin, {
plugin.server.onlinePlayers.forEach { it.playSound(it.location, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f) }
if (--countdown == 0) {
plugin.server.onlinePlayers.forEach { it.level = 0 }
task = task?.also { plugin.server.scheduler.cancelTask(it) }.let { null }
setup()
} else {
plugin.server.onlinePlayers.forEach { it.level = countdown }
}
}, 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 {
remove(player)
}
}
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 }
state = GameState.LOBBY
}
}
fun win(tntLeagueTeam: TNTLeagueTeam, reason: WinReason) {
if (state != GameState.RUNNING) return
end()
plugin.server.onlinePlayers.forEach { message.send("TEAM_WIN", it, message.parse(tntLeagueTeam.name, it).colorByTeam(tntLeagueTeam)) }
explode(tntLeagueTeam.opposite)
}
fun draw(reason: WinReason) {
if (state != GameState.RUNNING) return
end()
message.broadcast("DRAW")
}
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 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
}
}