Add perk system with GUI and integrations

Introduce a complete passive perk system: Perk base class, PerkManager (registration, selection, lifecycle, persistence), and PlayerPerksRepository (DB schema + upsert/find). Add four example perks (Oracle, Vampire, Featherweight, Bloodlust) and a single PerkEventDispatcher to route combat/environment/kill events to active perks. Provide PerkSelectorMenu GUI and /perks command, integrate perk initialization, registration, application and cleanup into SpeedHG and GameManager, and hook load/evict into StatsListener. Also add language entries and register the command in plugin.yml. This change enables players to select up to two passive perks, persists selections, and dispatches relevant events to perk implementations.
This commit is contained in:
TDSTOS
2026-04-04 03:16:43 +02:00
parent 88b0ba8b97
commit 8c2ab684bb
15 changed files with 977 additions and 1 deletions

View File

@@ -0,0 +1,60 @@
package club.mcscrims.speedhg.perk
import net.kyori.adventure.text.Component
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.entity.EntityDamageEvent
/**
* Basisklasse für alle passiven Perks in SpeedHG.
*
* Perks sind passive Boni, die Spieler neben ihrem Kit ausrüsten.
* Ein Spieler darf maximal [PerkManager.MAX_PERKS] Perks gleichzeitig aktiv haben.
*
* ## Neuen Perk erstellen
* 1. Diese Klasse erweitern.
* 2. Alle abstrakten Member implementieren.
* 3. Nur die Hooks überschreiben, die tatsächlich gebraucht werden — alle Defaults sind No-Ops.
* 4. Via `plugin.perkManager.registerPerk(DeinPerk())` in [SpeedHG.onEnable] registrieren.
*/
abstract class Perk {
/** Eindeutiger snake_case Bezeichner. Muss dem Sprachschlüssel-Prefix `perks.<id>.*` entsprechen. */
abstract val id: String
/** Angezeigter Name im GUI, aus der Sprachdatei geladen. */
abstract val displayName: Component
/** Kurze Lore-Zeilen als MiniMessage-Strings aus der Sprachdatei. */
abstract val lore: List<String>
/** Icon-Material im Perk-Selector-GUI. */
abstract val icon: Material
// ── Lifecycle ─────────────────────────────────────────────────────────────
/** Einmalig aufgerufen wenn das Spiel startet und dieser Perk für [player] aktiv ist. */
open fun onActivate(player: Player) {}
/** Aufgerufen wenn die Runde endet oder der Perk entfernt wird. Tasks hier abbrechen. */
open fun onDeactivate(player: Player) {}
// ── Combat Hooks (dispatched by PerkEventDispatcher) ──────────────────────
/** [attacker] (dieser Spieler) hat gerade [victim] per Nahkampf getroffen. */
open fun onHitEnemy(attacker: Player, victim: Player, event: EntityDamageByEntityEvent) {}
/** Dieser Spieler hat gerade einen Nahkampfhit von [attacker] erhalten. */
open fun onHitByEnemy(victim: Player, attacker: Player, event: EntityDamageByEntityEvent) {}
/** Dieser Spieler hat einen anderen Spieler getötet. */
open fun onKillEnemy(killer: Player, victim: Player) {}
/**
* Aufgerufen bei Umgebungsschaden (Fall, Feuer, etc.) — KEIN Nahkampf.
* Wird vom Dispatcher mit HIGH-Priority verarbeitet, damit Featherweight canceln kann.
*/
open fun onEnvironmentalDamage(player: Player, event: EntityDamageEvent) {}
}