DefaultBattleFlowEngine.kt
package io.github.lishangbu.avalon.game.battle.engine.core.runtime.flow
import io.github.lishangbu.avalon.game.battle.engine.core.constant.BattleAttributeKeys
import io.github.lishangbu.avalon.game.battle.engine.core.constant.BattleTargetRelationValues
import io.github.lishangbu.avalon.game.battle.engine.core.event.StandardHookNames
import io.github.lishangbu.avalon.game.battle.engine.core.runtime.support.BattleMoveDataReader
import io.github.lishangbu.avalon.game.battle.engine.spi.effect.EffectDefinitionRepository
/**
* 默认 battle 主流程实现。
*
* 设计意图:
* - 在 battle 层真正消费 effect 定义,而不只停留在规则处理器单测。
* - 通过 move resolution pipeline、mutation interceptor chain 和 hit policy 拆解 battle flow 主流程。
*
* @property effectRepository effect 定义查询入口。
* @property phaseProcessor battle hook phase 处理器。
* @property moveResolutionPipeline 单次出招主流程 pipeline。
*/
class DefaultBattleFlowEngine(
private val effectRepository: EffectDefinitionRepository,
private val phaseProcessor: BattleFlowPhaseProcessor,
private val moveResolutionPipeline: BattleMoveResolutionPipeline,
) : BattleFlowEngine {
/**
* 推进一次完整的 move resolution。
*/
override fun resolveMoveAction(
snapshot: BattleRuntimeSnapshot,
moveId: String,
attackerId: String,
targetId: String,
accuracy: Int?,
evasion: Int?,
basePower: Int,
damage: Int,
attributes: Map<String, Any?>,
): MoveResolutionResult {
val moveEffect = effectRepository.get(moveId)
val resolvedAccuracy = accuracy ?: BattleMoveDataReader.readAccuracy(moveEffect.data)
val resolvedEvasion = evasion ?: DEFAULT_EVASION
val context =
BattleMoveResolutionContext(
snapshot = snapshot,
moveEffect = moveEffect,
attackerId = attackerId,
targetId = targetId,
sourceId = attackerId,
attributes = attributes.withDefaultBattleAttributes(targetRelation = BattleTargetRelationValues.FOE),
accuracy = resolvedAccuracy,
evasion = resolvedEvasion,
basePower = basePower,
damage = damage,
)
return moveResolutionPipeline.resolve(context)
}
/**
* 以只关心命中与 hook 的简化参数推进一次 move resolution。
*/
override fun resolveMoveHit(
snapshot: BattleRuntimeSnapshot,
moveId: String,
attackerId: String,
targetId: String,
attributes: Map<String, Any?>,
): BattleRuntimeSnapshot =
resolveMoveAction(
snapshot = snapshot,
moveId = moveId,
attackerId = attackerId,
targetId = targetId,
accuracy = null,
evasion = null,
basePower = 0,
damage = 0,
attributes = attributes,
).snapshot
/**
* 推进一次 residual phase。
*/
override fun resolveResidualPhase(snapshot: BattleRuntimeSnapshot): BattleRuntimeSnapshot {
var currentSnapshot = snapshot
val unitIds = currentSnapshot.units.keys.toList()
unitIds.forEach { unitId ->
currentSnapshot =
phaseProcessor
.processAttachedEffects(
snapshot = currentSnapshot,
unitId = unitId,
hookName = StandardHookNames.ON_RESIDUAL.value,
targetId = null,
sourceId = null,
relay = null,
attributes = emptyMap(),
).snapshot
}
return currentSnapshot
}
/**
* 为 battle attributes 补上默认的 targetRelation。
*/
private fun Map<String, Any?>.withDefaultBattleAttributes(targetRelation: String): Map<String, Any?> =
if (containsKey(BattleAttributeKeys.TARGET_RELATION)) {
this
} else {
this + (BattleAttributeKeys.TARGET_RELATION to targetRelation)
}
private companion object {
private const val DEFAULT_EVASION: Int = 100
}
}