diff --git a/src/main/kotlin/net/hareworks/hcu/items/domain/impl/GliderItem.kt b/src/main/kotlin/net/hareworks/hcu/items/domain/impl/GliderItem.kt index 9a497ff..0954925 100644 --- a/src/main/kotlin/net/hareworks/hcu/items/domain/impl/GliderItem.kt +++ b/src/main/kotlin/net/hareworks/hcu/items/domain/impl/GliderItem.kt @@ -131,15 +131,21 @@ class GliderItem : SpecialItem("glider") { val item = event.item ?: return val tier = SpecialItem.getTier(item) - if (!canDeploy(player)) { - player.sendMessage(Component.text("グライダーを展開するには空中にいる必要があります!", NamedTextColor.RED)) + 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.sendMessage(Component.text("グライダーが壊れています!修理が必要です。", NamedTextColor.RED)) + player.sendActionBar( + Component.text("⚠ ", NamedTextColor.RED) + .append(Component.text("グライダーが壊れています!", NamedTextColor.YELLOW)) + ) + player.playSound(player.location, Sound.ENTITY_ITEM_BREAK, 0.5f, 0.8f) return } @@ -147,14 +153,47 @@ class GliderItem : SpecialItem("glider") { val isEnabled = meta.persistentDataContainer.get(KEY_GLIDER_ENABLED, PersistentDataType.BOOLEAN) ?: false if (isEnabled) { - disableGlider(player, item) - player.sendMessage(Component.text("グライダーを収納しました", NamedTextColor.YELLOW)) - } else { + player.sendActionBar( + Component.text("⬇ ", NamedTextColor.GRAY) + .append(Component.text("グライダー収納", NamedTextColor.YELLOW)) + ) + player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 0.8f, 0.8f) + player.playSound(player.location, Sound.BLOCK_WOOL_PLACE, 1.0f, 1.2f) + + player.world.spawnParticle( + Particle.CLOUD, + player.location.add(0.0, 1.5, 0.0), + 10, + 0.3, 0.3, 0.3, + 0.02 + ) + } else { enableGlider(player, item) - player.sendMessage(Component.text("グライダーを展開しました!", NamedTextColor.GREEN)) - player.playSound(player.location, Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1.0f, 1.0f) + + 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) + player.playSound(player.location, Sound.ENTITY_PHANTOM_FLAP, 0.7f, 1.5f) + + player.world.spawnParticle( + Particle.CLOUD, + player.location.add(0.0, 1.5, 0.0), + 20, + 0.5, 0.3, 0.5, + 0.05 + ) + player.world.spawnParticle( + Particle.FIREWORK, + player.location.add(0.0, 1.5, 0.0), + 5, + 0.3, 0.3, 0.3, + 0.02 + ) } event.isCancelled = true @@ -206,6 +245,10 @@ class GliderItem : SpecialItem("glider") { spawnGlidingParticles(player) } + if (state.ticksGliding % 10 == 0) { + updateGliderActionBar(player, item, tier, state) + } + val hungerInterval = TIER_HUNGER_INTERVAL[tier] ?: 80 if (state.ticksGliding % hungerInterval == 0) { @@ -218,6 +261,43 @@ class GliderItem : SpecialItem("glider") { } } + private fun updateGliderActionBar(player: Player, item: ItemStack, tier: Tier, state: GliderState) { + val meta = item.itemMeta + val currentDurability = if (meta is Damageable) { + val maxDamage = TIER_MAX_DURABILITY[tier] ?: 64 + maxDamage - meta.damage + } else { + 0 + } + val maxDurability = TIER_MAX_DURABILITY[tier] ?: 64 + val durabilityPercent = (currentDurability.toDouble() / maxDurability * 100).toInt() + + val durabilityColor = when { + durabilityPercent > 66 -> NamedTextColor.GREEN + durabilityPercent > 33 -> NamedTextColor.YELLOW + else -> NamedTextColor.RED + } + + val altitude = player.location.y.toInt() + val velocity = player.velocity + val speed = sqrt(velocity.x * velocity.x + velocity.z * velocity.z) + + player.sendActionBar( + Component.text("✈ ", NamedTextColor.AQUA) + .append(Component.text("滑空中 ", NamedTextColor.WHITE)) + .append(Component.text("| ", NamedTextColor.DARK_GRAY)) + .append(Component.text("速度: ", NamedTextColor.GRAY)) + .append(Component.text(String.format("%.1f", speed * 20), tier.color)) + .append(Component.text(" m/s ", NamedTextColor.GRAY)) + .append(Component.text("| ", NamedTextColor.DARK_GRAY)) + .append(Component.text("高度: ", NamedTextColor.GRAY)) + .append(Component.text("${altitude}m ", NamedTextColor.YELLOW)) + .append(Component.text("| ", NamedTextColor.DARK_GRAY)) + .append(Component.text("耐久: ", NamedTextColor.GRAY)) + .append(Component.text("${durabilityPercent}%", durabilityColor)) + ) + } + private fun applyGlidingPhysics(player: Player, tier: Tier) { val velocity = player.velocity val fallSpeed = TIER_FALL_SPEED[tier] ?: -0.05 @@ -255,7 +335,6 @@ class GliderItem : SpecialItem("glider") { maxOf(velocity.y, fallSpeed), horizontalDir.z * newHorizontalSpeed ) - player.sendActionBar(Component.text("Gliding at ${newHorizontalSpeed} m/s", NamedTextColor.GREEN)) player.velocity = newVelocity } @@ -290,6 +369,15 @@ class GliderItem : SpecialItem("glider") { player.velocity = Vector(player.velocity.x, UPDRAFT_BOOST, player.velocity.z) + + player.playSound(player.location, Sound.BLOCK_FIRE_AMBIENT, 0.3f, 1.5f) + + player.sendActionBar( + Component.text("🔥 ", NamedTextColor.GOLD) + .append(Component.text("上昇気流!", NamedTextColor.YELLOW)) + .append(Component.text(" ⬆", NamedTextColor.GREEN)) + ) + return true } } @@ -307,6 +395,15 @@ class GliderItem : SpecialItem("glider") { continue } player.velocity = Vector(player.velocity.x, UPDRAFT_BOOST, player.velocity.z) + + player.playSound(player.location, Sound.BLOCK_FIRE_AMBIENT, 0.3f, 1.5f) + + player.sendActionBar( + Component.text("🔥 ", NamedTextColor.GOLD) + .append(Component.text("上昇気流!", NamedTextColor.YELLOW)) + .append(Component.text(" ⬆", NamedTextColor.GREEN)) + ) + return true } } @@ -326,15 +423,40 @@ class GliderItem : SpecialItem("glider") { val currentDamage = meta.damage if (currentDamage >= maxDamage - 1) { - meta.damage = maxDamage item.itemMeta = meta disableGlider(player, item) + player.playSound(player.location, Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f) - player.sendMessage(Component.text("グライダーが壊れました!", NamedTextColor.RED)) + player.playSound(player.location, Sound.BLOCK_WOOL_BREAK, 1.0f, 0.5f) + + player.sendActionBar( + Component.text("💥 ", NamedTextColor.RED) + .append(Component.text("グライダーが壊れました!", NamedTextColor.DARK_RED)) + ) + + player.world.spawnParticle( + Particle.ITEM, + player.location.add(0.0, 1.5, 0.0), + 30, + 0.5, 0.5, 0.5, + 0.1, + ItemStack(Material.PHANTOM_MEMBRANE) + ) } else { meta.damage = currentDamage + 1 item.itemMeta = meta + + val durabilityPercent = ((maxDamage - currentDamage - 1).toDouble() / maxDamage * 100).toInt() + + if (durabilityPercent <= 20 && currentDamage % 10 == 0) { + player.sendActionBar( + Component.text("⚠ ", NamedTextColor.YELLOW) + .append(Component.text("耐久値低下! ", NamedTextColor.RED)) + .append(Component.text("残り${durabilityPercent}%", NamedTextColor.GOLD)) + ) + player.playSound(player.location, Sound.BLOCK_ANVIL_LAND, 0.3f, 2.0f) + } } } } diff --git a/src/main/kotlin/net/hareworks/hcu/items/domain/impl/GrapplingItem.kt b/src/main/kotlin/net/hareworks/hcu/items/domain/impl/GrapplingItem.kt index 4959a30..6f92bfa 100644 --- a/src/main/kotlin/net/hareworks/hcu/items/domain/impl/GrapplingItem.kt +++ b/src/main/kotlin/net/hareworks/hcu/items/domain/impl/GrapplingItem.kt @@ -15,10 +15,25 @@ class GrapplingItem : SpecialItem("grappling_hook") { val item = ItemStack(Material.FISHING_ROD) val meta = item.itemMeta ?: return item - meta.displayName(Component.text("Grappling Hook", tier.color)) + val speed = 1.0 + (tier.level * 0.4) + val efficiency = 1.0 + (tier.level * 0.1) + + val speedStars = "★".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.text("Cast and reel in to pull yourself!", NamedTextColor.GRAY), - Component.text("Tier: ${tier.level}", tier.color) + Component.empty(), + Component.text("投げて引き寄せられる!", NamedTextColor.GRAY), + Component.text("落下ダメージを軽減", NamedTextColor.GRAY), + Component.empty(), + Component.text("【性能】", NamedTextColor.WHITE), + Component.text("ティア: ${tier.name}", tier.color), + Component.text("引き寄せ速度: $speedStars", NamedTextColor.AQUA), + Component.text("燃費: $efficiencyStars", NamedTextColor.GREEN), + Component.empty(), + Component.text("右クリックで投げる", NamedTextColor.YELLOW), + Component.text("もう一度右クリックで引き寄せ", NamedTextColor.YELLOW) )) item.itemMeta = meta @@ -27,11 +42,11 @@ class GrapplingItem : SpecialItem("grappling_hook") { override fun onFish(event: PlayerFishEvent) { val hook = event.hook + val player = event.player val isStuck = hook.persistentDataContainer.has(KEY_HOOK_STUCK, org.bukkit.persistence.PersistentDataType.BYTE) if (event.state == PlayerFishEvent.State.REEL_IN && isStuck) { - val player = event.player val playerLoc = player.location val hookLoc = hook.location @@ -53,6 +68,30 @@ class GrapplingItem : SpecialItem("grappling_hook") { val hungerCost = (velocity.length() * hungerCostBase / efficiency).toInt().coerceAtLeast(1) player.foodLevel = (player.foodLevel - hungerCost).coerceAtLeast(0) + + player.playSound(playerLoc, org.bukkit.Sound.ENTITY_FISHING_BOBBER_RETRIEVE, 1.0f, 1.2f) + player.playSound(playerLoc, org.bukkit.Sound.ENTITY_ENDER_DRAGON_FLAP, 0.5f, 1.5f) + + val distance = playerLoc.distance(hookLoc) + player.sendActionBar( + Component.text("引き寄せ中! ", NamedTextColor.AQUA) + .append(Component.text("距離: ${String.format("%.1f", distance)}m", NamedTextColor.WHITE)) + .append(Component.text(" | ", NamedTextColor.DARK_GRAY)) + .append(Component.text("速度: ${String.format("%.1f", speed)}x", tier.color)) + ) + + for (i in 0..5) { + val particleLoc = playerLoc.clone().add( + vector.clone().normalize().multiply(i * distance / 5.0) + ) + player.world.spawnParticle( + org.bukkit.Particle.CRIT, + particleLoc, + 3, + 0.1, 0.1, 0.1, + 0.0 + ) + } } if (event.state == PlayerFishEvent.State.REEL_IN || @@ -71,8 +110,10 @@ class GrapplingItem : SpecialItem("grappling_hook") { val shooter = projectile.shooter if (shooter is org.bukkit.entity.Player) { - - projectile.velocity = projectile.velocity.add(shooter.velocity) + projectile.velocity = projectile.velocity.add(shooter.velocity) + + shooter.playSound(shooter.location, org.bukkit.Sound.ENTITY_FISHING_BOBBER_THROW, 1.0f, 0.8f) + shooter.playSound(shooter.location, org.bukkit.Sound.ENTITY_ARROW_SHOOT, 0.5f, 1.2f) } } @@ -91,9 +132,34 @@ class GrapplingItem : SpecialItem("grappling_hook") { stand.persistentDataContainer.set(KEY_ANCHOR_ID, org.bukkit.persistence.PersistentDataType.STRING, "grapple") } - if (anchor.addPassenger(hook)) { hook.persistentDataContainer.set(KEY_HOOK_STUCK, org.bukkit.persistence.PersistentDataType.BYTE, 1) + + location.world.playSound(location, org.bukkit.Sound.BLOCK_ANVIL_LAND, 0.5f, 2.0f) + location.world.playSound(location, org.bukkit.Sound.BLOCK_CHAIN_PLACE, 1.0f, 1.5f) + + location.world.spawnParticle( + org.bukkit.Particle.ENCHANTED_HIT, + location, + 15, + 0.2, 0.2, 0.2, + 0.1 + ) + location.world.spawnParticle( + org.bukkit.Particle.SMOKE, + location, + 5, + 0.1, 0.1, 0.1, + 0.02 + ) + + val shooter = hook.shooter + if (shooter is org.bukkit.entity.Player) { + shooter.sendActionBar( + Component.text("✓ ", NamedTextColor.GREEN) + .append(Component.text("フック固定!", NamedTextColor.WHITE)) + ) + } } else { anchor.remove() } @@ -106,6 +172,8 @@ class GrapplingItem : SpecialItem("grappling_hook") { val player = event.entity if (player is org.bukkit.entity.Player) { val item = player.inventory.itemInMainHand + if (!SpecialItem.isSpecialItem(item) || SpecialItem.getId(item) != "grappling_hook") return + val tier = SpecialItem.getTier(item) val originalDamage = event.damage @@ -119,14 +187,31 @@ class GrapplingItem : SpecialItem("grappling_hook") { if (player.foodLevel >= totalHungerCost) { player.foodLevel = (player.foodLevel - totalHungerCost).coerceAtLeast(0) event.damage = reducedDamage + + player.playSound(player.location, org.bukkit.Sound.BLOCK_WOOL_FALL, 1.0f, 0.8f) + player.playSound(player.location, org.bukkit.Sound.ENTITY_PLAYER_ATTACK_NODAMAGE, 0.5f, 1.5f) + + player.sendActionBar( + Component.text("🛡 ", NamedTextColor.GOLD) + .append(Component.text("落下ダメージ軽減! ", NamedTextColor.GREEN)) + .append(Component.text("-${String.format("%.1f", damageToAbsorb)}❤", NamedTextColor.RED)) + ) } else { val availableFood = player.foodLevel val damageWeCanAbsorb = availableFood / hungerCostPerDamage val damageWeCannotAbsorb = damageToAbsorb - damageWeCanAbsorb player.foodLevel = 0 - event.damage = reducedDamage + damageWeCannotAbsorb + + if (damageWeCanAbsorb > 0) { + player.playSound(player.location, org.bukkit.Sound.BLOCK_WOOL_FALL, 0.5f, 0.6f) + player.sendActionBar( + Component.text("⚠ ", NamedTextColor.YELLOW) + .append(Component.text("空腹不足! ", NamedTextColor.RED)) + .append(Component.text("部分軽減のみ", NamedTextColor.GRAY)) + ) + } } } }