Add kit system, KitManager and Goblin kit

Introduce a full kit framework and supporting utilities.

- Add core kit API: Kit, Playstyle, ActiveAbility, PassiveAbility, AbilityResult, charge state and PlayerChargeData.
- Implement KitManager for registration, lobby selections, apply/remove/clear lifecycle and charge tracking.
- Add KitEventDispatcher listener to centralize kit event handling (interact, hits, move) and integrate passive/active hooks.
- Provide example kit implementations: GoblinKit (functional abilities) and TemplateKit (reference).
- Add utilities: ItemBuilder and WorldEditUtils (WorldEdit-based sphere creation).
- Integrate into plugin: SpeedHG now initialises KitManager, registers kits and KitEventDispatcher, applies kits at game start and clears on end.
- LanguageManager: add default message/list/component helpers.
- Build changes: bump Kotlin plugin to 2.2.0 and add WorldEdit compileOnly deps; also expose legacySerializer in Extensions.

These changes implement the kit feature set (items, abilities, charge/recharge flow) and wire it into the game lifecycle.
This commit is contained in:
TDSTOS
2026-03-25 02:27:53 +01:00
parent e411879b20
commit 9d6bd6a6b8
18 changed files with 1330 additions and 6 deletions

View File

@@ -0,0 +1,144 @@
package club.mcscrims.speedhg.kit
import club.mcscrims.speedhg.SpeedHG
import club.mcscrims.speedhg.kit.charge.PlayerChargeData
import org.bukkit.entity.Player
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
/**
* Manages kit registration, player selections, and the charge state for all online players.
*
* ## Typical usage flow
* ```
* // During plugin startup
* kitManager.registerKit(WarriorKit())
* kitManager.registerKit(ArcherKit())
*
* // During lobby (e.g. GUI click)
* kitManager.selectKit(player, kitManager.getKit("warrior")!!)
* kitManager.selectPlaystyle(player, Playstyle.AGGRESSIVE)
*
* // When the game round starts (replace the TODO in GameManager.startGame)
* kitManager.applyKit(player) // gives items, runs onAssign / passive.onActivate
*
* // When the round ends
* kitManager.clearAll() // runs onRemove / passive.onDeactivate for every player
* ```
*/
class KitManager(
private val plugin: SpeedHG
) {
// All kits available this session, keyed by Kit.id
private val registeredKits = ConcurrentHashMap<String, Kit>()
// Lobby selections — set before the round starts
private val selectedKits = ConcurrentHashMap<UUID, Kit>()
private val selectedPlaystyles = ConcurrentHashMap<UUID, Playstyle>()
// Live charge state — populated by applyKit, cleared by removeKit/clearAll
private val chargeData = ConcurrentHashMap<UUID, PlayerChargeData>()
// -------------------------------------------------------------------------
// Kit registration
// -------------------------------------------------------------------------
fun registerKit(
kit: Kit
) {
registeredKits[kit.id] = kit
plugin.logger.info("[KitManager] Registered kit: ${kit.id}")
}
fun getRegisteredKits(): Collection<Kit> = registeredKits.values
fun getKit(id: String): Kit? = registeredKits[id]
// -------------------------------------------------------------------------
// Player selections (lobby phase)
// -------------------------------------------------------------------------
fun selectKit(
player: Player,
kit: Kit
) {
selectedKits[player.uniqueId] = kit
}
/**
* Set the playstyle for a player.
* Defaults to [Playstyle.DEFENSIVE] if never called.
*/
fun selectPlaystyle(
player: Player,
playstyle: Playstyle
) {
selectedPlaystyles[player.uniqueId] = playstyle
}
fun getSelectedKit( player: Player ): Kit? = selectedKits[player.uniqueId]
fun getSelectedPlaystyle( player: Player ): Playstyle =
selectedPlaystyles[player.uniqueId] ?: Playstyle.DEFENSIVE
// -------------------------------------------------------------------------
// Game lifecycle
// -------------------------------------------------------------------------
/**
* Apply the player's selected kit at game start.
*
* Call this from [GameManager.startGame] for every online player, **after** teleportation.
* If the player has not selected a kit, this is a no-op.
*/
fun applyKit(
player: Player
) {
val kit = selectedKits[player.uniqueId] ?: return
val playstyle = getSelectedPlaystyle( player )
val active = kit.getActiveAbility( playstyle )
chargeData[player.uniqueId] = PlayerChargeData( active.hitsRequired )
kit.onAssign( player, playstyle )
kit.getPassiveAbility( playstyle ).onActivate( player )
kit.giveItems( player, playstyle )
}
/**
* Remove and clean up a player's kit (round over, player eliminated, etc.).
* Safe to call even if the player has no kit assigned.
*/
fun removeKit(
player: Player
) {
val kit = selectedKits[player.uniqueId] ?: return
val playstyle = getSelectedPlaystyle( player )
kit.getPassiveAbility( playstyle ).onDeactivate( player )
kit.onRemove( player )
chargeData.remove( player.uniqueId )
}
/**
* Remove all kits and clear every selection.
* Call this at the end of each round, **before** resetting other game state.
*/
fun clearAll()
{
selectedKits.keys.toList().forEach { uuid ->
plugin.server.getPlayer( uuid )?.let { removeKit( it ) }
}
selectedKits.clear()
selectedPlaystyles.clear()
chargeData.clear()
}
// -------------------------------------------------------------------------
// Charge access (used by KitEventDispatcher)
// -------------------------------------------------------------------------
fun getChargeData( player: Player ): PlayerChargeData? = chargeData[player.uniqueId]
}