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
This commit is contained in:
TDSTOS
2026-04-14 04:28:46 +02:00
parent 86435a9dec
commit b48ccef3ac
4 changed files with 94 additions and 55 deletions

View File

@@ -70,6 +70,7 @@ class GameManager(
} }
world?.setGameRule( GameRule.ANNOUNCE_ADVANCEMENTS, false ) world?.setGameRule( GameRule.ANNOUNCE_ADVANCEMENTS, false )
world?.difficulty = Difficulty.PEACEFUL
}, 10L ) }, 10L )
} }
@@ -183,6 +184,7 @@ class GameManager(
timer = invincibilityTime timer = invincibilityTime
val world = Bukkit.getWorld( "world" ) ?: return val world = Bukkit.getWorld( "world" ) ?: return
world.time = 0 world.time = 0
world.setStorm( false ) world.setStorm( false )
@@ -193,6 +195,8 @@ class GameManager(
damageAmount = 1.0 damageAmount = 1.0
} }
world.difficulty = Difficulty.NORMAL
val speedEffect = PotionEffect( val speedEffect = PotionEffect(
PotionEffectType.SPEED, PotionEffectType.SPEED,
invincibilityTime * 20, invincibilityTime * 20,
@@ -333,8 +337,11 @@ class GameManager(
setGameState( GameState.ENDING ) setGameState( GameState.ENDING )
timer = 15 timer = 15
feastManager.reset()
pitManager.reset() pitManager.reset()
Bukkit.getWorld( "world" )?.difficulty = Difficulty.PEACEFUL
val winnerUUID = alivePlayers.firstOrNull() val winnerUUID = alivePlayers.firstOrNull()
val winnerTeam = if ( plugin.presetTeamManager.isEnabled && winnerUUID != null ) val winnerTeam = if ( plugin.presetTeamManager.isEnabled && winnerUUID != null )

View File

@@ -103,7 +103,7 @@ class SpieloKit : Kit()
internal val gamblingPlayers: MutableSet<UUID> = ConcurrentHashMap.newKeySet() internal val gamblingPlayers: MutableSet<UUID> = ConcurrentHashMap.newKeySet()
/** Cooldown timestamps for the aggressive instant-gamble. */ /** Cooldown timestamps for the aggressive instant-gamble. */
private val activeCooldowns: MutableMap<UUID, Long> = ConcurrentHashMap() internal val activeCooldowns: MutableMap<UUID, Long> = ConcurrentHashMap()
// ── Cached ability instances (avoid allocating per event call) ──────────── // ── Cached ability instances (avoid allocating per event call) ────────────
private val aggressiveActive = AggressiveActive() private val aggressiveActive = AggressiveActive()

View File

@@ -7,7 +7,6 @@ import org.bukkit.Bukkit
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.Sound import org.bukkit.Sound
import org.bukkit.attribute.Attribute import org.bukkit.attribute.Attribute
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.event.Event import org.bukkit.event.Event
import org.bukkit.event.EventHandler 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.block.LeavesDecayEvent
import org.bukkit.event.enchantment.EnchantItemEvent import org.bukkit.event.enchantment.EnchantItemEvent
import org.bukkit.event.entity.EntityDamageByEntityEvent import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.entity.EntitySpawnEvent
import org.bukkit.event.entity.FoodLevelChangeEvent import org.bukkit.event.entity.FoodLevelChangeEvent
import org.bukkit.event.entity.ItemDespawnEvent
import org.bukkit.event.inventory.* import org.bukkit.event.inventory.*
import org.bukkit.event.player.* import org.bukkit.event.player.*
import org.bukkit.inventory.EnchantingInventory 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 @EventHandler
fun onJoin( event: PlayerJoinEvent ) = setAttackSpeed( event.player ) fun onJoin( event: PlayerJoinEvent ) = setAttackSpeed( event.player )

View File

@@ -2,6 +2,7 @@ package club.mcscrims.speedhg.perk.impl
import club.mcscrims.speedhg.SpeedHG import club.mcscrims.speedhg.SpeedHG
import club.mcscrims.speedhg.game.GameState import club.mcscrims.speedhg.game.GameState
import club.mcscrims.speedhg.kit.impl.SpieloKit
import club.mcscrims.speedhg.perk.Perk import club.mcscrims.speedhg.perk.Perk
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.minimessage.MiniMessage import net.kyori.adventure.text.minimessage.MiniMessage
@@ -16,12 +17,21 @@ import java.util.concurrent.ConcurrentHashMap
/** /**
* ## Orakel * ## Orakel
* *
* Zeigt das Kit und die Entfernung des nächsten lebenden Gegners per Actionbar an, * Zeigt das Kit, den Namen und die **Entfernung in Blöcken** zum nächsten lebenden Gegner
* sobald der Träger **schleicht** oder **einen Kompass hält**. * per Actionbar an, sobald der Träger **schleicht** oder **einen Kompass hält**.
* *
* **Spielo-Synergie (Platzhalter):** Wenn der Träger das "spielo"-Kit besitzt, * ## Spielo-Synergie
* wird ein Hinweis auf das nächste Gamble-Ergebnis angezeigt. Logik muss beim *
* Implementieren des Spielo-Kits befüllt werden. * 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() { class OraclePerk : Perk() {
@@ -30,76 +40,112 @@ class OraclePerk : Perk() {
override val id = "oracle" override val id = "oracle"
override val displayName: Component override val displayName: Component
get() = plugin.languageManager.getDefaultComponent("perks.oracle.name", mapOf()) get() = plugin.languageManager.getDefaultComponent( "perks.oracle.name", mapOf() )
override val lore: List<String> override val lore: List<String>
get() = plugin.languageManager.getDefaultRawMessageList("perks.oracle.lore") get() = plugin.languageManager.getDefaultRawMessageList( "perks.oracle.lore" )
override val icon = Material.SPYGLASS override val icon = Material.SPYGLASS
/** Laufende Tracker-Tasks pro Spieler. */ /** Laufende Tracker-Tasks pro Spieler. */
private val trackerTasks = ConcurrentHashMap<UUID, BukkitTask>() private val trackerTasks = ConcurrentHashMap<UUID, BukkitTask>()
override fun onActivate(player: Player) { override fun onActivate(
player: Player
) {
val task = object : BukkitRunnable() { val task = object : BukkitRunnable() {
override fun run() { override fun run() {
if (!player.isOnline) { cancel(); return } if ( !player.isOnline ) { cancel(); return }
// Nur während aktiver Spielphasen anzeigen // Nur während aktiver Spielphasen anzeigen
val state = plugin.gameManager.currentState 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 // 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 // Nur anzeigen wenn Bedingung erfüllt
val isHoldingCompass = player.inventory.itemInMainHand.type == Material.COMPASS val isHoldingCompass = player.inventory.itemInMainHand.type == Material.COMPASS
if (!isHoldingCompass && !player.isSneaking) return if ( !isHoldingCompass && !player.isSneaking ) return
val nearest = findNearestEnemy(player) ?: return val nearest = findNearestEnemy( player ) ?: return
player.sendActionBar(buildTrackerComponent(player, nearest)) 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) { override fun onDeactivate(
trackerTasks.remove(player.uniqueId)?.cancel() player: Player
) {
trackerTasks.remove( player.uniqueId )?.cancel()
} }
// ── Intern ──────────────────────────────────────────────────────────────── // ── Intern ────────────────────────────────────────────────────────────────
private fun findNearestEnemy(player: Player): Player? = private fun findNearestEnemy(
player: Player
): Player? =
plugin.gameManager.alivePlayers plugin.gameManager.alivePlayers
.asSequence() .asSequence()
.filter { it != player.uniqueId } .filter { it != player.uniqueId }
.mapNotNull { plugin.server.getPlayer(it) } .mapNotNull { plugin.server.getPlayer( it ) }
.filter { !plugin.perkManager.isGhost(it) } .filter { !plugin.perkManager.isGhost( it ) }
.filter { !plugin.presetTeamManager.areInSameTeam( player, 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 { private fun buildTrackerComponent(
val distance = player.location.distance(nearest.location).toInt() player: Player,
val kitDisplay = plugin.kitManager.getSelectedKit(nearest)?.displayName nearest: Player
?: mm.deserialize("<gray>???") ): Component
{
val distance = player.location.distance( nearest.location ).toInt()
val kitDisplay = plugin.kitManager.getSelectedKit( nearest )?.displayName
?: mm.deserialize( "<gray>???" )
val base = mm.deserialize( val base = mm.deserialize(
"<gold>⚲ <yellow><name></yellow> <dark_gray>·</dark_gray> " + "<gold>⚲ <yellow><n></yellow> <dark_gray>·</dark_gray> " +
"<white>Kit: </white><kit><dark_gray> · </dark_gray><yellow><distance>m</yellow>", "<white>Kit: </white><kit>" +
Placeholder.unparsed("name", nearest.name), "<dark_gray> · </dark_gray><yellow><distance> blocks</yellow>",
Placeholder.component("kit", kitDisplay), Placeholder.unparsed( "name", nearest.name ),
Placeholder.unparsed("distance", distance.toString()) Placeholder.component( "kit", kitDisplay ),
Placeholder.unparsed( "distance", distance.toString() )
) )
// ── Spielo-Synergie (Platzhalter) ───────────────────────────────── // ── Spielo-Synergie ────────────────────────────────────────────────
val playerKit = plugin.kitManager.getSelectedKit(player) val playerKit = plugin.kitManager.getSelectedKit( player )
if (playerKit?.id != "spielo") return base if ( playerKit?.id != "spielo" ) return base
// TODO: Mit echter Spielo-Logik ersetzen sobald das Kit existiert. val spieloKit = playerKit as? SpieloKit ?: return base
// Temporär: alterniert sekündlich als visueller Platzhalter. val hint = buildSpieloHint( player, spieloKit )
val isGoodGamble = (System.currentTimeMillis() / 3_000L) % 2 == 0L
val hint = if (isGoodGamble) mm.deserialize(" <green>⬆ Good Gamble")
else mm.deserialize(" <red>⬇ Bad Gamble")
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( " <yellow>⚙ 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( " <dark_gray>⏳ Cooldown" )
else
mm.deserialize( " <green>🎲 Ready" )
} }
} }