483 lines
20 KiB
Kotlin
483 lines
20 KiB
Kotlin
package com.dowstats.service.w40k
|
|
|
|
import com.dowstats.Metadata
|
|
import com.dowstats.data.dto.BuildCost
|
|
import com.dowstats.data.dto.UnitDataToSave
|
|
import com.dowstats.data.dto.WeaponsData
|
|
import com.dowstats.data.entities.*
|
|
import com.dowstats.data.entities.ModifiersBase.ModifiersBaseCompanion.fillDataFromRgdTable
|
|
import com.dowstats.data.entities.ModifiersBase.ModifiersBaseCompanion.filterUseless
|
|
import com.dowstats.data.entities.ability.Ability
|
|
import com.dowstats.data.entities.sergant.Sergeant
|
|
import com.dowstats.data.entities.unit.DowUnit
|
|
import com.dowstats.data.entities.unit.Jump
|
|
import com.dowstats.data.entities.unit.JumpRequirements
|
|
import com.dowstats.data.entities.unit.UnitModifiers
|
|
import com.dowstats.data.entities.unit.UnitMoraleBrakeModifiers
|
|
import com.dowstats.data.entities.unit.UnitRequirements
|
|
import com.dowstats.data.entities.weapon.Weapon
|
|
import com.dowstats.data.rgd.RgdData
|
|
import com.dowstats.data.rgd.RgdDataUtil.getBooleanByName
|
|
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
|
|
import com.dowstats.data.rgd.RgdDataUtil.getIntByName
|
|
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
|
|
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
|
|
import org.slf4j.LoggerFactory
|
|
import org.springframework.beans.factory.annotation.Autowired
|
|
import org.springframework.stereotype.Service
|
|
|
|
@Service
|
|
class UnitRgdExtractService @Autowired constructor(
|
|
private val commonParseRgdService: CommonParseRgdService,
|
|
private val sergeantRgdExtractService: SergeantRgdExtractService,
|
|
private val deathExplosionRgdExtractService: DeathExplosionRgdExtractService,
|
|
) {
|
|
|
|
val log = LoggerFactory.getLogger(UnitRgdExtractService::class.java)
|
|
|
|
data class HealthAndMoraleDeathData(
|
|
val hitpoints: Double?,
|
|
val armour: Double?,
|
|
val regeneration: Double?,
|
|
val moraleDeathPenalty: Double?,
|
|
val maxRepairers: Int?,
|
|
)
|
|
|
|
data class RepairData(
|
|
val healthPerSecond: Int?,
|
|
val percentCost: Int?,
|
|
)
|
|
|
|
data class MoraleData(
|
|
val max: Double?,
|
|
val broken: Double?,
|
|
val regeneration: Double?,
|
|
val unitMoraleBrakeModifiers: List<UnitMoraleBrakeModifiers>,
|
|
)
|
|
|
|
data class SergeantData(
|
|
val filePath: String,
|
|
val cost: BuildCost,
|
|
)
|
|
|
|
data class MassData(
|
|
val mass: Int?,
|
|
val upTime: Double?,
|
|
)
|
|
|
|
|
|
fun extractToUnitEntity(
|
|
fileNameSquad: String,
|
|
fileNameUnit: String,
|
|
modDictionary: Map<Int, String>,
|
|
squadData: List<RgdData>,
|
|
unitData: List<RgdData>,
|
|
weapons: Set<Weapon>,
|
|
abilities: Set<Ability>,
|
|
race: String,
|
|
modFolderData: String,
|
|
mod: Mod,
|
|
races: List<Race>,
|
|
armorTypes: Set<ArmorType>,
|
|
modUnitsFull: Map<String, List<RgdData>>,
|
|
): UnitDataToSave {
|
|
|
|
val unit = DowUnit()
|
|
val unitRace = races.find { it.id == race } ?: throw Exception("Cant get race $race")
|
|
|
|
unit.race = unitRace
|
|
unit.armorType = commonParseRgdService.getArmorType(unitData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type")
|
|
unit.armorType2 = commonParseRgdService.getArmorType(unitData, armorTypes, "type_armour_2")
|
|
|
|
val squadUiInfo = squadData.getRgdTableByName("squad_ui_ext")
|
|
?.getRgdTableByName("ui_info") ?: throw Exception("Could not find ui_info at $fileNameSquad")
|
|
val squadUiData = commonParseRgdService.getUiInfo(squadUiInfo, modDictionary, modFolderData, mod, log)
|
|
unit.name = squadUiData.name
|
|
unit.description = squadUiData.description
|
|
unit.icon = squadUiData.iconPath
|
|
unit.filenameSquad = fileNameSquad
|
|
unit.filenameUnit = fileNameUnit
|
|
unit.uiIndexHint = unitData.getRgdTableByName("ui_ext")?.getIntByName("ui_index_hint") ?: 0
|
|
unit.uiHotkeyName = unitData.getRgdTableByName("ui_ext")?.getStringByName("ui_hotkey_name")
|
|
unit.haveReinforceMenu = squadData.getRgdTableByName("squad_reinforce_ext") != null
|
|
|
|
val buildCost = getBuildCost(unitData, squadData)
|
|
unit.buildCostRequisition = buildCost.requisition
|
|
unit.buildCostPower = buildCost.power
|
|
unit.buildCostPopulation = buildCost.population
|
|
unit.buildCostFaith = buildCost.faith
|
|
unit.buildCostSouls = buildCost.souls
|
|
unit.buildCostTime = buildCost.time
|
|
|
|
val squadCap = getSquadCap(squadData)
|
|
unit.capInfantry = squadCap.first?.toInt()
|
|
unit.capSupport = squadCap.second?.toInt()
|
|
|
|
val squadSize = getSquadSize(squadData)
|
|
unit.squadStartSize = squadSize.first?.toInt()
|
|
unit.squadMaxSize = squadSize.second?.toInt()
|
|
unit.squadLimit = getSquadLimit(squadData)?.toInt()
|
|
|
|
val healthData = getHealthAndMoraleDeathPenaltyData(unitData)
|
|
unit.health = healthData.hitpoints?.toInt()
|
|
unit.armour = healthData.armour
|
|
unit.healthRegeneration = healthData.regeneration
|
|
unit.moraleDeathPenalty = healthData.moraleDeathPenalty?.toInt()
|
|
unit.repairMax = healthData.maxRepairers
|
|
|
|
val repairData = getRepairData(unitData)
|
|
unit.repairSpeed = repairData.healthPerSecond
|
|
unit.repairCostPercent = repairData.percentCost
|
|
|
|
val incomeData = commonParseRgdService.getResourceIncome(unitData.getRgdTableByName("resource_ext"))
|
|
unit.faithIncome = incomeData.faith
|
|
unit.powerIncome = incomeData.power
|
|
unit.requisitionIncome = incomeData.requisition
|
|
|
|
val moraleData = getMoraleData(unit, squadData)
|
|
unit.moraleMax = moraleData.max?.toInt()
|
|
unit.moraleBroken = moraleData.broken?.toInt()
|
|
unit.moraleRegeneration = moraleData.regeneration?.toInt()
|
|
unit.unitMoraleBrakeModifiers = moraleData.unitMoraleBrakeModifiers.toMutableSet()
|
|
|
|
unit.moveSpeed = getUnitSpeed(unitData)?.toInt()
|
|
unit.sightRadius = getSightRadius(unitData)?.toInt()
|
|
unit.detectRadius = getDetectRadius(unitData)?.toInt()
|
|
|
|
val massData = getMassData(unitData)
|
|
unit.mass = massData.mass
|
|
unit.upTime = massData.upTime
|
|
|
|
val reinforceCostRgdData = getReinforceCostRgdData(squadData)
|
|
val reinforceCostData = commonParseRgdService.getBuildCost(reinforceCostRgdData)
|
|
unit.reinforceCostRequisition = reinforceCostData.requisition
|
|
unit.reinforceCostPower = reinforceCostData.power
|
|
unit.reinforceCostPopulation = reinforceCostData.population
|
|
unit.reinforceCostFaith = reinforceCostData.faith
|
|
unit.reinforceCostSouls = reinforceCostData.souls
|
|
unit.reinforceTime = reinforceCostData.time
|
|
|
|
unit.mobValue = getMobValue(unitData)
|
|
|
|
unit.unitModifiers = geUnitModifiers(unit, unitData, squadData).toMutableSet()
|
|
unit.unitRequirements = getUnitRequirements(unit, squadData).toMutableSet()
|
|
unit.jump = getJump(unit, squadData)
|
|
unit.abilities = commonParseRgdService.getAbilities(unitData, abilities).toMutableSet()
|
|
|
|
unit.deathExplosions.addAll(deathExplosionRgdExtractService.getDeathExplosions(unitData, armorTypes).toMutableSet())
|
|
|
|
|
|
val sergeantsData = getSergeantsData(squadData)
|
|
|
|
val sergeantsEntities: List<Pair<Sergeant, Set<Weapon>>>? = sergeantsData.first?.mapNotNull { sergeantData ->
|
|
val sergeantFile = sergeantData.filePath.split("\\").last().replace(".lua", ".rgd")
|
|
val sergeantRgdData = modUnitsFull[sergeantFile]
|
|
if (sergeantRgdData != null) {
|
|
sergeantRgdExtractService.extractToSergeantEntity(
|
|
sergeantFile,
|
|
modDictionary,
|
|
mod,
|
|
sergeantRgdData,
|
|
weapons,
|
|
abilities,
|
|
modFolderData,
|
|
sergeantData.cost,
|
|
armorTypes
|
|
)
|
|
} else null
|
|
}
|
|
|
|
unit.maxSergeants = sergeantsData.second
|
|
|
|
|
|
val unitWeapons = getUnitWeapons(unitData, weapons)
|
|
|
|
unit.modId = mod.id
|
|
|
|
return UnitDataToSave(unit, unitWeapons, sergeantsEntities)
|
|
}
|
|
|
|
fun getUnitRgdFileNameFromSquadData(squadRgdData: List<RgdData>): String? {
|
|
val baseUnitPath = squadRgdData.getRgdTableByName("squad_loadout_ext")
|
|
?.getRgdTableByName("trooper_base")
|
|
?.getStringByName("type")
|
|
return baseUnitPath?.split("\\")?.last()?.replace(".lua", ".rgd")
|
|
}
|
|
|
|
private fun getBuildCost(unitData: List<RgdData>, squadData: List<RgdData>): BuildCost {
|
|
|
|
val cost = commonParseRgdService.getBuildCost(unitData.getRgdTableByName("cost_ext")?.getRgdTableByName("time_cost"))
|
|
|
|
val minSquadSize = squadData.getRgdTableByName("squad_loadout_ext")
|
|
?.getDoubleByName("unit_min")?.toInt()
|
|
|
|
fun getCost(cost: Double?) =
|
|
cost?.let { it * (minSquadSize ?: 1) }
|
|
|
|
return BuildCost(
|
|
getCost(cost.requisition),
|
|
getCost(cost.power),
|
|
getCost(cost.population),
|
|
getCost(cost.faith),
|
|
getCost(cost.souls),
|
|
getCost(cost.time)
|
|
)
|
|
}
|
|
|
|
|
|
private fun getSquadCap(squadData: List<RgdData>): Pair<Double?, Double?> {
|
|
|
|
val squadCap = squadData.getRgdTableByName("squad_cap_ext")
|
|
|
|
return Pair(
|
|
squadCap?.getDoubleByName("squad_cap_usage"),
|
|
squadCap?.getDoubleByName("support_cap_usage")
|
|
)
|
|
|
|
}
|
|
|
|
private fun getSquadSize(squadData: List<RgdData>): Pair<Double?, Double?> {
|
|
|
|
val squadSize = squadData.getRgdTableByName("squad_loadout_ext")
|
|
|
|
val unitMin = squadSize?.getDoubleByName("unit_min")
|
|
val unitMax = squadSize?.getDoubleByName("unit_max")
|
|
|
|
return Pair(unitMin, unitMax)
|
|
}
|
|
|
|
private fun getSquadLimit(squadData: List<RgdData>): Double? {
|
|
val requirements = squadData.getRgdTableByName("squad_requirement_ext")
|
|
?.getRgdTableByName("requirements")
|
|
|
|
return requirements?.map {
|
|
if (it.type == 100) {
|
|
val reqirementData = it.value as List<RgdData>
|
|
reqirementData.find { it.name == "max_squad_cap" || it.name == "max_cumulative_squad_cap" }?.value as Double?
|
|
} else null
|
|
}?.filterNotNull()?.firstOrNull()
|
|
}
|
|
|
|
private fun getHealthAndMoraleDeathPenaltyData(unitData: List<RgdData>): HealthAndMoraleDeathData {
|
|
val healthExt = unitData.getRgdTableByName("health_ext")
|
|
val canRepair = healthExt?.getBooleanByName("can_be_repaired")
|
|
return HealthAndMoraleDeathData(
|
|
healthExt?.getDoubleByName("hitpoints"),
|
|
healthExt?.getDoubleByName("armour")?.minus(100),
|
|
healthExt?.getDoubleByName("regeneration_rate"),
|
|
healthExt?.getDoubleByName("morale_death"),
|
|
canRepair?.let { if (it) healthExt.getIntByName("max_repairers") else null }
|
|
)
|
|
}
|
|
|
|
private fun getRepairData(unitData: List<RgdData>): RepairData {
|
|
val repairExt = unitData.getRgdTableByName("repair_ext")
|
|
return RepairData(
|
|
repairExt?.getIntByName("health_repaired_per_second"),
|
|
repairExt?.getIntByName("percent_of_target_cost_for_full_repair"),
|
|
)
|
|
}
|
|
|
|
|
|
private fun getMoraleData(unit: DowUnit, squadData: List<RgdData>): MoraleData {
|
|
val moraleData = squadData.getRgdTableByName("squad_morale_ext")
|
|
val max = moraleData?.getDoubleByName("max")
|
|
val broken = moraleData?.getDoubleByName("broken_min_morale")
|
|
val regeneration = moraleData?.getDoubleByName("rate_per_second")
|
|
|
|
val unitMoraleBreakModifiers = moraleData?.getRgdTableByName("broken_modifiers")?.mapNotNull { m ->
|
|
if (m.name.contains("modifier_")) {
|
|
val mTable = m.value as List<RgdData>
|
|
if (mTable.getStringByName("\$REF") == "modifiers\\no_modifier.lua" ||
|
|
mTable.getStringByName("\$REF") == "modifiers\\morale_break_event.lua") null else {
|
|
UnitMoraleBrakeModifiers().also {
|
|
it.unit = unit
|
|
}.fillDataFromRgdTable(mTable)
|
|
}
|
|
} else null
|
|
} ?: emptyList()
|
|
|
|
return MoraleData(max, broken, regeneration, unitMoraleBreakModifiers)
|
|
}
|
|
|
|
private fun getMobValue(unitData: List<RgdData>): Double? =
|
|
unitData.getRgdTableByName("mob_ext")
|
|
?.getDoubleByName("mob_value")
|
|
|
|
private fun getMassData(unitData: List<RgdData>): MassData {
|
|
val massDataRgd = unitData
|
|
.getRgdTableByName("special_attack_physics_ext")
|
|
|
|
val unitMass = massDataRgd?.getIntByName("mass")
|
|
val unitUpTime = massDataRgd?.getDoubleByName("get_up_time")
|
|
|
|
return MassData(unitMass, unitUpTime)
|
|
}
|
|
|
|
private fun getUnitSpeed(unitData: List<RgdData>): Double? = unitData
|
|
.getRgdTableByName("moving_ext")
|
|
?.getDoubleByName("speed_max")
|
|
|
|
private fun getSightRadius(unitData: List<RgdData>): Double? = unitData
|
|
.getRgdTableByName("sight_ext")
|
|
?.getDoubleByName("sight_radius")
|
|
|
|
private fun getDetectRadius(unitData: List<RgdData>): Double? = unitData
|
|
.getRgdTableByName("sight_ext")
|
|
?.getDoubleByName("keen_sight_radius")
|
|
|
|
private fun getReinforceCostRgdData(squadData: List<RgdData>): List<RgdData>? = squadData
|
|
.getRgdTableByName("squad_reinforce_ext")
|
|
?.getRgdTableByName("cost")
|
|
|
|
private fun getUnitWeapons(unitData: List<RgdData>?, weapons: Set<Weapon>): List<WeaponsData>? = unitData
|
|
?.getRgdTableByName("combat_ext")
|
|
?.getRgdTableByName("hardpoints")
|
|
?.mapNotNull { hardpoint ->
|
|
if (hardpoint.name.contains("hardpoint_")) {
|
|
val hardpointValue = hardpoint.name.replace("hardpoint_", "").toInt()
|
|
val hardpointTable = hardpoint.value as List<RgdData>
|
|
|
|
|
|
|
|
hardpointTable.getRgdTableByName("weapon_table")?.let {
|
|
it.mapNotNull { weapon ->
|
|
(weapon.value as? List<RgdData>)?.getStringByName("weapon")?.let {
|
|
if (it != "") {
|
|
val weaponFileName = it.replace("weapon\\", "").replace(".rgd", "").replace(".lua", "")
|
|
val weaponEntity = weapons.find {
|
|
it.filename?.replace(".rgd", "") == weaponFileName
|
|
}
|
|
if(weaponEntity == null){
|
|
log.warn("Can't find weapon $weaponFileName")
|
|
null
|
|
} else {
|
|
WeaponsData(
|
|
hardpointValue,
|
|
weapon.name.replace("weapon_", "").toInt(),
|
|
weaponEntity
|
|
)
|
|
}
|
|
} else null
|
|
}
|
|
}
|
|
}
|
|
} else null
|
|
}?.flatten()
|
|
|
|
|
|
|
|
private fun getSergeantsData(squadData: List<RgdData>?): Pair<List<SergeantData>?, Int?> {
|
|
|
|
val squadTable = squadData
|
|
?.getRgdTableByName("squad_leader_ext")
|
|
|
|
val maxSergeants = squadTable?.getIntByName("max_leaders")
|
|
|
|
val sergeantsData = squadTable?.mapNotNull { sergeantData ->
|
|
if (sergeantData.name.contains("leader_")) {
|
|
val sergeantRgdTable = sergeantData.value as List<RgdData>
|
|
|
|
val sergeantLeaderFilePath = sergeantRgdTable.getRgdTableByName("leader")?.getStringByName("type")
|
|
|
|
if (sergeantLeaderFilePath == null || sergeantLeaderFilePath == "") null else {
|
|
|
|
SergeantData(
|
|
sergeantLeaderFilePath,
|
|
commonParseRgdService.getBuildCost(sergeantRgdTable.getRgdTableByName("cost_time"))
|
|
)
|
|
}
|
|
} else null
|
|
}
|
|
|
|
return Pair(sergeantsData, maxSergeants)
|
|
}
|
|
|
|
private fun geUnitModifiers(unit: DowUnit, unitData: List<RgdData>, squadData: List<RgdData>): List<UnitModifiers> {
|
|
|
|
fun getUnitModifiersFromTable(table: List<RgdData>?) : List<UnitModifiers> = table?.mapNotNull { m ->
|
|
if (m.name.contains("modifier_")) {
|
|
val mTable = m.value as List<RgdData>
|
|
if (mTable.getStringByName("\$REF") == "modifiers\\no_modifier.lua") null else {
|
|
UnitModifiers().also {
|
|
it.unit = unit
|
|
}.fillDataFromRgdTable(mTable)
|
|
}
|
|
} else null
|
|
}?.filterUseless() ?: emptyList()
|
|
|
|
fun getSquadModifiersFromTable(table: List<RgdData>?) : List<UnitModifiers> = table?.mapNotNull { m ->
|
|
if (m.name.contains("modifier_")) {
|
|
val mTable = m.value as List<RgdData>
|
|
val modifier = mTable.getRgdTableByName("modifier")
|
|
val maxLifetime = mTable.getDoubleByName("max_lifetime")
|
|
if (modifier?.getStringByName("\$REF") == "modifiers\\no_modifier.lua" || modifier == null) null else {
|
|
UnitModifiers().also {
|
|
it.unit = unit
|
|
it.maxLifeTime = maxLifetime
|
|
}.fillDataFromRgdTable(modifier)
|
|
}
|
|
} else null
|
|
}?.filterUseless() ?: emptyList()
|
|
|
|
val unitModifiersTable = unitData.getRgdTableByName("modifier_apply_ext")?.getRgdTableByName("modifiers")
|
|
val squadModifiersTable = squadData.getRgdTableByName("squad_modifier_apply_ext")?.getRgdTableByName("modifiers")
|
|
|
|
return getUnitModifiersFromTable(unitModifiersTable) + getSquadModifiersFromTable(squadModifiersTable)
|
|
|
|
}
|
|
|
|
private fun getUnitRequirements(unit: DowUnit, squadData: List<RgdData>): List<UnitRequirements> {
|
|
val requirements = squadData.getRgdTableByName("squad_requirement_ext")?.getRgdTableByName("requirements")
|
|
return requirements?.mapNotNull { r ->
|
|
if (r.name.contains("required_")) {
|
|
val rTable = r.value as List<RgdData>
|
|
if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
|
|
UnitRequirements().also {
|
|
it.unit = unit
|
|
it.reference = rTable.getStringByName("\$REF")
|
|
it.value = commonParseRgdService.getRequirementReference(it.reference, rTable, unit.filenameSquad!!, log)
|
|
}
|
|
}
|
|
} else null
|
|
} ?: emptyList()
|
|
}
|
|
|
|
private fun getJump(unit: DowUnit, squadData: List<RgdData>): Jump? {
|
|
val squadJumpExt = squadData.getRgdTableByName("squad_jump_ext") ?: return null
|
|
|
|
return Jump().also { jump ->
|
|
jump.unit = unit
|
|
jump.chargeRegeneration = squadJumpExt.getDoubleByName("charge_regeneration")
|
|
jump.goUpTime = squadJumpExt.getDoubleByName("go_up_time")
|
|
jump.jumpDistanceMax = squadJumpExt.getDoubleByName("jump_distance_max")
|
|
jump.breakdownTime = squadJumpExt.getDoubleByName("breakdown_time")
|
|
jump.chargeJumpCostMin = squadJumpExt.getDoubleByName("charge_jump_cost_min")
|
|
jump.jumpHeight = squadJumpExt.getDoubleByName("jump_height")
|
|
jump.chargeMax = squadJumpExt.getDoubleByName("charge_max")
|
|
jump.setupTime = squadJumpExt.getDoubleByName("setup_time")
|
|
jump.chargeJumpCostMax = squadJumpExt.getDoubleByName("charge_jump_cost_max")
|
|
jump.goDownTime = squadJumpExt.getDoubleByName("go_down_time")
|
|
jump.teleport = squadJumpExt.getBooleanByName("teleport") ?: false
|
|
jump.jumpTimeMax = squadJumpExt.getDoubleByName("jump_time_max")
|
|
jump.jumpTimeMin = squadJumpExt.getDoubleByName("jump_time_min")
|
|
jump.jumpMinHeight = squadJumpExt.getDoubleByName("jump_min_height")
|
|
|
|
// Parse jump requirements
|
|
val requirements = squadJumpExt.getRgdTableByName("requirements")
|
|
jump.jumpRequirements = requirements?.mapNotNull { r ->
|
|
if (r.name.contains("required_")) {
|
|
val rTable = r.value as List<RgdData>
|
|
if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
|
|
JumpRequirements().also {
|
|
it.jump = jump
|
|
it.reference = rTable.getStringByName("\$REF")
|
|
it.value = commonParseRgdService.getRequirementReference(it.reference, rTable, unit.filenameSquad!!, log)
|
|
}
|
|
}
|
|
} else null
|
|
}?.toMutableSet() ?: mutableSetOf()
|
|
}
|
|
}
|
|
}
|