BattleSessionAutoReplacementExecutor.kt

package io.github.lishangbu.avalon.game.battle.engine.core.session

import io.github.lishangbu.avalon.game.battle.engine.core.runtime.flow.BattleRuntimeSnapshot

/**
 * 自动替补执行器。
 *
 * 设计意图:
 * - 接收已经解析好的 `BattleSessionReplacementPlan`,负责真正执行
 *   switch out / active 替换 / force switch 清理 / switch in;
 * - 把自动替补的状态写回与日志事件记录从 resolver 主类中拆开;
 * - 让“是否需要替补”与“如何执行替补”形成清晰分层。
 */
internal class BattleSessionAutoReplacementExecutor(
    private val session: BattleSession,
) {
    /**
     * 根据计划执行自动替补。
     */
    fun execute(
        snapshot: BattleRuntimeSnapshot,
        plan: BattleSessionReplacementPlan,
    ): BattleRuntimeSnapshot {
        if (!plan.hasAutomaticReplacement) {
            return snapshot
        }
        var transitionSnapshot = snapshot
        plan.slotTransitions
            .mapNotNull(BattleSessionSlotTransition::outgoingUnitId)
            .forEach { outgoingUnitId ->
                val outgoingUnit = transitionSnapshot.units[outgoingUnitId]
                if ((outgoingUnit?.currentHp ?: 0) > 0) {
                    transitionSnapshot = session.processSwitchOut(outgoingUnitId, transitionSnapshot)
                }
                transitionSnapshot = BattleSessionSwitchBoostStateSupport.clearOutgoingBoosts(transitionSnapshot, outgoingUnitId)
            }
        val side = transitionSnapshot.sides.getValue(plan.sideId)
        val replacedSnapshot =
            transitionSnapshot.copy(
                sides = transitionSnapshot.sides + (plan.sideId to side.copy(activeUnitIds = plan.nextActiveUnitIds)),
            )
        transitionSnapshot =
            session.clearForceSwitchRequests(
                snapshot = replacedSnapshot,
                unitIds =
                    plan.slotTransitions.flatMap { transition ->
                        listOfNotNull(transition.outgoingUnitId, transition.incomingUnitId)
                    },
            )
        plan.slotTransitions
            .mapNotNull(BattleSessionSlotTransition::incomingUnitId)
            .forEach { incomingUnitId ->
                val outgoingUnitId =
                    plan.slotTransitions
                        .firstOrNull { transition -> transition.incomingUnitId == incomingUnitId }
                        ?.outgoingUnitId
                transitionSnapshot =
                    BattleSessionSwitchBoostStateSupport.applyIncomingBoostCarry(
                        snapshot = transitionSnapshot,
                        outgoingUnitId = outgoingUnitId,
                        incomingUnitId = incomingUnitId,
                    )
                transitionSnapshot = session.processSwitchIn(incomingUnitId, transitionSnapshot)
            }
        session.recordLog("Side ${plan.sideId} auto replaced active units: ${plan.beforeActiveUnitIds} -> ${plan.nextActiveUnitIds}.")
        session.recordEvent(
            BattleSessionAutoReplacedPayload(
                sideId = plan.sideId,
                before = plan.beforeActiveUnitIds,
                after = plan.nextActiveUnitIds,
            ),
        )
        return transitionSnapshot
    }
}