feat: add BlinkComponent and refine DoubleJumpComponent's glide event handling and GLIDER data component management.

This commit is contained in:
Kariya 2025-12-12 12:25:07 +00:00
parent 9a77176a53
commit b5a51fad96
3 changed files with 263 additions and 0 deletions

View File

@ -10,6 +10,7 @@ import net.hareworks.hcu.items.content.items.TestItem
import net.hareworks.hcu.items.content.items.GrapplingItem
import net.hareworks.hcu.items.content.components.GliderComponent
import net.hareworks.hcu.items.content.components.DoubleJumpComponent
import net.hareworks.hcu.items.content.components.BlinkComponent
import org.bukkit.permissions.PermissionDefault
import org.bukkit.plugin.java.JavaPlugin
@ -39,6 +40,7 @@ public class App : JavaPlugin() {
// Register Components
ComponentRegistry.register(GliderComponent(this))
ComponentRegistry.register(DoubleJumpComponent(this))
ComponentRegistry.register(BlinkComponent(this))
}
}

View File

@ -0,0 +1,254 @@
package net.hareworks.hcu.items.content.components
import io.papermc.paper.datacomponent.DataComponentType
import io.papermc.paper.datacomponent.DataComponentTypes
import net.hareworks.hcu.items.App
import net.hareworks.hcu.items.api.Tier
import net.hareworks.hcu.items.api.component.AbstractComponent
import net.hareworks.hcu.items.api.component.EquippableComponent
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextColor
import org.bukkit.Material
import org.bukkit.Particle
import org.bukkit.Sound
import org.bukkit.entity.Player
import org.bukkit.event.entity.EntityToggleGlideEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.util.Vector
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
class BlinkComponent(private val plugin: App) : AbstractComponent(plugin, "blink_component"), EquippableComponent {
override val displayName: String = "Blink"
override val maxTier: Int = 3
override val dataComponentDependencies: List<DataComponentType.NonValued>
get() = listOf(DataComponentTypes.GLIDER)
companion object {
// Track players who have used their blink (GLIDER is removed until landing)
private val usedBlink = ConcurrentHashMap.newKeySet<UUID>()
// Color scheme - Purple/Magenta for teleport/blink theme
private val PRIMARY_COLOR = TextColor.color(0xDA70D6) // Orchid
private val SECONDARY_COLOR = TextColor.color(0xE6E6FA) // Lavender
private val READY_COLOR = NamedTextColor.LIGHT_PURPLE
private val TRAIL_COLOR = TextColor.color(0x9400D3) // Dark Violet
}
override fun apply(item: ItemStack, tier: Tier?) {
if (isBoots(item.type)) {
super.apply(item, tier)
}
}
private fun isBoots(material: Material): Boolean {
return material.name.endsWith("_BOOTS")
}
@Suppress("DEPRECATION")
override fun onTick(player: Player, item: ItemStack) {
// Ensure GLIDER is present if this component is on the item
if (!item.hasData(DataComponentTypes.GLIDER) && !usedBlink.contains(player.uniqueId)) {
item.setData(DataComponentTypes.GLIDER)
}
// Reset blink when player lands on ground
if (player.isOnGround && usedBlink.contains(player.uniqueId)) {
usedBlink.remove(player.uniqueId)
// Re-add GLIDER component
if (!item.hasData(DataComponentTypes.GLIDER)) {
item.setData(DataComponentTypes.GLIDER)
}
// Landing feedback
showReadyMessage(player)
player.playSound(player.location, Sound.BLOCK_NOTE_BLOCK_BELL, 0.5f, 1.5f)
}
}
override fun onToggleGlide(player: Player, item: ItemStack, event: EntityToggleGlideEvent) {
// Only process if this item actually has this component
if (!has(item)) return
// Skip if another component already handled this event
if (event.isCancelled) return
// Only handle glide start events
if (!event.isGliding) return
// If player has already used blink, cancel the event to prevent vanilla gliding
if (usedBlink.contains(player.uniqueId)) {
item.unsetData(DataComponentTypes.GLIDER)
event.isCancelled = true
return
}
// Cancel the glide start
event.isCancelled = true
// Mark as used
usedBlink.add(player.uniqueId)
// Execute Blink
handleBlink(player, item)
// Remove GLIDER component to prevent further glide triggers
item.unsetData(DataComponentTypes.GLIDER)
}
private fun handleBlink(player: Player, item: ItemStack) {
val tier = getTier(item) ?: Tier.ONE
val currentVelocity = player.velocity
// Get movement direction from player's keyboard input
val inputDirection = getMovementDirectionFromInput(player)
// Determine boost direction
val boostDirection: Vector
val usingLookDirection: Boolean
if (inputDirection != null) {
// Player is pressing movement keys - use input direction
boostDirection = inputDirection
usingLookDirection = false
} else {
// Player is not pressing any movement keys - use look direction
val lookDirection = player.location.direction.clone()
boostDirection = Vector(lookDirection.x, 0.0, lookDirection.z).normalize()
usingLookDirection = true
}
// Calculate boost power based on tier
val baseBoost = 1.2 + (tier.level * 0.3) // Base horizontal acceleration
// Apply velocity boost - replace horizontal velocity with boost direction
val newVelocity = boostDirection.clone().multiply(baseBoost)
player.addPotionEffect(PotionEffect(PotionEffectType.SLOW_FALLING, 5, 1))
player.velocity = newVelocity
// Visual Effects
spawnBlinkParticles(player, boostDirection, usingLookDirection)
// Sound Effects
player.playSound(player.location, Sound.ENTITY_ENDERMAN_TELEPORT, 0.6f, 1.8f)
player.playSound(player.location, Sound.ENTITY_BREEZE_SHOOT, 0.4f, 1.5f)
player.playSound(player.location, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.8f, 1.2f)
// Action Bar message
showBlinkMessage(player, usingLookDirection)
}
/**
* Calculate movement direction based on player's current keyboard input (W/A/S/D)
* combined with the player's facing direction.
* Returns null if no movement keys are pressed.
*/
private fun getMovementDirectionFromInput(player: Player): Vector? {
val input = player.currentInput
// Check if any movement keys are pressed
val forward = input.isForward
val backward = input.isBackward
val left = input.isLeft
val right = input.isRight
if (!forward && !backward && !left && !right) {
return null // No movement input
}
// Get player's horizontal facing direction
val yaw = Math.toRadians(player.location.yaw.toDouble())
// Calculate forward vector (where player is looking horizontally)
val forwardX = -kotlin.math.sin(yaw)
val forwardZ = kotlin.math.cos(yaw)
// Calculate right vector (perpendicular to forward)
val rightX = -forwardZ
val rightZ = forwardX
// Build movement direction from input
var moveX = 0.0
var moveZ = 0.0
if (forward) {
moveX += forwardX
moveZ += forwardZ
}
if (backward) {
moveX -= forwardX
moveZ -= forwardZ
}
if (left) {
moveX -= rightX
moveZ -= rightZ
}
if (right) {
moveX += rightX
moveZ += rightZ
}
val result = Vector(moveX, 0.0, moveZ)
// Normalize if there's any movement
if (result.lengthSquared() > 0) {
return result.normalize()
}
return null
}
private fun spawnBlinkParticles(player: Player, direction: Vector, usingLook: Boolean) {
val loc = player.location
val world = player.world
// Origin burst - purple spiral
for (i in 0 until 16) {
val angle = (i * 22.5) * Math.PI / 180
val radius = 0.5 + (i * 0.05)
val x = kotlin.math.cos(angle) * radius
val z = kotlin.math.sin(angle) * radius
val y = i * 0.08
world.spawnParticle(Particle.REVERSE_PORTAL, loc.clone().add(x, y, z), 2, 0.0, 0.0, 0.0, 0.01)
}
// Directional trail particles
val trailDirection = direction.clone().normalize()
for (i in 1..8) {
val trailPos = loc.clone().add(trailDirection.clone().multiply(i * 0.5))
world.spawnParticle(Particle.ENCHANT, trailPos.add(0.0, 1.0, 0.0), 5, 0.1, 0.3, 0.1, 0.5)
world.spawnParticle(Particle.PORTAL, trailPos, 3, 0.1, 0.2, 0.1, 0.3)
}
// Central effect
world.spawnParticle(Particle.DRAGON_BREATH, loc.clone().add(0.0, 1.0, 0.0), 10, 0.2, 0.3, 0.2, 0.02)
// Look direction indicator if applicable
if (usingLook) {
world.spawnParticle(Particle.END_ROD, loc.clone().add(direction.clone().multiply(1.5)).add(0.0, 1.0, 0.0), 5, 0.1, 0.1, 0.1, 0.02)
}
}
private fun showBlinkMessage(player: Player, usingLook: Boolean) {
val modeText = if (usingLook) "視線方向" else "進行方向"
player.sendActionBar(
Component.text("", PRIMARY_COLOR)
.append(Component.text("ブリンク!", SECONDARY_COLOR))
.append(Component.text(" [$modeText]", NamedTextColor.GRAY))
.append(Component.text("", PRIMARY_COLOR))
)
}
private fun showReadyMessage(player: Player) {
player.sendActionBar(
Component.text("", READY_COLOR)
.append(Component.text("ブリンク準備完了", NamedTextColor.LIGHT_PURPLE))
)
}
}

View File

@ -73,6 +73,11 @@ class DoubleJumpComponent(private val plugin: App) : AbstractComponent(plugin, "
}
override fun onToggleGlide(player: Player, item: ItemStack, event: EntityToggleGlideEvent) {
// Only process if this item actually has this component
if (!has(item)) return
// Skip if another component already handled this event
if (event.isCancelled) return
// Only handle glide start events
if (!event.isGliding) return
@ -80,6 +85,8 @@ class DoubleJumpComponent(private val plugin: App) : AbstractComponent(plugin, "
// If player has already used double jump, don't cancel - let them glide normally
// (though GLIDER should be removed, so this shouldn't fire anyway)
if (usedDoubleJump.contains(player.uniqueId)) {
item.unsetData(DataComponentTypes.GLIDER)
event.isCancelled = true
return
}