AttachedEffectDurationResolver.kt

package io.github.lishangbu.avalon.game.battle.engine.core.runtime.action

import io.github.lishangbu.avalon.game.battle.engine.core.dsl.EffectDefinition
import io.github.lishangbu.avalon.game.battle.engine.core.event.EventContext
import io.github.lishangbu.avalon.game.battle.engine.core.runtime.support.BattleHeldItemRuntimeSupport

/**
 * 附着效果持续回合解析器。
 *
 * 设计意图:
 * - 优先尊重动作节点显式声明的 `duration`;
 * - 当动作未显式声明时,再尝试从目标 effect 定义的 `data` 中读取默认持续回合;
 * - 支持最小一档的“持有特定道具时延长持续回合”语义,避免把 5/8 之类常量继续散落在 move fixture 中。
 *
 * 当前约定的数据字段:
 * - `defaultDuration`: 默认持续回合
 * - `durationExtensionItemId`: 可延长该效果的道具 id
 * - `extendedDuration`: 持有延长道具时的持续回合
 */
object AttachedEffectDurationResolver {
    fun resolve(
        explicitDuration: Int?,
        effectId: String,
        context: EventContext,
    ): Int? {
        if (explicitDuration != null) {
            return explicitDuration
        }
        val effectDefinition = context.effectLookup?.invoke(effectId) ?: return null
        return resolveFromEffectDefinition(effectDefinition, context)
    }

    private fun resolveFromEffectDefinition(
        effectDefinition: EffectDefinition,
        context: EventContext,
    ): Int? {
        val defaultDuration = (effectDefinition.data["defaultDuration"] as? Number)?.toInt() ?: return null
        val extensionItemId = effectDefinition.data["durationExtensionItemId"]?.toString()
        val extendedDuration = (effectDefinition.data["extendedDuration"] as? Number)?.toInt()
        val field = context.field
        val holderItemId =
            if (field == null) {
                context.source?.itemId ?: context.self?.itemId
            } else {
                BattleHeldItemRuntimeSupport.activeItemId(context.source, field)
                    ?: BattleHeldItemRuntimeSupport.activeItemId(context.self, field)
            }
        return if (extensionItemId != null && extendedDuration != null && holderItemId == extensionItemId) {
            extendedDuration
        } else {
            defaultDuration
        }
    }
}