Files
GameModes-SpeedHG/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt
TDSTOS e411879b20 Remove legacy modules, add language & scoreboard
Large refactor removing many legacy subsystems (abilities, kit system, database repos, recraft, world manager, extensive config classes, lunar/luckperms integrations and various listeners/commands). Introduces a lightweight LanguageManager, AntiRunningManager, ScoreboardManager, ConnectListener and a SoupListener; simplifies the main SpeedHG plugin to initialize these components and register the connection listener. Build changes: update Gradle wrapper to 8.10, remove paperweight and several external dependencies, add fr.mrmicky:fastboard and simplify shadowJar/build task configuration. Adds default language resource (languages/en_US.yml) and updates plugin/config resources. Purpose: simplify and decouple the plugin, reduce dependency surface and prepare for a leaner, modular rewrite.
2026-03-25 00:55:20 +01:00

370 lines
8.6 KiB
Kotlin

package club.mcscrims.speedhg.game
import club.mcscrims.speedhg.SpeedHG
import club.mcscrims.speedhg.util.sendMsg
import club.mcscrims.speedhg.util.trans
import net.kyori.adventure.title.Title
import org.bukkit.*
import org.bukkit.attribute.Attribute
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.entity.EntityDamageEvent
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import org.bukkit.scheduler.BukkitTask
import java.util.*
import kotlin.random.Random
class GameManager(
private val plugin: SpeedHG
): Listener {
var currentState: GameState = GameState.LOBBY
private set
var timer = 0
val alivePlayers = mutableSetOf<UUID>()
private var gameTask: BukkitTask? = null
// Einstellungen aus Config (gecached für Performance)
private val minPlayers = plugin.config.getInt("game.min-players", 2)
private val lobbyTime = plugin.config.getInt("game.lobby-time", 60)
private val invincibilityTime = plugin.config.getInt("game.invincibility-time", 60)
private val startBorder = plugin.config.getDouble("game.border-start", 300.0)
private val endBorder = plugin.config.getDouble("game.border-end", 20.0)
// Zeit in Sekunden, bis Border komplett klein ist (z.B. 10 Min)
private val borderShrinkTime = plugin.config.getLong("game.border-shrink-time", 600)
init {
plugin.server.pluginManager.registerEvents( this, plugin )
gameTask = Bukkit.getScheduler().runTaskTimer( plugin, { ->
gameLoop()
}, 20L, 20L )
}
private fun gameLoop()
{
when( currentState )
{
GameState.LOBBY ->
{
if ( Bukkit.getOnlinePlayers().size >= minPlayers )
{
setGameState( GameState.STARTING )
timer = lobbyTime
}
}
GameState.STARTING ->
{
if ( Bukkit.getOnlinePlayers().size < minPlayers )
{
setGameState( GameState.LOBBY )
Bukkit.getOnlinePlayers().forEach { player ->
player.sendMsg( "game.start-aborted" )
}
return
}
timer--
if (timer in listOf( 60, 30, 10, 5, 4, 3, 2, 1 ))
{
Bukkit.getOnlinePlayers().forEach { p ->
p.sendMsg( "timer.lobby", "time" to timer.toString() )
p.playSound( p.location, Sound.BLOCK_NOTE_BLOCK_HAT, 1f, 1f )
}
}
if ( timer <= 0 )
startGame()
}
GameState.INVINCIBILITY ->
{
timer--
if ( timer <= 0 )
startFighting()
else
{
Bukkit.getOnlinePlayers().forEach { p ->
p.sendActionBar(p.trans( "timer.actionbar-invincibility", "time" to timer.toString() ))
}
}
}
GameState.INGAME ->
{
timer++
updateCompass()
checkWin()
}
GameState.ENDING ->
{
timer--
if ( timer <= 0 )
Bukkit.shutdown()
}
}
}
fun setGameState(
newState: GameState
) {
this.currentState = newState
}
private fun startGame()
{
setGameState( GameState.INVINCIBILITY )
timer = invincibilityTime
val world = Bukkit.getWorld( "world" ) ?: return
world.time = 0
world.setStorm( false )
val border = world.worldBorder
border.center = Location( world, 0.0, 0.0, 0.0 )
border.size = startBorder
border.damageBuffer = 0.0
border.damageAmount = 1.0
val speedEffect = PotionEffect(
PotionEffectType.SPEED,
timer,
0,
false,
false,
true
)
val hasteEffect = PotionEffect(
PotionEffectType.HASTE,
timer,
0,
false,
false,
true
)
alivePlayers.clear()
Bukkit.getOnlinePlayers().forEach { player ->
alivePlayers.add( player.uniqueId )
player.gameMode = GameMode.SURVIVAL
player.health = player.getAttribute( Attribute.GENERIC_MAX_HEALTH )?.value ?: 20.0
player.foodLevel = 20
player.inventory.clear()
player.activePotionEffects.forEach { player.removePotionEffect( it.type ) }
player.addPotionEffects(listOf( speedEffect, hasteEffect ))
teleportRandomly( player, world, startBorder / 2 )
// TODO: Kit Items geben
// plugin.kitManager.giveKit( player )
player.inventory.addItem(ItemStack( Material.COMPASS ))
player.sendMsg( "game.started" )
}
Bukkit.getOnlinePlayers().forEach { player ->
player.sendMsg( "game.invincibility-start" )
}
}
private fun startFighting()
{
setGameState( GameState.INGAME )
timer = 0 // Reset Timer für "Ingame"
val world = Bukkit.getWorld( "world" ) ?: return
world.worldBorder.setSize( endBorder, borderShrinkTime )
plugin.antiRunningManager.resetTimers()
Bukkit.getOnlinePlayers().forEach { p ->
p.sendMsg( "game.fighting-started" )
p.playSound( p.location, Sound.ENTITY_ENDER_DRAGON_GROWL, 1f, 1f )
p.showTitle(Title.title(
p.trans( "title.fight-main" ),
p.trans( "title.fight-sub" )
))
}
}
fun onPlayerEliminated(
player: Player,
killer: Player?
) {
if (!alivePlayers.contains( player.uniqueId )) return
alivePlayers.remove( player.uniqueId )
player.gameMode = GameMode.SPECTATOR
player.inventory.contents.filterNotNull().forEach {
player.world.dropItemNaturally( player.location, it )
}
val msgKey = if ( killer != null ) "game.death-killed" else "game.death-pve"
val killerName = killer?.name ?: "Environment"
Bukkit.getOnlinePlayers().forEach { p ->
p.sendMsg( msgKey, "player" to player.name, "killer" to killerName, "left" to alivePlayers.size.toString() )
}
checkWin()
}
private fun checkWin()
{
if ( currentState != GameState.INGAME && currentState != GameState.INVINCIBILITY ) return
if ( alivePlayers.size <= 1 )
{
val winnerUUID = alivePlayers.firstOrNull()
val winnerName = if ( winnerUUID != null ) Bukkit.getPlayer( winnerUUID )?.name ?: "N/A" else "N/A"
endGame( winnerName )
}
}
private fun endGame(
winnerName: String
) {
setGameState( GameState.ENDING )
timer = 15
Bukkit.getOnlinePlayers().forEach { p ->
p.showTitle(Title.title(
p.trans( "title.win-main", "winner" to winnerName ),
p.trans( "title.win-sub" )
))
p.sendMsg( "game.win-chat", "winner" to winnerName )
}
}
// --- Helfer Methoden ---
private fun teleportRandomly(
player: Player,
world: World,
maxRadius: Double
) {
var loc: Location
var safe = false
var attempts = 0
do {
val x = Random.nextDouble( -maxRadius, maxRadius )
val z = Random.nextDouble( -maxRadius, maxRadius )
val y = world.getHighestBlockYAt( x.toInt(), z.toInt() ) + 1.0
loc = Location( world, x, y, z )
val block = loc.block.type
val below = loc.subtract( 0.0, 1.0, 0.0 ).block.type
if ( below.isSolid && below != Material.LAVA && below != Material.CACTUS && block == Material.AIR )
safe = true
attempts++
} while ( !safe && attempts < 20 )
player.teleport(loc.add( 0.0, 1.0, 0.0 ))
}
private fun updateCompass()
{
val players = Bukkit.getOnlinePlayers().filter { alivePlayers.contains( it.uniqueId ) }
for ( p in players )
{
var nearest: Player? = null
var minDistance = Double.MAX_VALUE
for ( target in players )
{
if ( p == target ) continue
val dist = p.location.distanceSquared( target.location )
if ( dist < minDistance )
{
minDistance = dist
nearest = target
}
}
if ( nearest != null )
p.compassTarget = nearest.location
else
p.compassTarget = p.world.spawnLocation
}
}
// --- Event Listener Integration ---
@EventHandler
fun onDeath(
event: PlayerDeathEvent
) {
if ( currentState == GameState.INGAME || currentState == GameState.INVINCIBILITY )
{
event.deathMessage( null )
event.drops.clear()
Bukkit.getScheduler().runTask( plugin ) { ->
event.entity.spigot().respawn()
}
onPlayerEliminated( event.entity, event.entity.killer )
}
}
@EventHandler
fun onQuit(
event: PlayerQuitEvent
) {
if (alivePlayers.contains( event.player.uniqueId ))
{
if ( currentState == GameState.INGAME || currentState == GameState.INVINCIBILITY )
{
val lastDamageCause = event.player.lastDamageCause
if ( lastDamageCause != null && lastDamageCause is EntityDamageByEntityEvent )
{
val attacker = lastDamageCause.damager
if ( attacker is Player )
{
onPlayerEliminated( event.player, attacker )
return
}
}
onPlayerEliminated( event.player, null ) // PVE Tod
}
else alivePlayers.remove( event.player.uniqueId )
}
}
@EventHandler
fun onDamage(
event: EntityDamageEvent
) {
if ( currentState == GameState.INVINCIBILITY && event.entity is Player )
event.isCancelled = true
if ( currentState == GameState.LOBBY || currentState == GameState.STARTING || currentState == GameState.ENDING )
event.isCancelled = true // Nie Schaden in Lobby/Ende
}
}