From b48ccef3acea8659c6c44e92d685ac3ee87594ec Mon Sep 17 00:00:00 2001 From: TDSTOS Date: Tue, 14 Apr 2026 04:28:46 +0200 Subject: [PATCH] Update OraclePerk; improve game management OraclePerk now implements the SpieloKit; instead of removing mobs, the GameManager now sets the difficulty to peaceful in lobby / starting & ending state --- .../club/mcscrims/speedhg/game/GameManager.kt | 7 + .../mcscrims/speedhg/kit/impl/SpieloKit.kt | 2 +- .../speedhg/listener/GameStateListener.kt | 14 -- .../mcscrims/speedhg/perk/impl/OraclePerk.kt | 126 ++++++++++++------ 4 files changed, 94 insertions(+), 55 deletions(-) diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt b/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt index 029620a..e5d4f11 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt @@ -70,6 +70,7 @@ class GameManager( } world?.setGameRule( GameRule.ANNOUNCE_ADVANCEMENTS, false ) + world?.difficulty = Difficulty.PEACEFUL }, 10L ) } @@ -183,6 +184,7 @@ class GameManager( timer = invincibilityTime val world = Bukkit.getWorld( "world" ) ?: return + world.time = 0 world.setStorm( false ) @@ -193,6 +195,8 @@ class GameManager( damageAmount = 1.0 } + world.difficulty = Difficulty.NORMAL + val speedEffect = PotionEffect( PotionEffectType.SPEED, invincibilityTime * 20, @@ -333,8 +337,11 @@ class GameManager( setGameState( GameState.ENDING ) timer = 15 + feastManager.reset() pitManager.reset() + Bukkit.getWorld( "world" )?.difficulty = Difficulty.PEACEFUL + val winnerUUID = alivePlayers.firstOrNull() val winnerTeam = if ( plugin.presetTeamManager.isEnabled && winnerUUID != null ) diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/SpieloKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/SpieloKit.kt index ec7a527..5d5964c 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/SpieloKit.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/SpieloKit.kt @@ -103,7 +103,7 @@ class SpieloKit : Kit() internal val gamblingPlayers: MutableSet = ConcurrentHashMap.newKeySet() /** Cooldown timestamps for the aggressive instant-gamble. */ - private val activeCooldowns: MutableMap = ConcurrentHashMap() + internal val activeCooldowns: MutableMap = ConcurrentHashMap() // ── Cached ability instances (avoid allocating per event call) ──────────── private val aggressiveActive = AggressiveActive() diff --git a/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt b/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt index 2b39558..d37222b 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt @@ -7,7 +7,6 @@ import org.bukkit.Bukkit import org.bukkit.Material import org.bukkit.Sound import org.bukkit.attribute.Attribute -import org.bukkit.entity.LivingEntity import org.bukkit.entity.Player import org.bukkit.event.Event import org.bukkit.event.EventHandler @@ -19,9 +18,7 @@ import org.bukkit.event.block.BlockPlaceEvent import org.bukkit.event.block.LeavesDecayEvent import org.bukkit.event.enchantment.EnchantItemEvent import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.entity.EntitySpawnEvent import org.bukkit.event.entity.FoodLevelChangeEvent -import org.bukkit.event.entity.ItemDespawnEvent import org.bukkit.event.inventory.* import org.bukkit.event.player.* import org.bukkit.inventory.EnchantingInventory @@ -427,17 +424,6 @@ class GameStateListener : Listener { } } - @EventHandler - fun onEntitySpawn( - event: EntitySpawnEvent - ) { - if ( feastStarted ) return - // Item-Drops, XP-Orbs, Projekile tc. NICHT blockieren - if ( event.entity !is LivingEntity ) return - if ( event.entity is Player ) return - event.isCancelled = true - } - @EventHandler fun onJoin( event: PlayerJoinEvent ) = setAttackSpeed( event.player ) diff --git a/src/main/kotlin/club/mcscrims/speedhg/perk/impl/OraclePerk.kt b/src/main/kotlin/club/mcscrims/speedhg/perk/impl/OraclePerk.kt index 42f819a..9a32353 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/perk/impl/OraclePerk.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/perk/impl/OraclePerk.kt @@ -2,6 +2,7 @@ package club.mcscrims.speedhg.perk.impl import club.mcscrims.speedhg.SpeedHG import club.mcscrims.speedhg.game.GameState +import club.mcscrims.speedhg.kit.impl.SpieloKit import club.mcscrims.speedhg.perk.Perk import net.kyori.adventure.text.Component import net.kyori.adventure.text.minimessage.MiniMessage @@ -16,12 +17,21 @@ import java.util.concurrent.ConcurrentHashMap /** * ## Orakel * - * Zeigt das Kit und die Entfernung des nächsten lebenden Gegners per Actionbar an, - * sobald der Träger **schleicht** oder **einen Kompass hält**. + * Zeigt das Kit, den Namen und die **Entfernung in Blöcken** zum nächsten lebenden Gegner + * per Actionbar an, sobald der Träger **schleicht** oder **einen Kompass hält**. * - * **Spielo-Synergie (Platzhalter):** Wenn der Träger das "spielo"-Kit besitzt, - * wird ein Hinweis auf das nächste Gamble-Ergebnis angezeigt. Logik muss beim - * Implementieren des Spielo-Kits befüllt werden. + * ## Spielo-Synergie + * + * Wenn der Träger das `spielo`-Kit besitzt, wird ein zusätzlicher Gamble-Status-Hinweis + * an die Actionbar angehängt: + * + * | Zustand | Anzeige | + * |--------------------------------|--------------------------------| + * | Animation läuft gerade | `⚙ Gambling…` (gelb) | + * | Bereit (kein Cooldown aktiv) | `🎲 Ready` (grün) | + * | Cooldown läuft noch | `⏳ Cooldown` (dunkelgrau) | + * + * Der Cooldown-State wird über `activeCooldowns` im [SpieloKit] ausgelesen. */ class OraclePerk : Perk() { @@ -30,76 +40,112 @@ class OraclePerk : Perk() { override val id = "oracle" override val displayName: Component - get() = plugin.languageManager.getDefaultComponent("perks.oracle.name", mapOf()) + get() = plugin.languageManager.getDefaultComponent( "perks.oracle.name", mapOf() ) override val lore: List - get() = plugin.languageManager.getDefaultRawMessageList("perks.oracle.lore") + get() = plugin.languageManager.getDefaultRawMessageList( "perks.oracle.lore" ) override val icon = Material.SPYGLASS /** Laufende Tracker-Tasks pro Spieler. */ private val trackerTasks = ConcurrentHashMap() - override fun onActivate(player: Player) { + override fun onActivate( + player: Player + ) { val task = object : BukkitRunnable() { override fun run() { - if (!player.isOnline) { cancel(); return } + if ( !player.isOnline ) { cancel(); return } // Nur während aktiver Spielphasen anzeigen val state = plugin.gameManager.currentState - if (state != GameState.INGAME && state != GameState.INVINCIBILITY) return + if ( state != GameState.INGAME && state != GameState.INVINCIBILITY ) return // Träger muss selbst noch leben - if (!plugin.gameManager.alivePlayers.contains(player.uniqueId)) return + if ( !plugin.gameManager.alivePlayers.contains( player.uniqueId ) ) return // Nur anzeigen wenn Bedingung erfüllt val isHoldingCompass = player.inventory.itemInMainHand.type == Material.COMPASS - if (!isHoldingCompass && !player.isSneaking) return + if ( !isHoldingCompass && !player.isSneaking ) return - val nearest = findNearestEnemy(player) ?: return - player.sendActionBar(buildTrackerComponent(player, nearest)) + val nearest = findNearestEnemy( player ) ?: return + player.sendActionBar( buildTrackerComponent( player, nearest ) ) } - }.runTaskTimer(plugin, 0L, 10L) // Refresh alle 0.5 Sekunden + }.runTaskTimer( plugin, 0L, 10L ) - trackerTasks[player.uniqueId] = task + trackerTasks[ player.uniqueId ] = task } - override fun onDeactivate(player: Player) { - trackerTasks.remove(player.uniqueId)?.cancel() + override fun onDeactivate( + player: Player + ) { + trackerTasks.remove( player.uniqueId )?.cancel() } // ── Intern ──────────────────────────────────────────────────────────────── - private fun findNearestEnemy(player: Player): Player? = + private fun findNearestEnemy( + player: Player + ): Player? = plugin.gameManager.alivePlayers .asSequence() .filter { it != player.uniqueId } - .mapNotNull { plugin.server.getPlayer(it) } - .filter { !plugin.perkManager.isGhost(it) } + .mapNotNull { plugin.server.getPlayer( it ) } + .filter { !plugin.perkManager.isGhost( it ) } .filter { !plugin.presetTeamManager.areInSameTeam( player, it ) } - .minByOrNull { it.location.distanceSquared(player.location) } + .minByOrNull { it.location.distanceSquared( player.location ) } - private fun buildTrackerComponent(player: Player, nearest: Player): Component { - val distance = player.location.distance(nearest.location).toInt() - val kitDisplay = plugin.kitManager.getSelectedKit(nearest)?.displayName - ?: mm.deserialize("???") + private fun buildTrackerComponent( + player: Player, + nearest: Player + ): Component + { + val distance = player.location.distance( nearest.location ).toInt() + val kitDisplay = plugin.kitManager.getSelectedKit( nearest )?.displayName + ?: mm.deserialize( "???" ) val base = mm.deserialize( - " · " + - "Kit: · m", - Placeholder.unparsed("name", nearest.name), - Placeholder.component("kit", kitDisplay), - Placeholder.unparsed("distance", distance.toString()) + " · " + + "Kit: " + + " · blocks", + Placeholder.unparsed( "name", nearest.name ), + Placeholder.component( "kit", kitDisplay ), + Placeholder.unparsed( "distance", distance.toString() ) ) - // ── Spielo-Synergie (Platzhalter) ───────────────────────────────── - val playerKit = plugin.kitManager.getSelectedKit(player) - if (playerKit?.id != "spielo") return base + // ── Spielo-Synergie ──────────────────────────────────────────────── + val playerKit = plugin.kitManager.getSelectedKit( player ) + if ( playerKit?.id != "spielo" ) return base - // TODO: Mit echter Spielo-Logik ersetzen sobald das Kit existiert. - // Temporär: alterniert sekündlich als visueller Platzhalter. - val isGoodGamble = (System.currentTimeMillis() / 3_000L) % 2 == 0L - val hint = if (isGoodGamble) mm.deserialize(" ⬆ Good Gamble") - else mm.deserialize(" ⬇ Bad Gamble") + val spieloKit = playerKit as? SpieloKit ?: return base + val hint = buildSpieloHint( player, spieloKit ) - return base.append(hint) + return base.append( hint ) + } + + /** + * Ermittelt den aktuellen Gamble-Status des Spielers und gibt den passenden + * Actionbar-Anhang zurück. + * + * | Zustand | Rückgabe | + * |-----------------------|----------------------------| + * | Animation läuft | ` ⚙ Gambling…` (gelb) | + * | Cooldown aktiv | ` ⏳ Cooldown` (dunkelgrau) | + * | Bereit | ` 🎲 Ready` (grün) | + */ + private fun buildSpieloHint( + player: Player, + spieloKit: SpieloKit + ): Component + { + if ( spieloKit.gamblingPlayers.contains( player.uniqueId ) ) + return mm.deserialize( " ⚙ Gambling…" ) + + val lastUse = spieloKit.activeCooldowns[ player.uniqueId ] ?: 0L + val cooldownMs = SpieloKit.DEFAULT_ACTIVE_COOLDOWN_MS + val onCooldown = System.currentTimeMillis() - lastUse < cooldownMs + + return if ( onCooldown ) + mm.deserialize( " ⏳ Cooldown" ) + else + mm.deserialize( " 🎲 Ready" ) } } \ No newline at end of file