Add chat listener and fix ability height/fall logic

Add a ChatListener and register it to render chat with rank prefixes/colors. Enforce a maximum knockback altitude (MAX_KNOCKBACK_HEIGHT_Y) and block height-restricted ability use in Blitzcrank and BlackPanther; surface a localized height_restriction message. Add per-kit noFallDamage tracking for BlackPanther and Trident and cancel fall damage for flagged players in the KitEventDispatcher. Improve hitsRequired resolution: CustomGameSettings will treat a hardcoded 0 as an explicit cooldown-only value (never overridden) and the ActiveAbility getter now only accepts positive override values. Misc fixes: allow throwable items (potions/pearls) to bypass active-item handling, tighten ninja last-hit tracking, fix kit item drop detection in GameStateListener, tweak tablist prefix/suffix handling, and bump TheWorld ability cooldown from 20000ms to 25000ms. Also update language entries and minor formatting/cleanup.
This commit is contained in:
TDSTOS
2026-04-12 02:08:09 +02:00
parent 92260d90cb
commit 55a00ee15c
11 changed files with 179 additions and 27 deletions

View File

@@ -21,6 +21,7 @@ import club.mcscrims.speedhg.gui.listener.MenuListener
import club.mcscrims.speedhg.kit.KitManager import club.mcscrims.speedhg.kit.KitManager
import club.mcscrims.speedhg.kit.impl.* import club.mcscrims.speedhg.kit.impl.*
import club.mcscrims.speedhg.kit.listener.KitEventDispatcher import club.mcscrims.speedhg.kit.listener.KitEventDispatcher
import club.mcscrims.speedhg.listener.ChatListener
import club.mcscrims.speedhg.listener.ConnectListener import club.mcscrims.speedhg.listener.ConnectListener
import club.mcscrims.speedhg.listener.GameStateListener import club.mcscrims.speedhg.listener.GameStateListener
import club.mcscrims.speedhg.listener.SoupListener import club.mcscrims.speedhg.listener.SoupListener
@@ -288,6 +289,7 @@ class SpeedHG : JavaPlugin() {
pm.registerEvents(PerkEventDispatcher( this, perkManager ), this ) pm.registerEvents(PerkEventDispatcher( this, perkManager ), this )
pm.registerEvents( TeamListener(), this ) pm.registerEvents( TeamListener(), this )
pm.registerEvents( lobbyItemManager, this ) pm.registerEvents( lobbyItemManager, this )
pm.registerEvents(ChatListener( this, VolcanoServerRankProvider() ), this )
} }
private fun registerRecipes() private fun registerRecipes()

View File

@@ -37,9 +37,22 @@ data class CustomGameSettings(
/** /**
* Gibt den hitsRequired-Wert für ein Kit zurück. * Gibt den hitsRequired-Wert für ein Kit zurück.
* Priorität: kit-spezifisch > global > hardcoded Default * Priorität: kit-spezifisch > global > hardcoded Default
*
* Wenn hardcodedDefault == 0, ist das Kit explizit als cooldown-only markiert
* und der globale Wert wird niemals angewendet.
*/ */
fun hitsRequired(kitId: String, hardcodedDefault: Int): Int = fun hitsRequired(
kits[kitId]?.hitsRequired ?: globalHitsRequired.takeIf { it >= 0 } ?: hardcodedDefault kitId: String,
hardcodedDefault: Int
): Int
{
// A hardcoded 0 means the kit is explicitly cooldown-based — never override it.
if ( hardcodedDefault == 0 ) return 0
return kits[ kitId ]?.hitsRequired?.takeIf { it >= 0 }
?: globalHitsRequired.takeIf { it >= 0 }
?: hardcodedDefault
}
} }
// ----------------------------------------------------------------- // -----------------------------------------------------------------
@@ -88,7 +101,7 @@ data class CustomGameSettings(
@SerialName("pounce_timeout_ticks") val pounceTimeoutTicks: Long = 30L, @SerialName("pounce_timeout_ticks") val pounceTimeoutTicks: Long = 30L,
// TheWorld // TheWorld
@SerialName("tw_ability_cooldown_ms") val abilityCooldownMs: Long = 20_000L, @SerialName("tw_ability_cooldown_ms") val abilityCooldownMs: Long = 25_000L,
@SerialName("tw_shockwave_radius") val shockwaveRadius: Double = 6.0, @SerialName("tw_shockwave_radius") val shockwaveRadius: Double = 6.0,
@SerialName("tw_teleport_range") val teleportRange: Double = 10.0, @SerialName("tw_teleport_range") val teleportRange: Double = 10.0,
@SerialName("tw_max_teleport_charges") val maxTeleportCharges: Int = 3, @SerialName("tw_max_teleport_charges") val maxTeleportCharges: Int = 3,

View File

@@ -52,7 +52,7 @@ abstract class ActiveAbility(
private var _hitsRequired: Int = -1 private var _hitsRequired: Int = -1
val hitsRequired: Int val hitsRequired: Int
get() = _hitsRequired.takeIf { it >= 0 } ?: hardcodedHitsRequired get() = _hitsRequired.takeIf { it > 0 } ?: hardcodedHitsRequired
/** /**
* Einmalig beim applyKit() aufgerufen danach ist der Wert gecacht. * Einmalig beim applyKit() aufgerufen danach ist der Wert gecacht.

View File

@@ -7,6 +7,7 @@ import club.mcscrims.speedhg.kit.Playstyle
import club.mcscrims.speedhg.kit.ability.AbilityResult import club.mcscrims.speedhg.kit.ability.AbilityResult
import club.mcscrims.speedhg.kit.ability.ActiveAbility import club.mcscrims.speedhg.kit.ability.ActiveAbility
import club.mcscrims.speedhg.kit.ability.PassiveAbility import club.mcscrims.speedhg.kit.ability.PassiveAbility
import club.mcscrims.speedhg.kit.listener.KitEventDispatcher.Companion.MAX_KNOCKBACK_HEIGHT_Y
import club.mcscrims.speedhg.util.ItemBuilder import club.mcscrims.speedhg.util.ItemBuilder
import club.mcscrims.speedhg.util.WorldEditUtils import club.mcscrims.speedhg.util.WorldEditUtils
import club.mcscrims.speedhg.util.trans import club.mcscrims.speedhg.util.trans
@@ -61,6 +62,9 @@ class BlackPantherKit : Kit()
/** Players currently in Fist Mode: UUID → expiry timestamp (ms). */ /** Players currently in Fist Mode: UUID → expiry timestamp (ms). */
internal val fistModeExpiry: MutableMap<UUID, Long> = ConcurrentHashMap() internal val fistModeExpiry: MutableMap<UUID, Long> = ConcurrentHashMap()
/** Players currently in a pounce — fall damage is suppressed on landing. */
internal val noFallDamagePlayers: MutableSet<UUID> = ConcurrentHashMap.newKeySet()
companion object companion object
{ {
private fun override() = SpeedHG.instance.customGameManager.settings.kits.kits["blackpanther"] private fun override() = SpeedHG.instance.customGameManager.settings.kits.kits["blackpanther"]
@@ -106,9 +110,12 @@ class BlackPantherKit : Kit()
player.inventory.addItem(item) player.inventory.addItem(item)
} }
override fun onRemove(player: Player) { override fun onRemove(
fistModeExpiry.remove(player.uniqueId) player: Player
cachedItems.remove(player.uniqueId)?.forEach { player.inventory.remove(it) } ) {
fistModeExpiry.remove( player.uniqueId )
noFallDamagePlayers.remove( player.uniqueId )
cachedItems.remove( player.uniqueId )?.forEach { player.inventory.remove( it ) }
} }
// ========================================================================= // =========================================================================
@@ -128,7 +135,15 @@ class BlackPantherKit : Kit()
get() = plugin.languageManager.getDefaultRawMessage("kits.blackpanther.items.push.description") get() = plugin.languageManager.getDefaultRawMessage("kits.blackpanther.items.push.description")
override val triggerMaterial = Material.BLACK_DYE override val triggerMaterial = Material.BLACK_DYE
override fun execute(player: Player): AbilityResult { override fun execute(
player: Player
): AbilityResult
{
if ( player.location.y > MAX_KNOCKBACK_HEIGHT_Y )
return AbilityResult.ConditionNotMet(
plugin.languageManager.getRawMessage( player, "kits.height_restriction" )
)
val enemies = player.world val enemies = player.world
.getNearbyEntities(player.location, PUSH_RADIUS, PUSH_RADIUS, PUSH_RADIUS) .getNearbyEntities(player.location, PUSH_RADIUS, PUSH_RADIUS, PUSH_RADIUS)
.filterIsInstance<Player>() .filterIsInstance<Player>()
@@ -236,8 +251,10 @@ class BlackPantherKit : Kit()
override val description: String override val description: String
get() = plugin.languageManager.getDefaultRawMessage("kits.blackpanther.passive.defensive.description") get() = plugin.languageManager.getDefaultRawMessage("kits.blackpanther.passive.defensive.description")
override fun onMove(player: Player, event: PlayerMoveEvent) override fun onMove(
{ player: Player,
event: PlayerMoveEvent
) {
if ( event.to.y >= event.from.y ) return if ( event.to.y >= event.from.y ) return
if ( player.fallDistance < POUNCE_MIN_FALL ) return if ( player.fallDistance < POUNCE_MIN_FALL ) return
@@ -258,7 +275,6 @@ class BlackPantherKit : Kit()
impactLoc.world.playSound(impactLoc, Sound.ENTITY_GENERIC_EXPLODE, 1f, 0.7f) impactLoc.world.playSound(impactLoc, Sound.ENTITY_GENERIC_EXPLODE, 1f, 0.7f)
impactLoc.world.playSound(impactLoc, Sound.ENTITY_IRON_GOLEM_HURT, 1f, 0.5f) impactLoc.world.playSound(impactLoc, Sound.ENTITY_IRON_GOLEM_HURT, 1f, 0.5f)
// Async WorldEdit Krater (Vorsicht: Blöcke setzen muss synchron passieren, also normaler Scheduler)
Bukkit.getScheduler().runTaskLater(plugin, Runnable { Bukkit.getScheduler().runTaskLater(plugin, Runnable {
WorldEditUtils.createCylinder( WorldEditUtils.createCylinder(
impactLoc.world, impactLoc.clone().subtract(0.0, 1.0, 0.0), impactLoc.world, impactLoc.clone().subtract(0.0, 1.0, 0.0),
@@ -269,7 +285,8 @@ class BlackPantherKit : Kit()
player.sendActionBar(player.trans("kits.blackpanther.messages.wakanda_impact", player.sendActionBar(player.trans("kits.blackpanther.messages.wakanda_impact",
mapOf("count" to splashTargets.size.toString()))) mapOf("count" to splashTargets.size.toString())))
// Setze die Fall-Distanz auf 0 zurück, damit der Spieler selbst keinen Vanilla-Fallschaden bekommt // Suppress fall damage for this landing
noFallDamagePlayers.add( player.uniqueId )
player.fallDistance = 0f player.fallDistance = 0f
} }
} }

View File

@@ -6,6 +6,8 @@ import club.mcscrims.speedhg.kit.Playstyle
import club.mcscrims.speedhg.kit.ability.AbilityResult import club.mcscrims.speedhg.kit.ability.AbilityResult
import club.mcscrims.speedhg.kit.ability.ActiveAbility import club.mcscrims.speedhg.kit.ability.ActiveAbility
import club.mcscrims.speedhg.kit.ability.PassiveAbility import club.mcscrims.speedhg.kit.ability.PassiveAbility
import club.mcscrims.speedhg.kit.listener.KitEventDispatcher
import club.mcscrims.speedhg.kit.listener.KitEventDispatcher.Companion.MAX_KNOCKBACK_HEIGHT_Y
import club.mcscrims.speedhg.util.ItemBuilder import club.mcscrims.speedhg.util.ItemBuilder
import club.mcscrims.speedhg.util.trans import club.mcscrims.speedhg.util.trans
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
@@ -146,6 +148,12 @@ class BlitzcrankKit : Kit() {
private fun fireUlt( private fun fireUlt(
caster: Player caster: Player
) { ) {
if ( caster.location.y > MAX_KNOCKBACK_HEIGHT_Y )
{
caster.sendActionBar(caster.trans( "kits.height_restriction" ))
return
}
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val lastUlt = ultCooldowns[ caster.uniqueId ] ?: 0L val lastUlt = ultCooldowns[ caster.uniqueId ] ?: 0L
@@ -220,6 +228,11 @@ class BlitzcrankKit : Kit() {
override fun execute(player: Player): AbilityResult override fun execute(player: Player): AbilityResult
{ {
if ( player.location.y > MAX_KNOCKBACK_HEIGHT_Y )
return AbilityResult.ConditionNotMet(
plugin.languageManager.getRawMessage( player, "kits.height_restriction" )
)
val eyeLoc = player.eyeLocation val eyeLoc = player.eyeLocation
val dir = eyeLoc.direction.normalize() val dir = eyeLoc.direction.normalize()
@@ -289,7 +302,15 @@ class BlitzcrankKit : Kit() {
override val hardcodedHitsRequired = 15 override val hardcodedHitsRequired = 15
override val triggerMaterial = Material.PISTON override val triggerMaterial = Material.PISTON
override fun execute(player: Player): AbilityResult { override fun execute(
player: Player
): AbilityResult
{
if ( player.location.y > MAX_KNOCKBACK_HEIGHT_Y )
return AbilityResult.ConditionNotMet(
plugin.languageManager.getRawMessage( player, "kits.height_restriction" )
)
val targets = player.world val targets = player.world
.getNearbyEntities(player.location, STUN_RADIUS, STUN_RADIUS, STUN_RADIUS) .getNearbyEntities(player.location, STUN_RADIUS, STUN_RADIUS, STUN_RADIUS)
.filterIsInstance<Player>() .filterIsInstance<Player>()

View File

@@ -62,6 +62,9 @@ class TridentKit : Kit() {
private val diveMonitors: MutableMap<UUID, BukkitTask> = ConcurrentHashMap() private val diveMonitors: MutableMap<UUID, BukkitTask> = ConcurrentHashMap()
private val lastSequenceTime: MutableMap<UUID, Long> = ConcurrentHashMap() private val lastSequenceTime: MutableMap<UUID, Long> = ConcurrentHashMap()
/** Players who have recently launched a dive and should not receive fall damage. */
internal val noFallDamagePlayers: MutableSet<UUID> = ConcurrentHashMap.newKeySet()
companion object { companion object {
const val MAX_DIVE_CHARGES = 3 const val MAX_DIVE_CHARGES = 3
const val SEQUENCE_COOLDOWN_MS = 25_000L // Cooldown zwischen vollst. Sequenzen const val SEQUENCE_COOLDOWN_MS = 25_000L // Cooldown zwischen vollst. Sequenzen
@@ -129,6 +132,7 @@ class TridentKit : Kit() {
diveCharges.remove( player.uniqueId ) diveCharges.remove( player.uniqueId )
diveMonitors.remove( player.uniqueId )?.cancel() diveMonitors.remove( player.uniqueId )?.cancel()
lastSequenceTime.remove( player.uniqueId ) lastSequenceTime.remove( player.uniqueId )
noFallDamagePlayers.remove( player.uniqueId )
cachedItems.remove( player.uniqueId )?.forEach { player.inventory.remove( it ) } cachedItems.remove( player.uniqueId )?.forEach { player.inventory.remove( it ) }
} }
@@ -248,6 +252,7 @@ class TridentKit : Kit() {
else diveCharges[ player.uniqueId ] = charges - 1 else diveCharges[ player.uniqueId ] = charges - 1
player.velocity = player.velocity.clone().setY( 1.38 ) player.velocity = player.velocity.clone().setY( 1.38 )
noFallDamagePlayers.add( player.uniqueId )
val remaining = diveCharges.getOrDefault( player.uniqueId, 0 ) val remaining = diveCharges.getOrDefault( player.uniqueId, 0 )
player.sendActionBar(player.trans( "kits.trident.messages.dive_launched", "charges" to remaining.toString() )) player.sendActionBar(player.trans( "kits.trident.messages.dive_launched", "charges" to remaining.toString() ))

View File

@@ -10,6 +10,8 @@ import club.mcscrims.speedhg.kit.charge.ChargeState
import club.mcscrims.speedhg.kit.impl.AnchorKit import club.mcscrims.speedhg.kit.impl.AnchorKit
import club.mcscrims.speedhg.kit.impl.BlackPantherKit import club.mcscrims.speedhg.kit.impl.BlackPantherKit
import club.mcscrims.speedhg.kit.impl.IceMageKit import club.mcscrims.speedhg.kit.impl.IceMageKit
import club.mcscrims.speedhg.kit.impl.NinjaKit
import club.mcscrims.speedhg.kit.impl.TridentKit
import club.mcscrims.speedhg.kit.impl.VenomKit import club.mcscrims.speedhg.kit.impl.VenomKit
import club.mcscrims.speedhg.util.trans import club.mcscrims.speedhg.util.trans
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
@@ -26,6 +28,7 @@ import org.bukkit.event.EventPriority
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.block.BlockBreakEvent import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.entity.EntityDamageByEntityEvent import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.entity.EntityDamageEvent
import org.bukkit.event.entity.EntityDeathEvent import org.bukkit.event.entity.EntityDeathEvent
import org.bukkit.event.entity.EntityExplodeEvent import org.bukkit.event.entity.EntityExplodeEvent
import org.bukkit.event.entity.PlayerDeathEvent import org.bukkit.event.entity.PlayerDeathEvent
@@ -67,6 +70,11 @@ class KitEventDispatcher(
private val kitManager: KitManager, private val kitManager: KitManager,
) : Listener { ) : Listener {
companion object {
/** Above this Y-level, knockback abilities are disabled to prevent skybasing. */
const val MAX_KNOCKBACK_HEIGHT_Y = 100.0
}
// ========================================================================= // =========================================================================
// Hit tracking + charge system + passive combat hook // Hit tracking + charge system + passive combat hook
// ========================================================================= // =========================================================================
@@ -102,11 +110,19 @@ class KitEventDispatcher(
sendChargeUpdateActionBar( attacker, currentHits, chargeData.hitsRequired ) sendChargeUpdateActionBar( attacker, currentHits, chargeData.hitsRequired )
} }
// ── 2. Attacker passive hook ───────────────────────────────────────── // ── 2. Ninja last-hit tracking ───────────────────────────────────────
if ( attackerKit is NinjaKit &&
attackerPlaystyle == Playstyle.AGGRESSIVE )
{
attackerKit.lastHitEnemy[ attacker.uniqueId ] =
Pair( victim.uniqueId, System.currentTimeMillis() )
}
// ── 3. Attacker passive hook ─────────────────────────────────────────
attackerKit.getPassiveAbility( attackerPlaystyle ) attackerKit.getPassiveAbility( attackerPlaystyle )
.onHitEnemy( attacker, victim, event ) .onHitEnemy( attacker, victim, event )
// ── 3. Victim passive hook ──────────────────────────────────────────── // ── 4. Victim passive hook ────────────────────────────────────────────
kitManager.getSelectedKit( victim ) kitManager.getSelectedKit( victim )
?.getPassiveAbility(kitManager.getSelectedPlaystyle( victim )) ?.getPassiveAbility(kitManager.getSelectedPlaystyle( victim ))
?.onHitByEnemy( victim, attacker, event ) ?.onHitByEnemy( victim, attacker, event )
@@ -129,7 +145,6 @@ class KitEventDispatcher(
) { ) {
val player = event.player val player = event.player
// Only main-hand right-clicks — ignore left-click and off-hand duplicates
if ( event.hand != EquipmentSlot.HAND ) return if ( event.hand != EquipmentSlot.HAND ) return
if ( !event.action.isRightClick ) return if ( !event.action.isRightClick ) return
if ( !isIngame() ) return if ( !isIngame() ) return
@@ -144,6 +159,11 @@ class KitEventDispatcher(
val itemInHand = player.inventory.itemInMainHand val itemInHand = player.inventory.itemInMainHand
val active = kit.getActiveAbility( playstyle ) val active = kit.getActiveAbility( playstyle )
// Allow throwable items (potions, ender pearls, etc.) to pass through
if ( itemInHand.type == Material.SPLASH_POTION ||
itemInHand.type == Material.LINGERING_POTION ||
itemInHand.type == Material.ENDER_PEARL ) return
if ( itemInHand.type != active.triggerMaterial ) return if ( itemInHand.type != active.triggerMaterial ) return
event.isCancelled = true // prevent vanilla block interaction on ability item event.isCancelled = true // prevent vanilla block interaction on ability item
@@ -392,10 +412,39 @@ class KitEventDispatcher(
event.droppedExp = 0 event.droppedExp = 0
} }
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = false)
fun onLeapFallDamage(
event: EntityDamageEvent
) {
if ( event.cause != EntityDamageEvent.DamageCause.FALL ) return
if ( !isIngame() ) return
val player = event.entity as? Player ?: return
when(val kit = kitManager.getSelectedKit( player ))
{
is TridentKit ->
{
if ( kit.noFallDamagePlayers.remove( player.uniqueId ) )
event.isCancelled = true
}
is BlackPantherKit ->
{
if ( kit.noFallDamagePlayers.remove( player.uniqueId ) )
event.isCancelled = true
}
else -> return
}
}
// ========================================================================= // =========================================================================
// Helpers // Helpers
// ========================================================================= // =========================================================================
private fun isAboveKnockbackHeight(
player: Player
): Boolean = player.location.y > MAX_KNOCKBACK_HEIGHT_Y
private fun changeGladiatorBlock( private fun changeGladiatorBlock(
event: Cancellable, event: Cancellable,
block: Block block: Block

View File

@@ -0,0 +1,42 @@
package club.mcscrims.speedhg.listener
import club.mcscrims.speedhg.SpeedHG
import club.mcscrims.speedhg.scoreboard.ServerRankProvider
import io.papermc.paper.event.player.AsyncChatEvent
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.minimessage.MiniMessage
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
class ChatListener(
private val plugin: SpeedHG,
private val rankProvider: ServerRankProvider
) : Listener {
private val mm = MiniMessage.miniMessage()
@EventHandler
fun onAsyncChat(
event: AsyncChatEvent
) {
val player = event.player
val prefix = rankProvider.getRankPrefix( player )
val nameColor = rankProvider.getRankColor( player )
event.renderer { source, _, message, _ ->
val coloredName = mm.deserialize(
"${nameColor}${source.name}<reset>"
)
Component.empty()
.append( prefix )
.append( Component.space() )
.append( coloredName )
.append(mm.deserialize( "<dark_gray>: <gray>" ))
.append(message.colorIfAbsent( NamedTextColor.GRAY ))
}
}
}

View File

@@ -5,7 +5,6 @@ import club.mcscrims.speedhg.game.GameState
import club.mcscrims.speedhg.util.sendMsg import club.mcscrims.speedhg.util.sendMsg
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.Sound import org.bukkit.Sound
import org.bukkit.attribute.Attribute
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
@@ -157,17 +156,19 @@ class GameStateListener : Listener {
val player = event.player val player = event.player
if ( gameManager.currentState != GameState.INVINCIBILITY && if ( gameManager.currentState != GameState.INVINCIBILITY &&
gameManager.currentState != GameState.INGAME ) gameManager.currentState != GameState.INGAME )
{ {
event.isCancelled = true event.isCancelled = true
player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f )
return return
} }
val kitItems = plugin.kitManager.getSelectedKit( player )?.cachedItems?.get( player.uniqueId ) ?: return val kitItems = plugin.kitManager.getSelectedKit( player )
?.cachedItems?.get( player.uniqueId ) ?: return
val item = event.itemDrop.itemStack val item = event.itemDrop.itemStack
if (kitItems.contains( item )) val isKitItem = kitItems.any { kitItem -> kitItem.isSimilar( item ) }
if ( isKitItem )
{ {
event.isCancelled = true event.isCancelled = true
player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f )

View File

@@ -234,16 +234,15 @@ class TablistManager(
team.prefix(rankProvider.getRankPrefix( player )) team.prefix(rankProvider.getRankPrefix( player ))
// ── playerListName: farbiger Spielername ─────────────────────────── // ── playerListName: farbiger Spielername ───────────────────────────
// Ersetzt den Standard-Anzeigenamen in der Namens-Spalte. // WICHTIG: KEIN <reset> hier. Das <reset> machen wir am Anfang des Suffixes!
// Endergebnis: [PREFIX] [NAME] [SUFFIX]
val nameColor = rankProvider.getRankColor( player ) val nameColor = rankProvider.getRankColor( player )
player.playerListName(mm.deserialize( "${nameColor}${player.name}<reset>" )) player.playerListName(mm.deserialize( "${nameColor}${player.name}" ))
// ── Suffix: SpeedHG-Rang (z. B. "[Gold II]") ────────────────────── // ── Suffix: SpeedHG-Rang (z. B. "[Gold II]") ──────────────────────
team.suffix(buildSpeedHGRankSuffix( player )) team.suffix(buildSpeedHGRankSuffix( player ))
// Spieler dem Team zuweisen // Spieler dem Team zuweisen
if (!team.hasEntry( player.name )) team.addEntry( player.name ) if ( !team.hasEntry( player.name ) ) team.addEntry( player.name )
playerTeams[ player.uniqueId ] = newTeamName playerTeams[ player.uniqueId ] = newTeamName
// Scoreboard dem Spieler zuweisen (notwendig damit Teams sichtbar sind) // Scoreboard dem Spieler zuweisen (notwendig damit Teams sichtbar sind)
@@ -259,7 +258,9 @@ class TablistManager(
val games = ( stats?.wins ?: 0 ) + ( stats?.losses ?: 0 ) val games = ( stats?.wins ?: 0 ) + ( stats?.losses ?: 0 )
val rankTag = Rank.getFormattedRankTag( score, games ) val rankTag = Rank.getFormattedRankTag( score, games )
mm.deserialize( " <dark_gray>[<reset>${rankTag}<dark_gray>]</dark_gray>" ) // Führendes <reset> stellt sicher, dass die Spielerfarbe nicht in den Suffix blutet
// und erzwingt einen Cut, den Bukkit/Paper als neues Suffix-Objekt erkennt.
mm.deserialize( "<reset> <dark_gray>[<reset>${rankTag}<dark_gray>]</dark_gray>" )
} }
/** Entfernt das Scoreboard-Team des Spielers vollständig. */ /** Entfernt das Scoreboard-Team des Spielers vollständig. */

View File

@@ -307,6 +307,7 @@ perks:
kits: kits:
needed_hits: '<gold>⚡ Ability: <white><current>/<required> Hits</white></gold>' needed_hits: '<gold>⚡ Ability: <white><current>/<required> Hits</white></gold>'
ability_charged: '<green><bold>⚡ ABILITY READY!</bold></green>' ability_charged: '<green><bold>⚡ ABILITY READY!</bold></green>'
height_restriction: '<red>⚠ This ability cannot be used at high altitudes!'
backup: backup:
name: '<gradient:gold:#ff841f><bold>Backup</bold></gradient>' name: '<gradient:gold:#ff841f><bold>Backup</bold></gradient>'
@@ -624,8 +625,8 @@ kits:
name: '<gradient:gold:yellow><bold>Spielo</bold></gradient>' name: '<gradient:gold:yellow><bold>Spielo</bold></gradient>'
lore: lore:
- ' ' - ' '
- '<gray>AGGRESSIVE: Gambling at the push of a button</gray>' - 'AGGRESSIVE: Gambling at the push of a button'
- '<gray>DEFENSIVE: Slot machine - no instant death</gray>' - 'DEFENSIVE: Slot machine - no instant death'
items: items:
automat: automat:
name: '<gold><bold>Slot Machine</bold></gold>' name: '<gold><bold>Slot Machine</bold></gold>'