Anibus 74bfa790a7 - Add unit, buildings, sergeants income resources
- Add repair speed
- Add UA support
2025-06-25 16:01:40 +03:00

189 lines
8.5 KiB
Kotlin

package com.dowstats.service.w40k
import com.dowstats.data.entities.ArmorType
import com.dowstats.data.entities.Mod
import com.dowstats.data.entities.Weapon
import com.dowstats.data.entities.WeaponArmorPiercing
import com.dowstats.data.repositories.ArmorTypeRepository
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
import java.io.File
import java.math.BigDecimal
import java.nio.file.Path
import kotlin.io.path.exists
@Service
class WeaponRgdExtractService @Autowired constructor(
private val armorTypeRepository: ArmorTypeRepository,
private val iconsService: IconsService,
private val modAttribPathService: ModAttribPathService,
) {
val log = LoggerFactory.getLogger(WeaponRgdExtractService::class.java)
data class BuildCost(
val requisition: Int?,
val power: Int?,
val seconds: Int?
)
data class AreaEffectData(
val minDamage: Double,
val maxDamage: Double,
val damageRadius: Double,
val throwForceMin: Double,
val throwForceMax: Double,
val minDamageValue: Double,
val moraleDamage: Double,
val armourPiercing: List<WeaponArmorPiercing>,
)
data class WeaponUiInfo(
val name: String?,
val description: String?,
val iconPath: String?,
val haveEquipButton: Boolean
)
fun extractToWeaponEntity(weaponFileName: String, weaponData: List<RgdData>, mod: Mod, armorTypes: Set<ArmorType>, modFolderData: String, modDictionary: Map<Int, String>): Weapon? {
val weapon = Weapon()
weapon.filename = weaponFileName
val weaponUiData = getWeaponNameAndDescription(weaponData, modDictionary, modFolderData, mod)
weapon.name = weaponUiData.name
weapon.icon = weaponUiData.iconPath
weapon.description = weaponUiData.description
weapon.haveEquipButton = weaponUiData.haveEquipButton
val cost = getCost(weaponData)
weapon.costRequisition = cost.requisition ?: 0
weapon.costPower = cost.power ?: 0
weapon.costTimeSeconds = cost.seconds ?: 0
weapon.accuracy = weaponData.getDoubleByName("accuracy") ?: 1.0
weapon.accuracyReductionMoving = weaponData.getDoubleByName("accuracy_reduction_when_moving")
weapon.minRange = weaponData.getDoubleByName("min_range")
weapon.maxRange = weaponData.getDoubleByName("max_range")
weapon.reloadTime = weaponData.getDoubleByName("reload_time")
weapon.setupTime = weaponData.getDoubleByName("setup_time")
weapon.isMeleeWeapon = weaponData.getBooleanByName("melee_weapon") ?: false
weapon.canAttackAir = weaponData.getBooleanByName("can_attack_air_units") ?: true
weapon.canAttackGround = weaponData.getBooleanByName("can_attack_ground_units") ?: true
val areaEffectData = getAreaEffectData(weaponData, armorTypes, weapon)
weapon.minDamageValue = areaEffectData.minDamageValue
weapon.minDamage = areaEffectData.minDamage
weapon.maxDamage = areaEffectData.maxDamage
weapon.throwForceMin = areaEffectData.throwForceMin
weapon.throwForceMax = areaEffectData.throwForceMax
weapon.moraleDamage = areaEffectData.moraleDamage
weapon.weaponPiercings = areaEffectData.armourPiercing
weapon.damageRadius = areaEffectData.damageRadius
weapon.modId = mod.id
return if(weapon.minDamage == 0.0 && weapon.maxDamage == 0.0 && weapon.moraleDamage == 0.0){
null
} else weapon
}
private fun getCost(weaponData: List<RgdData>): BuildCost {
val costTable = weaponData.getRgdTableByName("cost")
val costTime = costTable?.getIntByName("time_seconds")
val costCost = costTable?.getRgdTableByName("cost")
val power = costCost?.getIntByName("power")
val requisition = costCost?.getIntByName("requisition")
return BuildCost(requisition, power, costTime)
}
private fun getAreaEffectData(weaponData: List<RgdData>, armorTypes: Set<ArmorType>, thisWeapon: Weapon): AreaEffectData {
val areaEffect = weaponData.getRgdTableByName("area_effect")
val areaEffectInformation = areaEffect?.getRgdTableByName("area_effect_information")
val cantHaveRadius = areaEffectInformation?.getRgdTableByName("area_type")?.getStringByName("\$REF")?.contains("tp_area_effect_point") == true
val damageRadius = if(cantHaveRadius) 0.0 else areaEffectInformation?.getDoubleByName("radius") ?: 0.0
val throwData = areaEffect?.getRgdTableByName("throw_data")
val forceMin = throwData?.getDoubleByName("force_min") ?: 0.0
val forceMax = throwData?.getDoubleByName("force_max") ?: 0.0
val armourDamage = areaEffect
?.getRgdTableByName("weapon_damage")
?.getRgdTableByName("armour_damage")
val minDamage = armourDamage?.getDoubleByName("min_damage") ?: 0.0
val maxDamage = armourDamage?.getDoubleByName("max_damage") ?: 0.0
val minDamageValue = armourDamage?.getDoubleByName("min_damage_value") ?: 0.0
val moraleDamage = armourDamage?.getDoubleByName("morale_damage") ?: 0.0
val defaultArmourPiercing = armourDamage?.getDoubleByName("armour_piercing")
val weaponDmgMap: Map<String, Double> =
armourDamage?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing ->
if (armour_piercing.name.contains("entry")) {
val entry = armour_piercing.value as List<RgdData>
val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","")
val dmgValue = entry.getDoubleByName("armour_piercing_value")
dmgType!! to dmgValue!!
} else null
}?.toMap() ?: emptyMap()
val armoursPiercing = armorTypes.map {
val weaponArmourPiercing = WeaponArmorPiercing()
weaponArmourPiercing.weapon = thisWeapon
weaponArmourPiercing.armorType = it
val piercingValue = (weaponDmgMap[it.id] ?: defaultArmourPiercing)?.toBigDecimal()
weaponArmourPiercing.piercingValue = piercingValue?.let {pv -> if(pv <= BigDecimal(100)) pv else BigDecimal(100) }
weaponArmourPiercing
}
return AreaEffectData(minDamage, maxDamage, damageRadius, forceMin, forceMax, minDamageValue, moraleDamage, armoursPiercing)
}
private fun getWeaponNameAndDescription(weaponData: List<RgdData>, modDictionary: Map<Int, String>, modFolderData: String, mod: Mod): WeaponUiInfo {
val weaponUiInfo = weaponData.getRgdTableByName("ui_info")
val nameRef = weaponUiInfo?.getStringByName("screen_name_id")?.replace("$", "")
val name = nameRef?.let { try { modDictionary[it.toInt()] } catch (e: Exception) { null } }
val descriptionRefs = weaponUiInfo?.getRgdTableByName("help_text_list")
?.mapNotNull{try {
(it.value as String).replace("$", "")
} catch (e: Exception) {
log.error("Error parsing ui help_text weapon $name", e)
null
}}
?.filter{it != "0" && it != "tables\\text_table.lua" && it != ""}
?.sortedBy { try { it.toInt() } catch (e: Exception) { 0 } }
val description = try {
descriptionRefs?.map { try { modDictionary[it.toInt()] } catch (e: Exception) { "" } }?.joinToString ( "\n" )
} catch(e:Exception) {
log.warn("Error parsing ui description weapon $name", e)
null
}
val icon = try {
val iconPath = weaponUiInfo?.getStringByName("icon_name")
iconPath?.let { modAttribPathService.getIconPath(modFolderData, iconPath) }
?.let { iconsService.convertTgaToJpegImage(iconPath, it, mod.name) }
} catch (e: Exception) {
log.error("Error parsing ui icon path for weapon $name", e)
null
}
val haveUpgradeButton = weaponUiInfo?.getBooleanByName("no_button")?.let { !it } ?: false
return WeaponUiInfo(name, description, icon, haveUpgradeButton)
}
}