284 lines
10 KiB
Kotlin
284 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.util.Area
|
|
import de.steamwar.message.SubMessage
|
|
import de.steamwar.network.NetworkSender
|
|
import de.steamwar.network.packets.common.FightInfoPacket
|
|
import de.steamwar.scoreboard.SWScoreboard
|
|
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.TNTLeagueScoreboard
|
|
import org.bukkit.GameMode
|
|
import org.bukkit.Location
|
|
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
|
|
import kotlin.random.Random
|
|
|
|
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(TNTLeagueConfig.config.blueTeam)
|
|
val redTeam = TNTLeagueTeam(TNTLeagueConfig.config.redTeam)
|
|
|
|
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()
|
|
|
|
updateFightinfo()
|
|
|
|
message.broadcast("GAME_STARTED")
|
|
|
|
start = Timestamp.from(Instant.now())
|
|
|
|
var spawnCount = 0
|
|
spawnerTask = plugin.server.scheduler.runTaskTimer(plugin, bukkit {
|
|
val coinsToSpawn = if (spawnCount % 28 == 0) {
|
|
DealerInventory.huge_coins
|
|
} else if (spawnCount % 7 == 0) {
|
|
DealerInventory.big_coins
|
|
} else {
|
|
DealerInventory.coins
|
|
}
|
|
spawnCount++
|
|
|
|
if (world.getNearbyEntitiesByType(Item::class.java, TNTLeagueWorldConfig.blueTeam.itemSpawn, 3.0).sumOf { it.itemStack.amount } <= 256) {
|
|
spawnItems(TNTLeagueWorldConfig.blueTeam.itemSpawn, coinsToSpawn)
|
|
}
|
|
if (world.getNearbyEntitiesByType(Item::class.java, TNTLeagueWorldConfig.redTeam.itemSpawn, 3.0).sumOf { it.itemStack.amount } <= 256) {
|
|
spawnItems(TNTLeagueWorldConfig.redTeam.itemSpawn, coinsToSpawn)
|
|
}
|
|
}, 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)
|
|
|
|
updateFightinfo()
|
|
}
|
|
|
|
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(player: Player) = if (TNTLeagueConfig.isEvent()) getEventFreeTeam(player) else getNormalFreeTeam(player)
|
|
|
|
private fun getNormalFreeTeam(player: Player) = if (blueTeam.leader == null && (TNTLeagueConfig.config.blueLeader == null || player.uniqueId == TNTLeagueConfig.config.blueLeader)) blueTeam
|
|
else if (redTeam.leader == null && TNTLeagueConfig.config.redLeader == null || player.uniqueId == TNTLeagueConfig.config.redLeader) redTeam
|
|
else null
|
|
|
|
private fun getEventFreeTeam(player: Player) = SteamwarUser.get(player.uniqueId).let { user ->
|
|
if (user.team == TNTLeagueConfig.config.eventTeamBlue.teamId && blueTeam.members.size < TNTLeagueConfig.config.event.maximumTeamMembers) blueTeam
|
|
else if (user.team == TNTLeagueConfig.config.eventTeamRed.teamId && redTeam.members.size < TNTLeagueConfig.config.event.maximumTeamMembers) 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")
|
|
}
|
|
|
|
updateFightinfo()
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
updateFightinfo()
|
|
}
|
|
|
|
fun win(tntLeagueTeam: TNTLeagueTeam, reason: WinReason) {
|
|
if (state != GameState.RUNNING) return
|
|
end()
|
|
plugin.server.onlinePlayers.forEach { message.send("TEAM_WIN", it, tntLeagueTeam.name) }
|
|
explode(tntLeagueTeam.opposite)
|
|
|
|
if (TNTLeagueConfig.isEvent()) {
|
|
TNTLeagueConfig.config.eventFight.ergebnis = tntLeagueTeam.ergebnisInt
|
|
}
|
|
}
|
|
|
|
fun draw(reason: WinReason) {
|
|
if (state != GameState.RUNNING) return
|
|
end()
|
|
message.broadcast("DRAW")
|
|
|
|
if (TNTLeagueConfig.isEvent()) {
|
|
TNTLeagueConfig.config.eventFight.ergebnis = 3
|
|
}
|
|
}
|
|
|
|
fun explode(team: TNTLeagueTeam) {
|
|
Area(team.config.worldConfig.spawnLocation.clone().add(20.0, 30.0, 20.0), team.config.worldConfig.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
|
|
}
|
|
}
|
|
}
|
|
|
|
fun updateFightinfo() {
|
|
NetworkSender.send(FightInfoPacket(
|
|
plugin.server.worlds.first().name,
|
|
"TNTLeague",
|
|
"",
|
|
blueTeam.name.message.colorByTeam(blueTeam),
|
|
redTeam.name.message.colorByTeam(redTeam),
|
|
state.lobbyName,
|
|
TNTLeagueConfig.config.gameTime - gameTimeRemaining,
|
|
blueTeam.leader?.let { SteamwarUser.get(it.uniqueId).id } ?: 0,
|
|
redTeam.leader?.let { SteamwarUser.get(it.uniqueId).id } ?: 0,
|
|
0,
|
|
0,
|
|
blueTeam.members.map { SteamwarUser.get(it.uniqueId).id },
|
|
redTeam.members.map { SteamwarUser.get(it.uniqueId).id },
|
|
plugin.server.onlinePlayers.filter {! blueTeam.members.contains(it) && !redTeam.members.contains(it) }.map { SteamwarUser.get(it.uniqueId).id }
|
|
))
|
|
}
|
|
|
|
enum class GameState(val listener: Listener, val lobbyName: String) {
|
|
LOBBY(LobbyListener, "PRE_LEADER_SETUP"),
|
|
STARTING(LobbyListener, "POST_SCHEM_SETUP"),
|
|
RUNNING(IngameListener, "RUNNING"),
|
|
END(DummyListener, "SPECTATE");
|
|
}
|
|
|
|
enum class WinReason {
|
|
TIMEOUT,
|
|
DESTROYED,
|
|
LEAVE
|
|
}
|
|
} |