BattleSessionSwitchActionExecutor.kt
package io.github.lishangbu.avalon.game.battle.engine.core.session
import io.github.lishangbu.avalon.game.battle.engine.core.runtime.flow.BattleRuntimeSnapshot
/**
* session switch action 执行器。
*
* 设计意图:
* - 收敛换人 action 的前置校验、switch out/in 生命周期触发、
* active 槽位替换以及 force switch 标记清理;
* - 让换人编排从通用 action support 中拆出去,后续补复杂换位规则时
* 可以在这里独立演进。
*/
class BattleSessionSwitchActionExecutor {
/**
* 执行 switch action 并更新当前 active 列表。
*/
fun apply(
session: BattleSession,
action: BattleSessionSwitchAction,
): BattleRuntimeSnapshot {
val side = requireNotNull(session.currentSnapshot.sides[action.sideId]) { "Side '${action.sideId}' was not found." }
require(action.outgoingUnitId in side.activeUnitIds) {
"Outgoing unit '${action.outgoingUnitId}' is not active on side '${action.sideId}'."
}
require(action.incomingUnitId in side.unitIds) {
"Incoming unit '${action.incomingUnitId}' is not registered on side '${action.sideId}'."
}
require((session.currentSnapshot.units[action.incomingUnitId]?.currentHp ?: 0) > 0) {
"Incoming unit '${action.incomingUnitId}' is not able to battle."
}
session.currentSnapshot = session.processSwitchOut(action.outgoingUnitId, session.currentSnapshot)
session.currentSnapshot = BattleSessionSwitchBoostStateSupport.clearOutgoingBoosts(session.currentSnapshot, action.outgoingUnitId)
val nextActiveIds =
side.activeUnitIds.map { unitId ->
if (unitId == action.outgoingUnitId) action.incomingUnitId else unitId
}
val switchedSnapshot =
session.currentSnapshot.copy(
sides = session.currentSnapshot.sides + (action.sideId to side.copy(activeUnitIds = nextActiveIds)),
)
session.currentSnapshot =
session.clearForceSwitchRequests(
snapshot = switchedSnapshot,
unitIds = listOf(action.outgoingUnitId, action.incomingUnitId),
)
session.currentSnapshot =
BattleSessionSwitchBoostStateSupport.applyIncomingBoostCarry(
snapshot = session.currentSnapshot,
outgoingUnitId = action.outgoingUnitId,
incomingUnitId = action.incomingUnitId,
)
session.currentSnapshot = session.processSwitchIn(action.incomingUnitId, session.currentSnapshot)
session.recordLog("Executed switch for side ${action.sideId}: ${action.outgoingUnitId} -> ${action.incomingUnitId}.")
session.recordEvent(
BattleSessionSwitchExecutedPayload(
sideId = action.sideId,
outgoingUnitId = action.outgoingUnitId,
incomingUnitId = action.incomingUnitId,
),
)
return session.currentSnapshot
}
}