SmartBattleUnitImportService.kt
package io.github.lishangbu.avalon.game.service.unit
import io.github.lishangbu.avalon.dataset.api.service.CreatureReader
import io.github.lishangbu.avalon.dataset.api.service.MoveReader
import io.github.lishangbu.avalon.dataset.api.service.NatureReader
import io.github.lishangbu.avalon.game.battle.engine.core.unit.BattleMoveSlotInput
import io.github.lishangbu.avalon.game.battle.engine.core.unit.BattleUnitAssembler
import io.github.lishangbu.avalon.game.battle.engine.core.unit.BattleUnitAssemblyRequest
import io.github.lishangbu.avalon.game.battle.engine.core.unit.CreatureAbilityOptionRecord
import io.github.lishangbu.avalon.game.battle.engine.core.unit.CreatureUnitImportRecord
import io.github.lishangbu.avalon.game.battle.engine.core.unit.NatureImportRecord
import io.github.lishangbu.avalon.game.calculator.growthrate.GrowthRateCalculatorFactory
import io.github.lishangbu.avalon.game.calculator.stat.StatCalculatorFactory
import org.springframework.stereotype.Service
/**
* 基于真实数据的战斗单位智能导入服务。
*/
@Service
class SmartBattleUnitImportService(
private val creatureReader: CreatureReader,
private val natureReader: NatureReader,
private val moveReader: MoveReader,
private val statCalculatorFactory: StatCalculatorFactory,
private val growthRateCalculatorFactory: GrowthRateCalculatorFactory,
) : BattleUnitImporter {
override fun importUnit(request: BattleUnitImportRequest): BattleUnitImportResult {
val creature = loadCreature(request)
val nature = loadNature(request)
val movePpDefaults = loadMovePpDefaults(request.moves)
val assembled =
BattleUnitAssembler.assemble(
request =
BattleUnitAssemblyRequest(
unitId = request.unitId,
metadata =
io.github.lishangbu.avalon.game.battle.engine.core.model.UnitMetadataState(
level = request.metadata.level,
creatureId = request.metadata.creatureId,
creatureInternalName = request.metadata.creatureInternalName,
natureId = request.metadata.natureId,
captureContext = request.metadata.captureContext,
ivs = request.metadata.ivs,
evs = request.metadata.evs,
),
abilityInternalName = request.abilityInternalName,
itemId = request.itemId,
moves = request.moves.map { move -> BattleMoveSlotInput(move.moveId, move.currentPp) },
currentHp = request.currentHp,
statusState = request.statusState,
volatileStates = request.volatileStates,
conditionStates = request.conditionStates,
boosts = request.boosts,
forceSwitchRequested = request.forceSwitchRequested,
),
creature = creature,
nature = nature,
movePpDefaults = movePpDefaults,
statCalculatorFactory = statCalculatorFactory,
growthRateCalculatorFactory = growthRateCalculatorFactory,
)
return BattleUnitImportResult(
unit = assembled.unit,
creatureId = assembled.creatureId,
creatureInternalName = assembled.creatureInternalName,
creatureName = assembled.creatureName,
level = assembled.level,
requiredExperience = assembled.requiredExperience,
calculatedStats = assembled.calculatedStats,
)
}
private fun loadCreature(request: BattleUnitImportRequest): CreatureUnitImportRecord {
val creature =
when {
request.metadata.creatureId != null -> creatureReader.findCreatureById(request.metadata.creatureId)
request.metadata.creatureInternalName != null -> creatureReader.findCreatureByInternalName(request.metadata.creatureInternalName)
else -> error("Either creatureId or creatureInternalName must be provided.")
} ?: error("Creature was not found for request '${request.unitId}'.")
return CreatureUnitImportRecord(
id = creature.id,
speciesId = creature.speciesId,
internalName = creature.internalName,
name = creature.name,
weight = creature.weight,
growthRateInternalName = creature.growthRateInternalName,
captureRate = creature.captureRate,
typeIds = creature.typeInternalNames,
baseStats = creature.baseStats,
abilityOptions =
creature.abilityOptions.map { ability ->
CreatureAbilityOptionRecord(
internalName = ability.internalName,
slot = ability.slot,
hidden = ability.hidden,
)
},
)
}
private fun loadNature(request: BattleUnitImportRequest): NatureImportRecord? {
val nature =
when {
request.metadata.natureId != null -> natureReader.findNatureById(request.metadata.natureId)
request.metadata.natureInternalName != null -> natureReader.findNatureByInternalName(request.metadata.natureInternalName)
else -> null
} ?: return null
return NatureImportRecord(
id = nature.id,
internalName = nature.internalName,
increasedStatInternalName = nature.increasedStatInternalName,
decreasedStatInternalName = nature.decreasedStatInternalName,
)
}
private fun loadMovePpDefaults(moves: List<BattleMoveImportRequest>): Map<String, Int> {
val movesByInternalName = moveReader.findMovesByInternalNames(moves.map { move -> move.moveId }.toSet())
return moves.associate { move ->
move.moveId to (movesByInternalName[move.moveId]?.pp ?: 0)
}
}
}