feat: Migrate Glider item functionality to GliderComponent, enabling dynamic tier attributes and right-click deployment, and add min/max tier enforcement to SpecialItem creation.

This commit is contained in:
Kariya 2025-12-10 12:05:44 +00:00
parent 400d02d320
commit ee4ac4467e
8 changed files with 201 additions and 312 deletions

View File

@ -8,7 +8,6 @@ import net.hareworks.hcu.items.registry.ItemRegistry
import net.hareworks.hcu.items.registry.ComponentRegistry import net.hareworks.hcu.items.registry.ComponentRegistry
import net.hareworks.hcu.items.content.items.TestItem 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.items.GliderItem
import net.hareworks.hcu.items.content.components.GliderComponent import net.hareworks.hcu.items.content.components.GliderComponent
import org.bukkit.permissions.PermissionDefault import org.bukkit.permissions.PermissionDefault
@ -36,7 +35,6 @@ public class App : JavaPlugin() {
// Register items // Register items
ItemRegistry.register(TestItem()) ItemRegistry.register(TestItem())
ItemRegistry.register(GrapplingItem()) ItemRegistry.register(GrapplingItem())
ItemRegistry.register(GliderItem())
// Register Components // Register Components
val gliderComponent = GliderComponent(this) val gliderComponent = GliderComponent(this)

View File

@ -6,7 +6,7 @@ import net.kyori.adventure.text.format.TextColor
data class Tier(val level: Int) { data class Tier(val level: Int) {
val color: TextColor val color: TextColor
get() = when(level) { get() = when((level - 1) % 5 + 1) {
1 -> NamedTextColor.WHITE 1 -> NamedTextColor.WHITE
2 -> NamedTextColor.GREEN 2 -> NamedTextColor.GREEN
3 -> NamedTextColor.AQUA 3 -> NamedTextColor.AQUA

View File

@ -14,6 +14,9 @@ abstract class AbstractComponent(
override val key: NamespacedKey = NamespacedKey(plugin, id) override val key: NamespacedKey = NamespacedKey(plugin, id)
protected val tierKey: NamespacedKey = NamespacedKey(plugin, "${id}_tier") protected val tierKey: NamespacedKey = NamespacedKey(plugin, "${id}_tier")
override val maxTier: Int = 5
override val minTier: Int = 1
override fun apply(item: ItemStack, tier: Tier?) { override fun apply(item: ItemStack, tier: Tier?) {
val meta = item.itemMeta ?: return val meta = item.itemMeta ?: return
// Mark as present // Mark as present
@ -21,7 +24,8 @@ abstract class AbstractComponent(
// Save Tier if provided // Save Tier if provided
if (tier != null) { if (tier != null) {
meta.persistentDataContainer.set(tierKey, PersistentDataType.INTEGER, tier.level) val validTierLevel = tier.level.coerceIn(minTier, maxTier)
meta.persistentDataContainer.set(tierKey, PersistentDataType.INTEGER, validTierLevel)
} else { } else {
// Clean up if no tier? Or leave existing? // Clean up if no tier? Or leave existing?
// Ideally if apply(null) is called, we might imply "remove tier info" or "custom component without tier". // Ideally if apply(null) is called, we might imply "remove tier info" or "custom component without tier".

View File

@ -7,8 +7,14 @@ import org.bukkit.inventory.ItemStack
interface CustomComponent { interface CustomComponent {
val key: NamespacedKey val key: NamespacedKey
val displayName: String val displayName: String
val maxTier: Int
val minTier: Int
fun apply(item: ItemStack, tier: Tier? = null) fun apply(item: ItemStack, tier: Tier? = null)
fun has(item: ItemStack): Boolean fun has(item: ItemStack): Boolean
fun remove(item: ItemStack) fun remove(item: ItemStack)
fun onInteract(event: org.bukkit.event.player.PlayerInteractEvent) {}
fun onEntityDamage(event: org.bukkit.event.entity.EntityDamageEvent) {}
fun onPlayerMove(event: org.bukkit.event.player.PlayerMoveEvent) {}
} }

View File

@ -7,12 +7,19 @@ import org.bukkit.persistence.PersistentDataType
abstract class SpecialItem(val id: String) { abstract class SpecialItem(val id: String) {
open val maxTier: Int = 5
open val minTier: Int = 1
fun createItemStack(tier: Tier = Tier.ONE): ItemStack { fun createItemStack(tier: Tier = Tier.ONE): ItemStack {
val item = buildItem(tier) val validTier = if (tier.level < minTier) Tier.fromLevel(minTier)
else if (tier.level > maxTier) Tier.fromLevel(maxTier)
else tier
val item = buildItem(validTier)
val meta = item.itemMeta ?: return item val meta = item.itemMeta ?: return item
meta.persistentDataContainer.set(KEY_HCU_ITEM_ID, PersistentDataType.STRING, id) meta.persistentDataContainer.set(KEY_HCU_ITEM_ID, PersistentDataType.STRING, id)
meta.persistentDataContainer.set(KEY_HCU_ITEM_TIER, PersistentDataType.INTEGER, tier.level) meta.persistentDataContainer.set(KEY_HCU_ITEM_TIER, PersistentDataType.INTEGER, validTier.level)
item.itemMeta = meta item.itemMeta = meta
return item return item

View File

@ -14,41 +14,112 @@ import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable import org.bukkit.inventory.meta.Damageable
import org.bukkit.util.Vector import org.bukkit.util.Vector
import org.bukkit.persistence.PersistentDataType
import java.util.UUID import java.util.UUID
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.math.sqrt import kotlin.math.sqrt
import kotlin.math.pow
class GliderComponent(plugin: App) : AbstractComponent(plugin, "glider_component"), EquippableComponent { class GliderComponent(plugin: App) : AbstractComponent(plugin, "glider_component"), EquippableComponent {
override val displayName: String = "Glider" override val displayName: String = "Glider"
// Custom maxTier for Glider
override val maxTier: Int = 10
companion object { companion object {
val activeGliders = ConcurrentHashMap<UUID, GliderState>() val activeGliders = ConcurrentHashMap<UUID, GliderState>()
val KEY_GLIDER_ENABLED = org.bukkit.NamespacedKey("hcu_items", "glider_enabled")
private const val DURABILITY_TICK_INTERVAL = 100 private const val DURABILITY_TICK_INTERVAL = 100
private const val UPDRAFT_BOOST = 0.5 private const val UPDRAFT_BOOST = 0.5
private const val HUNGER_EXHAUSTION = 0.3f private const val HUNGER_EXHAUSTION = 0.3f
private val TIER_MAX_DURABILITY = mapOf(
Tier.ONE to 64, Tier.TWO to 128, Tier.THREE to 256, Tier.FOUR to 512, Tier.FIVE to 1024
)
private val TIER_FALL_SPEED = mapOf(
Tier.ONE to -0.08, Tier.TWO to -0.065, Tier.THREE to -0.05, Tier.FOUR to -0.04, Tier.FIVE to -0.03
)
private val TIER_HUNGER_INTERVAL = mapOf(
Tier.ONE to 40, Tier.TWO to 60, Tier.THREE to 80, Tier.FOUR to 120, Tier.FIVE to 200
)
private val UPDRAFT_BLOCKS = setOf( private val UPDRAFT_BLOCKS = setOf(
Material.FIRE, Material.SOUL_FIRE, Material.CAMPFIRE, Material.SOUL_CAMPFIRE, Material.MAGMA_BLOCK, Material.LAVA Material.FIRE, Material.SOUL_FIRE, Material.CAMPFIRE, Material.SOUL_CAMPFIRE, Material.MAGMA_BLOCK, Material.LAVA
) )
} }
// Dynamic Tier attributes
private fun getMaxDurability(tier: Tier): Int = (64 * 2.0.pow(tier.level - 1)).toInt()
private fun getFallSpeed(tier: Tier): Double {
// -0.08 base, getting closer to 0 by 0.015~ per level, capped at -0.01
// Simulating the map: 1->-0.08, 2->-0.065, 3->-0.05, 4->-0.04, 5->-0.03
return when (tier.level) {
1 -> -0.08
2 -> -0.065
3 -> -0.05
4 -> -0.04
5 -> -0.03
else -> (-0.03 + (tier.level - 5) * 0.005).coerceAtMost(-0.005)
}
}
private fun getHungerInterval(tier: Tier): Int = 40 + (tier.level - 1) * 20 // 1->40, 5->120... close approximation or simplier formula
data class GliderState( data class GliderState(
var ticksGliding: Int = 0, var ticksGliding: Int = 0,
var lastTickTime: Long = System.currentTimeMillis() var lastTickTime: Long = System.currentTimeMillis()
) )
override fun onInteract(event: org.bukkit.event.player.PlayerInteractEvent) {
val player = event.player
val item = event.item ?: return
// Only handle Right Click
if (event.action != org.bukkit.event.block.Action.RIGHT_CLICK_AIR && event.action != org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) return
val tier = getTier(item) ?: Tier.ONE
if (!canDeploy(player)) {
player.sendActionBar(Component.text("", NamedTextColor.RED).append(Component.text("空中にいる必要があります!", NamedTextColor.YELLOW)))
player.playSound(player.location, Sound.ENTITY_VILLAGER_NO, 0.5f, 1.0f)
return
}
if (isBroken(item)) {
player.sendActionBar(Component.text("", NamedTextColor.RED).append(Component.text("グライダーが壊れています!", NamedTextColor.YELLOW)))
player.playSound(player.location, Sound.ENTITY_ITEM_BREAK, 0.5f, 0.8f)
return
}
val isEnabled = isGliderEnabled(item)
if (isEnabled) {
disableGlider(player, item)
player.sendActionBar(Component.text("", NamedTextColor.GRAY).append(Component.text("グライダー収納", NamedTextColor.YELLOW)))
player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 0.8f, 0.8f)
} else {
enableGlider(player, item)
player.sendActionBar(Component.text("", NamedTextColor.GREEN).append(Component.text("グライダー展開!", NamedTextColor.AQUA)).append(Component.text(" [Tier ${tier.level}]", tier.color)))
player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1.0f, 1.2f)
}
// event.isCancelled = true // Maybe? GliderItem did it.
}
override fun onPlayerMove(event: org.bukkit.event.player.PlayerMoveEvent) {
val player = event.player
val item = player.inventory.itemInMainHand
// Safety check if method called correctly
if (!has(item)) return
if (!isGliderEnabled(item)) return
if (isFlyingBlocked(player)) {
disableGlider(player, item, silent = true)
return
}
}
override fun onEntityDamage(event: org.bukkit.event.entity.EntityDamageEvent) {
if (event.cause == org.bukkit.event.entity.EntityDamageEvent.DamageCause.FALL) {
val entity = event.entity
if (entity is Player && activeGliders.containsKey(entity.uniqueId)) {
event.isCancelled = true
}
}
}
override fun onTick(player: Player, item: ItemStack) { override fun onTick(player: Player, item: ItemStack) {
if (activeGliders.containsKey(player.uniqueId)) { if (activeGliders.containsKey(player.uniqueId)) {
val state = activeGliders[player.uniqueId]!! val state = activeGliders[player.uniqueId]!!
@ -56,7 +127,7 @@ class GliderComponent(plugin: App) : AbstractComponent(plugin, "glider_component
val tier = getTier(item) ?: Tier.ONE val tier = getTier(item) ?: Tier.ONE
if (isFlyingBlocked(player)) { if (isFlyingBlocked(player)) {
disableGlider(player) disableGlider(player, item, silent = true)
return return
} }
@ -73,27 +144,58 @@ class GliderComponent(plugin: App) : AbstractComponent(plugin, "glider_component
if (state.ticksGliding % 5 == 0) spawnGlidingParticles(player) if (state.ticksGliding % 5 == 0) spawnGlidingParticles(player)
if (state.ticksGliding % 10 == 0) updateGliderActionBar(player, item, tier, state) if (state.ticksGliding % 10 == 0) updateGliderActionBar(player, item, tier, state)
val hungerInterval = TIER_HUNGER_INTERVAL[tier] ?: 80 val hungerInterval = getHungerInterval(tier)
if (state.ticksGliding % hungerInterval == 0) consumeHunger(player) if (state.ticksGliding % hungerInterval == 0) consumeHunger(player)
if (state.ticksGliding % DURABILITY_TICK_INTERVAL == 0) consumeDurability(player, item, tier) if (state.ticksGliding % DURABILITY_TICK_INTERVAL == 0) consumeDurability(player, item, tier)
} else { } else {
// Activation Logic for Equippable (Sneak + Jump/Drop) // Activation Logic for Equippable (Sneak + Jump/Drop)
if (player.isSneaking && canDeploy(player)) { if (player.isSneaking && canDeploy(player)) {
enableGlider(player) enableGlider(player, item)
} }
} }
} }
fun enableGlider(player: Player) { private fun enableGlider(player: Player, item: ItemStack, silent: Boolean = false) {
val meta = item.itemMeta ?: return
meta.persistentDataContainer.set(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN, true)
item.itemMeta = meta
// Memory update
if (!activeGliders.containsKey(player.uniqueId)) { if (!activeGliders.containsKey(player.uniqueId)) {
val tier = getTier(item) ?: Tier.ONE
activeGliders[player.uniqueId] = GliderState() activeGliders[player.uniqueId] = GliderState()
player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1.0f, 1.2f)
// Visuals
if (!silent) {
player.sendActionBar(Component.text("", NamedTextColor.GREEN).append(Component.text("グライダー展開!", NamedTextColor.AQUA)).append(Component.text(" [Tier ${tier.level}]", tier.color)))
player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1.0f, 1.2f)
}
} }
} }
fun disableGlider(player: Player) { private fun disableGlider(player: Player, item: ItemStack, silent: Boolean = false) {
activeGliders.remove(player.uniqueId) val meta = item.itemMeta ?: return
meta.persistentDataContainer.set(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN, false)
item.itemMeta = meta
// Memory update
if (activeGliders.containsKey(player.uniqueId)) {
activeGliders.remove(player.uniqueId)
// Visuals
if (!silent) {
player.sendActionBar(Component.text("", NamedTextColor.GRAY).append(Component.text("グライダー収納", NamedTextColor.YELLOW)))
player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 0.8f, 0.8f)
}
}
}
// Check if item has 'enabled' state in PDC
private fun isGliderEnabled(item: ItemStack): Boolean {
if (item.type.isAir) return false
val meta = item.itemMeta ?: return false
return meta.persistentDataContainer.get(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN) ?: false
} }
private fun canDeploy(player: Player): Boolean { private fun canDeploy(player: Player): Boolean {
@ -109,6 +211,14 @@ class GliderComponent(plugin: App) : AbstractComponent(plugin, "glider_component
return isInAir || isFalling return isInAir || isFalling
} }
private fun isBroken(item: ItemStack): Boolean {
// Simple check: is it broken based on our custom max durability?
// Actually for now false is safe default if we don't strictly track custom durability item destruction
// Users might want custom durability handling -> consuming durability updates the item damage.
// If item is fully damaged (vanilla), it might break.
return false
}
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private fun isFlyingBlocked(player: Player): Boolean { private fun isFlyingBlocked(player: Player): Boolean {
return player.isOnGround || player.isInWater || player.isSwimming || (player.isGliding && !activeGliders.containsKey(player.uniqueId)) return player.isOnGround || player.isInWater || player.isSwimming || (player.isGliding && !activeGliders.containsKey(player.uniqueId))
@ -133,7 +243,7 @@ class GliderComponent(plugin: App) : AbstractComponent(plugin, "glider_component
private fun applyGlidingPhysics(player: Player, tier: Tier) { private fun applyGlidingPhysics(player: Player, tier: Tier) {
val velocity = player.velocity val velocity = player.velocity
val fallSpeed = TIER_FALL_SPEED[tier] ?: -0.05 val fallSpeed = getFallSpeed(tier)
val direction = player.location.direction val direction = player.location.direction
val horizontalDir = Vector(direction.x, 0.0, direction.z) val horizontalDir = Vector(direction.x, 0.0, direction.z)
@ -166,7 +276,27 @@ class GliderComponent(plugin: App) : AbstractComponent(plugin, "glider_component
} }
private fun updateGliderActionBar(player: Player, item: ItemStack, tier: Tier, state: GliderState) { private fun updateGliderActionBar(player: Player, item: ItemStack, tier: Tier, state: GliderState) {
player.sendActionBar(Component.text("Gliding... Speed: ${String.format("%.1f", player.velocity.length() * 20)}", NamedTextColor.AQUA)) val speed = player.velocity.length() * 20
val altitude = player.location.y
val meta = item.itemMeta
val durabilityText = if (meta is Damageable) {
val maxDurability = getMaxDurability(tier)
val currentDamage = meta.damage
val remaining = maxDurability - currentDamage
"$remaining/$maxDurability"
} else {
""
}
player.sendActionBar(
Component.text("✈ 速度: ", NamedTextColor.GRAY)
.append(Component.text(String.format("%.1f m/s", speed), NamedTextColor.AQUA))
.append(Component.text(" | 高度: ", NamedTextColor.DARK_GRAY))
.append(Component.text(String.format("%.0f", altitude), NamedTextColor.GREEN))
.append(Component.text(" | 耐久: ", NamedTextColor.DARK_GRAY))
.append(Component.text(durabilityText, NamedTextColor.WHITE))
)
} }
private fun consumeHunger(player: Player) { private fun consumeHunger(player: Player) {
@ -176,11 +306,11 @@ class GliderComponent(plugin: App) : AbstractComponent(plugin, "glider_component
private fun consumeDurability(player: Player, item: ItemStack, tier: Tier) { private fun consumeDurability(player: Player, item: ItemStack, tier: Tier) {
val meta = item.itemMeta ?: return val meta = item.itemMeta ?: return
if (meta is Damageable) { if (meta is Damageable) {
val maxDamage = TIER_MAX_DURABILITY[tier] ?: 64 val maxDamage = getMaxDurability(tier)
val currentDamage = meta.damage val currentDamage = meta.damage
if (currentDamage >= maxDamage - 1) { if (currentDamage >= maxDamage - 1) {
meta.damage = maxDamage meta.damage = maxDamage
disableGlider(player) disableGlider(player, item) // Use item aware disable
player.playSound(player.location, Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f) player.playSound(player.location, Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f)
} else { } else {
meta.damage = currentDamage + 1 meta.damage = currentDamage + 1

View File

@ -1,267 +0,0 @@
package net.hareworks.hcu.items.content.items
import net.hareworks.hcu.items.api.Tier
import net.hareworks.hcu.items.api.item.SpecialItem
import net.hareworks.hcu.items.content.components.GliderComponent
import net.hareworks.hcu.items.registry.ComponentRegistry
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.Particle
import org.bukkit.Sound
import org.bukkit.block.data.Lightable
import org.bukkit.entity.Player
import org.bukkit.event.entity.EntityDamageEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import org.bukkit.persistence.PersistentDataType
import org.bukkit.util.Vector
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.math.sqrt
// NOTE: This class logic is partially duplicated with GliderComponent during migration.
// Ideally, this item should just be a shell that applies the GliderComponent.
class GliderItem : SpecialItem("glider") {
companion object {
val activeGliders = ConcurrentHashMap<UUID, GliderState>()
val KEY_GLIDER_ENABLED = NamespacedKey("hcu_items", "glider_enabled")
private const val DURABILITY_TICK_INTERVAL = 100
private const val UPDRAFT_BOOST = 0.5
private val TIER_MAX_DURABILITY = mapOf(
Tier.ONE to 64, Tier.TWO to 128, Tier.THREE to 256, Tier.FOUR to 512, Tier.FIVE to 1024
)
private val TIER_FALL_SPEED = mapOf(
Tier.ONE to -0.08, Tier.TWO to -0.065, Tier.THREE to -0.05, Tier.FOUR to -0.04, Tier.FIVE to -0.03
)
private val TIER_HUNGER_INTERVAL = mapOf(
Tier.ONE to 40, Tier.TWO to 60, Tier.THREE to 80, Tier.FOUR to 120, Tier.FIVE to 200
)
private const val HUNGER_EXHAUSTION = 0.3f
private val UPDRAFT_BLOCKS = setOf(
Material.FIRE, Material.SOUL_FIRE, Material.CAMPFIRE, Material.SOUL_CAMPFIRE, Material.MAGMA_BLOCK, Material.LAVA
)
}
data class GliderState(
var ticksGliding: Int = 0,
var lastTickTime: Long = System.currentTimeMillis()
)
override fun buildItem(tier: Tier): ItemStack {
val item = ItemStack(Material.PHANTOM_MEMBRANE)
val meta = item.itemMeta ?: return item
val maxDurability = TIER_MAX_DURABILITY[tier] ?: 64
val glideStars = "".repeat(tier.level) + "".repeat(5 - tier.level)
val efficiencyStars = "".repeat(tier.level) + "".repeat(5 - tier.level)
meta.displayName(Component.text("グライダー", tier.color))
meta.lore(listOf(
Component.empty(),
Component.text("空中で右クリックで展開", NamedTextColor.GRAY),
Component.text("地上や水中で自動収納", NamedTextColor.GRAY),
Component.empty(),
Component.text("【性能】", NamedTextColor.WHITE),
Component.text("ティア: ${tier.name}", tier.color),
Component.text("滞空力: $glideStars", NamedTextColor.AQUA),
Component.text("燃費: $efficiencyStars", NamedTextColor.GREEN),
Component.text("耐久値: $maxDurability", NamedTextColor.DARK_GRAY)
))
meta.persistentDataContainer.set(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN, false)
item.itemMeta = meta
// Apply new GliderComponent
val gliderComponent = ComponentRegistry.getAll()
.find { it is GliderComponent }
gliderComponent?.apply(item, tier) // Passing tier
return item
}
// ... Keeping Legacy Logic for compatibility until full migration ...
// (Omitted methods implementation detail for brevity - Assuming legacy system still runs side-by-side or this file is fully kept as is but moved)
// IMPORTANT: I will reproduce the full content to ensure it works.
override fun onInteract(event: PlayerInteractEvent) {
val player = event.player
val item = event.item ?: return
val tier = SpecialItem.getTier(item)
if (!canDeploy(player)) {
player.sendActionBar(Component.text("", NamedTextColor.RED).append(Component.text("空中にいる必要があります!", NamedTextColor.YELLOW)))
player.playSound(player.location, Sound.ENTITY_VILLAGER_NO, 0.5f, 1.0f)
return
}
if (isBroken(item)) {
player.sendActionBar(Component.text("", NamedTextColor.RED).append(Component.text("グライダーが壊れています!", NamedTextColor.YELLOW)))
player.playSound(player.location, Sound.ENTITY_ITEM_BREAK, 0.5f, 0.8f)
return
}
val meta = item.itemMeta ?: return
val isEnabled = meta.persistentDataContainer.get(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN) ?: false
if (isEnabled) {
disableGlider(player, item)
player.sendActionBar(Component.text("", NamedTextColor.GRAY).append(Component.text("グライダー収納", NamedTextColor.YELLOW)))
player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 0.8f, 0.8f)
} else {
enableGlider(player, item)
player.sendActionBar(Component.text("", NamedTextColor.GREEN).append(Component.text("グライダー展開!", NamedTextColor.AQUA)).append(Component.text(" [Tier ${tier.level}]", tier.color)))
player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1.0f, 1.2f)
}
event.isCancelled = true
}
override fun onPlayerMove(event: PlayerMoveEvent) {
val player = event.player
val item = player.inventory.itemInMainHand
if (!isGliderEnabled(item)) return
if (isFlyingBlocked(player)) {
disableGlider(player, item)
return
}
}
fun tickGlider(player: Player) {
val item = player.inventory.itemInMainHand
if (!SpecialItem.isSpecialItem(item) || SpecialItem.getId(item) != "glider") return
if (!isGliderEnabled(item)) return
val tier = SpecialItem.getTier(item)
val state = activeGliders.getOrPut(player.uniqueId) { GliderState() }
if (isFlyingBlocked(player)) {
disableGlider(player, item)
return
}
state.ticksGliding++
player.fallDistance = 0f
if (checkAndApplyUpdraft(player)) {
spawnUpdraftParticles(player)
return
}
applyGlidingPhysics(player, tier)
if (state.ticksGliding % 5 == 0) spawnGlidingParticles(player)
if (state.ticksGliding % 10 == 0) updateGliderActionBar(player, item, tier, state)
val hungerInterval = TIER_HUNGER_INTERVAL[tier] ?: 80
if (state.ticksGliding % hungerInterval == 0) consumeHunger(player)
if (state.ticksGliding % DURABILITY_TICK_INTERVAL == 0) consumeDurability(player, item, tier)
}
private fun updateGliderActionBar(player: Player, item: ItemStack, tier: Tier, state: GliderState) {
// Reduced for brevity but functionally same
player.sendActionBar(Component.text("Gliding...", NamedTextColor.AQUA))
}
private fun applyGlidingPhysics(player: Player, tier: Tier) {
val velocity = player.velocity
val fallSpeed = TIER_FALL_SPEED[tier] ?: -0.05
val direction = player.location.direction
val horizontalDir = Vector(direction.x, 0.0, direction.z)
if (horizontalDir.lengthSquared() > 0) horizontalDir.normalize()
val currentHorizontalSpeed = sqrt(velocity.x * velocity.x + velocity.z * velocity.z)
val baseGlideSpeed = 0.4
val tierBonus = (tier.level - 1) * 0.05
val targetSpeed = baseGlideSpeed + tierBonus
val newHorizontalSpeed = if (currentHorizontalSpeed < targetSpeed) minOf(currentHorizontalSpeed + 0.1, targetSpeed) else maxOf(currentHorizontalSpeed * 0.95, targetSpeed)
val newVelocity = Vector(horizontalDir.x * newHorizontalSpeed, maxOf(velocity.y, fallSpeed), horizontalDir.z * newHorizontalSpeed)
player.velocity = newVelocity
}
private fun consumeHunger(player: Player) { player.exhaustion = player.exhaustion + HUNGER_EXHAUSTION }
private fun checkAndApplyUpdraft(player: Player): Boolean {
// Simplified logic
return false
}
private fun consumeDurability(player: Player, item: ItemStack, tier: Tier) {
val meta = item.itemMeta ?: return
if (meta is Damageable) {
val maxDamage = TIER_MAX_DURABILITY[tier] ?: 64
val currentDamage = meta.damage
if (currentDamage >= maxDamage - 1) {
meta.damage = maxDamage
item.itemMeta = meta
disableGlider(player, item)
} else {
meta.damage = currentDamage + 1
item.itemMeta = meta
}
}
}
private fun enableGlider(player: Player, item: ItemStack) {
val meta = item.itemMeta ?: return
meta.persistentDataContainer.set(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN, true)
item.itemMeta = meta
activeGliders[player.uniqueId] = GliderState()
}
private fun disableGlider(player: Player, item: ItemStack) {
val meta = item.itemMeta ?: return
meta.persistentDataContainer.set(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN, false)
item.itemMeta = meta
activeGliders.remove(player.uniqueId)
}
private fun isGliderEnabled(item: ItemStack?): Boolean {
if (item == null || item.type.isAir) return false
val meta = item.itemMeta ?: return false
return meta.persistentDataContainer.get(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN) ?: false
}
private fun isBroken(item: ItemStack): Boolean {
// Simplified Logic
return false
}
private fun canDeploy(player: Player): Boolean {
// Simplified Logic
return !player.isOnGround
}
private fun checkNearUpdraft(player: Player): Boolean = false
@Suppress("DEPRECATION")
private fun isFlyingBlocked(player: Player): Boolean {
return player.isOnGround || player.isInWater || player.isSwimming || player.isGliding
}
private fun spawnGlidingParticles(player: Player) {
player.world.spawnParticle(Particle.CLOUD, player.location.add(0.0, 2.0, 0.0), 1, 0.3, 0.0, 0.3, 0.01)
}
private fun spawnUpdraftParticles(player: Player) {}
override fun onEntityDamage(event: EntityDamageEvent) {
if (event.cause == EntityDamageEvent.DamageCause.FALL) {
val entity = event.entity
if (entity is Player && activeGliders.containsKey(entity.uniqueId)) {
event.isCancelled = true
}
}
}
}

View File

@ -8,24 +8,8 @@ import org.bukkit.inventory.EquipmentSlot
import net.hareworks.hcu.items.App import net.hareworks.hcu.items.App
import net.hareworks.hcu.items.registry.ItemRegistry import net.hareworks.hcu.items.registry.ItemRegistry
import net.hareworks.hcu.items.api.item.SpecialItem import net.hareworks.hcu.items.api.item.SpecialItem
import net.hareworks.hcu.items.content.items.GliderItem
class EventListener(private val plugin: App) : Listener { class EventListener(private val plugin: App) : Listener {
init {
plugin.server.scheduler.runTaskTimer(plugin, Runnable {
tickAllGliders()
}, 1L, 1L)
}
private fun tickAllGliders() {
val gliderItem = ItemRegistry.get("glider") as? GliderItem ?: return
for ((uuid, _) in GliderItem.activeGliders) {
val player = plugin.server.getPlayer(uuid) ?: continue
gliderItem.tickGlider(player)
}
}
@EventHandler @EventHandler
fun onInteract(event: PlayerInteractEvent) { fun onInteract(event: PlayerInteractEvent) {
@ -33,12 +17,19 @@ class EventListener(private val plugin: App) : Listener {
val item = event.item ?: return val item = event.item ?: return
// Legacy SpecialItem logic
if (SpecialItem.isSpecialItem(item)) { if (SpecialItem.isSpecialItem(item)) {
val id = SpecialItem.getId(item) ?: return val id = SpecialItem.getId(item) ?: return
val specialItem = ItemRegistry.get(id) val specialItem = ItemRegistry.get(id)
specialItem?.onInteract(event) specialItem?.onInteract(event)
} }
// Component Logic
for (component in net.hareworks.hcu.items.registry.ComponentRegistry.getAll()) {
if (component.has(item)) {
component.onInteract(event)
}
}
} }
@EventHandler @EventHandler
@ -49,7 +40,6 @@ class EventListener(private val plugin: App) : Listener {
if (SpecialItem.isSpecialItem(item)) { if (SpecialItem.isSpecialItem(item)) {
val id = SpecialItem.getId(item) ?: return val id = SpecialItem.getId(item) ?: return
val specialItem = ItemRegistry.get(id) val specialItem = ItemRegistry.get(id)
specialItem?.onFish(event) specialItem?.onFish(event)
} }
} }
@ -89,11 +79,22 @@ class EventListener(private val plugin: App) : Listener {
val entity = event.entity val entity = event.entity
if (entity is org.bukkit.entity.Player) { if (entity is org.bukkit.entity.Player) {
val item = entity.inventory.itemInMainHand val item = entity.inventory.itemInMainHand
// Legacy Logic
if (SpecialItem.isSpecialItem(item)) { if (SpecialItem.isSpecialItem(item)) {
val id = SpecialItem.getId(item) ?: return val id = SpecialItem.getId(item) ?: return
val specialItem = ItemRegistry.get(id) val specialItem = ItemRegistry.get(id)
specialItem?.onEntityDamage(event) specialItem?.onEntityDamage(event)
} }
// Component Logic
if (!item.type.isAir) {
for (component in net.hareworks.hcu.items.registry.ComponentRegistry.getAll()) {
if (component.has(item)) {
component.onEntityDamage(event)
}
}
}
} }
} }
@ -102,10 +103,20 @@ class EventListener(private val plugin: App) : Listener {
val player = event.player val player = event.player
val item = player.inventory.itemInMainHand val item = player.inventory.itemInMainHand
// Legacy Logic
if (SpecialItem.isSpecialItem(item)) { if (SpecialItem.isSpecialItem(item)) {
val id = SpecialItem.getId(item) ?: return val id = SpecialItem.getId(item) ?: return
val specialItem = ItemRegistry.get(id) val specialItem = ItemRegistry.get(id)
specialItem?.onPlayerMove(event) specialItem?.onPlayerMove(event)
} }
// Component Logic
if (!item.type.isAir) {
for (component in net.hareworks.hcu.items.registry.ComponentRegistry.getAll()) {
if (component.has(item)) {
component.onPlayerMove(event)
}
}
}
} }
} }