LocalCreatureReader.kt
package io.github.lishangbu.avalon.dataset.service.api
import io.github.lishangbu.avalon.dataset.api.model.CreatureAbilityOptionInfo
import io.github.lishangbu.avalon.dataset.api.model.CreatureInfo
import io.github.lishangbu.avalon.dataset.api.service.CreatureReader
import io.github.lishangbu.avalon.dataset.entity.dto.CreatureSpecification
import io.github.lishangbu.avalon.dataset.entity.dto.CreatureView
import io.github.lishangbu.avalon.dataset.repository.AbilityRepository
import io.github.lishangbu.avalon.dataset.repository.CreatureAbilityRepository
import io.github.lishangbu.avalon.dataset.repository.CreatureElementRepository
import io.github.lishangbu.avalon.dataset.repository.CreatureRepository
import io.github.lishangbu.avalon.dataset.repository.CreatureSpeciesRepository
import io.github.lishangbu.avalon.dataset.repository.CreatureStatRepository
import io.github.lishangbu.avalon.dataset.repository.StatRepository
import io.github.lishangbu.avalon.dataset.repository.TypeRepository
import org.springframework.stereotype.Service
@Service
class LocalCreatureReader(
private val creatureRepository: CreatureRepository,
private val creatureSpeciesRepository: CreatureSpeciesRepository,
private val creatureElementRepository: CreatureElementRepository,
private val creatureStatRepository: CreatureStatRepository,
private val creatureAbilityRepository: CreatureAbilityRepository,
private val abilityRepository: AbilityRepository,
private val typeRepository: TypeRepository,
private val statRepository: StatRepository,
) : CreatureReader {
override fun findCreatureById(id: Long): CreatureInfo? = creatureRepository.loadViewById(id)?.toCreatureInfo()
override fun findCreatureByInternalName(internalName: String): CreatureInfo? =
creatureRepository
.listViews(CreatureSpecification(internalName = internalName))
.firstOrNull { creature -> creature.internalName == internalName }
?.toCreatureInfo()
private fun CreatureView.toCreatureInfo(): CreatureInfo {
val creatureId = id.toLong()
val species = creatureSpecies?.id?.toLong()?.let(creatureSpeciesRepository::loadViewById)
return CreatureInfo(
id = creatureId,
speciesId = species?.id?.toLong(),
internalName = requireNotNull(internalName) { "Creature id=$creatureId is missing internalName." },
name = name ?: requireNotNull(internalName),
weight = weight,
growthRateInternalName = species?.growthRate?.internalName,
captureRate = species?.captureRate,
typeInternalNames = loadCreatureElements(creatureId),
baseStats = loadCreatureStats(creatureId),
abilityOptions = loadCreatureAbilities(creatureId),
)
}
private fun loadCreatureElements(creatureId: Long): List<String> {
val rows = creatureElementRepository.findAll().filter { row -> row.id.creatureId == creatureId }
val slots = rows.associate { row -> row.id.typeId to (row.slot ?: Int.MAX_VALUE) }
return typeRepository
.findAllById(slots.keys)
.sortedBy { type -> slots[type.id] ?: Int.MAX_VALUE }
.mapNotNull { type -> type.internalName }
}
private fun loadCreatureStats(creatureId: Long): Map<String, Int> {
val rows = creatureStatRepository.findAll().filter { row -> row.id.creatureId == creatureId }
val statsById = statRepository.findAllById(rows.map { row -> row.id.statId }.toSet()).associateBy { stat -> stat.id }
return rows
.mapNotNull { row ->
val stat = statsById[row.id.statId] ?: return@mapNotNull null
val internalName = stat.internalName ?: return@mapNotNull null
internalName to (row.baseStat ?: 0)
}.toMap(LinkedHashMap())
}
private fun loadCreatureAbilities(creatureId: Long): List<CreatureAbilityOptionInfo> {
val rows = creatureAbilityRepository.findAll().filter { row -> row.id.creatureId == creatureId }
val abilitiesById = abilityRepository.findAllById(rows.map { row -> row.id.abilityId }.toSet()).associateBy { ability -> ability.id }
return rows
.mapNotNull { row ->
val ability = abilitiesById[row.id.abilityId] ?: return@mapNotNull null
val internalName = ability.internalName ?: return@mapNotNull null
CreatureAbilityOptionInfo(
internalName = internalName,
slot = row.slot ?: Int.MAX_VALUE,
hidden = row.hidden == true,
)
}.sortedWith(compareBy<CreatureAbilityOptionInfo> { ability -> ability.hidden }.thenBy { ability -> ability.slot })
}
}