feat: add BlinkComponent and refine DoubleJumpComponent's glide event handling and GLIDER data component management.
This commit is contained in:
parent
9a77176a53
commit
b5a51fad96
|
|
@ -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.items.GrapplingItem
|
||||||
import net.hareworks.hcu.items.content.components.GliderComponent
|
import net.hareworks.hcu.items.content.components.GliderComponent
|
||||||
import net.hareworks.hcu.items.content.components.DoubleJumpComponent
|
import net.hareworks.hcu.items.content.components.DoubleJumpComponent
|
||||||
|
import net.hareworks.hcu.items.content.components.BlinkComponent
|
||||||
|
|
||||||
import org.bukkit.permissions.PermissionDefault
|
import org.bukkit.permissions.PermissionDefault
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
@ -39,6 +40,7 @@ public class App : JavaPlugin() {
|
||||||
// Register Components
|
// Register Components
|
||||||
ComponentRegistry.register(GliderComponent(this))
|
ComponentRegistry.register(GliderComponent(this))
|
||||||
ComponentRegistry.register(DoubleJumpComponent(this))
|
ComponentRegistry.register(DoubleJumpComponent(this))
|
||||||
|
ComponentRegistry.register(BlinkComponent(this))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -73,6 +73,11 @@ class DoubleJumpComponent(private val plugin: App) : AbstractComponent(plugin, "
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onToggleGlide(player: Player, item: ItemStack, event: EntityToggleGlideEvent) {
|
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
|
// Only handle glide start events
|
||||||
if (!event.isGliding) return
|
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
|
// 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)
|
// (though GLIDER should be removed, so this shouldn't fire anyway)
|
||||||
if (usedDoubleJump.contains(player.uniqueId)) {
|
if (usedDoubleJump.contains(player.uniqueId)) {
|
||||||
|
item.unsetData(DataComponentTypes.GLIDER)
|
||||||
|
event.isCancelled = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user