- Add unit, buildings, sergeants income resources

- Add repair speed
- Add UA support
This commit is contained in:
Anibus 2025-06-25 16:01:40 +03:00
parent 926156a1dc
commit 74bfa790a7
26 changed files with 742 additions and 54 deletions

View File

@ -14,7 +14,11 @@ data class SergeantDto(
val buildCostFaith: Double?,
val buildCostSouls: Double?,
val buildCostTime: Int?,
val faithIncome: Double?,
val powerIncome: Double?,
val requisitionIncome: Double?,
val health: Int?,
val armour: Double?,
val healthRegeneration: Double?,
val moraleDeathPenalty: Int?,
val mass: Int?,

View File

@ -15,12 +15,16 @@ data class UnitFullDto(
val buildCostFaith: Double?,
val buildCostSouls: Double?,
val buildCostTime: Int?,
val faithIncome: Double?,
val powerIncome: Double?,
val requisitionIncome: Double?,
val capInfantry: Int?,
val capSupport: Int?,
val squadStartSize: Int?,
val squadMaxSize: Int?,
val squadLimit: Int?,
val health: Int?,
val armour: Double?,
val healthRegeneration: Double?,
val moraleDeathPenalty: Int?,
val moraleMax: Int?,
@ -38,6 +42,7 @@ data class UnitFullDto(
val reinforceCostSouls: Int?,
val reinforceTime: Int?,
val repairMax: Int?,
val repairSpeed: Int?,
val maxSergeants: Int?,
val icon: String?,
val modId: Long,

View File

@ -3,9 +3,9 @@ package com.dowstats.data.dto.controllers
data class RaceUnits(
val race: RaceDto,
val infantry: List<UnitShortDto>,
val tech: List<UnitShortDto>,
val support: List<UnitShortDto>,
val infantry: Set<UnitShortDto>,
val tech: Set<UnitShortDto>,
val support: Set<UnitShortDto>,
)
data class UnitShortDto(

View File

@ -2,7 +2,9 @@ package com.dowstats.data.dto.controllers.building
import com.dowstats.data.dto.controllers.ArmorTypeDto
import com.dowstats.data.dto.controllers.RaceDto
import com.dowstats.data.dto.controllers.UnitShortDto
import com.dowstats.data.dto.controllers.WeaponSlotDto
import com.dowstats.data.entities.DowUnit
data class BuildingFullDto(
var id: Long?,
@ -18,7 +20,11 @@ data class BuildingFullDto(
var buildCostFaith: Double?,
var buildCostSouls: Double?,
var buildCostTime: Int?,
var faithIncome: Double?,
var powerIncome: Double?,
var requisitionIncome: Double?,
var health: Int?,
val armour: Double?,
var healthRegeneration: Double?,
var sightRadius: Int?,
var detectRadius: Int?,
@ -26,5 +32,6 @@ data class BuildingFullDto(
var icon: String?,
var modId: Long?,
var addons: Set<BuildingAddonDto>?,
val units: Set<UnitShortDto>,
val weapons: Set<WeaponSlotDto>?,
)

View File

@ -1,6 +1,7 @@
package com.dowstats.data.dto.controllers.building
import com.dowstats.data.dto.controllers.RaceDto
import com.dowstats.data.dto.controllers.UnitShortDto
data class RaceBuildings(
val race: RaceDto,
@ -11,6 +12,7 @@ data class BuildingShortDto(
val name: String,
val icon: String,
val id: Long,
val units: Set<UnitShortDto>,
val armourTypeName: String,
val canDetect: Boolean,
)

View File

@ -13,6 +13,9 @@ class ArmorType {
var id: String? = null
var name: String? = null
var isClassic: Boolean? = null
var isUa: Boolean? = null
fun toDto() = ArmorTypeDto(
id!!,
name

View File

@ -1,7 +1,9 @@
package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.UnitShortDto
import com.dowstats.data.dto.controllers.building.BuildingAddonDto
import com.dowstats.data.dto.controllers.building.BuildingFullDto
import com.dowstats.data.entities.DowUnitObject.toUnitDto
import com.dowstats.data.entities.Weapon.HardpointPosition
import jakarta.persistence.*
@ -14,6 +16,8 @@ class Building {
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
var canBuild: Boolean = true
@ManyToOne
@JoinColumn(name = "race_id", nullable = false)
var race: Race? = null
@ -36,12 +40,16 @@ class Building {
var buildCostSouls: Double? = null
var buildCostTime: Int? = null
var health: Int? = null
var armour: Double? = null
var healthRegeneration: Double? = null
var sightRadius: Int? = null
var detectRadius: Int? = null
var repairMax: Int? = null
var icon: String? = null
var modId: Long? = null
var faithIncome: Double? = null
var powerIncome: Double? = null
var requisitionIncome: Double? = null
@OneToMany(mappedBy = "building", cascade = [CascadeType.ALL])
var addons: MutableSet<BuildingAddon>? = null
@ -49,6 +57,12 @@ class Building {
@OneToMany(mappedBy = "building", cascade = [CascadeType.ALL])
var weapons: MutableSet<BuildingWeapon>? = null
@ManyToMany
@JoinTable(name = "buildings_units",
joinColumns = [JoinColumn(name = "building_id")],
inverseJoinColumns = [JoinColumn(name = "unit_id")])
var units: MutableSet<DowUnit>? = null
@Transient
var weaponHardpoints: MutableSet<HardpointPosition> = mutableSetOf()
@ -67,7 +81,11 @@ class Building {
buildCostFaith,
buildCostSouls,
buildCostTime,
faithIncome,
powerIncome,
requisitionIncome,
health,
armour,
healthRegeneration,
sightRadius,
detectRadius,
@ -75,6 +93,7 @@ class Building {
icon,
modId!!,
buildingAddons,
units?.toList()?.toUnitDto() ?: emptySet(),
weapons?.map { it.toWeaponSlotDto() }?.toSet(),
)

View File

@ -1,6 +1,7 @@
package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.UnitFullDto
import com.dowstats.data.dto.controllers.UnitShortDto
import jakarta.persistence.*
@ -24,6 +25,9 @@ class DowUnit {
@JoinColumn(name = "armour_type_2_id")
var armorType2: ArmorType? = null
@ManyToMany(mappedBy = "units")
var buildFrom: Set<Building> = setOf()
var name: String? = null
var description: String? = null
var filename: String? = null
@ -39,9 +43,12 @@ class DowUnit {
var squadMaxSize: Int? = null
var squadLimit: Int? = null
var health: Int? = null
var armour: Double? = null
var healthRegeneration: Double? = null
var moraleDeathPenalty: Int? = null
var repairMax: Int? = null
var repairSpeed: Int? = null
var repairCostPercent: Int? = null
var moraleMax: Int? = null
var moraleBroken: Int? = null
var moraleRegeneration: Int? = null
@ -57,6 +64,9 @@ class DowUnit {
var reinforceCostSouls: Int? = null
var reinforceTime: Int? = null
var maxSergeants: Int? = null
var faithIncome: Double? = null
var powerIncome: Double? = null
var requisitionIncome: Double? = null
var icon: String? = null
var modId: Long? = null
@ -67,6 +77,7 @@ class DowUnit {
var weapons: MutableSet<UnitWeapon>? = null
fun toDto(): UnitFullDto =
UnitFullDto(
id!!,
@ -82,12 +93,16 @@ class DowUnit {
buildCostFaith,
buildCostSouls,
buildCostTime,
faithIncome,
powerIncome,
requisitionIncome,
capInfantry,
capSupport,
squadStartSize,
squadMaxSize,
squadLimit,
health,
armour,
healthRegeneration,
moraleDeathPenalty,
moraleMax,
@ -105,6 +120,7 @@ class DowUnit {
reinforceCostSouls,
reinforceTime,
repairMax,
repairSpeed,
maxSergeants,
icon,
modId!!,
@ -112,3 +128,27 @@ class DowUnit {
weapons?.map { it.toWeaponSlotDto() }?.toSet()
)
}
object DowUnitObject {
fun List<DowUnit>.toUnitDto(): Set<UnitShortDto> =
this.mapNotNull {
val name = it.name ?: it.filename
val icon = it.icon
if (name == null || icon == null) null else UnitShortDto(name, icon, it.id!!,
it.armorType?.name!!,
(it.detectRadius ?: 0) > 0)
}.toSet()
fun List<DowUnit>.filterCompanyUnits(): List<DowUnit> =
this.filter {
it.filename?.contains("_sp.") != true
&& it.filename?.contains("_sp_") != true
&& it.filename?.contains("sp_eldar_") != true
&& it.filename?.contains("_dxp3.") != true
&& it.filename?.contains("_dxp3_") != true
&& it.filename?.contains("_nis.") != true
&& it.filename?.contains("_exarch_council.") != true
&& it.filename?.contains("_dark_reapers_base.") != true
&& it.filename?.contains("tau_squad_slave_murdered") != true
}
}

View File

@ -37,6 +37,7 @@ class Sergeant {
var buildCostSouls: Double? = null
var buildCostTime: Int? = null
var health: Int? = null
var armour: Double? = null
var healthRegeneration: Double? = null
var moraleDeathPenalty: Int? = null
var mass: Int? = null
@ -44,6 +45,10 @@ class Sergeant {
var sightRadius: Int? = null
var detectRadius: Int? = null
var icon: String? = null
var faithIncome: Double? = null
var powerIncome: Double? = null
var requisitionIncome: Double? = null
@Transient
var weaponHardpoints: MutableSet<HardpointPosition> = mutableSetOf()
@ -65,7 +70,11 @@ class Sergeant {
buildCostFaith,
buildCostSouls,
buildCostTime,
faithIncome,
powerIncome,
requisitionIncome,
health,
armour,
healthRegeneration,
moraleDeathPenalty,
mass,

View File

@ -5,4 +5,7 @@ import com.dowstats.data.entities.Race
import com.dowstats.data.entities.User
import org.springframework.data.repository.*
interface ArmorTypeRepository : CrudRepository<ArmorType, Long>
interface ArmorTypeRepository : CrudRepository<ArmorType, Long>{
fun findByIsClassic(isClassic: Boolean): List<ArmorType>
fun findByIsUa(isUa: Boolean): List<ArmorType>
}

View File

@ -18,5 +18,7 @@ interface UnitRepository : CrudRepository<DowUnit, Long> {
""")
fun findByModIdAndRace(modId: Long, race: Race?): List<DowUnit>
fun findByFilenameAndRaceAndModId(fileName: String, race: Race, modId: Long): DowUnit?
fun deleteAllByModIdAndRaceId(modId: Long, raceId: String)
}

View File

@ -6,6 +6,8 @@ import com.dowstats.data.dto.controllers.building.BuildingShortDto
import com.dowstats.data.dto.controllers.building.RaceBuildings
import com.dowstats.data.entities.AddonRequirements
import com.dowstats.data.entities.Building
import com.dowstats.data.entities.DowUnitObject.filterCompanyUnits
import com.dowstats.data.entities.DowUnitObject.toUnitDto
import com.dowstats.data.repositories.AddonRepository
import com.dowstats.data.repositories.BuildingRepository
import com.dowstats.data.repositories.RaceRepository
@ -102,11 +104,14 @@ class DowBuildingMappingService @Autowired constructor(
private fun List<Building>.toBuildingDto(): List<BuildingShortDto> =
this.mapNotNull {
val name = it.name ?: it.filename
this
.sortedWith(compareBy<Building> { it.units?.isEmpty() }.thenBy{ it.buildCostRequisition?.let { 0 - it } })
.mapNotNull {
val name = it.name ?: it.filename?.replace(".rgd", "")?.replace("_", " ")
val icon = it.icon
if (name == null || icon == null) null else BuildingShortDto(
name, icon, it.id!!,
it.units?.toList()?.filterCompanyUnits()?.toUnitDto() ?: emptySet(),
it.armorType?.name!!,
(it.detectRadius ?: 0) > 0
)
@ -126,11 +131,17 @@ class DowBuildingMappingService @Autowired constructor(
&& it.filename?.contains("_dxp3.") != true
&& it.filename?.contains("_dxp3_") != true
&& it.filename?.contains("_nis.") != true
&& it.filename?.contains("_interface_relay.") != true
&& it.filename?.contains("necron_tunnel.rgd") != true
&& it.filename?.contains("_exarch_council.") != true
&& it.filename?.contains("_dark_reapers_base.") != true
&& it.filename?.contains("eldar_deep_strike_building.rgd") != true
&& it.filename?.contains("ork_deep_strike_building.rgd") != true
&& it.filename?.contains("_caravel_ai.rgd") != true
&& it.filename?.contains("sisters_tanktrap_ai.rgd") != true
&& it.filename?.contains("sisters_hq_ktgm.rgd") != true
&& it.filename?.contains("tau_squad_slave_murdered") != true
&& it.filename?.contains("space_marine_single_player_only_drop_pod_building_2.rgd") != true
&& it.filename?.contains("space_marine_single_player_only_drop_pod_building.rgd") != true
&& it.filename?.contains("single_player_only") != true
&& it.filename?.contains("space_marine_drop_pod_building.rgd") != true
}
}

View File

@ -3,6 +3,8 @@ package com.dowstats.service.datamaps
import com.dowstats.data.dto.controllers.RaceUnits
import com.dowstats.data.dto.controllers.UnitShortDto
import com.dowstats.data.entities.DowUnit
import com.dowstats.data.entities.DowUnitObject.filterCompanyUnits
import com.dowstats.data.entities.DowUnitObject.toUnitDto
import com.dowstats.data.entities.Race
import com.dowstats.data.repositories.RaceRepository
import com.dowstats.data.repositories.UnitRepository
@ -47,31 +49,9 @@ class DowUnitMappingService @Autowired constructor(
}
private fun List<DowUnit>.toUnitDto(): List<UnitShortDto> =
this.mapNotNull {
val name = it.name ?: it.filename
val icon = it.icon
if (name == null || icon == null) null else UnitShortDto(name, icon, it.id!!,
it.armorType?.name!!,
(it.detectRadius ?: 0) > 0)
}
private fun getAllUnits(modId: Long, race: String? = null): List<DowUnit> {
val raceEntity = race?.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") }
return filterCompanyUnits(unitRepository.findByModIdAndRace(modId, raceEntity))
return unitRepository.findByModIdAndRace(modId, raceEntity).filterCompanyUnits()
}
private fun filterCompanyUnits(units: List<DowUnit>): List<DowUnit> =
units.filter {
it.filename?.contains("_sp.") != true
&& it.filename?.contains("_sp_") != true
&& it.filename?.contains("sp_eldar_") != true
&& it.filename?.contains("_dxp3.") != true
&& it.filename?.contains("_dxp3_") != true
&& it.filename?.contains("_nis.") != true
&& it.filename?.contains("_exarch_council.") != true
&& it.filename?.contains("_dark_reapers_base.") != true
&& it.filename?.contains("tau_squad_slave_murdered") != true
}
}

View File

@ -3,11 +3,13 @@ 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.repositories.UnitRepository
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 com.dowstats.service.w40k.UnitRgdExtractService.ResourceIncome
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
@ -27,6 +29,7 @@ class BuildingRgdExtractService @Autowired constructor(
data class HealthData(
val hitpoints: Double?,
val regeneration: Double?,
val armour: Double?,
val maxRepaires: Int?,
)
@ -46,16 +49,16 @@ class BuildingRgdExtractService @Autowired constructor(
modDictionary: Map<Int, String>,
buildingData: List<RgdData>,
weapons: Set<Weapon>,
race: String,
units: List<DowUnit>,
race: Race,
modFolderData: String,
mod: Mod,
races: List<Race>,
armorTypes: List<ArmorType>,
armorTypes: Set<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")
@ -74,10 +77,15 @@ class BuildingRgdExtractService @Autowired constructor(
building.buildCostSouls = buildCost.souls
building.buildCostTime = buildCost.time
val incomeData = getResource(buildingData)
building.faithIncome = incomeData.faith
building.powerIncome = incomeData.power
building.requisitionIncome = incomeData.requisition
val healthData = getHealthData(buildingData)
building.health = healthData.hitpoints?.toInt()
building.armour = healthData.armour
building.healthRegeneration = healthData.regeneration
building.sightRadius = getSightRadius(buildingData)?.toInt()
@ -85,6 +93,8 @@ class BuildingRgdExtractService @Autowired constructor(
building.repairMax = healthData.maxRepaires
building.units = getUnits(buildingData, units).toMutableSet()
val addons = getAddons(buildingData)
building.addons = addons?.mapNotNull {addonFileName ->
val addonRgdData = addonsRgdData[addonFileName]
@ -142,6 +152,15 @@ class BuildingRgdExtractService @Autowired constructor(
)
}
private fun getResource(buildingData: List<RgdData>): ResourceIncome {
val resourceExt = buildingData.getRgdTableByName("resource_ext")
return ResourceIncome(
resourceExt?.getDoubleByName("faith_per_second")?.let { it * 10 },
resourceExt?.getDoubleByName("power_per_second")?.let { it * 10 },
resourceExt?.getDoubleByName("requisition_per_second")?.let { it * 10 },
)
}
private fun getBuildingNameAndDescription(buildingData: List<RgdData>, modDictionary: Map<Int, String>): BuildingTexts {
val uiInfo = buildingData.getRgdTableByName("ui_ext")
?.getRgdTableByName("ui_info")
@ -169,6 +188,7 @@ class BuildingRgdExtractService @Autowired constructor(
return HealthData(
healthExt?.getDoubleByName("hitpoints"),
healthExt?.getDoubleByName("regeneration_rate"),
healthExt?.getDoubleByName("armour")?.minus(100),
healthExt?.getIntByName("max_repairers")
)
}
@ -227,5 +247,17 @@ class BuildingRgdExtractService @Autowired constructor(
.plus(".rgd")
} else null
}
private fun getUnits(buildingData: List<RgdData>, units: List<DowUnit>): List<DowUnit> {
return buildingData.getRgdTableByName("spawner_ext")
?.getRgdTableByName("squad_table")
?.mapNotNull { unit ->
if (unit.name.contains("squad_")) {
val fileName = unit.value.toString().split("\\").last().replace(".lua", ".rgd")
units.find { it.filename == fileName }
} else null
} ?: emptyList()
}
}

View File

@ -63,6 +63,10 @@ class ModParserService @Autowired constructor(
.drop(1)
.filter { Files.isDirectory(it) }.map { it.name }.toList()
val armorTypes = if(mod.technicalName?.contains("UltimateApocalypse") == true)
armorTypeRepository.findByIsUa(true).toSet()
else armorTypeRepository.findByIsClassic(true).toSet()
racesList.forEach {
unitRepository.deleteAllByModIdAndRaceId(mod.id!!, it)
}
@ -73,10 +77,10 @@ class ModParserService @Autowired constructor(
val enrichedModDictionary = defaultDictionary + modDictionary
val weapons = saveWeapons(modFolderData, mod, enrichedModDictionary)
saveUnits(modFolderData, weapons, racesList, mod, enrichedModDictionary)
val weapons = saveWeapons(modFolderData, mod, armorTypes, enrichedModDictionary)
saveUnits(modFolderData, weapons, racesList, armorTypes, mod, enrichedModDictionary)
saveBuildings(modFolderData, weapons, racesList, mod, enrichedModDictionary)
saveBuildings(modFolderData, weapons, racesList, armorTypes, mod, enrichedModDictionary)
}
@ -110,12 +114,12 @@ class ModParserService @Autowired constructor(
modFolderData: String,
weapons: Set<Weapon>,
racesList: List<String>,
armorTypes: Set<ArmorType>,
mod: Mod,
modDictionary: Map<Int, String>
) {
val races = raceRepository.findAll().toList()
val armorTypes = armorTypeRepository.findAll().toList()
racesList.forEach { raceFolder ->
@ -236,11 +240,11 @@ class ModParserService @Autowired constructor(
modFolderData: String,
weapons: Set<Weapon>,
racesList: List<String>,
armorTypes: Set<ArmorType>,
mod: Mod, modDictionary: Map<Int, String>
) {
val races = raceRepository.findAll().toList()
val armorTypes = armorTypeRepository.findAll().toList()
val addonsRgdData = getAddonsRgdData(modFolderData)
@ -262,6 +266,14 @@ class ModParserService @Autowired constructor(
val modStructuresFull = classicRgdDataStructures + modRgdDataStructures
if(raceFolder == "npc") return@forEach
val race = races.find { it.id == raceFolder }
if(race == null){
log.warn("Can't find race $raceFolder")
return@forEach
}
val raceUnits = unitRepository.findByModIdAndRace(mod.id!!, race)
modStructuresFull.forEach { structure ->
val structureDataToSave =
try {
@ -270,7 +282,8 @@ class ModParserService @Autowired constructor(
modDictionary,
structure.value,
weapons,
raceFolder,
raceUnits,
race,
modFolderData,
mod,
races,
@ -322,7 +335,7 @@ class ModParserService @Autowired constructor(
return classicAddons + modAddons
}
private fun saveWeapons(modFolderData: String, mod: Mod, modDictionary: Map<Int, String>): Set<Weapon> {
private fun saveWeapons(modFolderData: String, mod: Mod, armorTypes: Set<ArmorType>, modDictionary: Map<Int, String>): Set<Weapon> {
val classicWeapons =
rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/weapon")
@ -333,7 +346,7 @@ class ModParserService @Autowired constructor(
val allWeapons = classicWeapons + modWeapons
val weaponsToSave = allWeapons.mapNotNull {
weaponRgdExtractService.extractToWeaponEntity(it.key, it.value, mod, modFolderData, modDictionary)
weaponRgdExtractService.extractToWeaponEntity(it.key, it.value, mod, armorTypes, modFolderData, modDictionary)
}
return try {

View File

@ -7,6 +7,7 @@ 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 com.dowstats.service.w40k.UnitRgdExtractService.ResourceIncome
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
@ -25,6 +26,7 @@ class SergeantRgdExtractService @Autowired constructor(
data class HealthAndMoraleDeathData(
val hitpoints: Double?,
val armour: Double?,
val regeneration: Double?,
val moraleDeathPenalty: Double?,
)
@ -53,7 +55,7 @@ class SergeantRgdExtractService @Autowired constructor(
weapons: Set<Weapon>,
modFolderData: String,
buildCost: BuildCost,
armorTypes: List<ArmorType>,
armorTypes: Set<ArmorType>,
): Pair<Sergeant, Set<Weapon>> {
val sergeant = Sergeant()
@ -75,9 +77,15 @@ class SergeantRgdExtractService @Autowired constructor(
val healthData = getHealthAndMoraleDeathPenaltyData(sergeantData)
sergeant.health = healthData.hitpoints?.toInt()
sergeant.armour = healthData.armour
sergeant.healthRegeneration = healthData.regeneration
sergeant.moraleDeathPenalty = healthData.moraleDeathPenalty?.toInt()
val incomeData = getResource(sergeantData)
sergeant.faithIncome = incomeData.faith
sergeant.powerIncome = incomeData.power
sergeant.requisitionIncome = incomeData.requisition
sergeant.sightRadius = getSightRadius(sergeantData)?.toInt()
sergeant.detectRadius = getDetectRadius(sergeantData)?.toInt()
@ -139,11 +147,21 @@ class SergeantRgdExtractService @Autowired constructor(
val healthExt = unitData.getRgdTableByName("health_ext")
return HealthAndMoraleDeathData(
healthExt?.getDoubleByName("hitpoints"),
healthExt?.getDoubleByName("armour")?.minus(100),
healthExt?.getDoubleByName("regeneration_rate"),
healthExt?.getDoubleByName("morale_death")
)
}
private fun getResource(sergeantData: List<RgdData>): ResourceIncome {
val resourceExt = sergeantData.getRgdTableByName("resource_ext")
return ResourceIncome(
resourceExt?.getDoubleByName("faith_per_second")?.let { it * 10 },
resourceExt?.getDoubleByName("power_per_second")?.let { it * 10 },
resourceExt?.getDoubleByName("requisition_per_second")?.let { it * 10 },
)
}
private fun getMassData(unitData: List<RgdData>): MassData {
val massDataRgd = unitData
.getRgdTableByName("special_attack_physics_ext")

View File

@ -13,9 +13,6 @@ 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 UnitRgdExtractService @Autowired constructor(
@ -28,11 +25,23 @@ class UnitRgdExtractService @Autowired constructor(
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 ResourceIncome(
val faith: Double?,
val power: Double?,
val requisition: Double?,
)
data class MoraleData(
val max: Double?,
val broken: Double?,
@ -64,7 +73,7 @@ class UnitRgdExtractService @Autowired constructor(
modFolderData: String,
mod: Mod,
races: List<Race>,
armorTypes: List<ArmorType>,
armorTypes: Set<ArmorType>,
modUnitsFull: Map<String, List<RgdData>>,
): UnitDataToSave {
@ -99,10 +108,20 @@ class UnitRgdExtractService @Autowired constructor(
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 = getResource(unitData)
unit.faithIncome = incomeData.faith
unit.powerIncome = incomeData.power
unit.requisitionIncome = incomeData.requisition
val moraleData = getMoraleData(squadData)
unit.moraleMax = moraleData.max?.toInt()
unit.moraleBroken = moraleData.broken?.toInt()
@ -273,12 +292,30 @@ class UnitRgdExtractService @Autowired constructor(
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 getResource(unitData: List<RgdData>): ResourceIncome {
val resourceExt = unitData.getRgdTableByName("resource_ext")
return ResourceIncome(
resourceExt?.getDoubleByName("faith_per_second")?.let { it * 10 },
resourceExt?.getDoubleByName("power_per_second")?.let { it * 10 },
resourceExt?.getDoubleByName("requisition_per_second")?.let { it * 10 },
)
}
private fun getMoraleData(squadData: List<RgdData>): MoraleData {
val moraleData = squadData.getRgdTableByName("squad_morale_ext")
val max = moraleData?.getDoubleByName("max")

View File

@ -52,9 +52,7 @@ class WeaponRgdExtractService @Autowired constructor(
val haveEquipButton: Boolean
)
fun extractToWeaponEntity(weaponFileName: String, weaponData: List<RgdData>, mod: Mod, modFolderData: String, modDictionary: Map<Int, String>): Weapon? {
val armorTypes = armorTypeRepository.findAll().toSet()
fun extractToWeaponEntity(weaponFileName: String, weaponData: List<RgdData>, mod: Mod, armorTypes: Set<ArmorType>, modFolderData: String, modDictionary: Map<Int, String>): Weapon? {
val weapon = Weapon()
@ -69,7 +67,7 @@ class WeaponRgdExtractService @Autowired constructor(
weapon.costRequisition = cost.requisition ?: 0
weapon.costPower = cost.power ?: 0
weapon.costTimeSeconds = cost.seconds ?: 0
weapon.accuracy = weaponData.getDoubleByName("accuracy")
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")
@ -157,14 +155,19 @@ class WeaponRgdExtractService @Autowired constructor(
val name = nameRef?.let { try { modDictionary[it.toInt()] } catch (e: Exception) { null } }
val descriptionRefs = weaponUiInfo?.getRgdTableByName("help_text_list")
?.map{(it.value as String).replace("$", "")}
?.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", e)
log.warn("Error parsing ui description weapon $name", e)
null
}
@ -174,7 +177,7 @@ class WeaponRgdExtractService @Autowired constructor(
iconPath?.let { modAttribPathService.getIconPath(modFolderData, iconPath) }
?.let { iconsService.convertTgaToJpegImage(iconPath, it, mod.name) }
} catch (e: Exception) {
log.error("Error parsing ui icon path", e)
log.error("Error parsing ui icon path for weapon $name", e)
null
}

View File

@ -0,0 +1,101 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Fill armor_type table ua armor",
"author": "anibus",
"changes": [
{
"insert": {
"tableName": "armor_types",
"columns": [
{
"column": {
"name": "id",
"value": "daemon_low"
}
},{
"column": {
"name": "name",
"value": "Demon Low"
}
},{
"column": {
"name": "is_classic",
"value": false
}
}
]
}
},{
"insert": {
"tableName": "armor_types",
"columns": [
{
"column": {
"name": "id",
"value": "living_metal"
}
},{
"column": {
"name": "name",
"value": "Living Metal"
}
},{
"column": {
"name": "is_classic",
"value": false
}
}
]
}
},{
"insert": {
"tableName": "armor_types",
"columns": [
{
"column": {
"name": "id",
"value": "titan"
}
},{
"column": {
"name": "name",
"value": "Titan"
}
},{
"column": {
"name": "is_classic",
"value": false
}
}
]
}
},{
"insert": {
"tableName": "armor_types",
"columns": [
{
"column": {
"name": "id",
"value": "building_super"
}
},{
"column": {
"name": "name",
"value": "Building Super"
}
},{
"column": {
"name": "is_classic",
"value": false
}
}
]
}
}
]
}
}
]
}

View File

@ -0,0 +1,33 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add is_classic and is_ua to armor_types table",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "is_classic",
"type": "boolean",
"defaultValueBoolean": true
}
},
{
"column": {
"name": "is_ua",
"type": "boolean",
"defaultValueBoolean": true
}
}
],
"tableName": "armor_types"
}
}
]
}
}
]
}

View File

@ -0,0 +1,79 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add faith,power and requisition income columns to buildings",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "faith_income",
"type": "number"
}
},
{
"column": {
"name": "power_income",
"type": "number"
}
},
{
"column": {
"name": "requisition_income",
"type": "number"
}
}
],
"tableName": "buildings"
}
}
]
}
},
{
"changeSet": {
"id": "Add armour column to buildings",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "armour",
"type": "number"
}
}
],
"tableName": "buildings"
}
}
]
}
},
{
"changeSet": {
"id": "Add can_build column",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "can_build",
"type": "boolean"
}
}
],
"tableName": "buildings"
}
}
]
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add buildings_units table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "buildings_units",
"columns": [
{
"column": {
"name": "building_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "unit_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "building_id, unit_id",
"constraintName": "uc_buildings_units_unit_id_building_id",
"tableName": "buildings_units"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "building_id",
"baseTableName": "buildings_units",
"constraintName": "fk_buildings_buildings_units",
"referencedColumnNames": "id",
"referencedTableName": "buildings",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "unit_id",
"baseTableName": "buildings_units",
"constraintName": "fk_units_buildings_units",
"referencedColumnNames": "id",
"referencedTableName": "units",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,58 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add faith,power and requisition income columns to sergeants",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "faith_income",
"type": "number"
}
},
{
"column": {
"name": "power_income",
"type": "number"
}
},
{
"column": {
"name": "requisition_income",
"type": "number"
}
}
],
"tableName": "sergeants"
}
}
]
}
},
{
"changeSet": {
"id": "Add armour column to sergeants",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "armour",
"type": "number"
}
}
],
"tableName": "sergeants"
}
}
]
}
}
]
}

View File

@ -20,6 +20,87 @@
}
]
}
},
{
"changeSet": {
"id": "Add repair_speed and repair_cost column to units",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "repair_speed",
"type": "int"
}
},
{
"column": {
"name": "repair_cost_percent",
"type": "int"
}
}
],
"tableName": "units"
}
}
]
}
},
{
"changeSet": {
"id": "Add faith,power and requisition income columns to units",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "faith_income",
"type": "number"
}
},
{
"column": {
"name": "power_income",
"type": "number"
}
},
{
"column": {
"name": "requisition_income",
"type": "number"
}
}
],
"tableName": "units"
}
}
]
}
},
{
"changeSet": {
"id": "Add armour column to units",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "armour",
"type": "number"
}
}
],
"tableName": "units"
}
}
]
}
}
]
}

View File

@ -0,0 +1,56 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Drop null constraints for accuracy and reload time for weapons table",
"author": "anibus",
"changes": [
{
"dropNotNullConstraint": {
"columnDataType": "number",
"columnName": "accuracy",
"tableName": "weapons"
}
},
{
"dropNotNullConstraint": {
"columnDataType": "number",
"columnName": "reload_time",
"tableName": "weapons"
}
}
]
}
},
{
"changeSet": {
"id": "Drop setup time constraint for weapons table",
"author": "anibus",
"changes": [
{
"dropNotNullConstraint": {
"columnDataType": "number",
"columnName": "setup_time",
"tableName": "weapons"
}
}
]
}
},
{
"changeSet": {
"id": "Drop accuracy_reduction_moving constraint for weapons table",
"author": "anibus",
"changes": [
{
"dropNotNullConstraint": {
"columnDataType": "number",
"columnName": "accuracy_reduction_moving",
"tableName": "weapons"
}
}
]
}
}
]
}

View File

@ -81,6 +81,32 @@
"include": {
"file": "db/0.0.2/schema/units.json"
}
},
{
"include": {
"file": "db/0.0.2/schema/buildings.json"
}
},{
"include": {
"file": "db/0.0.2/schema/buildings_units.json"
}
},{
"include": {
"file": "db/0.0.2/schema/sergeants.json"
}
},
{
"include": {
"file": "db/0.0.2/schema/armor_types.json"
}
},{
"include": {
"file": "db/0.0.2/schema/weapons.json"
}
},{
"include": {
"file": "db/0.0.2/data/armor_types.json"
}
}
]
}