forked from SteamWar/SteamWar
277 lines
10 KiB
Kotlin
277 lines
10 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.message.*
|
|
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.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.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.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.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 && members.isNotEmpty() && state == GameState.RUNNING) {
|
|
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
|
|
}
|
|
} |