BattleSessionWinnerResolver.kt

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

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

/**
 * 胜负判定器。
 *
 * 设计意图:
 * - 收敛“当前还有哪些 side 仍有可战斗单位”与“是否应当结束 battle”
 *   这组判定逻辑;
 * - 让 replacement resolver 不再同时承担替补与胜负决策两类职责;
 * - 便于后续扩展不同 battle 模式下的结束规则。
 */
internal class BattleSessionWinnerResolver(
    private val session: BattleSession,
) {
    /**
     * 根据当前快照更新胜者信息。
     */
    fun updateWinnerIfNeeded(snapshot: BattleRuntimeSnapshot): BattleRuntimeSnapshot {
        val survivingSideIds =
            snapshot.sides.values
                .filter { side ->
                    side.unitIds.any { unitId -> (snapshot.units[unitId]?.currentHp ?: 0) > 0 }
                }.map { side -> side.id }

        return when {
            survivingSideIds.size == 1 -> {
                val winnerId = survivingSideIds.single()
                session.recordLog("Battle ended. Winner: $winnerId.")
                session.recordEvent(BattleSessionBattleEndedPayload(winner = winnerId))
                // 这里只负责宣布 battle 已结束,不直接掺入战后资源、副作用结算。
                snapshot.copy(
                    battle =
                        snapshot.battle.copy(
                            lifecycle = BattleLifecycle.ENDED_UNSETTLED,
                            winner = winnerId,
                        ),
                )
            }

            survivingSideIds.isEmpty() -> {
                session.recordLog("Battle ended with no surviving sides.")
                session.recordEvent(BattleSessionBattleEndedPayload(winner = null))
                // 平局同样先进入“已结束待结算”,由上层决定是否还有额外副作用要处理。
                snapshot.copy(
                    battle =
                        snapshot.battle.copy(
                            lifecycle = BattleLifecycle.ENDED_UNSETTLED,
                            winner = null,
                        ),
                )
            }

            else -> {
                snapshot
            }
        }
    }
}