DefaultBattleSessionActionSortingStrategy.kt

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

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

/**
 * 默认 battle session action 排序策略。
 *
 * 设计意图:
 * - 在保留现有 `priority` 语义的基础上,把速度排序升级为可读取当前快照的有效速度;
 * - 先接入 `tailwind / trick-room` 这组最常见速度规则;
 * - 为后续继续补 battle type 差异化排序、随机 tie-break 与更完整优先级系统预留入口。
 */
class DefaultBattleSessionActionSortingStrategy(
    private val actionSpeedResolver: BattleSessionActionSpeedResolver = BattleSessionActionSpeedResolver(),
) : BattleSessionActionSortingStrategy {
    /**
     * 对当前队列中的 action 进行排序。
     */
    override fun sort(
        snapshot: BattleRuntimeSnapshot,
        actions: List<QueuedBattleSessionMoveAction>,
    ): List<BattleSessionAction> {
        val reverseSpeedOrder = actionSpeedResolver.isSpeedOrderReversed(snapshot)
        val sideOrderById =
            snapshot.sides.keys
                .withIndex()
                .associate { indexed -> indexed.value to indexed.index }
        return actions
            .sortedWith { left, right ->
                compareValuesBy(
                    left,
                    right,
                    { queued -> -queued.action.priority },
                    { queued ->
                        val speed = actionSpeedResolver.resolveEffectiveSpeed(snapshot, queued.action)
                        if (reverseSpeedOrder) speed else -speed
                    },
                    { queued -> queued.action.kind.sortOrder },
                    { queued -> sideOrder(snapshot, queued.action, sideOrderById) },
                    { queued -> activeSlotOrder(snapshot, queued.action) },
                    QueuedBattleSessionMoveAction::enqueueOrder,
                )
            }.map(QueuedBattleSessionMoveAction::action)
    }

    private fun sideOrder(
        snapshot: BattleRuntimeSnapshot,
        action: BattleSessionAction,
        sideOrderById: Map<String, Int>,
    ): Int {
        val sideId =
            when (action) {
                is BattleSessionSideAction -> {
                    action.sideId
                }

                is BattleSessionSubmittingAction -> {
                    snapshot.sides.values
                        .firstOrNull { side -> action.submittingUnitId in side.unitIds }
                        ?.id
                }

                else -> {
                    null
                }
            }
        return sideId?.let { resolved -> sideOrderById[resolved] } ?: Int.MAX_VALUE
    }

    private fun activeSlotOrder(
        snapshot: BattleRuntimeSnapshot,
        action: BattleSessionAction,
    ): Int {
        val unitId =
            when (action) {
                is BattleSessionSubmittingAction -> action.submittingUnitId
                else -> null
            } ?: return Int.MAX_VALUE
        val side = snapshot.sides.values.firstOrNull { current -> unitId in current.unitIds } ?: return Int.MAX_VALUE
        return side.activeUnitIds.indexOf(unitId).let { index -> if (index >= 0) index else Int.MAX_VALUE }
    }
}