From 2b875399e8e7b6f7cefb2dd89b8e90da262bc8d8 Mon Sep 17 00:00:00 2001 From: TDSTOS Date: Tue, 14 Apr 2026 00:23:29 +0200 Subject: [PATCH] Fix 9 bugs and features 9 identified bugs have been fixed, aswell as the podium where you would not spawn in the middle and missing messages --- .../mcscrims/speedhg/game/PodiumManager.kt | 9 +- .../speedhg/game/modules/FeastManager.kt | 15 +- .../speedhg/kit/impl/BlackPantherKit.kt | 155 ++++++++++++++---- .../mcscrims/speedhg/kit/impl/TridentKit.kt | 2 +- .../mcscrims/speedhg/kit/impl/VenomKit.kt | 4 +- .../speedhg/listener/GameStateListener.kt | 80 ++++++--- .../mcscrims/speedhg/perk/impl/GourmetPerk.kt | 15 +- src/main/resources/languages/en_US.yml | 27 +-- 8 files changed, 225 insertions(+), 82 deletions(-) diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/PodiumManager.kt b/src/main/kotlin/club/mcscrims/speedhg/game/PodiumManager.kt index b718544..30c7c26 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/game/PodiumManager.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/game/PodiumManager.kt @@ -233,9 +233,12 @@ class PodiumManager( val xOff = COLUMN_X_OFFSET[ entry.placement ] ?: return@forEach val height = COLUMN_HEIGHT[ entry.placement ] ?: return@forEach - val standLoc = center.clone() - .add( xOff.toDouble(), height.toDouble(), 0.0 ) - .apply { yaw = 0f; pitch = 0f } + val standLoc = Location( + center.world, + center.blockX + xOff.toDouble() + 0.5, // Block-Mitte X + center.y + height, + center.blockZ.toDouble() + 0.5 // Block-Mitte Z + ).apply { yaw = 0f; pitch = 0f } entry.player.teleport( standLoc ) entry.player.gameMode = GameMode.ADVENTURE diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/modules/FeastManager.kt b/src/main/kotlin/club/mcscrims/speedhg/game/modules/FeastManager.kt index 8d46309..0039e81 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/game/modules/FeastManager.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/game/modules/FeastManager.kt @@ -129,10 +129,14 @@ class FeastManager( // State erst im nächsten Tick lesen — Block-Commit braucht einen Tick Bukkit.getScheduler().runTaskLater( plugin, { -> - val chest = chestBlock.state as? Chest ?: return@runTaskLater + val freshBlock = world.getBlockAt( cx, platformY + 1, cz ) + if ( freshBlock.type != Material.CHEST ) return@runTaskLater + + val chest = freshBlock.state as? Chest ?: return@runTaskLater + chest.update( true, false ) // BlockState commiten, bevor Inventar beschrieben wird + fillChestWithLoot( chest ) - chest.update( true ) - }, 1L ) + }, 2L ) } }, 5L ) @@ -203,11 +207,12 @@ class FeastManager( ) { val loot = buildLootTable() val inventory = chest.blockInventory - val slots = ( 0 until inventory.size ).shuffled() + val slots = ( 0 until inventory.size ).shuffled().toMutableList() loot.forEachIndexed { idx, item -> - if ( idx < slots.size ) inventory.setItem(slots[ idx ], item ) + if ( idx < slots.size ) inventory.setItem( slots[ idx ], item ) } + // kein chest.update() nötig — blockInventory ist eine Live-Referenz } /** diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt index b99cf40..af72339 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt @@ -7,6 +7,7 @@ import club.mcscrims.speedhg.kit.ability.AbilityResult import club.mcscrims.speedhg.kit.ability.ActiveAbility import club.mcscrims.speedhg.kit.ability.PassiveAbility import club.mcscrims.speedhg.kit.listener.KitEventDispatcher.Companion.MAX_KNOCKBACK_HEIGHT_Y +import club.mcscrims.speedhg.util.AbilityUtils import club.mcscrims.speedhg.util.ItemBuilder import club.mcscrims.speedhg.util.WorldEditUtils import club.mcscrims.speedhg.util.trans @@ -18,6 +19,7 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent import org.bukkit.event.player.PlayerMoveEvent import org.bukkit.inventory.ItemStack import org.bukkit.persistence.PersistentDataType +import org.bukkit.util.Vector import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -184,11 +186,23 @@ class BlackPantherKit : Kit() player: Player, playstyle: Playstyle ) { - if ( playstyle != Playstyle.AGGRESSIVE ) return - val item = ItemBuilder( Material.BLACK_DYE ) - .name( aggressiveActive.name ) - .lore(listOf( aggressiveActive.description )) - .build() + val item = when( playstyle ) + { + Playstyle.AGGRESSIVE -> { + ItemBuilder( Material.BLACK_DYE ) + .name( aggressiveActive.name ) + .lore(listOf( aggressiveActive.description )) + .build() + } + + Playstyle.DEFENSIVE -> { + ItemBuilder( Material.FEATHER ) + .name( defensiveActive.name ) + .lore(listOf( defensiveActive.description )) + .build() + } + } + cachedItems[ player.uniqueId ] = listOf( item ) player.inventory.addItem( item ) } @@ -230,12 +244,11 @@ class BlackPantherKit : Kit() plugin.languageManager.getRawMessage( player, "kits.height_restriction" ) ) - // Werte zum Aktivierungszeitpunkt snapshotten - val capturedPushRadius = pushRadius - val capturedKnockbackSpeed = pushKnockbackSpeed - val capturedKnockbackY = pushKnockbackY - val capturedFistModeDurationMs = fistModeDurationMs - val capturedProjectileDelay = projectileDelayTicks + val capturedPushRadius = pushRadius + val capturedKnockbackSpeed = pushKnockbackSpeed + val capturedKnockbackY = pushKnockbackY + val capturedFistModeDurationMs = fistModeDurationMs + val capturedProjectileDelay = projectileDelayTicks val enemies = player.world .getNearbyEntities( player.location, capturedPushRadius, capturedPushRadius, capturedPushRadius ) @@ -245,6 +258,18 @@ class BlackPantherKit : Kit() if ( enemies.isEmpty() ) return AbilityResult.ConditionNotMet( "No enemies within ${capturedPushRadius.toInt()} blocks!" ) + // ── Schockwellen-Partikel am Spieler (Aktivierungs-Burst) ────────────── + player.world.spawnParticle( + Particle.LARGE_SMOKE, + player.location.clone().add( 0.0, 1.0, 0.0 ), + 30, 1.0, 0.4, 1.0, 0.08 + ) + player.world.spawnParticle( + Particle.EXPLOSION, + player.location.clone().add( 0.0, 0.5, 0.0 ), + 2, 0.3, 0.2, 0.3, 0.0 + ) + val pushKey = NamespacedKey( plugin, PUSH_PROJECTILE_KEY ) enemies.forEach { enemy -> @@ -255,49 +280,116 @@ class BlackPantherKit : Kit() .setY( capturedKnockbackY ) enemy.velocity = knockDir + + // ── Partikel-Spur von Spieler → Gegner (farbige Dust-Linie) ───────── + AbilityUtils.drawColoredParticleLine( + player.eyeLocation, + enemy.location.clone().add( 0.0, 1.0, 0.0 ), + 20, + Color.fromRGB( 10, 10, 10 ) // Schwarz/Vibranium-Dunkel + ) + + // ── Aufprall-Burst am Gegner ───────────────────────────────────────── enemy.world.spawnParticle( Particle.CRIT, enemy.location.clone().add( 0.0, 1.0, 0.0 ), - 10, 0.3, 0.3, 0.3, 0.0 + 20, 0.4, 0.4, 0.4, 0.1 + ) + enemy.world.spawnParticle( + Particle.LARGE_SMOKE, + enemy.location.clone().add( 0.0, 1.0, 0.0 ), + 8, 0.3, 0.3, 0.3, 0.04 ) Bukkit.getScheduler().runTaskLater( plugin, { -> if ( !player.isOnline ) return@runTaskLater + val snowball = player.world.spawn( player.eyeLocation, Snowball::class.java ) snowball.shooter = player - val travelDir = enemy.location.toVector() + + val travelDir = enemy.location.clone().add( 0.0, 1.0, 0.0 ) + .toVector() .subtract( player.eyeLocation.toVector() ) .normalize() .multiply( 1.8 ) snowball.velocity = travelDir snowball.persistentDataContainer.set( pushKey, PersistentDataType.BYTE, 1 ) + + // ── Partikel-Spur für das Projektil (nachleuchtend) ───────────── + AbilityUtils.drawGenericParticleLine( + player.eyeLocation, + enemy.location.clone().add( 0.0, 1.0, 0.0 ), + 15, + Particle.SOUL_FIRE_FLAME + ) }, capturedProjectileDelay ) } fistModeExpiry[ player.uniqueId ] = System.currentTimeMillis() + capturedFistModeDurationMs player.sendActionBar( player.trans( "kits.blackpanther.messages.fist_mode_active" ) ) - player.world.playSound( player.location, Sound.ENTITY_RAVAGER_ROAR, 1f, 1.1f ) + player.world.playSound( player.location, Sound.ENTITY_RAVAGER_ROAR, 1f, 1.1f ) player.world.playSound( player.location, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 0.8f, 0.7f ) return AbilityResult.Success } + } // ========================================================================= - // DEFENSIVE active – no active ability + // DEFENSIVE active — Wakanda Leap (neu) // ========================================================================= - private class DefensiveActive : ActiveAbility( Playstyle.DEFENSIVE ) + private inner class DefensiveActive : ActiveAbility( Playstyle.DEFENSIVE ) { + private val plugin get() = SpeedHG.instance + override val kitId: String = "blackpanther" - override val name = "None" - override val description = "None" - override val hardcodedHitsRequired: Int = 0 - override val triggerMaterial = Material.BARRIER + + override val name: String + get() = plugin.languageManager.getDefaultRawMessage( "kits.blackpanther.items.leap.name" ) + + override val description: String + get() = plugin.languageManager.getDefaultRawMessage( "kits.blackpanther.items.leap.description" ) + + override val hardcodedHitsRequired: Int = 10 + + override val triggerMaterial: Material + get() = Material.FEATHER + override fun execute( player: Player - ) = AbilityResult.Success + ): AbilityResult + { + if ( player.location.y > MAX_KNOCKBACK_HEIGHT_Y ) + return AbilityResult.ConditionNotMet( + plugin.languageManager.getRawMessage( player, "kits.height_restriction" ) + ) + + // Steil nach oben schleudern — leicht in Blickrichtung geneigt (wie Riptide) + val lookDir = player.location.direction.setY( 0.0 ).normalize() + val launchVec = Vector( lookDir.x * 0.4, 2.6, lookDir.z * 0.4 ) + player.velocity = launchVec + + // Fallschaden unterdrücken + Spieler als "in der Luft per Ability" markieren + noFallDamagePlayers.add( player.uniqueId ) + + player.world.playSound( player.location, Sound.ENTITY_RAVAGER_ROAR, 1f, 1.4f ) + player.world.playSound( player.location, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 0.8f, 0.6f ) + player.world.spawnParticle( + Particle.LARGE_SMOKE, + player.location.clone().add( 0.0, 0.5, 0.0 ), + 20, 0.3, 0.2, 0.3, 0.07 + ) + player.world.spawnParticle( + Particle.CRIT, + player.location.clone().add( 0.0, 1.0, 0.0 ), + 12, 0.4, 0.3, 0.4, 0.1 + ) + + player.sendActionBar( player.trans( "kits.blackpanther.messages.wakanda_leap" ) ) + return AbilityResult.Success + } } // ========================================================================= @@ -340,12 +432,11 @@ class BlackPantherKit : Kit() } // ========================================================================= - // DEFENSIVE passive – Wakanda Forever! (fall-pounce → AOE + crater) + // DEFENSIVE passive — Wakanda Forever! (Aufprall-AoE nach Ability-Launch) // ========================================================================= private inner class DefensivePassive : PassiveAbility( Playstyle.DEFENSIVE ) { - private val plugin get() = SpeedHG.instance override val name: String @@ -357,17 +448,16 @@ class BlackPantherKit : Kit() player: Player, event: PlayerMoveEvent ) { + // Nur feuern wenn Spieler per Ability in der Luft ist + if ( !noFallDamagePlayers.contains( player.uniqueId ) ) return + // Nur wenn Spieler nach unten bewegt (fällt) if ( event.to.y >= event.from.y ) return - val capturedPounceMinFall = pounceMinFall - if ( player.fallDistance < capturedPounceMinFall ) return - val blockBelow = event.to.clone().subtract( 0.0, 0.1, 0.0 ).block if ( !blockBelow.type.isSolid ) return + // ── Aufprall-AoE ──────────────────────────────────────────────────── val impactLoc = event.to.clone() - - // Werte zum Aktivierungszeitpunkt snapshotten val capturedPounceRadius = pounceRadius val capturedPounceDamage = pounceDamage @@ -378,8 +468,10 @@ class BlackPantherKit : Kit() splashTargets.forEach { it.damage( capturedPounceDamage, player ) } - impactLoc.world.spawnParticle( Particle.EXPLOSION, impactLoc, 3, 0.5, 0.5, 0.5, 0.0 ) - impactLoc.world.spawnParticle( Particle.LARGE_SMOKE, impactLoc, 20, 1.0, 0.5, 1.0, 0.05 ) + // ── Partikel & Sounds ──────────────────────────────────────────────── + impactLoc.world.spawnParticle( Particle.EXPLOSION, impactLoc, 3, 0.5, 0.5, 0.5, 0.0 ) + impactLoc.world.spawnParticle( Particle.LARGE_SMOKE, impactLoc, 25, 1.2, 0.5, 1.2, 0.06 ) + impactLoc.world.spawnParticle( Particle.CRIT, impactLoc, 20, 1.0, 0.3, 1.0, 0.15 ) impactLoc.world.playSound( impactLoc, Sound.ENTITY_GENERIC_EXPLODE, 1f, 0.7f ) impactLoc.world.playSound( impactLoc, Sound.ENTITY_IRON_GOLEM_HURT, 1f, 0.5f ) @@ -395,7 +487,8 @@ class BlackPantherKit : Kit() mapOf( "count" to splashTargets.size.toString() ) ) ) - noFallDamagePlayers.add( player.uniqueId ) + // Fallschaden wird vom KitEventDispatcher.onLeapFallDamage unterdrückt + // (noFallDamagePlayers.remove() passiert dort, NICHT hier) player.fallDistance = 0f } } diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/TridentKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/TridentKit.kt index abe037b..51a18f5 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/TridentKit.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/TridentKit.kt @@ -169,7 +169,7 @@ class TridentKit : Kit() val nameKey = if ( playstyle == Playstyle.AGGRESSIVE ) "kits.trident.items.trident.aggressive.name" else - "kits.trident.item.trident.defensive.name" + "kits.trident.items.trident.defensive.name" val trident = ItemBuilder( Material.TRIDENT ) .name( plugin.languageManager.getDefaultRawMessage( nameKey ) ) diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/VenomKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/VenomKit.kt index f83c70f..48b3929 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/VenomKit.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/VenomKit.kt @@ -191,8 +191,8 @@ class VenomKit : Kit() AbilityUtils.createBeam( player, - player.location, - player.eyeLocation.toVector(), + player.eyeLocation, // Startpunkt = Augenpunkt + player.eyeLocation.direction, // Richtungsvektor aus Blickrichtung Particle.DRAGON_BREATH, 7.5, 0.1 ) { target -> diff --git a/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt b/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt index 12d209a..36e9113 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt @@ -7,6 +7,7 @@ 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 @@ -114,10 +115,16 @@ class GameStateListener : Listener { return } - // BUG 2 FIX: Kein separater Invis-Whitelist-Block mehr. - // Beide States (INVINCIBILITY + INGAME) laufen direkt in pickupBlock — - // Map-Protection (Diamant, Eisen) ist bereits oben abgehandelt. - pickupBlock( event, player ) + val isAlwaysPickup = alwaysMaterials.containsKey( block.type ) + val isInvisOnlyPickup = beforeInvisMaterials.containsKey( block.type ) + + when { + isAlwaysPickup -> + pickupBlock( event, player ) + + isInvisOnlyPickup && gameManager.currentState == GameState.INVINCIBILITY -> + pickupBlock( event, player ) + } } private fun pickupBlock( @@ -135,6 +142,14 @@ class GameStateListener : Listener { } } + if ( block.type == Material.CACTUS ) + { + activeCactusBreaker[ player.uniqueId ] = player + Bukkit.getScheduler().runTask( plugin ) { -> + activeCactusBreaker.remove( player.uniqueId ) + } + } + event.isCancelled = true // BUG 1 FIX: !! entfernt → Elvis mit null-Safe-Fallback auf GENERIC_SOUND. @@ -178,38 +193,54 @@ class GameStateListener : Listener { * Kettenreaktion-Drops bekommen soll. */ private val activeMushroomBreaker: MutableMap = ConcurrentHashMap() + private val activeCactusBreaker: MutableMap = ConcurrentHashMap() @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = false) fun onBlockPhysics( event: BlockPhysicsEvent ) { val block = event.block - if ( block.type != Material.RED_MUSHROOM && - block.type != Material.BROWN_MUSHROOM ) return if ( gameManager.currentState != GameState.INVINCIBILITY && gameManager.currentState != GameState.INGAME ) return - // Einen Spieler in der Nähe finden der gerade einen Pilz abbaut - val harvester = activeMushroomBreaker.values - .firstOrNull { it.location.distanceSquared( block.location ) <= 25.0 } - ?: return // Keine aktive Spieleraktion — normales Physik-Event, ignorieren - - // Block vor dem Pop einsammeln - event.isCancelled = true - - val drops = block.getDrops( harvester.inventory.itemInMainHand, harvester ) - - if ( !hasInventorySpace( harvester ) ) + // ── Pilze (bestehende Logik) ────────────────────────────────────────── + if ( block.type == Material.RED_MUSHROOM || + block.type == Material.BROWN_MUSHROOM ) { - drops.forEach { harvester.world.dropItem( block.location, it ) } - } - else - { - drops.forEach { harvester.inventory.addItem( it ) } + val harvester = activeMushroomBreaker.values + .firstOrNull { it.location.distanceSquared( block.location ) <= 25.0 } + ?: return + + event.isCancelled = true + val drops = block.getDrops( harvester.inventory.itemInMainHand, harvester ) + + if ( !hasInventorySpace( harvester ) ) + drops.forEach { harvester.world.dropItem( block.location, it ) } + else + drops.forEach { harvester.inventory.addItem( it ) } + + block.type = Material.AIR + return } - block.type = Material.AIR + // ── Kakteen (NEU) ───────────────────────────────────────────────────── + if ( block.type == Material.CACTUS ) + { + val harvester = activeCactusBreaker.values + .firstOrNull { it.location.distanceSquared( block.location ) <= 25.0 } + ?: return + + event.isCancelled = true + val drops = block.getDrops( harvester.inventory.itemInMainHand, harvester ) + + if ( !hasInventorySpace( harvester ) ) + drops.forEach { harvester.world.dropItem( block.location, it ) } + else + drops.forEach { harvester.inventory.addItem( it ) } + + block.type = Material.AIR + } } @EventHandler @@ -404,6 +435,9 @@ class GameStateListener : Listener { 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 } diff --git a/src/main/kotlin/club/mcscrims/speedhg/perk/impl/GourmetPerk.kt b/src/main/kotlin/club/mcscrims/speedhg/perk/impl/GourmetPerk.kt index f722336..335eae8 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/perk/impl/GourmetPerk.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/perk/impl/GourmetPerk.kt @@ -89,21 +89,24 @@ class GourmetPerk : Perk(), Listener { // ── Soup listener ───────────────────────────────────────────────────────── - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + @EventHandler( priority = EventPriority.MONITOR, ignoreCancelled = true ) fun onSoupConsume( event: PlayerInteractEvent ) { + // Nur Rechtsklick — verhindert Trigger beim Schlagen/Links-Klick + if ( !event.action.isRightClick ) return if ( event.hand != EquipmentSlot.HAND ) return val player = event.player if ( !activePlayers.contains( player.uniqueId ) ) return - val item = event.item ?: return - if ( item.type != Material.MUSHROOM_STEW ) return + // Ursprüngliches Item muss MUSHROOM_STEW gewesen sein + val originalItem = event.item ?: return + if ( originalItem.type != Material.MUSHROOM_STEW ) return - // Only fire when the item is actually going to be consumed — i.e. when - // the player is not at full hunger (vanilla consumption gate). - if ( player.foodLevel >= 20 ) return + // Nach SoupListener (HIGHEST) ist der Slot eine Bowl, wenn tatsächlich getrunken + val currentItem = player.inventory.getItem( event.hand!! ) + if ( currentItem.type != Material.BOWL ) return val dur = durationTicks player.addPotionEffect( PotionEffect( PotionEffectType.REGENERATION, dur, 0, false, true, true ) ) diff --git a/src/main/resources/languages/en_US.yml b/src/main/resources/languages/en_US.yml index e4f3933..7012e8a 100644 --- a/src/main/resources/languages/en_US.yml +++ b/src/main/resources/languages/en_US.yml @@ -599,29 +599,33 @@ kits: curse_received: '🔮 You have been cursed by a Voodoo player!' ability_charged: '⚡ Ability recharged!' - # ── Black Panther ───────────────────────────────────────────────────────────── - # FIX: Added clarity that AGGRESSIVE is Push + 12 s Vibranium Fists (6.5 dmg/hit). - # DEFENSIVE Wakanda pounce now mentions 3-block fall requirement and crater. + # ── BlackPanther (neue Defensive-Leap Fähigkeit aus vorherigem Fix) ───────── blackpanther: - name: 'Black Panther' + # Lore-Zeile für DEF aktualisieren: lore: - ' ' - '[AGG] Push nearby enemies, then unleash 6.5 dmg fists for 12 s.' - - '[DEF] Fall 3+ blocks onto a foe — AOE damage + crater.' + - '[DEF] Activate to leap skyward — land for AOE impact + crater.' items: push: name: '⚡ Vibranium Pulse' description: 'Knock back all nearby enemies and activate 12 s Vibranium Fist Mode' + # NEU — Leap-Item für Defensive: + leap: + name: '⬆ Wakanda Leap' + description: 'Launch skyward — land on enemies for AOE damage + crater (fall damage disabled)' passive: aggressive: name: 'Vibranium Fists' description: '6.5 bare-hand damage for 12 s after activating Push' defensive: name: 'Wakanda Forever!' - description: 'Fall from 3+ blocks onto an enemy for AOE damage and a crater impact' + description: 'Activate Leap, then land on enemies for AOE damage and a crater' messages: fist_mode_active: '⚡ Vibranium Fists active for 12 seconds!' wakanda_impact: '⚡ Wakanda Forever! Hit enemy(s)!' + # NEU: + wakanda_leap: '⬆ Wakanda Forever — brace for impact!' ability_charged: '⚡ Ability recharged!' # ── TheWorld ────────────────────────────────────────────────────────────────── @@ -768,11 +772,12 @@ kits: name: 'Trident Parry' description: '20% chance to parry melee attacks: launch attacker back with Slowness I' messages: - dive_launched: '⚡ Launched! charge(s) remaining.' - charges_left: ' dive charge(s) left!' - sequence_done: 'Dive sequence complete.' - parry_success: '⚡ Parried!' - parried_by_victim: '⚡ Your attack was parried!' + dive_launched: '⚡ Launched! Brace for impact!' + parry_success: '🛡 Parried! Attacker bounced back!' + parried_by_victim: 'Your attack was parried!' + charges_left: ' dive charge(s) remaining!' + sequence_done: 'Sequence complete — recharging...' + ability_charged: '⚡ Ability recharged!' # ── Blitzcrank ──────────────────────────────────────────────────────────────── blitzcrank: