BattleUnitEffectDurationManager.kt
package io.github.lishangbu.avalon.game.battle.engine.core.session
import io.github.lishangbu.avalon.game.battle.engine.core.model.AttachedEffectState
import io.github.lishangbu.avalon.game.battle.engine.core.model.UnitState
import io.github.lishangbu.avalon.game.battle.engine.core.mutation.RemoveConditionMutation
import io.github.lishangbu.avalon.game.battle.engine.core.mutation.RemoveStatusMutation
import io.github.lishangbu.avalon.game.battle.engine.core.mutation.RemoveVolatileMutation
import io.github.lishangbu.avalon.game.battle.engine.core.runtime.flow.BattleRuntimeSnapshot
import io.github.lishangbu.avalon.game.battle.engine.core.type.StandardTargetSelectorIds
/**
* 单位挂载效果的持续回合推进器。
*
* 设计意图:
* - 在回合末统一递减主状态、挥发状态和普通 condition 的剩余回合;
* - 只维护 runtime state,不再同步旧的镜像 id 字段。
*/
internal class BattleUnitEffectDurationManager {
/**
* 推进当前快照中全部单位挂载效果的剩余持续时间。
*/
fun advance(snapshot: BattleRuntimeSnapshot): BattleEffectDurationAdvanceResult {
if (snapshot.units.isEmpty()) {
return BattleEffectDurationAdvanceResult(snapshot)
}
val expirationMutations = mutableListOf<BattleSessionScopedMutation>()
val nextUnits =
snapshot.units.mapValues { (_, unit) ->
advanceUnit(unit, expirationMutations)
}
val nextSnapshot =
if (nextUnits == snapshot.units) {
snapshot
} else {
snapshot.copy(units = nextUnits)
}
return BattleEffectDurationAdvanceResult(
snapshot = nextSnapshot,
expirationMutations = expirationMutations,
)
}
/**
* 推进单个单位上的全部挂载效果。
*/
private fun advanceUnit(
unit: UnitState,
expirationMutations: MutableList<BattleSessionScopedMutation>,
): UnitState {
val nextStatusState =
advanceState(unit.statusState) { state ->
expirationMutations +=
BattleSessionScopedMutation(
selfId = unit.id,
targetId = unit.id,
sourceId = state.sourceId,
mutation = RemoveStatusMutation(StandardTargetSelectorIds.SELF),
)
}
val nextConditionStates =
advanceStateMap(unit.conditionStates) { state ->
expirationMutations +=
BattleSessionScopedMutation(
selfId = unit.id,
targetId = unit.id,
sourceId = state.sourceId,
mutation =
RemoveConditionMutation(
target = StandardTargetSelectorIds.SELF,
conditionEffectId = state.effectId,
),
)
}
val nextVolatileStates =
advanceStateMap(unit.volatileStates) { state ->
expirationMutations +=
BattleSessionScopedMutation(
selfId = unit.id,
targetId = unit.id,
sourceId = state.sourceId,
mutation =
RemoveVolatileMutation(
target = StandardTargetSelectorIds.SELF,
volatileEffectId = state.effectId,
),
)
}
return if (
nextStatusState == unit.statusState &&
nextConditionStates == unit.conditionStates &&
nextVolatileStates == unit.volatileStates
) {
unit
} else {
unit.copy(
statusState = nextStatusState,
conditionStates = nextConditionStates,
volatileStates = nextVolatileStates,
)
}
}
/**
* 推进一组 map 形式的挂载效果状态。
*/
private fun advanceStateMap(
states: Map<String, AttachedEffectState>,
onExpired: (AttachedEffectState) -> Unit,
): Map<String, AttachedEffectState> {
if (states.isEmpty()) {
return states
}
val nextStates = linkedMapOf<String, AttachedEffectState>()
states.values
.sortedBy(AttachedEffectState::effectOrder)
.forEach { state ->
val nextState = advanceState(state, onExpired)
if (nextState != null) {
nextStates[nextState.effectId] = nextState
}
}
return nextStates
}
/**
* 推进单个挂载效果的剩余回合。
*
* 返回空值表示该效果已在本次回合结束时自然到期。
*/
private fun advanceState(
state: AttachedEffectState?,
onExpired: (AttachedEffectState) -> Unit = {},
): AttachedEffectState? {
val currentState = state ?: return null
val duration = currentState.duration ?: return currentState
val nextDuration = duration - 1
return if (nextDuration > 0) {
currentState.copy(duration = nextDuration)
} else {
onExpired(currentState)
currentState
}
}
}