Soulstorm-wiki-backend/src/main/kotlin/com/dowstats/service/w40k/BuildingRgdExtractService.kt
2025-06-07 23:41:33 +03:00

232 lines
8.6 KiB
Kotlin

package com.dowstats.service.w40k
import com.dowstats.data.dto.BuildCost
import com.dowstats.data.dto.BuildingDataToSave
import com.dowstats.data.entities.*
import com.dowstats.data.rgd.RgdData
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.nio.file.Path
import kotlin.io.path.exists
@Service
class BuildingRgdExtractService @Autowired constructor(
private val modAttribPathService: ModAttribPathService,
private val iconsService: IconsService,
private val addonRgdExtractService: BuildingAddonRgdExtractService,
) {
val log = LoggerFactory.getLogger(BuildingRgdExtractService::class.java)
data class HealthData(
val hitpoints: Double?,
val regeneration: Double?,
val maxRepaires: Int?,
)
data class WeaponsData(
val hardpoint: Int,
val hardpointOrder: Int,
val weaponFilename: String,
)
data class BuildingTexts(
val name: String?,
val description: String?,
)
fun extractToBuildingEntity(
fileName: String,
modDictionary: Map<Int, String>,
buildingData: List<RgdData>,
weapons: Set<Weapon>,
race: String,
modFolderData: String,
mod: Mod,
races: List<Race>,
armorTypes: List<ArmorType>,
addonsRgdData: Map<String, List<RgdData>>,
): BuildingDataToSave {
val building = Building()
val race = races.find { it.id == race } ?: throw Exception("Cant get race $race")
building.race = race
building.armorType = getBuildingArmourType(buildingData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type")
building.armorType2 = getBuildingArmourType(buildingData, armorTypes, "type_armour_2")
val nameAndDescription = getBuildingNameAndDescription(buildingData, modDictionary)
building.name = nameAndDescription.name
building.description = nameAndDescription.description
building.filename = fileName
val buildCost = getBuildCost(buildingData)
building.buildCostRequisition = buildCost.requisition
building.buildCostPower = buildCost.power
building.buildCostPopulation = buildCost.population
building.buildCostFaith = buildCost.faith
building.buildCostSouls = buildCost.souls
building.buildCostTime = buildCost.time
val healthData = getHealthData(buildingData)
building.health = healthData.hitpoints?.toInt()
building.healthRegeneration = healthData.regeneration
building.sightRadius = getSightRadius(buildingData)?.toInt()
building.detectRadius = getDetectRadius(buildingData)?.toInt()
building.repairMax = healthData.maxRepaires
val addons = getAddons(buildingData)
building.addons = addons?.mapNotNull {addonFileName ->
val addonRgdData = addonsRgdData[addonFileName]
if(addonRgdData != null){
addonRgdExtractService.extractToAddonEntity(addonFileName, modDictionary, addonRgdData, modFolderData, building, mod)
} else {
log.warn("Can't find addon $addonFileName")
null
}
}?.toMutableSet()
val buildingIcon = convertIconAndReturnPath(buildingData, modFolderData, mod.name)
building.icon = buildingIcon
val buildingWeapons = getBuildingWeapon(buildingData)?.mapNotNull { weaponData ->
weapons.find {
it.filename == weaponData.weaponFilename + ".rgd"
}?.also {wp ->
building.weaponHardpoints.add(Weapon.HardpointPosition( wp.id!!, weaponData.hardpoint, weaponData.hardpointOrder))
}
}.orEmpty().toSet()
building.modId = mod.id
return BuildingDataToSave(building, buildingWeapons)
}
private fun getBuildingArmourType(
unitData: List<RgdData>,
armorTypes: Iterable<ArmorType>,
armorTypeTableName: String
): ArmorType? {
val armorType = unitData.getRgdTableByName("type_ext")
?.getRgdTableByName(armorTypeTableName)
?.getStringByName("\$REF")
return armorTypes.find { it.id == armorType?.replace("type_armour\\tp_", "")?.replace(".lua", "") }
}
private fun getBuildCost(buildingData: List<RgdData>): BuildCost {
val cost = buildingData.getRgdTableByName("cost_ext")
?.getRgdTableByName("time_cost")
val costResources = cost?.getRgdTableByName("cost")
return BuildCost(
costResources?.getDoubleByName("requisition"),
costResources?.getDoubleByName("power"),
costResources?.getDoubleByName("population"),
costResources?.getDoubleByName("faith"),
costResources?.getDoubleByName("souls"),
cost?.getDoubleByName("time_seconds")?.toInt()
)
}
private fun getBuildingNameAndDescription(buildingData: List<RgdData>, modDictionary: Map<Int, String>): BuildingTexts {
val uiInfo = buildingData.getRgdTableByName("ui_ext")
?.getRgdTableByName("ui_info")
val nameRef = uiInfo?.getStringByName("screen_name_id")?.replace("$", "")
val name = nameRef?.let { try{modDictionary[it.toInt()]} catch (e: Exception) { null } }
val descriptionRefs = uiInfo?.getRgdTableByName("help_text_list")
?.map{(it.value as String).replace("$", "")}
?.filter{it != "0" && it != "tables\\text_table.lua" && it != ""}
?.sortedBy { try { it.toInt() } catch (e: Exception) { 0 } }
val description = try {
descriptionRefs?.map { modDictionary[it.toInt()] }?.joinToString ( "\n" )
} catch(e:Exception) {
log.warn("Error parsing ui description", e)
null
}
return BuildingTexts(name, description)
}
private fun getHealthData(buildingData: List<RgdData>): HealthData {
val healthExt = buildingData.getRgdTableByName("health_ext")
return HealthData(
healthExt?.getDoubleByName("hitpoints"),
healthExt?.getDoubleByName("regeneration_rate"),
healthExt?.getIntByName("max_repairers")
)
}
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 getBuildingWeapon(buildingData: List<RgdData>?): List<WeaponsData>? = buildingData
?.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 != "") {
WeaponsData(hardpointValue,
weapon.name.replace("weapon_", "").toInt(),
it.replace("weapon\\", "").replace(".lua", ""))
} else null
}
}
}
} else null
}?.flatten()
private fun convertIconAndReturnPath(buildingData: List<RgdData>, modFolderData: String, modName: String?): String? {
val iconPathInMod = buildingData
.getRgdTableByName("ui_ext")
?.getRgdTableByName("ui_info")
?.getStringByName("icon_name")
return iconPathInMod?.let { modAttribPathService.getIconPath(modFolderData, iconPathInMod) }
?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it, modName) }
}
private fun getAddons(buildingData: List<RgdData>): List<String>? = buildingData
.getRgdTableByName("addon_ext")
?.getRgdTableByName("addons")
?.mapNotNull { addon ->
if (addon.value != "") {
addon.value.toString()
.replace("addons\\", "")
.replace(".lua", "")
.plus(".rgd")
} else null
}
}