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, ) data class WeaponUiInfo( val name: String?, val description: String?, val iconPath: String?, val haveEquipButton: Boolean ) fun extractToWeaponEntity(weaponFileName: String, weaponData: List, mod: Mod, armorTypes: Set, modFolderData: String, modDictionary: Map): 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): 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, armorTypes: Set, 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 = armourDamage?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing -> if (armour_piercing.name.contains("entry")) { val entry = armour_piercing.value as List 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, modDictionary: Map, 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) } }