refactor: Extract common item scroll adjustment logic into ItemActionUtil and manage scroll states for items.
This commit is contained in:
parent
3b62b26273
commit
d4c8cd84fb
|
|
@ -18,6 +18,7 @@ import net.hareworks.hcu.items.content.components.AreaTillerComponent
|
||||||
|
|
||||||
import org.bukkit.permissions.PermissionDefault
|
import org.bukkit.permissions.PermissionDefault
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import net.hareworks.hcu.items.util.ItemActionUtil
|
||||||
|
|
||||||
public class App : JavaPlugin() {
|
public class App : JavaPlugin() {
|
||||||
|
|
||||||
|
|
@ -53,4 +54,9 @@ public class App : JavaPlugin() {
|
||||||
|
|
||||||
server.pluginManager.registerEvents(net.hareworks.hcu.items.listeners.EventListener(this), this)
|
server.pluginManager.registerEvents(net.hareworks.hcu.items.listeners.EventListener(this), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDisable() {
|
||||||
|
ItemActionUtil.clearAllScrollStates()
|
||||||
|
logger.info("Items plugin disabled!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import java.util.UUID
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import net.hareworks.hcu.items.util.ItemActionUtil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AreaTillerComponent - 広範囲一括耕地化コンポーネント
|
* AreaTillerComponent - 広範囲一括耕地化コンポーネント
|
||||||
|
|
@ -302,39 +303,27 @@ class AreaTillerComponent(private val plugin: JavaPlugin) : ToolComponent {
|
||||||
*/
|
*/
|
||||||
private fun handleItemHeld(event: PlayerItemHeldEvent, item: ItemStack) {
|
private fun handleItemHeld(event: PlayerItemHeldEvent, item: ItemStack) {
|
||||||
val player = event.player
|
val player = event.player
|
||||||
|
val session = editSessions.getOrPut(player.uniqueId) { EditSession() }
|
||||||
// スニーク中のみ調整
|
|
||||||
if (!player.isSneaking) return
|
if (!player.isSneaking) return
|
||||||
|
|
||||||
val session = editSessions.getOrPut(player.uniqueId) { EditSession() }
|
ItemActionUtil.handleScrollAdjustment(event) { delta ->
|
||||||
val tier = getTier(item)
|
val tier = getTier(item)
|
||||||
val maxRange = getMaxRangeForTier(tier)
|
val maxRange = getMaxRangeForTier(tier)
|
||||||
|
|
||||||
val now = System.currentTimeMillis()
|
// プレイヤーの向きから、どの方角の範囲を伸長するか決定
|
||||||
if (now - session.lastScrollTime < 50) return
|
val facing = getDirectionFacing(player.location.yaw)
|
||||||
|
|
||||||
session.lastScrollTime = now
|
when (facing) {
|
||||||
|
BlockFace.NORTH -> session.north = (session.north + delta).coerceIn(0, maxRange)
|
||||||
|
BlockFace.SOUTH -> session.south = (session.south + delta).coerceIn(0, maxRange)
|
||||||
|
BlockFace.EAST -> session.east = (session.east + delta).coerceIn(0, maxRange)
|
||||||
|
BlockFace.WEST -> session.west = (session.west + delta).coerceIn(0, maxRange)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
// スクロール方向を判定
|
player.playSound(player.location, Sound.UI_BUTTON_CLICK, 0.3f, 1.2f)
|
||||||
val diff = event.newSlot - event.previousSlot
|
|
||||||
val scrollUp = (diff == -1 || diff == 8)
|
|
||||||
val delta = if (scrollUp) 1 else -1
|
|
||||||
|
|
||||||
// プレイヤーの向きから、どの方角の範囲を伸長するか決定
|
|
||||||
val facing = getDirectionFacing(player.location.yaw)
|
|
||||||
|
|
||||||
when (facing) {
|
|
||||||
BlockFace.NORTH -> session.north = (session.north + delta).coerceIn(0, maxRange)
|
|
||||||
BlockFace.SOUTH -> session.south = (session.south + delta).coerceIn(0, maxRange)
|
|
||||||
BlockFace.EAST -> session.east = (session.east + delta).coerceIn(0, maxRange)
|
|
||||||
BlockFace.WEST -> session.west = (session.west + delta).coerceIn(0, maxRange)
|
|
||||||
else -> {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
player.playSound(player.location, Sound.UI_BUTTON_CLICK, 0.3f, 1.2f)
|
|
||||||
|
|
||||||
// イベントをキャンセルしてスロット切り替えを防ぐ
|
|
||||||
event.isCancelled = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import org.bukkit.entity.Item
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.inventory.ItemStack
|
import org.bukkit.inventory.ItemStack
|
||||||
import org.bukkit.persistence.PersistentDataType
|
import org.bukkit.persistence.PersistentDataType
|
||||||
|
import net.hareworks.hcu.items.util.ItemActionUtil
|
||||||
|
|
||||||
class MagnetItem : AbstractItem("magnet_item") {
|
class MagnetItem : AbstractItem("magnet_item") {
|
||||||
|
|
||||||
|
|
@ -109,42 +110,37 @@ class MagnetItem : AbstractItem("magnet_item") {
|
||||||
val item = player.inventory.getItem(event.previousSlot) ?: return
|
val item = player.inventory.getItem(event.previousSlot) ?: return
|
||||||
|
|
||||||
if (!Config.magnet.allowScrollChange) return
|
if (!Config.magnet.allowScrollChange) return
|
||||||
|
|
||||||
if (!player.isSneaking) return
|
if (!player.isSneaking) return
|
||||||
|
|
||||||
// 動いていない時のみ調整可能
|
|
||||||
if (player.velocity.length() > 0.08) return // 完全な0は難しいため閾値を設定
|
|
||||||
|
|
||||||
val configInfo = Config.magnet
|
ItemActionUtil.handleScrollAdjustment(event) { delta ->
|
||||||
val tier = getTier(item)
|
val configInfo = Config.magnet
|
||||||
val maxRadius = configInfo.radiusBase + (tier.level * configInfo.radiusPerTier)
|
val tier = getTier(item)
|
||||||
|
val maxRadius = configInfo.radiusBase + (tier.level * configInfo.radiusPerTier)
|
||||||
val diff = event.newSlot - event.previousSlot
|
|
||||||
val scrollUp = (diff == -1 || diff == 8)
|
|
||||||
val change = if (scrollUp) 1.0 else -1.0
|
|
||||||
|
|
||||||
// Get current radius
|
|
||||||
var currentRadius = item.itemMeta?.persistentDataContainer?.get(KEY_RADIUS, PersistentDataType.DOUBLE) ?: maxRadius
|
|
||||||
|
|
||||||
currentRadius += change
|
|
||||||
currentRadius = currentRadius.coerceIn(1.0, maxRadius)
|
|
||||||
|
|
||||||
// Save to PDC
|
|
||||||
val meta = item.itemMeta
|
|
||||||
meta.persistentDataContainer.set(KEY_RADIUS, PersistentDataType.DOUBLE, currentRadius)
|
|
||||||
item.itemMeta = meta
|
|
||||||
|
|
||||||
// Feedback
|
|
||||||
player.sendActionBar(Component.text("回収範囲: ", NamedTextColor.AQUA)
|
|
||||||
.append(Component.text(String.format("%.1f", currentRadius) + "m", NamedTextColor.YELLOW))
|
|
||||||
.append(Component.text(" | ", NamedTextColor.DARK_GRAY))
|
|
||||||
.append(Component.text("最大: ", NamedTextColor.GRAY))
|
|
||||||
.append(Component.text(String.format("%.1f", maxRadius) + "m", NamedTextColor.WHITE)))
|
|
||||||
|
|
||||||
// 視覚的なフィードバック (BlockDisplayによるリング表示)
|
// Get current radius
|
||||||
updateRadiusRing(player, currentRadius, tier)
|
var currentRadius = item.itemMeta?.persistentDataContainer?.get(KEY_RADIUS, PersistentDataType.DOUBLE) ?: maxRadius
|
||||||
|
|
||||||
|
currentRadius += delta.toDouble()
|
||||||
|
currentRadius = currentRadius.coerceIn(1.0, maxRadius)
|
||||||
|
|
||||||
|
// Save to PDC
|
||||||
|
val meta = item.itemMeta
|
||||||
|
meta.persistentDataContainer.set(KEY_RADIUS, PersistentDataType.DOUBLE, currentRadius)
|
||||||
|
item.itemMeta = meta
|
||||||
|
|
||||||
|
// Feedback
|
||||||
|
player.sendActionBar(Component.text("回収範囲: ", NamedTextColor.AQUA)
|
||||||
|
.append(Component.text(String.format("%.1f", currentRadius) + "m", NamedTextColor.YELLOW))
|
||||||
|
.append(Component.text(" | ", NamedTextColor.DARK_GRAY))
|
||||||
|
.append(Component.text("最大: ", NamedTextColor.GRAY))
|
||||||
|
.append(Component.text(String.format("%.1f", maxRadius) + "m", NamedTextColor.WHITE)))
|
||||||
|
|
||||||
|
// 視覚的なフィードバック (BlockDisplayによるリング表示)
|
||||||
|
updateRadiusRing(player, currentRadius, tier)
|
||||||
|
|
||||||
// Cancel event to prevent slot switch
|
player.playSound(player.location, Sound.UI_BUTTON_CLICK, 0.3f, 1.2f)
|
||||||
event.isCancelled = true
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val activeRings = java.util.concurrent.ConcurrentHashMap<java.util.UUID, RingSession>()
|
private val activeRings = java.util.concurrent.ConcurrentHashMap<java.util.UUID, RingSession>()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ import org.bukkit.event.entity.ProjectileHitEvent
|
||||||
import org.bukkit.event.entity.EntityExplodeEvent
|
import org.bukkit.event.entity.EntityExplodeEvent
|
||||||
import org.bukkit.event.entity.EntityInteractEvent
|
import org.bukkit.event.entity.EntityInteractEvent
|
||||||
import org.bukkit.event.block.*
|
import org.bukkit.event.block.*
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent
|
||||||
import org.bukkit.inventory.ItemStack
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import net.hareworks.hcu.items.util.ItemActionUtil
|
||||||
import org.bukkit.plugin.EventExecutor
|
import org.bukkit.plugin.EventExecutor
|
||||||
import org.bukkit.plugin.Plugin
|
import org.bukkit.plugin.Plugin
|
||||||
import org.bukkit.event.EventPriority
|
import org.bukkit.event.EventPriority
|
||||||
|
|
@ -120,6 +122,11 @@ class EventListener(private val plugin: Plugin) : Listener {
|
||||||
dispatchGlobalToComponents(event)
|
dispatchGlobalToComponents(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onPlayerQuit(event: PlayerQuitEvent) {
|
||||||
|
ItemActionUtil.clearScrollState(event.player)
|
||||||
|
}
|
||||||
|
|
||||||
private fun <T : Event> dispatchGlobalToComponents(event: T) {
|
private fun <T : Event> dispatchGlobalToComponents(event: T) {
|
||||||
val eventClass = event.javaClass.kotlin
|
val eventClass = event.javaClass.kotlin
|
||||||
ComponentRegistry.getAll().forEach { component ->
|
ComponentRegistry.getAll().forEach { component ->
|
||||||
|
|
|
||||||
107
src/main/kotlin/net/hareworks/hcu/items/util/ItemActionUtil.kt
Normal file
107
src/main/kotlin/net/hareworks/hcu/items/util/ItemActionUtil.kt
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
package net.hareworks.hcu.items.util
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.player.PlayerItemHeldEvent
|
||||||
|
import java.util.UUID
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アイテム操作に関する共通ユーティリティ
|
||||||
|
*/
|
||||||
|
object ItemActionUtil {
|
||||||
|
// プレイヤーごとのスクロール状態を管理
|
||||||
|
private data class ScrollState(
|
||||||
|
var lastScrollTime: Long = 0L,
|
||||||
|
var accumulatedDelta: Double = 0.0,
|
||||||
|
var lastProcessedTime: Long = 0L
|
||||||
|
)
|
||||||
|
|
||||||
|
private val scrollStates = ConcurrentHashMap<UUID, ScrollState>()
|
||||||
|
|
||||||
|
// 設定値
|
||||||
|
private const val SCROLL_COOLDOWN_MS = 50L // スクロール間隔(ミリ秒)
|
||||||
|
private const val ACCELERATION_THRESHOLD = 150L // 加速判定の時間閾値
|
||||||
|
private const val DELTA_THRESHOLD = 0.8 // 累積値の閾値
|
||||||
|
private const val MAX_ACCUMULATED_DELTA = 5.0 // 最大累積値
|
||||||
|
|
||||||
|
/**
|
||||||
|
* マウスホイール(スロット変更)による設定値の調整ロジックを処理します。
|
||||||
|
* スクロールの速度に応じて滑らかに、かつ連続スクロール時には加速して調整します。
|
||||||
|
*
|
||||||
|
* @param event スロット変更イベント
|
||||||
|
* @param onAdjust 調整が有効な場合に呼び出されるコールバック (delta: Int)
|
||||||
|
*/
|
||||||
|
fun handleScrollAdjustment(event: PlayerItemHeldEvent, onAdjust: (Int) -> Unit) {
|
||||||
|
val player = event.player
|
||||||
|
|
||||||
|
// 2. プレイヤーがほぼ静止状態のみ有効
|
||||||
|
if (player.velocity.length() > 0.08) return
|
||||||
|
|
||||||
|
// 3. スクロール方向を判定
|
||||||
|
val diff = event.newSlot - event.previousSlot
|
||||||
|
val scrollUp = (diff == -1 || diff == 8)
|
||||||
|
val rawDelta = if (scrollUp) 1.0 else -1.0
|
||||||
|
|
||||||
|
// 4. 滑らかなスクロール処理
|
||||||
|
val state = scrollStates.computeIfAbsent(player.uniqueId) { ScrollState() }
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
val timeSinceLastScroll = currentTime - state.lastScrollTime
|
||||||
|
|
||||||
|
// クールダウンチェック(連続スクロールの検出)
|
||||||
|
if (timeSinceLastScroll < SCROLL_COOLDOWN_MS) {
|
||||||
|
event.isCancelled = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加速度の計算(素早くスクロールすると累積値が増える)
|
||||||
|
val accelerationFactor = if (timeSinceLastScroll < ACCELERATION_THRESHOLD) {
|
||||||
|
1.5 // 素早いスクロールで加速
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 累積デルタ値の更新
|
||||||
|
state.accumulatedDelta += rawDelta * accelerationFactor
|
||||||
|
state.lastScrollTime = currentTime
|
||||||
|
|
||||||
|
// 累積値の制限
|
||||||
|
state.accumulatedDelta = state.accumulatedDelta.coerceIn(
|
||||||
|
-MAX_ACCUMULATED_DELTA,
|
||||||
|
MAX_ACCUMULATED_DELTA
|
||||||
|
)
|
||||||
|
|
||||||
|
// 閾値を超えたら実際に値を変更
|
||||||
|
val timeSinceLastProcess = currentTime - state.lastProcessedTime
|
||||||
|
if (kotlin.math.abs(state.accumulatedDelta) >= DELTA_THRESHOLD ||
|
||||||
|
timeSinceLastProcess > 200L) { // または一定時間経過
|
||||||
|
|
||||||
|
val deltaToApply = state.accumulatedDelta.toInt()
|
||||||
|
if (deltaToApply != 0) {
|
||||||
|
onAdjust(deltaToApply)
|
||||||
|
state.lastProcessedTime = currentTime
|
||||||
|
state.accumulatedDelta = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. イベントをキャンセルしてスロット切り替えを防ぐ
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* プレイヤーのスクロール状態をクリアします。
|
||||||
|
* プレイヤーがログアウトした際などに呼び出してください。
|
||||||
|
*
|
||||||
|
* @param player 対象プレイヤー
|
||||||
|
*/
|
||||||
|
fun clearScrollState(player: Player) {
|
||||||
|
scrollStates.remove(player.uniqueId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全てのスクロール状態をクリアします。
|
||||||
|
* プラグインの無効化時などに呼び出してください。
|
||||||
|
*/
|
||||||
|
fun clearAllScrollStates() {
|
||||||
|
scrollStates.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user