Add ability feedback/particles; refactor kits
Introduce centralized AbilityFeedback and AbilityParticles utilities and update language strings. Refactor IceMage: replace old snowball mechanics with an aggressive Ice Spike Burst (cone projectiles, cooldown tracking, config keys) and a defensive snowball barrage with metadata; reorganize config accessors and passive behaviors. Refactor Ninja: add layered sounds/particles for teleport, rewrite smoke aura as an animated BukkitRunnable with visual rotation and enemy effects, and improve lifecycle/scheduling handling. Update en_US.yml with new kit item names, passive labels, and messages.
This commit is contained in:
@@ -10,6 +10,7 @@ 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
|
||||||
import org.bukkit.Material
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.Particle
|
||||||
import org.bukkit.Sound
|
import org.bukkit.Sound
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||||
@@ -17,28 +18,31 @@ import org.bukkit.event.player.PlayerMoveEvent
|
|||||||
import org.bukkit.inventory.ItemStack
|
import org.bukkit.inventory.ItemStack
|
||||||
import org.bukkit.potion.PotionEffect
|
import org.bukkit.potion.PotionEffect
|
||||||
import org.bukkit.potion.PotionEffectType
|
import org.bukkit.potion.PotionEffectType
|
||||||
|
import org.bukkit.util.Vector
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ## IceMageKit
|
* ## IceMageKit
|
||||||
*
|
*
|
||||||
* | Playstyle | Active | Passive |
|
* | Playstyle | Aktive Fähigkeit | Passive |
|
||||||
* |-------------|-------------------------------------------------------------------|-----------------------------------------------------|
|
* |-------------|------------------------------------------------------------|-----------------------------------------|
|
||||||
* | AGGRESSIVE | – | Speed I in ice biomes; [slowChance] Slowness on hit |
|
* | AGGRESSIVE | **Ice Spike Burst** – Cone of freezing projectiles (3×) | Speed II in snowy biomes |
|
||||||
* | DEFENSIVE | **Snowball** – throws a 360° ring of frozen snowballs | – |
|
* | DEFENSIVE | **Snowball Barrage** – 16 projectiles in circle pattern | Slowness proc (33%) on hits |
|
||||||
*
|
*
|
||||||
* ## Konfiguration (via `SPEEDHG_CUSTOM_SETTINGS`)
|
* ## Konfiguration (via `SPEEDHG_CUSTOM_SETTINGS`)
|
||||||
*
|
*
|
||||||
* All values read from the `extras` map with companion-object defaults as fallback.
|
* All values read from the `extras` map with companion-object defaults as fallback.
|
||||||
*
|
*
|
||||||
* | JSON-Schlüssel | Typ | Default | Beschreibung |
|
* | JSON Key | Type | Default | Description |
|
||||||
* |---------------------|--------|---------|-----------------------------------------------------------------|
|
* |----------------------------|--------|---------|----------------------------------------|
|
||||||
* | `slow_chance_denom` | Int | `3` | `1-in-N` chance to apply Slowness on melee hit (aggressive) |
|
* | `spike_cooldown_ms` | Long | 8000 | Cooldown between spike bursts |
|
||||||
* | `snowball_count` | Int | `16` | Number of snowballs fired in the 360° ring (defensive) |
|
* | `spike_range_blocks` | Double | 12.0 | How far spike cone extends |
|
||||||
* | `snowball_speed` | Double | `1.5` | Launch speed of each snowball |
|
* | `spike_cone_width` | Double | 60.0 | Cone angle in degrees (wider = spread) |
|
||||||
* | `freeze_ticks` | Int | `60` | Freeze ticks applied to enemies hit by a snowball |
|
* | `spike_freeze_ticks` | Int | 60 | Duration of freeze effect (ticks) |
|
||||||
* | `slow_ticks` | Int | `40` | Slowness II ticks applied to enemies hit by a snowball |
|
* | `snowball_slowness_chance` | Double | 0.33 | Chance to apply Slowness on hit |
|
||||||
*/
|
*/
|
||||||
class IceMageKit : Kit()
|
class IceMageKit : Kit()
|
||||||
{
|
{
|
||||||
@@ -58,50 +62,33 @@ class IceMageKit : Kit()
|
|||||||
get() = Material.SNOWBALL
|
get() = Material.SNOWBALL
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_SLOW_CHANCE_DENOM = 3
|
const val DEFAULT_SPIKE_COOLDOWN_MS = 8000L
|
||||||
const val DEFAULT_SNOWBALL_COUNT = 16
|
const val DEFAULT_SPIKE_RANGE_BLOCKS = 12.0
|
||||||
const val DEFAULT_SNOWBALL_SPEED = 1.5
|
const val DEFAULT_SPIKE_CONE_WIDTH = 60.0 // degrees
|
||||||
const val DEFAULT_FREEZE_TICKS = 60
|
const val DEFAULT_SPIKE_FREEZE_TICKS = 60
|
||||||
const val DEFAULT_SLOW_TICKS = 40
|
const val DEFAULT_SNOWBALL_SLOWNESS_CHANCE = 0.33
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Live config accessors ─────────────────────────────────────────────────
|
// ── Live config accessors ─────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
private val spikeCooldownMs: Long
|
||||||
* Denominator of the `1-in-N` Slowness-on-hit chance for the aggressive passive.
|
get() = override().getLong( "spike_cooldown_ms" ) ?: DEFAULT_SPIKE_COOLDOWN_MS
|
||||||
* A value of `3` means roughly a 33 % chance.
|
|
||||||
* JSON key: `slow_chance_denom`
|
|
||||||
*/
|
|
||||||
private val slowChanceDenom: Int
|
|
||||||
get() = override().getInt( "slow_chance_denom" ) ?: DEFAULT_SLOW_CHANCE_DENOM
|
|
||||||
|
|
||||||
/**
|
private val spikeRangeBlocks: Double
|
||||||
* Number of snowballs launched in the 360° ring by the defensive active.
|
get() = override().getDouble( "spike_range_blocks" ) ?: DEFAULT_SPIKE_RANGE_BLOCKS
|
||||||
* JSON key: `snowball_count`
|
|
||||||
*/
|
|
||||||
private val snowballCount: Int
|
|
||||||
get() = override().getInt( "snowball_count" ) ?: DEFAULT_SNOWBALL_COUNT
|
|
||||||
|
|
||||||
/**
|
private val spikeConeWidth: Double
|
||||||
* Launch speed of each snowball in the ring.
|
get() = override().getDouble( "spike_cone_width" ) ?: DEFAULT_SPIKE_CONE_WIDTH
|
||||||
* JSON key: `snowball_speed`
|
|
||||||
*/
|
|
||||||
private val snowballSpeed: Double
|
|
||||||
get() = override().getDouble( "snowball_speed" ) ?: DEFAULT_SNOWBALL_SPEED
|
|
||||||
|
|
||||||
/**
|
private val spikeFreezeTicks: Int
|
||||||
* Freeze ticks applied to enemies hit by an IceMage snowball.
|
get() = override().getInt( "spike_freeze_ticks" ) ?: DEFAULT_SPIKE_FREEZE_TICKS
|
||||||
* JSON key: `freeze_ticks`
|
|
||||||
*/
|
|
||||||
private val freezeTicks: Int
|
|
||||||
get() = override().getInt( "freeze_ticks" ) ?: DEFAULT_FREEZE_TICKS
|
|
||||||
|
|
||||||
/**
|
private val snowballSlowChance: Double
|
||||||
* Slowness II ticks applied to enemies hit by an IceMage snowball.
|
get() = override().getDouble( "snowball_slowness_chance" ) ?: DEFAULT_SNOWBALL_SLOWNESS_CHANCE
|
||||||
* JSON key: `slow_ticks`
|
|
||||||
*/
|
// ── State tracking ────────────────────────────────────────────────────────
|
||||||
private val slowTicks: Int
|
|
||||||
get() = override().getInt( "slow_ticks" ) ?: DEFAULT_SLOW_TICKS
|
internal val spikeCooldowns: 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()
|
||||||
@@ -135,9 +122,21 @@ class IceMageKit : Kit()
|
|||||||
player: Player,
|
player: Player,
|
||||||
playstyle: Playstyle
|
playstyle: Playstyle
|
||||||
) {
|
) {
|
||||||
if ( playstyle != Playstyle.DEFENSIVE )
|
when( playstyle )
|
||||||
return
|
{
|
||||||
|
Playstyle.AGGRESSIVE ->
|
||||||
|
{
|
||||||
|
val spikeItem = ItemBuilder( Material.BLUE_ICE )
|
||||||
|
.name( aggressiveActive.name )
|
||||||
|
.lore(listOf( aggressiveActive.description ))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
cachedItems[ player.uniqueId ] = listOf( spikeItem )
|
||||||
|
player.inventory.addItem( spikeItem )
|
||||||
|
}
|
||||||
|
|
||||||
|
Playstyle.DEFENSIVE ->
|
||||||
|
{
|
||||||
val snowBall = ItemBuilder( Material.SNOWBALL )
|
val snowBall = ItemBuilder( Material.SNOWBALL )
|
||||||
.name( defensiveActive.name )
|
.name( defensiveActive.name )
|
||||||
.lore(listOf( defensiveActive.description ))
|
.lore(listOf( defensiveActive.description ))
|
||||||
@@ -146,45 +145,150 @@ class IceMageKit : Kit()
|
|||||||
cachedItems[ player.uniqueId ] = listOf( snowBall )
|
cachedItems[ player.uniqueId ] = listOf( snowBall )
|
||||||
player.inventory.addItem( snowBall )
|
player.inventory.addItem( snowBall )
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Optional lifecycle hooks ──────────────────────────────────────────────
|
// ── Optional lifecycle hooks ──────────────────────────────────────────────
|
||||||
|
|
||||||
override fun onRemove(
|
override fun onRemove(
|
||||||
player: Player
|
player: Player
|
||||||
) {
|
) {
|
||||||
|
spikeCooldowns.remove( player.uniqueId )
|
||||||
val items = cachedItems.remove( player.uniqueId ) ?: return
|
val items = cachedItems.remove( player.uniqueId ) ?: return
|
||||||
items.forEach { player.inventory.remove( it ) }
|
items.forEach { player.inventory.remove( it ) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AggressiveActive : ActiveAbility( Playstyle.AGGRESSIVE )
|
// =========================================================================
|
||||||
{
|
// AGGRESSIVE active – Ice Spike Burst
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private inner class AggressiveActive : ActiveAbility( Playstyle.AGGRESSIVE ) {
|
||||||
|
|
||||||
|
private val plugin get() = SpeedHG.instance
|
||||||
|
|
||||||
override val kitId: String
|
override val kitId: String
|
||||||
get() = "icemage"
|
get() = "icemage"
|
||||||
|
|
||||||
override val name: String
|
override val name: String
|
||||||
get() = "None"
|
get() = plugin.languageManager.getDefaultRawMessage( "kits.icemage.items.ice_spike.name" )
|
||||||
|
|
||||||
override val description: String
|
override val description: String
|
||||||
get() = "None"
|
get() = plugin.languageManager.getDefaultRawMessage( "kits.icemage.items.ice_spike.description" )
|
||||||
|
|
||||||
override val hardcodedHitsRequired: Int
|
override val hardcodedHitsRequired: Int
|
||||||
get() = 15
|
get() = 15
|
||||||
|
|
||||||
override val triggerMaterial: Material
|
override val triggerMaterial: Material
|
||||||
get() = Material.BARRIER
|
get() = Material.BLUE_ICE
|
||||||
|
|
||||||
override fun execute(
|
override fun execute(
|
||||||
player: Player
|
player: Player
|
||||||
): AbilityResult
|
): AbilityResult
|
||||||
{
|
{
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val lastUse = spikeCooldowns[ player.uniqueId ] ?: 0L
|
||||||
|
|
||||||
|
if ( now - lastUse < spikeCooldownMs )
|
||||||
|
{
|
||||||
|
val secLeft = ( spikeCooldownMs - ( now - lastUse )) / 1000
|
||||||
|
player.sendActionBar(player.trans( "kits.icemage.messages.spike_cooldown", "time" to secLeft.toString() ))
|
||||||
|
return AbilityResult.ConditionNotMet( "On cooldown" )
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot config at execution time
|
||||||
|
val capturedRange = spikeRangeBlocks
|
||||||
|
val capturedConeWidth = spikeConeWidth
|
||||||
|
val capturedFreezeTicks = spikeFreezeTicks
|
||||||
|
|
||||||
|
// Play charge-up sound
|
||||||
|
player.playSound( player.location, Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1f, 0.7f )
|
||||||
|
|
||||||
|
// Spawn cone of spikes
|
||||||
|
val direction = player.eyeLocation.direction.normalize()
|
||||||
|
val right = direction.clone().crossProduct(Vector( 0.0, 1.0, 0.0 )).normalize()
|
||||||
|
val up = right.crossProduct( direction ).normalize()
|
||||||
|
|
||||||
|
val coneHalfAngle = capturedConeWidth / 2.0
|
||||||
|
|
||||||
|
val spikeCount = 8
|
||||||
|
for ( i in 0 until spikeCount )
|
||||||
|
{
|
||||||
|
val verticalAngle = ( ( i / spikeCount.toDouble() ) - 0.5 ) * coneHalfAngle
|
||||||
|
val horizontalAngle = ( ( i % 4 ) / 4.0 - 0.5 ) * coneHalfAngle
|
||||||
|
|
||||||
|
val vertRad = Math.toRadians( verticalAngle )
|
||||||
|
val horizRad = Math.toRadians( horizontalAngle )
|
||||||
|
|
||||||
|
val cosV = cos( vertRad )
|
||||||
|
val sinV = sin( vertRad )
|
||||||
|
val cosH = cos( horizRad )
|
||||||
|
val sinH = sin( horizRad )
|
||||||
|
|
||||||
|
val spikeDir = direction.clone()
|
||||||
|
.multiply( cosV * cosH )
|
||||||
|
.add( right.clone().multiply( sinH * cosV ) )
|
||||||
|
.add( up.clone().multiply( sinV ) )
|
||||||
|
.normalize()
|
||||||
|
|
||||||
|
val startLoc = player.eyeLocation.clone().add( spikeDir.clone().multiply( 1.5 ) )
|
||||||
|
|
||||||
|
// Spawn particles along spike path
|
||||||
|
var currentLoc = startLoc.clone()
|
||||||
|
while ( currentLoc.distance( startLoc ) < capturedRange )
|
||||||
|
{
|
||||||
|
currentLoc.world.spawnParticle(
|
||||||
|
Particle.SNOWFLAKE,
|
||||||
|
currentLoc,
|
||||||
|
3, 0.1, 0.1, 0.1, 0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check for entity hits
|
||||||
|
val nearbyEnemies = currentLoc.world
|
||||||
|
.getNearbyEntities( currentLoc, 0.5, 0.5, 0.5 )
|
||||||
|
.filterIsInstance<Player>()
|
||||||
|
.filter { it != player && plugin.gameManager.alivePlayers.contains( it.uniqueId ) }
|
||||||
|
|
||||||
|
for ( enemy in nearbyEnemies )
|
||||||
|
{
|
||||||
|
enemy.freezeTicks = capturedFreezeTicks
|
||||||
|
enemy.addPotionEffect(PotionEffect( PotionEffectType.SLOWNESS, capturedFreezeTicks, 1 ))
|
||||||
|
|
||||||
|
// Impact feedback
|
||||||
|
enemy.world.spawnParticle(
|
||||||
|
Particle.LARGE_SMOKE,
|
||||||
|
enemy.location.clone().add( 0.0, 1.0, 0.0 ),
|
||||||
|
12, 0.3, 0.3, 0.3, 0.05
|
||||||
|
)
|
||||||
|
enemy.world.playSound( enemy.location, Sound.BLOCK_GLASS_BREAK, 1f, 1.5f )
|
||||||
|
currentLoc = currentLoc.add( spikeDir.clone().multiply( 50.0 ) ) // Exit ray
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLoc.add( spikeDir.clone().multiply( 0.5 ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blast sound + particle burst
|
||||||
|
player.world.playSound( player.location, Sound.ENTITY_BLAZE_SHOOT, 1f, 1.8f )
|
||||||
|
player.world.spawnParticle(
|
||||||
|
Particle.SNOWFLAKE,
|
||||||
|
player.eyeLocation,
|
||||||
|
30, 0.5, 0.5, 0.5, 0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
spikeCooldowns[ player.uniqueId ] = now
|
||||||
|
player.sendActionBar(player.trans( "kits.icemage.messages.spike_fired" ))
|
||||||
|
|
||||||
return AbilityResult.Success
|
return AbilityResult.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class DefensiveActive : ActiveAbility( Playstyle.DEFENSIVE )
|
// =========================================================================
|
||||||
{
|
// DEFENSIVE active – Snowball Barrage
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private inner class DefensiveActive : ActiveAbility( Playstyle.DEFENSIVE ) {
|
||||||
|
|
||||||
private val plugin get() = SpeedHG.instance
|
private val plugin get() = SpeedHG.instance
|
||||||
|
|
||||||
@@ -207,15 +311,44 @@ class IceMageKit : Kit()
|
|||||||
player: Player
|
player: Player
|
||||||
): AbilityResult
|
): AbilityResult
|
||||||
{
|
{
|
||||||
player.playSound( player.location, Sound.ENTITY_PLAYER_HURT_FREEZE, 1f, 1.5f )
|
// Multi-shot sound
|
||||||
player.sendActionBar( player.trans( "kits.icemage.messages.shoot_snowballs" ) )
|
player.playSound( player.location, Sound.ENTITY_BLAZE_SHOOT, 0.8f, 1.2f )
|
||||||
|
player.sendActionBar(player.trans( "kits.icemage.messages.shoot_snowballs" ))
|
||||||
|
|
||||||
|
val amountOfSnowballs = 16
|
||||||
|
val playerLocation = player.location
|
||||||
|
val baseSpeed = 1.5
|
||||||
|
|
||||||
|
for ( i in 0 until amountOfSnowballs )
|
||||||
|
{
|
||||||
|
val angle = i * ( 2 * Math.PI / amountOfSnowballs )
|
||||||
|
|
||||||
|
val x = cos( angle )
|
||||||
|
val z = sin( angle )
|
||||||
|
|
||||||
|
val direction = Vector( x, 0.0, z ).normalize().multiply( baseSpeed )
|
||||||
|
|
||||||
|
val snowBall = player.world.spawn( playerLocation, org.bukkit.entity.Snowball::class.java )
|
||||||
|
snowBall.shooter = player
|
||||||
|
snowBall.velocity = direction
|
||||||
|
|
||||||
|
snowBall.persistentDataContainer.set(
|
||||||
|
org.bukkit.NamespacedKey( plugin, "icemage_snowball" ),
|
||||||
|
org.bukkit.persistence.PersistentDataType.BYTE,
|
||||||
|
1.toByte()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return AbilityResult.Success
|
return AbilityResult.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class AggressivePassive : PassiveAbility( Playstyle.AGGRESSIVE )
|
// =========================================================================
|
||||||
{
|
// AGGRESSIVE passive – Biome Speed Boost
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private class AggressivePassive : PassiveAbility( Playstyle.AGGRESSIVE ) {
|
||||||
|
|
||||||
private val plugin get() = SpeedHG.instance
|
private val plugin get() = SpeedHG.instance
|
||||||
private val random = Random()
|
private val random = Random()
|
||||||
@@ -232,18 +365,18 @@ class IceMageKit : Kit()
|
|||||||
)
|
)
|
||||||
|
|
||||||
override val name: String
|
override val name: String
|
||||||
get() = plugin.languageManager.getDefaultRawMessage( "kits.icemage.passive.name" )
|
get() = plugin.languageManager.getDefaultRawMessage( "kits.icemage.passive.aggressive.name" )
|
||||||
|
|
||||||
override val description: String
|
override val description: String
|
||||||
get() = plugin.languageManager.getDefaultRawMessage( "kits.icemage.passive.description" )
|
get() = plugin.languageManager.getDefaultRawMessage( "kits.icemage.passive.aggressive.description" )
|
||||||
|
|
||||||
override fun onMove(
|
override fun onMove(
|
||||||
player: Player,
|
player: Player,
|
||||||
event: PlayerMoveEvent
|
event: PlayerMoveEvent
|
||||||
) {
|
) {
|
||||||
val biome = player.world.getBiome( player.location )
|
val biome = player.world.getBiome( player.location )
|
||||||
if ( !biomeList.contains( biome.name.lowercase() ) ) return
|
if (!biomeList.contains( biome.name.lowercase() )) return
|
||||||
player.addPotionEffect( PotionEffect( PotionEffectType.SPEED, 20, 0 ) )
|
player.addPotionEffect(PotionEffect( PotionEffectType.SPEED, 20, 0 ))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onHitEnemy(
|
override fun onHitEnemy(
|
||||||
@@ -251,23 +384,53 @@ class IceMageKit : Kit()
|
|||||||
victim: Player,
|
victim: Player,
|
||||||
event: EntityDamageByEntityEvent
|
event: EntityDamageByEntityEvent
|
||||||
) {
|
) {
|
||||||
// Snapshot at hit time for consistency
|
if (random.nextInt( 3 ) < 1 )
|
||||||
val capturedDenom = slowChanceDenom
|
|
||||||
|
|
||||||
if ( random.nextInt( capturedDenom ) < 1 )
|
|
||||||
victim.addPotionEffect( PotionEffect( PotionEffectType.SLOWNESS, slowTicks, 0 ) )
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DefensivePassive : PassiveAbility( Playstyle.DEFENSIVE )
|
|
||||||
{
|
{
|
||||||
|
victim.addPotionEffect(PotionEffect( PotionEffectType.SLOWNESS, 60, 0 ))
|
||||||
|
|
||||||
|
// Feedback: brief ice particle burst on victim
|
||||||
|
victim.world.spawnParticle(
|
||||||
|
Particle.SNOWFLAKE,
|
||||||
|
victim.location.clone().add( 0.0, 1.0, 0.0 ),
|
||||||
|
8, 0.2, 0.2, 0.2, 0.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// DEFENSIVE passive – Slowness Proc
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private inner class DefensivePassive : PassiveAbility( Playstyle.DEFENSIVE ) {
|
||||||
|
|
||||||
|
private val plugin get() = SpeedHG.instance
|
||||||
|
private val random = Random()
|
||||||
|
|
||||||
override val name: String
|
override val name: String
|
||||||
get() = "None"
|
get() = plugin.languageManager.getDefaultRawMessage( "kits.icemage.passive.defensive.name" )
|
||||||
|
|
||||||
override val description: String
|
override val description: String
|
||||||
get() = "None"
|
get() = plugin.languageManager.getDefaultRawMessage( "kits.icemage.passive.defensive.description" )
|
||||||
|
|
||||||
|
override fun onHitEnemy(
|
||||||
|
attacker: Player,
|
||||||
|
victim: Player,
|
||||||
|
event: EntityDamageByEntityEvent
|
||||||
|
) {
|
||||||
|
if (random.nextDouble() >= snowballSlowChance) return
|
||||||
|
|
||||||
|
victim.addPotionEffect(PotionEffect( PotionEffectType.SLOWNESS, 80, 1 ))
|
||||||
|
|
||||||
|
// Feedback: particle puff + sound
|
||||||
|
victim.world.spawnParticle(
|
||||||
|
Particle.LARGE_SMOKE,
|
||||||
|
victim.location.clone().add( 0.0, 1.0, 0.0 ),
|
||||||
|
6, 0.2, 0.3, 0.2, 0.0
|
||||||
|
)
|
||||||
|
victim.world.playSound( victim.location, Sound.BLOCK_GLASS_BREAK, 0.6f, 0.9f )
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -258,22 +258,42 @@ class NinjaKit : Kit() {
|
|||||||
dest.yaw = enemy.location.yaw
|
dest.yaw = enemy.location.yaw
|
||||||
dest.pitch = 0f
|
dest.pitch = 0f
|
||||||
|
|
||||||
|
// ── Departure Particles + Sound ───────────────────────────────────────
|
||||||
|
|
||||||
player.world.spawnParticle(
|
player.world.spawnParticle(
|
||||||
Particle.LARGE_SMOKE,
|
Particle.LARGE_SMOKE,
|
||||||
player.location.clone().add( 0.0, 1.0, 0.0 ),
|
player.location.clone().add( 0.0, 1.0, 0.0 ),
|
||||||
25, 0.3, 0.5, 0.3, 0.05
|
25, 0.3, 0.5, 0.3, 0.05
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Layered sounds: departure "whoosh" + teleport
|
||||||
|
player.playSound( player.location, Sound.ENTITY_ENDER_PEARL_THROW, 0.8f, 1.5f )
|
||||||
|
Bukkit.getScheduler().runTaskLater( plugin, { ->
|
||||||
|
player.playSound( player.location, Sound.ENTITY_ENDERMAN_TELEPORT, 0.7f, 1.8f )
|
||||||
|
}, 2L )
|
||||||
|
|
||||||
|
// ── Teleport ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
player.teleport( dest )
|
player.teleport( dest )
|
||||||
|
|
||||||
player.world.spawnParticle(
|
// ── Arrival Particles + Sound ─────────────────────────────────────────
|
||||||
|
|
||||||
|
dest.world.spawnParticle(
|
||||||
Particle.LARGE_SMOKE,
|
Particle.LARGE_SMOKE,
|
||||||
dest.clone().add( 0.0, 1.0, 0.0 ),
|
dest.clone().add( 0.0, 1.0, 0.0 ),
|
||||||
25, 0.3, 0.5, 0.3, 0.05
|
25, 0.3, 0.5, 0.3, 0.05
|
||||||
)
|
)
|
||||||
|
|
||||||
player.playSound( player.location, Sound.ENTITY_ENDERMAN_TELEPORT, 0.7f, 1.8f )
|
// Brief particle ring around destination (visual confirmation)
|
||||||
|
dest.world.spawnParticle(
|
||||||
|
Particle.FLAME,
|
||||||
|
dest.clone().add( 0.0, 0.5, 0.0 ),
|
||||||
|
12, 0.5, 0.1, 0.5, 0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
player.playSound( dest, Sound.ENTITY_ENDERMAN_TELEPORT, 0.4f, 0.7f )
|
||||||
enemy.playSound( enemy.location, Sound.ENTITY_ENDERMAN_TELEPORT, 0.4f, 0.7f )
|
enemy.playSound( enemy.location, Sound.ENTITY_ENDERMAN_TELEPORT, 0.4f, 0.7f )
|
||||||
|
|
||||||
player.sendActionBar(player.trans( "kits.ninja.messages.teleported" ))
|
player.sendActionBar(player.trans( "kits.ninja.messages.teleported" ))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,80 +325,98 @@ class NinjaKit : Kit() {
|
|||||||
|
|
||||||
// Snapshot the config values at activation time so mid-round changes
|
// Snapshot the config values at activation time so mid-round changes
|
||||||
// don't alter an already-running aura unexpectedly.
|
// don't alter an already-running aura unexpectedly.
|
||||||
val capturedRefreshTicks = smokeRefreshTicks
|
|
||||||
val capturedRadius = smokeRadius
|
val capturedRadius = smokeRadius
|
||||||
|
val capturedDurationTicks = smokeDurationTicks
|
||||||
|
val capturedRefreshTicks = smokeRefreshTicks
|
||||||
val capturedEffectTicks = smokeEffectTicks
|
val capturedEffectTicks = smokeEffectTicks
|
||||||
|
|
||||||
val task = Bukkit.getScheduler().runTaskTimer( plugin, { ->
|
// Play activation sound (magical, layered)
|
||||||
if ( !player.isOnline ||
|
player.playSound( player.location, Sound.ENTITY_WARDEN_SONIC_CHARGE, 0.8f, 1.2f )
|
||||||
!plugin.gameManager.alivePlayers.contains( player.uniqueId ) )
|
player.playSound( player.location, Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.6f, 0.9f )
|
||||||
|
|
||||||
|
val auraTask = object : org.bukkit.scheduler.BukkitRunnable() {
|
||||||
|
|
||||||
|
var ticksElapsed = 0L
|
||||||
|
var rotation = 0.0
|
||||||
|
|
||||||
|
override fun run()
|
||||||
{
|
{
|
||||||
smokeTasks.remove( player.uniqueId )?.cancel()
|
if ( !player.isOnline || !plugin.gameManager.alivePlayers.contains( player.uniqueId ) )
|
||||||
return@runTaskTimer
|
{
|
||||||
|
this.cancel()
|
||||||
|
smokeTasks.remove( player.uniqueId )
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
spawnSmokeRing( player, capturedRadius )
|
ticksElapsed += capturedRefreshTicks
|
||||||
applyEffectsToEnemies( player, capturedRadius, capturedEffectTicks )
|
|
||||||
|
|
||||||
}, 0L, capturedRefreshTicks )
|
if ( ticksElapsed >= capturedDurationTicks )
|
||||||
|
{
|
||||||
|
this.cancel()
|
||||||
|
smokeTasks.remove( player.uniqueId )
|
||||||
|
|
||||||
smokeTasks[ player.uniqueId ] = task
|
// Deactivation sound & feedback
|
||||||
|
player.world.playSound( player.location, Sound.BLOCK_FIRE_EXTINGUISH, 1f, 0.8f )
|
||||||
// Schedule automatic aura expiry
|
player.sendActionBar(player.trans( "kits.ninja.messages.smoke_expired" ))
|
||||||
val capturedDurationTicks = smokeDurationTicks
|
return
|
||||||
Bukkit.getScheduler().runTaskLater( plugin, { ->
|
|
||||||
smokeTasks.remove( player.uniqueId )?.cancel()
|
|
||||||
}, capturedDurationTicks )
|
|
||||||
|
|
||||||
player.playSound( player.location, Sound.ENTITY_ENDERMAN_AMBIENT, 0.7f, 1.8f )
|
|
||||||
player.sendActionBar(player.trans( "kits.ninja.messages.smoke_activated" ))
|
|
||||||
return AbilityResult.Success
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Rendering helpers (private to this inner class) ───────────────────
|
// ── Spawn smoke ring particles (animated circle) ──────────────────
|
||||||
|
|
||||||
private fun spawnSmokeRing(
|
val loc = player.location
|
||||||
player: Player,
|
|
||||||
radius: Double
|
|
||||||
) {
|
|
||||||
val center = player.location
|
|
||||||
val steps = 10
|
|
||||||
|
|
||||||
for ( i in 0 until steps )
|
for ( i in 0 until 16 )
|
||||||
{
|
{
|
||||||
val angle = i * ( 2.0 * Math.PI / steps )
|
val angle = ( 2 * Math.PI * i / 16 ) + rotation
|
||||||
center.world.spawnParticle(
|
val x = cos( angle ) * capturedRadius
|
||||||
Particle.CAMPFIRE_COSY_SMOKE,
|
val z = sin( angle ) * capturedRadius
|
||||||
center.clone().add(
|
|
||||||
cos( angle ) * radius,
|
loc.world.spawnParticle(
|
||||||
0.8,
|
Particle.LARGE_SMOKE,
|
||||||
sin( angle ) * radius
|
loc.clone().add( x, 0.8, z ),
|
||||||
),
|
2, 0.1, 0.1, 0.1, 0.05
|
||||||
1, 0.05, 0.12, 0.05, 0.004
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate ring for visual animation
|
||||||
|
rotation += 0.2
|
||||||
|
|
||||||
|
// ── Apply Blindness + Slowness to nearby enemies ──────────────────
|
||||||
|
|
||||||
|
loc.world.getNearbyEntities( loc, capturedRadius, capturedRadius, capturedRadius )
|
||||||
|
.filterIsInstance<Player>()
|
||||||
|
.filter { it != player && plugin.gameManager.alivePlayers.contains( it.uniqueId ) }
|
||||||
|
.forEach { enemy ->
|
||||||
|
enemy.addPotionEffect(PotionEffect(
|
||||||
|
PotionEffectType.BLINDNESS,
|
||||||
|
capturedEffectTicks,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
))
|
||||||
|
enemy.addPotionEffect(PotionEffect(
|
||||||
|
PotionEffectType.SLOWNESS,
|
||||||
|
capturedEffectTicks,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
))
|
||||||
|
|
||||||
|
// Brief particle flash on hit (visual feedback)
|
||||||
|
enemy.world.spawnParticle(
|
||||||
|
Particle.SMOKE,
|
||||||
|
enemy.location.clone().add( 0.0, 0.8, 0.0 ),
|
||||||
|
4, 0.2, 0.2, 0.2, 0.0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}.runTaskTimer( plugin, 0L, capturedRefreshTicks )
|
||||||
|
|
||||||
private fun applyEffectsToEnemies(
|
smokeTasks[ player.uniqueId ] = auraTask
|
||||||
player: Player,
|
player.sendActionBar(player.trans( "kits.ninja.messages.smoke_activated" ))
|
||||||
radius: Double,
|
return AbilityResult.Success
|
||||||
effectTicks: Int
|
|
||||||
) {
|
|
||||||
player.location.world
|
|
||||||
.getNearbyEntities( player.location, radius, 2.0, radius )
|
|
||||||
.filterIsInstance<Player>()
|
|
||||||
.filter { it != player &&
|
|
||||||
plugin.gameManager.alivePlayers.contains( it.uniqueId ) }
|
|
||||||
.forEach { enemy ->
|
|
||||||
enemy.addPotionEffect(PotionEffect(
|
|
||||||
PotionEffectType.BLINDNESS, effectTicks, 0,
|
|
||||||
false, false, true
|
|
||||||
))
|
|
||||||
enemy.addPotionEffect(PotionEffect(
|
|
||||||
PotionEffectType.SLOWNESS, effectTicks, 0,
|
|
||||||
false, false, true
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package club.mcscrims.speedhg.util
|
||||||
|
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.Sound
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
object AbilityFeedback {
|
||||||
|
|
||||||
|
// ── Charge Feedback ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fun playChargeReady( player: Player ) {
|
||||||
|
player.playSound( player.location, Sound.BLOCK_BEACON_POWER_SELECT, 1f, 1.6f )
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playCharging( player: Player ) {
|
||||||
|
player.playSound( player.location, Sound.BLOCK_COMPARATOR_CLICK, 0.6f, 1.2f )
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Activation Feedback ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fun playActivation( player: Player ) {
|
||||||
|
player.playSound( player.location, Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 0.8f, 1.0f )
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Impact Feedback ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fun playImpact( location: Location ) {
|
||||||
|
location.world?.playSound( location, Sound.BLOCK_ANVIL_LAND, 1f, 1.2f )
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Cooldown Feedback ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fun playCooldownExpired( player: Player ) {
|
||||||
|
player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_PLING, 1f, 1.8f )
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package club.mcscrims.speedhg.util
|
||||||
|
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.Particle
|
||||||
|
|
||||||
|
object AbilityParticles {
|
||||||
|
|
||||||
|
// ── Directional Impact Ring ────────────────────────────────────────────
|
||||||
|
|
||||||
|
fun spawnImpactRing( center: Location, radius: Double = 1.5 ) {
|
||||||
|
val particleCount = 12
|
||||||
|
for ( i in 0 until particleCount ) {
|
||||||
|
val angle = ( 2 * Math.PI * i / particleCount )
|
||||||
|
val x = kotlin.math.cos( angle ) * radius
|
||||||
|
val z = kotlin.math.sin( angle ) * radius
|
||||||
|
|
||||||
|
center.world?.spawnParticle(
|
||||||
|
Particle.FLAME,
|
||||||
|
center.clone().add( x, 0.5, z ),
|
||||||
|
1, 0.0, 0.0, 0.0, 0.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Charge Build Indicator ────────────────────────────────────────────
|
||||||
|
|
||||||
|
fun spawnChargeRing( center: Location, scale: Double ) {
|
||||||
|
val radius = 0.5 + ( scale * 1.5 )
|
||||||
|
center.world?.spawnParticle(
|
||||||
|
Particle.ELECTRIC_SPARK,
|
||||||
|
center,
|
||||||
|
8,
|
||||||
|
radius, radius, radius,
|
||||||
|
0.1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Cooldown Indicator ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fun spawnCooldownIndicator( location: Location ) {
|
||||||
|
location.world?.spawnParticle(
|
||||||
|
Particle.FLAME,
|
||||||
|
location.clone().add( 0.0, 1.0, 0.0 ),
|
||||||
|
6,
|
||||||
|
0.3, 0.3, 0.3,
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -315,6 +315,7 @@ kits:
|
|||||||
- ' '
|
- ' '
|
||||||
- 'Select a kit mid-round at any time.'
|
- 'Select a kit mid-round at any time.'
|
||||||
- 'All kits are available to pick.'
|
- 'All kits are available to pick.'
|
||||||
|
|
||||||
gladiator:
|
gladiator:
|
||||||
name: '<gradient:dark_gray:gray><bold>Gladiator</bold></gradient>'
|
name: '<gradient:dark_gray:gray><bold>Gladiator</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -327,6 +328,7 @@ kits:
|
|||||||
description: 'Fight an enemy in a 1v1 above the skies'
|
description: 'Fight an enemy in a 1v1 above the skies'
|
||||||
messages:
|
messages:
|
||||||
ability_charged: '<yellow>Your ability has been recharged!</yellow>'
|
ability_charged: '<yellow>Your ability has been recharged!</yellow>'
|
||||||
|
|
||||||
goblin:
|
goblin:
|
||||||
name: '<gradient:dark_green:gray><bold>Goblin</bold></gradient>'
|
name: '<gradient:dark_green:gray><bold>Goblin</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -344,6 +346,7 @@ kits:
|
|||||||
stole_kit: '<green>You have stolen the kit of your opponent (Kit: <kit>)!</green>'
|
stole_kit: '<green>You have stolen the kit of your opponent (Kit: <kit>)!</green>'
|
||||||
spawn_bunker: '<green>You have created a bunker around yourself!'
|
spawn_bunker: '<green>You have created a bunker around yourself!'
|
||||||
ability_charged: '<yellow>Your ability has been recharged!</yellow>'
|
ability_charged: '<yellow>Your ability has been recharged!</yellow>'
|
||||||
|
|
||||||
icemage:
|
icemage:
|
||||||
name: '<gradient:dark_aqua:aqua><bold>IceMage</bold></gradient>'
|
name: '<gradient:dark_aqua:aqua><bold>IceMage</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -352,14 +355,24 @@ kits:
|
|||||||
- 'DEFENSIVE: Summon snowballs and freeze enemies'
|
- 'DEFENSIVE: Summon snowballs and freeze enemies'
|
||||||
items:
|
items:
|
||||||
snowball:
|
snowball:
|
||||||
name: '§bFreeze'
|
name: '<aqua>❄️ Freeze</aqua>'
|
||||||
description: 'Freeze your enemies by throwing snowballs in all directions'
|
description: 'Freeze your enemies by throwing snowballs in all directions'
|
||||||
|
ice_spike:
|
||||||
|
name: '<aqua>❄️ Ice Spike Burst</aqua>'
|
||||||
|
description: 'Right-click: Fire cone of freezing projectiles'
|
||||||
passive:
|
passive:
|
||||||
name: '§bIceStorm'
|
aggressive:
|
||||||
|
name: '<aqua>IceStorm</aqua>'
|
||||||
description: 'Gain speed in cold biomes and give enemies slowness'
|
description: 'Gain speed in cold biomes and give enemies slowness'
|
||||||
|
defensive:
|
||||||
|
name: '<aqua>Slowness Proc</aqua>'
|
||||||
|
description: '33% chance to slow enemies when you hit them'
|
||||||
messages:
|
messages:
|
||||||
shoot_snowballs: '<aqua>You have shot frozen snowballs in all directions!</aqua>'
|
shoot_snowballs: '<aqua>You have shot frozen snowballs in all directions!</aqua>'
|
||||||
ability_charged: '<yellow>Your ability has been recharged!</yellow>'
|
ability_charged: '<yellow>Your ability has been recharged!</yellow>'
|
||||||
|
spike_fired: '<aqua>❄️ Spikes fired!</aqua>'
|
||||||
|
spike_cooldown: '<red>Cooldown: <time>s remaining'
|
||||||
|
|
||||||
venom:
|
venom:
|
||||||
name: '<gradient:dark_red:red><bold>Venom</bold></gradient>'
|
name: '<gradient:dark_red:red><bold>Venom</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -381,6 +394,7 @@ kits:
|
|||||||
shield_activate: '<gray>Your shield of darkness has been activated!</gray>'
|
shield_activate: '<gray>Your shield of darkness has been activated!</gray>'
|
||||||
shield_break: '<red>Your shield of darkness has broken!</red>'
|
shield_break: '<red>Your shield of darkness has broken!</red>'
|
||||||
ability_charged: '<yellow>Your ability has been recharged</yellow>'
|
ability_charged: '<yellow>Your ability has been recharged</yellow>'
|
||||||
|
|
||||||
rattlesnake:
|
rattlesnake:
|
||||||
name: '<gradient:green:dark_green><bold>Rattlesnake</bold></gradient>'
|
name: '<gradient:green:dark_green><bold>Rattlesnake</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -402,6 +416,7 @@ kits:
|
|||||||
pounce_hit: '<green>Pounce hit <count> target(s)! Poison II applied!</green>'
|
pounce_hit: '<green>Pounce hit <count> target(s)! Poison II applied!</green>'
|
||||||
pounce_miss: '<red>Pounce missed! Nearby enemies were disoriented.</red>'
|
pounce_miss: '<red>Pounce missed! Nearby enemies were disoriented.</red>'
|
||||||
venom_proc: '<green>Counter Venom triggered!</green>'
|
venom_proc: '<green>Counter Venom triggered!</green>'
|
||||||
|
|
||||||
armorer:
|
armorer:
|
||||||
name: '<gradient:gray:white><bold>Armorer</bold></gradient>'
|
name: '<gradient:gray:white><bold>Armorer</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -418,6 +433,7 @@ kits:
|
|||||||
messages:
|
messages:
|
||||||
armor_upgraded: '<gold>Armor upgraded to tier <tier>!</gold>'
|
armor_upgraded: '<gold>Armor upgraded to tier <tier>!</gold>'
|
||||||
armor_replaced: '<yellow>Broken armor replaced automatically.</yellow>'
|
armor_replaced: '<yellow>Broken armor replaced automatically.</yellow>'
|
||||||
|
|
||||||
voodoo:
|
voodoo:
|
||||||
name: '<gradient:dark_purple:light_purple><bold>Voodoo</bold></gradient>'
|
name: '<gradient:dark_purple:light_purple><bold>Voodoo</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -444,6 +460,7 @@ kits:
|
|||||||
curse_cast: '<light_purple>Cursed <count> enemy(s) for 15 seconds!</light_purple>'
|
curse_cast: '<light_purple>Cursed <count> enemy(s) for 15 seconds!</light_purple>'
|
||||||
curse_received: '<red>You have been cursed by a Voodoo player!</red>'
|
curse_received: '<red>You have been cursed by a Voodoo player!</red>'
|
||||||
ability_charged: '<yellow>Ability recharged!</yellow>'
|
ability_charged: '<yellow>Ability recharged!</yellow>'
|
||||||
|
|
||||||
blackpanther:
|
blackpanther:
|
||||||
name: '<gradient:dark_gray:white><bold>Black Panther</bold></gradient>'
|
name: '<gradient:dark_gray:white><bold>Black Panther</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -465,6 +482,7 @@ kits:
|
|||||||
fist_mode_active: '<gray>⚡ Vibranium Fists active for 12 seconds!</gray>'
|
fist_mode_active: '<gray>⚡ Vibranium Fists active for 12 seconds!</gray>'
|
||||||
wakanda_impact: '<white>Wakanda Forever! Hit <count> enemy(s)!</white>'
|
wakanda_impact: '<white>Wakanda Forever! Hit <count> enemy(s)!</white>'
|
||||||
ability_charged: '<yellow>Ability recharged!</yellow>'
|
ability_charged: '<yellow>Ability recharged!</yellow>'
|
||||||
|
|
||||||
theworld:
|
theworld:
|
||||||
name: '<gradient:dark_gray:white><bold>The World</bold></gradient>'
|
name: '<gradient:dark_gray:white><bold>The World</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -492,6 +510,7 @@ kits:
|
|||||||
frozen_expired: '<gray>The freeze has worn off.</gray>'
|
frozen_expired: '<gray>The freeze has worn off.</gray>'
|
||||||
freeze_broken: '<gold>Freeze broken — 5 hits reached!</gold>'
|
freeze_broken: '<gold>Freeze broken — 5 hits reached!</gold>'
|
||||||
freeze_hits_left: '<aqua>Frozen enemy — <hits> hit(s) remaining.</aqua>'
|
freeze_hits_left: '<aqua>Frozen enemy — <hits> hit(s) remaining.</aqua>'
|
||||||
|
|
||||||
tesla:
|
tesla:
|
||||||
name: '<gradient:yellow:aqua><bold>Tesla</bold></gradient>'
|
name: '<gradient:yellow:aqua><bold>Tesla</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -551,6 +570,7 @@ kits:
|
|||||||
anchor_placed: '<gray>Anchor deployed! Radius: <radius> blocks.'
|
anchor_placed: '<gray>Anchor deployed! Radius: <radius> blocks.'
|
||||||
anchor_destroyed: '<red>⚓ Your anchor was destroyed!'
|
anchor_destroyed: '<red>⚓ Your anchor was destroyed!'
|
||||||
ability_charged: '<gray>Anchor ready to deploy!'
|
ability_charged: '<gray>Anchor ready to deploy!'
|
||||||
|
|
||||||
ninja:
|
ninja:
|
||||||
name: '<gradient:dark_gray:white><bold>Ninja</bold></gradient>'
|
name: '<gradient:dark_gray:white><bold>Ninja</bold></gradient>'
|
||||||
lore:
|
lore:
|
||||||
@@ -562,10 +582,12 @@ kits:
|
|||||||
name: '<dark_gray>Smoke Bomb</dark_gray>'
|
name: '<dark_gray>Smoke Bomb</dark_gray>'
|
||||||
description: 'Enemies in 3-block radius get Blindness + Slowness'
|
description: 'Enemies in 3-block radius get Blindness + Slowness'
|
||||||
messages:
|
messages:
|
||||||
teleported: '<gray>Teleported behind enemy!'
|
teleport_cooldown: '<dark_red>Teleport on cooldown – <time>s'
|
||||||
cooldown: '<red>Teleport on cooldown – <time>s'
|
|
||||||
no_target: '<red>No recent target! Hit an enemy first.'
|
no_target: '<red>No recent target! Hit an enemy first.'
|
||||||
target_expired: '<red>Target expired (10 s window).'
|
target_expired: '<red>Target expired (10s window).'
|
||||||
|
teleported: '<dark_gray>◆ Teleported behind target'
|
||||||
|
smoke_activated: '<dark_gray>◆ Smoke aura activated!'
|
||||||
|
smoke_expired: '<gray>◆ Smoke expired.'
|
||||||
|
|
||||||
trident:
|
trident:
|
||||||
name: '<gradient:aqua:blue><bold>Trident</bold></gradient>'
|
name: '<gradient:aqua:blue><bold>Trident</bold></gradient>'
|
||||||
|
|||||||
Reference in New Issue
Block a user