BattleSessionActionSpeedResolver.kt
package io.github.lishangbu.avalon.game.battle.engine.core.session
import io.github.lishangbu.avalon.game.battle.engine.core.constant.BattleFieldConditionIds
import io.github.lishangbu.avalon.game.battle.engine.core.runtime.flow.BattleRuntimeSnapshot
/**
* battle session 行动速度解析器。
*
* 设计意图:
* - 把“动作原始 speed 字段”提升为可读取当前快照的有效速度解析;
* - 让回合内排序能够感知 side condition 等运行时规则,而不把这些规则散落进排序器本体;
* - 为后续继续接入更完整的速度修正体系保留单一扩展点。
*
* 当前已接入:
* - `tailwind`:将所属 side 的有效速度翻倍;
* - `trick-room-field`:当 field 上挂有该 room 标记时,反转速度排序方向;
* - 当 action 自身未显式传入 speed 时,会从提交单位或所属 side 的 active 单位推导速度。
*/
class BattleSessionActionSpeedResolver {
/**
* 解析单个 action 在当前快照下的有效速度。
*/
fun resolveEffectiveSpeed(
snapshot: BattleRuntimeSnapshot,
action: BattleSessionAction,
): Int {
val baseSpeed = resolveBaseSpeed(snapshot, action)
val side = resolveSide(snapshot, action) ?: return baseSpeed
return if (TAILWIND_EFFECT_ID in side.conditionStates) {
baseSpeed * 2
} else {
baseSpeed
}
}
/**
* 当前快照下是否应按 Trick Room 反转速度排序。
*
* `trick-room-field` 已收口为正式 field condition,
* 后续同类 room 规则也应沿这一公共场地状态链路接入。
*/
fun isSpeedOrderReversed(snapshot: BattleRuntimeSnapshot): Boolean = BattleFieldConditionIds.TRICK_ROOM in snapshot.field.conditionStates
private fun resolveBaseSpeed(
snapshot: BattleRuntimeSnapshot,
action: BattleSessionAction,
): Int {
if (action.speed != 0) {
return action.speed
}
return when (action) {
is BattleSessionSubmittingAction -> {
resolveUnitSpeed(snapshot, action.submittingUnitId)
}
is BattleSessionSideAction -> {
val side = snapshot.sides[action.sideId]
side
?.activeUnitIds
?.map { unitId -> resolveUnitSpeed(snapshot, unitId) }
?.maxOrNull()
?: 0
}
else -> {
0
}
}
}
private fun resolveSide(
snapshot: BattleRuntimeSnapshot,
action: BattleSessionAction,
) = when (action) {
is BattleSessionSideAction -> {
snapshot.sides[action.sideId]
}
is BattleSessionSubmittingAction -> {
snapshot.sides.values.firstOrNull { side -> action.submittingUnitId in side.unitIds }
}
else -> {
null
}
}
private fun resolveUnitSpeed(
snapshot: BattleRuntimeSnapshot,
unitId: String,
): Int {
val stats = snapshot.units[unitId]?.stats.orEmpty()
return (stats["speed"] ?: stats["spe"]) ?: 0
}
private companion object {
private const val TAILWIND_EFFECT_ID: String = "tailwind-boost"
}
}