refactor: Centralize event dispatching to special items and components, enhance item hand checks, and add new content creation documentation.
This commit is contained in:
parent
d4e84fea1b
commit
2f441e334d
68
docs/creating_new_content.md
Normal file
68
docs/creating_new_content.md
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
# コンテンツの作成ガイド
|
||||||
|
|
||||||
|
このプロジェクトでは、HcuItemsプラグインを使用して新しいアイテムや機能(コンポーネント)を追加するための仕組みを提供しています。
|
||||||
|
イベント処理は`EventListener`によって統一的に管理され、アイテムのPersistentDataContainer (PDC)の情報に基づいて適切なクラスに委譲されます。
|
||||||
|
|
||||||
|
## アーキテクチャ概要
|
||||||
|
|
||||||
|
全てのイベント(右クリック、ダメージ、移動など)は `EventListener` で受信されます。
|
||||||
|
`EventListener` は以下の手順で処理を委譲します:
|
||||||
|
|
||||||
|
1. イベントに関与したアイテムを取得します。
|
||||||
|
2. アイテムのPDCを確認します。
|
||||||
|
- `SpecialItem` ID (`hcu_items:id`) がある場合 -> `ItemRegistry` からアイテム定義を取得して実行。
|
||||||
|
- コンポーネント用のキー (`hcu_items:component_id`) がある場合 -> `ComponentRegistry` からコンポーネント定義を取得して実行。
|
||||||
|
|
||||||
|
## 1. SpecialItem (独自のアイテム) の作成
|
||||||
|
|
||||||
|
完全に独自の挙動を持つアイテムを作成する場合に使用します。
|
||||||
|
|
||||||
|
### 手順
|
||||||
|
|
||||||
|
1. `SpecialItem` クラスを継承した新しいクラスを作成します。
|
||||||
|
2. コンストラクタで一意のIDを指定します。
|
||||||
|
3. `buildItem` メソッドを実装し、アイテムの見た目(Material, Name, Loreなど)を定義します。
|
||||||
|
4. 必要なイベントハンドラ(`onInteract`, `onPlayerMove` など)をオーバーライドします。
|
||||||
|
5. `App.kt` の `onEnable` で `ItemRegistry.register(YourItem())` を呼び出して登録します。
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class MyCustomItem : SpecialItem("my_custom_item") {
|
||||||
|
override fun buildItem(tier: Tier): ItemStack {
|
||||||
|
return ItemStack(Material.DIAMOND_SWORD).apply {
|
||||||
|
// Meta設定
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onInteract(event: PlayerInteractEvent) {
|
||||||
|
event.player.sendMessage("Used custom item!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. CustomComponent (既存アイテムへの機能追加) の作成
|
||||||
|
|
||||||
|
既存のアイテムや、特定の条件を満たすアイテムに共通の機能(例:グライダー、パッシブ効果)を付与する場合に使用します。
|
||||||
|
|
||||||
|
### 手順
|
||||||
|
|
||||||
|
1. `AbstractComponent` を継承し、`CustomComponent` (または `EquippableComponent`) を実装したクラスを作成します。
|
||||||
|
2. コンストラクタで一意のコンポーネントIDを指定します。
|
||||||
|
3. `apply` メソッドでアイテムにコンポーネントを適用するロジック(PDCへのキー登録は自動で行われますが、追加のメタデータが必要な場合は記述)を実装します。
|
||||||
|
4. 必要なイベントハンドラをオーバーライドします。
|
||||||
|
5. `App.kt` の `onEnable` で `ComponentRegistry.register(YourComponent(this))` を呼び出して登録します。
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class MyComponent(plugin: App) : AbstractComponent(plugin, "my_component") {
|
||||||
|
override fun onInteract(event: PlayerInteractEvent) {
|
||||||
|
event.player.sendMessage("Component interaction!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### イベントの仕組み
|
||||||
|
`AbstractComponent` は初期化時に `NamespacedKey` を生成し、`apply` 時にこのキーをアイテムのPDCに書き込みます。
|
||||||
|
`EventListener` はアイテムのPDCにこのキーが存在することを確認すると、あなたのコンポーネントのイベントハンドラを呼び出します。
|
||||||
|
|
||||||
|
## 注意事項
|
||||||
|
- `SpecialItem` と `CustomComponent` は共存可能です。1つのアイテムが `SpecialItem` であり、かつ複数の `CustomComponent` を持つことができます。
|
||||||
|
- イベントハンドラ内では、必要に応じて `event.isCancelled` などを適切に制御してください。
|
||||||
|
|
@ -26,7 +26,7 @@ public class App : JavaPlugin() {
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
instance = this
|
instance = this
|
||||||
saveDefaultConfig()
|
saveDefaultConfig()
|
||||||
server.pluginManager.registerEvents(net.hareworks.hcu.items.listeners.EventListener(this), this)
|
server.pluginManager.registerEvents(net.hareworks.hcu.items.listeners.EventListener(), this)
|
||||||
server.pluginManager.registerEvents(net.hareworks.hcu.items.listeners.ComponentListener(this), this)
|
server.pluginManager.registerEvents(net.hareworks.hcu.items.listeners.ComponentListener(this), this)
|
||||||
logger.info("Items plugin enabled!")
|
logger.info("Items plugin enabled!")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,8 +174,15 @@ class GrapplingItem : SpecialItem("grappling_hook") {
|
||||||
if (event.cause == org.bukkit.event.entity.EntityDamageEvent.DamageCause.FALL) {
|
if (event.cause == org.bukkit.event.entity.EntityDamageEvent.DamageCause.FALL) {
|
||||||
val player = event.entity
|
val player = event.entity
|
||||||
if (player is org.bukkit.entity.Player) {
|
if (player is org.bukkit.entity.Player) {
|
||||||
val item = player.inventory.itemInMainHand
|
// Check both hands for the hook
|
||||||
if (!SpecialItem.isSpecialItem(item) || SpecialItem.getId(item) != "grappling_hook") return
|
val mainHand = player.inventory.itemInMainHand
|
||||||
|
val offHand = player.inventory.itemInOffHand
|
||||||
|
|
||||||
|
val item = when {
|
||||||
|
isGrapplingHook(mainHand) -> mainHand
|
||||||
|
isGrapplingHook(offHand) -> offHand
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
|
||||||
val tier = SpecialItem.getTier(item)
|
val tier = SpecialItem.getTier(item)
|
||||||
|
|
||||||
|
|
@ -220,6 +227,10 @@ class GrapplingItem : SpecialItem("grappling_hook") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isGrapplingHook(item: ItemStack): Boolean {
|
||||||
|
return SpecialItem.getId(item) == "grappling_hook"
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val KEY_HOOK_STUCK = org.bukkit.NamespacedKey("hcu_items", "hook_stuck")
|
val KEY_HOOK_STUCK = org.bukkit.NamespacedKey("hcu_items", "hook_stuck")
|
||||||
val KEY_ANCHOR_ID = org.bukkit.NamespacedKey("hcu_items", "anchor_id")
|
val KEY_ANCHOR_ID = org.bukkit.NamespacedKey("hcu_items", "anchor_id")
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,25 @@
|
||||||
package net.hareworks.hcu.items.listeners
|
package net.hareworks.hcu.items.listeners
|
||||||
|
|
||||||
import org.bukkit.event.Listener
|
import net.hareworks.hcu.items.api.component.CustomComponent
|
||||||
import org.bukkit.event.EventHandler
|
|
||||||
import org.bukkit.event.player.PlayerInteractEvent
|
|
||||||
import org.bukkit.inventory.EquipmentSlot
|
|
||||||
|
|
||||||
import net.hareworks.hcu.items.App
|
|
||||||
import net.hareworks.hcu.items.registry.ItemRegistry
|
|
||||||
import net.hareworks.hcu.items.api.item.SpecialItem
|
import net.hareworks.hcu.items.api.item.SpecialItem
|
||||||
class EventListener(private val plugin: App) : Listener {
|
import net.hareworks.hcu.items.registry.ComponentRegistry
|
||||||
|
import net.hareworks.hcu.items.registry.ItemRegistry
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.inventory.EquipmentSlot
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
|
class EventListener : Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
fun onInteract(event: PlayerInteractEvent) {
|
fun onInteract(event: org.bukkit.event.player.PlayerInteractEvent) {
|
||||||
if (event.hand == EquipmentSlot.OFF_HAND) return
|
if (event.hand == EquipmentSlot.OFF_HAND) return
|
||||||
|
|
||||||
val item = event.item ?: return
|
val item = event.item ?: return
|
||||||
|
|
||||||
// Legacy SpecialItem logic
|
dispatchToItem(item) { it.onInteract(event) }
|
||||||
if (SpecialItem.isSpecialItem(item)) {
|
dispatchToComponents(item) { it.onInteract(event) }
|
||||||
val id = SpecialItem.getId(item) ?: return
|
|
||||||
val specialItem = ItemRegistry.get(id)
|
|
||||||
specialItem?.onInteract(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Logic
|
|
||||||
for (component in net.hareworks.hcu.items.registry.ComponentRegistry.getAll()) {
|
|
||||||
if (component.has(item)) {
|
|
||||||
component.onInteract(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
|
@ -37,11 +27,8 @@ 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
|
||||||
|
|
||||||
if (SpecialItem.isSpecialItem(item)) {
|
dispatchToItem(item) { it.onFish(event) }
|
||||||
val id = SpecialItem.getId(item) ?: return
|
// CustomComponent interface does not define onFish, so no dispatchToComponents here.
|
||||||
val specialItem = ItemRegistry.get(id)
|
|
||||||
specialItem?.onFish(event)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
|
@ -49,13 +36,12 @@ class EventListener(private val plugin: App) : Listener {
|
||||||
val projectile = event.entity
|
val projectile = event.entity
|
||||||
val shooter = projectile.shooter
|
val shooter = projectile.shooter
|
||||||
|
|
||||||
if (shooter is org.bukkit.entity.Player) {
|
if (shooter is Player) {
|
||||||
val item = shooter.inventory.itemInMainHand
|
// For ProjectileHitEvent, SpecialItems often need to check if the projectile belongs to them,
|
||||||
if (SpecialItem.isSpecialItem(item)) {
|
// which might not be the item currently held by the player.
|
||||||
val id = SpecialItem.getId(item) ?: return
|
// Therefore, we iterate through all registered SpecialItems.
|
||||||
val specialItem = ItemRegistry.get(id)
|
ItemRegistry.getAll().forEach { it.onProjectileHit(event) }
|
||||||
specialItem?.onProjectileHit(event)
|
// CustomComponent interface does not define onProjectileHit, so no dispatchToComponents here.
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,36 +50,21 @@ class EventListener(private val plugin: App) : Listener {
|
||||||
val projectile = event.entity
|
val projectile = event.entity
|
||||||
val shooter = projectile.shooter
|
val shooter = projectile.shooter
|
||||||
|
|
||||||
if (shooter is org.bukkit.entity.Player) {
|
if (shooter is Player) {
|
||||||
val item = shooter.inventory.itemInMainHand
|
val item = shooter.inventory.itemInMainHand
|
||||||
if (SpecialItem.isSpecialItem(item)) {
|
dispatchToItem(item) { it.onProjectileLaunch(event) }
|
||||||
val id = SpecialItem.getId(item) ?: return
|
// CustomComponent interface does not define onProjectileLaunch, so no dispatchToComponents here.
|
||||||
val specialItem = ItemRegistry.get(id)
|
|
||||||
specialItem?.onProjectileLaunch(event)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
fun onEntityDamage(event: org.bukkit.event.entity.EntityDamageEvent) {
|
fun onEntityDamage(event: org.bukkit.event.entity.EntityDamageEvent) {
|
||||||
val entity = event.entity
|
val entity = event.entity
|
||||||
if (entity is org.bukkit.entity.Player) {
|
if (entity is Player) {
|
||||||
val item = entity.inventory.itemInMainHand
|
val items = getEquipmentItems(entity)
|
||||||
|
for (item in items) {
|
||||||
// Legacy Logic
|
dispatchToItem(item) { it.onEntityDamage(event) }
|
||||||
if (SpecialItem.isSpecialItem(item)) {
|
dispatchToComponents(item) { it.onEntityDamage(event) }
|
||||||
val id = SpecialItem.getId(item) ?: return
|
|
||||||
val specialItem = ItemRegistry.get(id)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -103,39 +74,46 @@ 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
|
dispatchToItem(item) { it.onPlayerMove(event) }
|
||||||
if (SpecialItem.isSpecialItem(item)) {
|
dispatchToComponents(item) { it.onPlayerMove(event) }
|
||||||
val id = SpecialItem.getId(item) ?: return
|
|
||||||
val specialItem = ItemRegistry.get(id)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
fun onToggleSneak(event: org.bukkit.event.player.PlayerToggleSneakEvent) {
|
fun onToggleSneak(event: org.bukkit.event.player.PlayerToggleSneakEvent) {
|
||||||
val player = event.player
|
val player = event.player
|
||||||
val equipment = player.equipment ?: return
|
val items = getEquipmentItems(player)
|
||||||
|
|
||||||
val itemsToCheck = mutableListOf<org.bukkit.inventory.ItemStack>()
|
for (item in items) {
|
||||||
itemsToCheck.addAll(equipment.armorContents.filterNotNull())
|
// SpecialItem interface does not define onToggleSneak.
|
||||||
itemsToCheck.add(equipment.itemInMainHand)
|
dispatchToComponents(item) { it.onToggleSneak(player, item, event) }
|
||||||
itemsToCheck.add(equipment.itemInOffHand)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (item in itemsToCheck) {
|
private fun getEquipmentItems(player: Player): List<ItemStack> {
|
||||||
if (item.type.isAir) continue
|
val equipment = player.equipment ?: return emptyList()
|
||||||
for (component in net.hareworks.hcu.items.registry.ComponentRegistry.getAll()) {
|
val items = mutableListOf<ItemStack>()
|
||||||
if (component.has(item)) {
|
items.addAll(equipment.armorContents.filterNotNull())
|
||||||
component.onToggleSneak(player, item, event)
|
items.add(equipment.itemInMainHand)
|
||||||
}
|
items.add(equipment.itemInOffHand)
|
||||||
|
return items.filter { !it.type.isAir }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dispatchToItem(item: ItemStack, action: (SpecialItem) -> Unit) {
|
||||||
|
if (item.type.isAir) return
|
||||||
|
val id = SpecialItem.getId(item) ?: return
|
||||||
|
val specialItem = ItemRegistry.get(id) ?: return
|
||||||
|
action(specialItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dispatchToComponents(item: ItemStack, action: (CustomComponent) -> Unit) {
|
||||||
|
if (item.type.isAir) return
|
||||||
|
val meta = item.itemMeta ?: return
|
||||||
|
val pdc = meta.persistentDataContainer
|
||||||
|
|
||||||
|
for (key in pdc.keys) {
|
||||||
|
val component = ComponentRegistry.get(key)
|
||||||
|
if (component != null) {
|
||||||
|
action(component)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,17 @@ import net.hareworks.hcu.items.api.component.CustomComponent
|
||||||
import net.hareworks.hcu.items.api.component.EquippableComponent
|
import net.hareworks.hcu.items.api.component.EquippableComponent
|
||||||
|
|
||||||
object ComponentRegistry {
|
object ComponentRegistry {
|
||||||
private val components = mutableListOf<CustomComponent>()
|
private val components = mutableMapOf<org.bukkit.NamespacedKey, CustomComponent>()
|
||||||
|
|
||||||
fun register(component: CustomComponent) {
|
fun register(component: CustomComponent) {
|
||||||
components.add(component)
|
components[component.key] = component
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAll(): List<CustomComponent> = components.toList()
|
fun getAll(): List<CustomComponent> = components.values.toList()
|
||||||
|
|
||||||
|
fun get(key: org.bukkit.NamespacedKey): CustomComponent? = components[key]
|
||||||
|
|
||||||
fun getEquippableComponents(): List<EquippableComponent> {
|
fun getEquippableComponents(): List<EquippableComponent> {
|
||||||
return components.filterIsInstance<EquippableComponent>()
|
return components.values.filterIsInstance<EquippableComponent>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user