Add team system and team-based win condition

Introduce a full in-memory team system and integrate it into gameplay and UI.

- Add Team, TeamManager, TeamListener and TeamCommand to manage teams, invites, accept/deny, leave, kick and info. TeamManager stores teams and pending invites (INVITE_TTL_MS) using concurrent maps and exposes helper/query methods (areInSameTeam, allAliveInSameTeam, getInviterFor, cleanExpiredInvites, reset).
- Wire TeamManager into SpeedHG: initialize, schedule periodic invite cleanup, register command and listener, and reset teams on plugin disable.
- Update GameManager to consider teams for win conditions, build team winner names, exclude teammates from random teleport/compass logic, and credit all winning team members for stats/ranking.
- Move ability charge feedback out of individual kits into KitEventDispatcher: remove per-kit onFullyCharged overrides and add centralized ActionBar/sound feedback (sendChargeUpdateActionBar/sendChargeReadyActionBar). ActiveAbility no longer defines onFullyCharged.
- Make OraclePerk ignore teammates when finding nearest enemy.
- Add config entries for teams and corresponding language strings; tweak some default values in CustomGameSettings (gladiator arena radius/height and minor formatting changes).

These changes enable 2-player teams (configurable), friendly-fire prevention, invite lifecycle handling, and team-aware endgame logic.
This commit is contained in:
TDSTOS
2026-04-09 02:33:14 +02:00
parent a6675c882b
commit 4d32fe677c
23 changed files with 788 additions and 110 deletions

View File

@@ -274,15 +274,27 @@ class GameManager(
checkWin()
}
private fun checkWin()
{
if ( currentState != GameState.INGAME && currentState != GameState.INVINCIBILITY ) return
private fun checkWin() {
if (currentState != GameState.INGAME && currentState != GameState.INVINCIBILITY) return
if ( alivePlayers.size <= 1 )
{
val teamManager = plugin.teamManager
val roundOver = when {
// Nur noch 0 oder 1 Spieler übrig → immer Ende
alivePlayers.size <= 1 -> true
// Teams aktiv: Alle Überlebenden im selben Team → Team gewinnt
teamManager.isEnabled && teamManager.allAliveInSameTeam(alivePlayers) -> true
else -> false
}
if (roundOver) {
val winnerUUID = alivePlayers.firstOrNull()
val winnerName = if ( winnerUUID != null ) Bukkit.getPlayer( winnerUUID )?.name ?: "N/A" else "N/A"
endGame( winnerName )
// Den sichtbaren Gewinner-Namen ermitteln:
// Bei Team-Sieg alle Mitglieder des Gewinner-Teams anzeigen.
val winnerName = buildWinnerName(winnerUUID)
endGame(winnerName)
}
}
@@ -296,18 +308,18 @@ class GameManager(
val winnerUUID = alivePlayers.firstOrNull()
val winnerTeam = if ( plugin.teamManager.isEnabled && winnerUUID != null )
plugin.teamManager.getTeam( winnerUUID ) else null
Bukkit.getOnlinePlayers().forEach { p ->
if ( p.uniqueId == winnerUUID )
val isWinner = winnerTeam?.contains( p.uniqueId ) ?: ( p.uniqueId == winnerUUID )
if ( isWinner )
{
plugin.statsManager.addWin( p.uniqueId )
plugin.rankingManager.onPlayerResult( p, isWinner = true )
}
}
plugin.kitManager.clearAll()
plugin.perkManager.removeAllActivePerks()
Bukkit.getOnlinePlayers().forEach { p ->
p.showTitle(Title.title(
p.trans( "title.win-main", "winner" to winnerName ),
p.trans( "title.win-sub" )
@@ -315,6 +327,9 @@ class GameManager(
p.sendMsg( "game.win-chat", "winner" to winnerName )
}
plugin.kitManager.clearAll()
plugin.perkManager.removeAllActivePerks()
plugin.discordWebhookManager.broadcastEmbed(
title = "🏆 Spiel beendet!",
description = "**$winnerName** hat das Spiel gewonnen! GG!",
@@ -328,6 +343,25 @@ class GameManager(
// --- Helfer Methoden ---
private fun buildWinnerName(anyAliveUUID: UUID?): String {
anyAliveUUID ?: return "N/A"
val teamManager = plugin.teamManager
if (!teamManager.isEnabled) {
return Bukkit.getPlayer(anyAliveUUID)?.name ?: "N/A"
}
val winnerTeam = teamManager.getTeam(anyAliveUUID)
return if (winnerTeam != null && winnerTeam.size > 1) {
// "PlayerA & PlayerB" als Team-Gewinner-String
winnerTeam.members
.mapNotNull { Bukkit.getPlayer(it)?.name }
.joinToString(" & ")
} else {
Bukkit.getPlayer(anyAliveUUID)?.name ?: "N/A"
}
}
private fun teleportRandomly(
player: Player,
world: World,
@@ -368,6 +402,10 @@ class GameManager(
{
if ( p == target ) continue
if ( plugin.teamManager.isEnabled &&
plugin.teamManager.areInSameTeam( p, target ))
continue
val dist = p.location.distanceSquared( target.location )
if ( dist < minDistance )
{