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() {
|
||||
instance = this
|
||||
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)
|
||||
logger.info("Items plugin enabled!")
|
||||
|
||||
|
|
|
|||
|
|
@ -174,8 +174,15 @@ class GrapplingItem : SpecialItem("grappling_hook") {
|
|||
if (event.cause == org.bukkit.event.entity.EntityDamageEvent.DamageCause.FALL) {
|
||||
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
|
||||
// Check both hands for the hook
|
||||
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)
|
||||
|
||||
|
|
@ -220,6 +227,10 @@ class GrapplingItem : SpecialItem("grappling_hook") {
|
|||
}
|
||||
}
|
||||
|
||||
private fun isGrapplingHook(item: ItemStack): Boolean {
|
||||
return SpecialItem.getId(item) == "grappling_hook"
|
||||
}
|
||||
|
||||
companion object {
|
||||
val KEY_HOOK_STUCK = org.bukkit.NamespacedKey("hcu_items", "hook_stuck")
|
||||
val KEY_ANCHOR_ID = org.bukkit.NamespacedKey("hcu_items", "anchor_id")
|
||||
|
|
|
|||
|
|
@ -1,35 +1,25 @@
|
|||
package net.hareworks.hcu.items.listeners
|
||||
|
||||
import org.bukkit.event.Listener
|
||||
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.component.CustomComponent
|
||||
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
|
||||
fun onInteract(event: PlayerInteractEvent) {
|
||||
fun onInteract(event: org.bukkit.event.player.PlayerInteractEvent) {
|
||||
if (event.hand == EquipmentSlot.OFF_HAND) return
|
||||
|
||||
val item = event.item ?: return
|
||||
|
||||
// Legacy SpecialItem logic
|
||||
if (SpecialItem.isSpecialItem(item)) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
dispatchToItem(item) { it.onInteract(event) }
|
||||
dispatchToComponents(item) { it.onInteract(event) }
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
|
@ -37,11 +27,8 @@ class EventListener(private val plugin: App) : Listener {
|
|||
val player = event.player
|
||||
val item = player.inventory.itemInMainHand
|
||||
|
||||
if (SpecialItem.isSpecialItem(item)) {
|
||||
val id = SpecialItem.getId(item) ?: return
|
||||
val specialItem = ItemRegistry.get(id)
|
||||
specialItem?.onFish(event)
|
||||
}
|
||||
dispatchToItem(item) { it.onFish(event) }
|
||||
// CustomComponent interface does not define onFish, so no dispatchToComponents here.
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
|
@ -49,13 +36,12 @@ class EventListener(private val plugin: App) : Listener {
|
|||
val projectile = event.entity
|
||||
val shooter = projectile.shooter
|
||||
|
||||
if (shooter is org.bukkit.entity.Player) {
|
||||
val item = shooter.inventory.itemInMainHand
|
||||
if (SpecialItem.isSpecialItem(item)) {
|
||||
val id = SpecialItem.getId(item) ?: return
|
||||
val specialItem = ItemRegistry.get(id)
|
||||
specialItem?.onProjectileHit(event)
|
||||
}
|
||||
if (shooter is Player) {
|
||||
// For ProjectileHitEvent, SpecialItems often need to check if the projectile belongs to them,
|
||||
// which might not be the item currently held by the player.
|
||||
// Therefore, we iterate through all registered SpecialItems.
|
||||
ItemRegistry.getAll().forEach { it.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 shooter = projectile.shooter
|
||||
|
||||
if (shooter is org.bukkit.entity.Player) {
|
||||
if (shooter is Player) {
|
||||
val item = shooter.inventory.itemInMainHand
|
||||
if (SpecialItem.isSpecialItem(item)) {
|
||||
val id = SpecialItem.getId(item) ?: return
|
||||
val specialItem = ItemRegistry.get(id)
|
||||
specialItem?.onProjectileLaunch(event)
|
||||
}
|
||||
dispatchToItem(item) { it.onProjectileLaunch(event) }
|
||||
// CustomComponent interface does not define onProjectileLaunch, so no dispatchToComponents here.
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onEntityDamage(event: org.bukkit.event.entity.EntityDamageEvent) {
|
||||
val entity = event.entity
|
||||
if (entity is org.bukkit.entity.Player) {
|
||||
val item = entity.inventory.itemInMainHand
|
||||
|
||||
// Legacy Logic
|
||||
if (SpecialItem.isSpecialItem(item)) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
if (entity is Player) {
|
||||
val items = getEquipmentItems(entity)
|
||||
for (item in items) {
|
||||
dispatchToItem(item) { it.onEntityDamage(event) }
|
||||
dispatchToComponents(item) { it.onEntityDamage(event) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -103,39 +74,46 @@ class EventListener(private val plugin: App) : Listener {
|
|||
val player = event.player
|
||||
val item = player.inventory.itemInMainHand
|
||||
|
||||
// Legacy Logic
|
||||
if (SpecialItem.isSpecialItem(item)) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
dispatchToItem(item) { it.onPlayerMove(event) }
|
||||
dispatchToComponents(item) { it.onPlayerMove(event) }
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onToggleSneak(event: org.bukkit.event.player.PlayerToggleSneakEvent) {
|
||||
val player = event.player
|
||||
val equipment = player.equipment ?: return
|
||||
val items = getEquipmentItems(player)
|
||||
|
||||
val itemsToCheck = mutableListOf<org.bukkit.inventory.ItemStack>()
|
||||
itemsToCheck.addAll(equipment.armorContents.filterNotNull())
|
||||
itemsToCheck.add(equipment.itemInMainHand)
|
||||
itemsToCheck.add(equipment.itemInOffHand)
|
||||
for (item in items) {
|
||||
// SpecialItem interface does not define onToggleSneak.
|
||||
dispatchToComponents(item) { it.onToggleSneak(player, item, event) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEquipmentItems(player: Player): List<ItemStack> {
|
||||
val equipment = player.equipment ?: return emptyList()
|
||||
val items = mutableListOf<ItemStack>()
|
||||
items.addAll(equipment.armorContents.filterNotNull())
|
||||
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 (item in itemsToCheck) {
|
||||
if (item.type.isAir) continue
|
||||
for (component in net.hareworks.hcu.items.registry.ComponentRegistry.getAll()) {
|
||||
if (component.has(item)) {
|
||||
component.onToggleSneak(player, item, event)
|
||||
}
|
||||
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
|
||||
|
||||
object ComponentRegistry {
|
||||
private val components = mutableListOf<CustomComponent>()
|
||||
private val components = mutableMapOf<org.bukkit.NamespacedKey, 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> {
|
||||
return components.filterIsInstance<EquippableComponent>()
|
||||
return components.values.filterIsInstance<EquippableComponent>()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user