Add TNTLeague Module

This commit is contained in:
2024-08-16 11:38:54 +02:00
parent f8f184514b
commit 66f5b6491c
24 changed files with 1446 additions and 0 deletions
@@ -0,0 +1,259 @@
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.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) {
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
}
}
@@ -0,0 +1,146 @@
package de.steamwar.tntleague.game
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
import de.steamwar.tntleague.config.targetedBlocks
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.*
import net.kyori.adventure.sound.Sound
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextColor
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.awt.Color.green
data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private val team: Team) {
var leader: Player? = null
set(player) {
field = player
if (player != null) {
with(player.inventory) {
clear()
setItem(4, readyItem())
}
}
}
val members = mutableListOf<Player>()
val invites = mutableListOf<Player>()
val name: String
get() = team.name.lowercase()
val color: TextColor
get() = team.color
var isReady: Boolean = false
set(value) {
field = value
leader?.inventory?.setItem(4, readyItem())
leader?.playSound(Sound.sound(org.bukkit.Sound.BLOCK_NOTE_BLOCK_PLING.key, Sound.Source.MASTER, 1f, 1f))
plugin.server.onlinePlayers.forEach { it.sendActionBar(translate(if (value) "isReady" else "isNotReady", translate(this.name).colorByTeam(this)).let { cmp ->
if (value) {
cmp.green()
} else {
cmp.red()
}
}) }
if (value && opposite.isReady) {
TNTLeagueGame.checkStart()
}
}
var damagedBlocks: Int = 0
set(value) {
field = value
if (value >= targetedBlocks) {
TNTLeagueGame.win(this, TNTLeagueGame.WinReason.DESTROYED)
}
}
val opposite: TNTLeagueTeam
get() = when (team) {
Team.BLUE -> TNTLeagueGame.redTeam
Team.RED -> TNTLeagueGame.blueTeam
}
fun join(player: Player): Boolean {
members.add(player)
with(player) {
teleport(config.spawnLocation)
gameMode = GameMode.ADVENTURE
inventory.clear()
plugin.server.broadcast(translate("joinTeam", name().colorByTeam(this@TNTLeagueTeam), translate(this@TNTLeagueTeam.name).colorByTeam(this@TNTLeagueTeam)).basic())
}
if (leader == null) {
leader = player
}
return true
}
fun readyItem() = if (isReady) {
ItemStack.of(Material.LIME_DYE).apply {
itemMeta = itemMeta.apply {
displayName(translate("ready").green().translate(leader!!))
}
}
} else {
ItemStack.of(Material.RED_DYE).apply {
itemMeta = itemMeta.apply {
displayName(translate("notReady").red().translate(leader!!))
}
}
}
fun start() = members.forEach {
with(it) {
gameMode = GameMode.SURVIVAL
inventory.addItem(ItemStack.of(Material.DIAMOND_PICKAXE).apply {
itemMeta = itemMeta.apply {
isUnbreakable = true
addEnchant(Enchantment.EFFICIENCY, 1, false)
}
})
}
}
fun leave(player: Player) {
if (TNTLeagueGame.state == TNTLeagueGame.GameState.RUNNING) {
TNTLeagueGame.playerLeave(player)
} else {
members.remove(player)
if (members.isEmpty()) {
plugin.server.onlinePlayers.firstOrNull { it != player && TNTLeagueGame.getTeam(it) == null }?.run {
members.add(this)
}
}
if (leader == player) {
leader = members.firstOrNull()
}
}
}
fun remove(player: Player) {
leave(player)
with(player) {
teleport(TNTLeagueWorldConfig.lobby)
gameMode = GameMode.SPECTATOR
inventory.clear()
plugin.server.broadcast(translate("quitTeam", name().colorByTeam(this@TNTLeagueTeam), translate(this@TNTLeagueTeam.name).colorByTeam(this@TNTLeagueTeam)).basic())
}
}
enum class Team(val color: TextColor) {
BLUE(NamedTextColor.BLUE),
RED(NamedTextColor.RED);
}
}