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:
232
src/main/kotlin/club/mcscrims/speedhg/command/TeamCommand.kt
Normal file
232
src/main/kotlin/club/mcscrims/speedhg/command/TeamCommand.kt
Normal file
@@ -0,0 +1,232 @@
|
||||
package club.mcscrims.speedhg.command
|
||||
|
||||
import club.mcscrims.speedhg.SpeedHG
|
||||
import club.mcscrims.speedhg.game.GameState
|
||||
import club.mcscrims.speedhg.team.TeamManager
|
||||
import club.mcscrims.speedhg.util.sendMsg
|
||||
import org.bukkit.command.Command
|
||||
import org.bukkit.command.CommandExecutor
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.command.TabCompleter
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
class TeamCommand : CommandExecutor, TabCompleter {
|
||||
|
||||
private val plugin get() = SpeedHG.instance
|
||||
|
||||
// ── Guard: Teams nur in Lobby-Phasen ändern ──────────────────────────────
|
||||
|
||||
private fun isLobbyPhase(): Boolean = when (plugin.gameManager.currentState) {
|
||||
GameState.LOBBY, GameState.STARTING -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// onCommand dispatch
|
||||
// =========================================================================
|
||||
|
||||
override fun onCommand(
|
||||
sender: CommandSender,
|
||||
command: Command,
|
||||
label: String,
|
||||
args: Array<out String>
|
||||
): Boolean {
|
||||
val player = sender as? Player ?: run {
|
||||
sender.sendMessage("§cNur Spieler können diesen Befehl nutzen.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (!plugin.teamManager.isEnabled) {
|
||||
player.sendMsg("team.disabled")
|
||||
return true
|
||||
}
|
||||
|
||||
when (args.firstOrNull()?.lowercase()) {
|
||||
"invite" -> handleInvite(player, args)
|
||||
"accept" -> handleAccept(player, args)
|
||||
"deny" -> handleDeny(player, args)
|
||||
"leave" -> handleLeave(player)
|
||||
"kick" -> handleKick(player, args)
|
||||
"info" -> handleInfo(player)
|
||||
else -> player.sendMsg("team.usage")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Subcommand Handlers
|
||||
// =========================================================================
|
||||
|
||||
private fun handleInvite(player: Player, args: Array<out String>) {
|
||||
if (!isLobbyPhase()) { player.sendMsg("team.game_running"); return }
|
||||
|
||||
val targetName = args.getOrNull(1) ?: run { player.sendMsg("team.invite.usage"); return }
|
||||
val target = plugin.server.getPlayerExact(targetName) ?: run {
|
||||
player.sendMsg("team.player_not_found", "name" to targetName); return
|
||||
}
|
||||
|
||||
when (plugin.teamManager.invite(player, target)) {
|
||||
is TeamManager.InviteResult.Success -> {
|
||||
player.sendMsg("team.invite.sent", "name" to target.name)
|
||||
target.sendMsg("team.invite.received",
|
||||
"name" to player.name,
|
||||
"time" to (TeamManager.INVITE_TTL_MS / 1000).toString()
|
||||
)
|
||||
}
|
||||
is TeamManager.InviteResult.TeamsDisabled -> player.sendMsg("team.disabled")
|
||||
is TeamManager.InviteResult.InvitedSelf -> player.sendMsg("team.invite.self")
|
||||
is TeamManager.InviteResult.AlreadyInSameTeam -> player.sendMsg("team.invite.already_teammate")
|
||||
is TeamManager.InviteResult.TargetAlreadyInTeam -> player.sendMsg("team.invite.target_has_team", "name" to target.name)
|
||||
is TeamManager.InviteResult.SenderAlreadyInFullTeam -> player.sendMsg("team.invite.team_full")
|
||||
is TeamManager.InviteResult.InviteAlreadyPending -> player.sendMsg("team.invite.already_pending", "name" to target.name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAccept(player: Player, args: Array<out String>) {
|
||||
if (!isLobbyPhase()) { player.sendMsg("team.game_running"); return }
|
||||
|
||||
val inviterName = args.getOrNull(1) ?: run { player.sendMsg("team.accept.usage"); return }
|
||||
val inviter = plugin.server.getPlayerExact(inviterName) ?: run {
|
||||
player.sendMsg("team.player_not_found", "name" to inviterName); return
|
||||
}
|
||||
|
||||
when (val result = plugin.teamManager.accept(player, inviter.uniqueId)) {
|
||||
is TeamManager.AcceptResult.Success -> {
|
||||
val memberNames = result.team.members
|
||||
.mapNotNull { plugin.server.getPlayer(it)?.name }
|
||||
.joinToString(", ")
|
||||
|
||||
// Alle Teammitglieder benachrichtigen
|
||||
result.team.members.forEach { uuid ->
|
||||
plugin.server.getPlayer(uuid)?.sendMsg(
|
||||
"team.accept.joined",
|
||||
"name" to player.name,
|
||||
"members" to memberNames
|
||||
)
|
||||
}
|
||||
}
|
||||
is TeamManager.AcceptResult.TeamsDisabled -> player.sendMsg("team.disabled")
|
||||
is TeamManager.AcceptResult.NoInvite -> player.sendMsg("team.accept.no_invite", "name" to inviterName)
|
||||
is TeamManager.AcceptResult.WrongInviter -> player.sendMsg("team.accept.no_invite", "name" to inviterName)
|
||||
is TeamManager.AcceptResult.InviteExpired -> player.sendMsg("team.accept.expired", "name" to inviterName)
|
||||
is TeamManager.AcceptResult.AlreadyInTeam -> player.sendMsg("team.already_in_team")
|
||||
is TeamManager.AcceptResult.TeamFull -> player.sendMsg("team.invite.team_full")
|
||||
is TeamManager.AcceptResult.InviterNotFound -> player.sendMsg("team.player_not_found", "name" to inviterName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDeny(player: Player, args: Array<out String>) {
|
||||
if (!isLobbyPhase()) { player.sendMsg("team.game_running"); return }
|
||||
|
||||
val inviterName = args.getOrNull(1) ?: run { player.sendMsg("team.deny.usage"); return }
|
||||
val inviter = plugin.server.getPlayerExact(inviterName) ?: run {
|
||||
player.sendMsg("team.player_not_found", "name" to inviterName); return
|
||||
}
|
||||
|
||||
if (plugin.teamManager.deny(player, inviter.uniqueId)) {
|
||||
player.sendMsg("team.deny.success", "name" to inviterName)
|
||||
inviter.sendMsg("team.deny.received", "name" to player.name)
|
||||
} else {
|
||||
player.sendMsg("team.accept.no_invite", "name" to inviterName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLeave(player: Player) {
|
||||
if (!isLobbyPhase()) { player.sendMsg("team.game_running"); return }
|
||||
|
||||
val team = plugin.teamManager.getTeam(player)
|
||||
|
||||
when (plugin.teamManager.leave(player)) {
|
||||
is TeamManager.LeaveResult.NotInTeam -> {
|
||||
player.sendMsg("team.not_in_team")
|
||||
}
|
||||
is TeamManager.LeaveResult.TeamDisbanded -> {
|
||||
// Alle Ex-Mitglieder (außer dem Verlassenden) benachrichtigen
|
||||
team?.members?.forEach { uuid ->
|
||||
plugin.server.getPlayer(uuid)?.sendMsg("team.leave.disbanded")
|
||||
}
|
||||
player.sendMsg("team.leave.disbanded")
|
||||
}
|
||||
is TeamManager.LeaveResult.Success -> {
|
||||
player.sendMsg("team.leave.success")
|
||||
// Verbleibende Mitglieder informieren
|
||||
team?.members?.forEach { uuid ->
|
||||
plugin.server.getPlayer(uuid)?.sendMsg(
|
||||
"team.leave.member_left", "name" to player.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleKick(player: Player, args: Array<out String>) {
|
||||
if (!isLobbyPhase()) { player.sendMsg("team.game_running"); return }
|
||||
|
||||
val targetName = args.getOrNull(1) ?: run { player.sendMsg("team.kick.usage"); return }
|
||||
val target = plugin.server.getPlayerExact(targetName) ?: run {
|
||||
player.sendMsg("team.player_not_found", "name" to targetName); return
|
||||
}
|
||||
|
||||
when (plugin.teamManager.kick(player, target)) {
|
||||
is TeamManager.KickResult.Success -> {
|
||||
player.sendMsg("team.kick.success", "name" to target.name)
|
||||
target.sendMsg("team.kick.received", "name" to player.name)
|
||||
}
|
||||
is TeamManager.KickResult.NotInTeam -> player.sendMsg("team.not_in_team")
|
||||
is TeamManager.KickResult.NotLeader -> player.sendMsg("team.kick.not_leader")
|
||||
is TeamManager.KickResult.TargetNotInTeam -> player.sendMsg("team.kick.not_in_your_team", "name" to targetName)
|
||||
is TeamManager.KickResult.CannotKickSelf -> player.sendMsg("team.kick.self")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleInfo(player: Player) {
|
||||
val team = plugin.teamManager.getTeam(player) ?: run {
|
||||
player.sendMsg("team.not_in_team"); return
|
||||
}
|
||||
|
||||
player.sendMsg("team.info.header")
|
||||
team.members.forEachIndexed { i, uuid ->
|
||||
val name = plugin.server.getPlayer(uuid)?.name ?: uuid.toString().take(8)
|
||||
val isLeader = team.isLeader(uuid)
|
||||
player.sendMsg(
|
||||
"team.info.member",
|
||||
"index" to (i + 1).toString(),
|
||||
"name" to name,
|
||||
"leader" to if (isLeader) " ★" else ""
|
||||
)
|
||||
}
|
||||
player.sendMsg("team.info.footer", "size" to team.size.toString(), "max" to plugin.teamManager.maxTeamSize.toString())
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Tab Completion
|
||||
// =========================================================================
|
||||
|
||||
override fun onTabComplete(
|
||||
sender: CommandSender,
|
||||
command: Command,
|
||||
label: String,
|
||||
args: Array<out String>
|
||||
): List<String> {
|
||||
if (sender !is Player) return emptyList()
|
||||
|
||||
if (args.size == 1) {
|
||||
return listOf("invite", "accept", "deny", "leave", "kick", "info")
|
||||
.filter { it.startsWith(args[0], ignoreCase = true) }
|
||||
}
|
||||
|
||||
if (args.size == 2) {
|
||||
return when (args[0].lowercase()) {
|
||||
"invite", "accept", "deny", "kick" ->
|
||||
plugin.server.onlinePlayers
|
||||
.filter { it != sender }
|
||||
.map { it.name }
|
||||
.filter { it.startsWith(args[1], ignoreCase = true) }
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user