From 74bfa790a7d71bac2a46c61574d80a8fbd493fec Mon Sep 17 00:00:00 2001 From: Anibus Date: Wed, 25 Jun 2025 16:01:40 +0300 Subject: [PATCH] - Add unit, buildings, sergeants income resources - Add repair speed - Add UA support --- .../data/dto/controllers/SergeantDto.kt | 4 + .../data/dto/controllers/UnitFullDto.kt | 5 + .../data/dto/controllers/UnitShortDto.kt | 6 +- .../controllers/building/BuildingFullDto.kt | 7 ++ .../controllers/building/BuildingShortDto.kt | 2 + .../com/dowstats/data/entities/ArmorType.kt | 3 + .../com/dowstats/data/entities/Building.kt | 19 ++++ .../com/dowstats/data/entities/DowUnit.kt | 40 +++++++ .../com/dowstats/data/entities/Sergant.kt | 9 ++ .../data/repositories/ArmorTypeRepository.kt | 5 +- .../data/repositories/UnitRepository.kt | 2 + .../datamaps/DowBuildingMappingService.kt | 19 +++- .../service/datamaps/DowUnitMappingService.kt | 26 +---- .../service/w40k/BuildingRgdExtractService.kt | 38 ++++++- .../dowstats/service/w40k/ModParserService.kt | 29 +++-- .../service/w40k/SergantRgdExtractService.kt | 20 +++- .../service/w40k/UnitRgdExtractService.kt | 45 +++++++- .../service/w40k/WeaponRgdExtractService.kt | 17 +-- .../resources/db/0.0.2/data/armor_types.json | 101 ++++++++++++++++++ .../db/0.0.2/schema/armor_types.json | 33 ++++++ .../resources/db/0.0.2/schema/buildings.json | 79 ++++++++++++++ .../db/0.0.2/schema/buildings_units.json | 66 ++++++++++++ .../resources/db/0.0.2/schema/sergeants.json | 58 ++++++++++ src/main/resources/db/0.0.2/schema/units.json | 81 ++++++++++++++ .../resources/db/0.0.2/schema/weapons.json | 56 ++++++++++ src/main/resources/db/changelog-master.json | 26 +++++ 26 files changed, 742 insertions(+), 54 deletions(-) create mode 100644 src/main/resources/db/0.0.2/data/armor_types.json create mode 100644 src/main/resources/db/0.0.2/schema/armor_types.json create mode 100644 src/main/resources/db/0.0.2/schema/buildings.json create mode 100644 src/main/resources/db/0.0.2/schema/buildings_units.json create mode 100644 src/main/resources/db/0.0.2/schema/sergeants.json create mode 100644 src/main/resources/db/0.0.2/schema/weapons.json diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/SergeantDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/SergeantDto.kt index c4619b2..0b07f4d 100644 --- a/src/main/kotlin/com/dowstats/data/dto/controllers/SergeantDto.kt +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/SergeantDto.kt @@ -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?, diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/UnitFullDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/UnitFullDto.kt index edd1c84..b5cc754 100644 --- a/src/main/kotlin/com/dowstats/data/dto/controllers/UnitFullDto.kt +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/UnitFullDto.kt @@ -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, diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/UnitShortDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/UnitShortDto.kt index 4366b28..e7f14a9 100644 --- a/src/main/kotlin/com/dowstats/data/dto/controllers/UnitShortDto.kt +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/UnitShortDto.kt @@ -3,9 +3,9 @@ package com.dowstats.data.dto.controllers data class RaceUnits( val race: RaceDto, - val infantry: List, - val tech: List, - val support: List, + val infantry: Set, + val tech: Set, + val support: Set, ) data class UnitShortDto( diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/building/BuildingFullDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/building/BuildingFullDto.kt index fa10804..bee71ef 100644 --- a/src/main/kotlin/com/dowstats/data/dto/controllers/building/BuildingFullDto.kt +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/building/BuildingFullDto.kt @@ -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?, + val units: Set, val weapons: Set?, ) diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/building/BuildingShortDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/building/BuildingShortDto.kt index e69c73b..422dddf 100644 --- a/src/main/kotlin/com/dowstats/data/dto/controllers/building/BuildingShortDto.kt +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/building/BuildingShortDto.kt @@ -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, val armourTypeName: String, val canDetect: Boolean, ) \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/entities/ArmorType.kt b/src/main/kotlin/com/dowstats/data/entities/ArmorType.kt index 43c36af..7a924d4 100644 --- a/src/main/kotlin/com/dowstats/data/entities/ArmorType.kt +++ b/src/main/kotlin/com/dowstats/data/entities/ArmorType.kt @@ -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 diff --git a/src/main/kotlin/com/dowstats/data/entities/Building.kt b/src/main/kotlin/com/dowstats/data/entities/Building.kt index c65219a..95b3a7c 100644 --- a/src/main/kotlin/com/dowstats/data/entities/Building.kt +++ b/src/main/kotlin/com/dowstats/data/entities/Building.kt @@ -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? = null @@ -49,6 +57,12 @@ class Building { @OneToMany(mappedBy = "building", cascade = [CascadeType.ALL]) var weapons: MutableSet? = null + @ManyToMany + @JoinTable(name = "buildings_units", + joinColumns = [JoinColumn(name = "building_id")], + inverseJoinColumns = [JoinColumn(name = "unit_id")]) + var units: MutableSet? = null + @Transient var weaponHardpoints: MutableSet = 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(), ) diff --git a/src/main/kotlin/com/dowstats/data/entities/DowUnit.kt b/src/main/kotlin/com/dowstats/data/entities/DowUnit.kt index 30b4fdf..34d6822 100644 --- a/src/main/kotlin/com/dowstats/data/entities/DowUnit.kt +++ b/src/main/kotlin/com/dowstats/data/entities/DowUnit.kt @@ -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 = 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? = 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.toUnitDto(): Set = + 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.filterCompanyUnits(): List = + 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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/entities/Sergant.kt b/src/main/kotlin/com/dowstats/data/entities/Sergant.kt index ae46ad5..e5c09d4 100644 --- a/src/main/kotlin/com/dowstats/data/entities/Sergant.kt +++ b/src/main/kotlin/com/dowstats/data/entities/Sergant.kt @@ -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 = mutableSetOf() @@ -65,7 +70,11 @@ class Sergeant { buildCostFaith, buildCostSouls, buildCostTime, + faithIncome, + powerIncome, + requisitionIncome, health, + armour, healthRegeneration, moraleDeathPenalty, mass, diff --git a/src/main/kotlin/com/dowstats/data/repositories/ArmorTypeRepository.kt b/src/main/kotlin/com/dowstats/data/repositories/ArmorTypeRepository.kt index 9ac6542..988087a 100644 --- a/src/main/kotlin/com/dowstats/data/repositories/ArmorTypeRepository.kt +++ b/src/main/kotlin/com/dowstats/data/repositories/ArmorTypeRepository.kt @@ -5,4 +5,7 @@ import com.dowstats.data.entities.Race import com.dowstats.data.entities.User import org.springframework.data.repository.* -interface ArmorTypeRepository : CrudRepository \ No newline at end of file +interface ArmorTypeRepository : CrudRepository{ + fun findByIsClassic(isClassic: Boolean): List + fun findByIsUa(isUa: Boolean): List +} \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/repositories/UnitRepository.kt b/src/main/kotlin/com/dowstats/data/repositories/UnitRepository.kt index e66a350..490ecc7 100644 --- a/src/main/kotlin/com/dowstats/data/repositories/UnitRepository.kt +++ b/src/main/kotlin/com/dowstats/data/repositories/UnitRepository.kt @@ -18,5 +18,7 @@ interface UnitRepository : CrudRepository { """) fun findByModIdAndRace(modId: Long, race: Race?): List + fun findByFilenameAndRaceAndModId(fileName: String, race: Race, modId: Long): DowUnit? + fun deleteAllByModIdAndRaceId(modId: Long, raceId: String) } \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/service/datamaps/DowBuildingMappingService.kt b/src/main/kotlin/com/dowstats/service/datamaps/DowBuildingMappingService.kt index 2c3d4f4..782417a 100644 --- a/src/main/kotlin/com/dowstats/service/datamaps/DowBuildingMappingService.kt +++ b/src/main/kotlin/com/dowstats/service/datamaps/DowBuildingMappingService.kt @@ -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.toBuildingDto(): List = - this.mapNotNull { - val name = it.name ?: it.filename + this + .sortedWith(compareBy { 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 } } \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/service/datamaps/DowUnitMappingService.kt b/src/main/kotlin/com/dowstats/service/datamaps/DowUnitMappingService.kt index e06e0b6..a460512 100644 --- a/src/main/kotlin/com/dowstats/service/datamaps/DowUnitMappingService.kt +++ b/src/main/kotlin/com/dowstats/service/datamaps/DowUnitMappingService.kt @@ -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.toUnitDto(): List = - 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 { 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): List = - 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 - } } \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/service/w40k/BuildingRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/BuildingRgdExtractService.kt index ca585f1..39b465d 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/BuildingRgdExtractService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/BuildingRgdExtractService.kt @@ -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, buildingData: List, weapons: Set, - race: String, + units: List, + race: Race, modFolderData: String, mod: Mod, races: List, - armorTypes: List, + armorTypes: Set, addonsRgdData: Map>, ): 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): 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, modDictionary: Map): 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, units: List): List { + 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() + } + } diff --git a/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt b/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt index 01bd46d..5ea18fc 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt @@ -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, racesList: List, + armorTypes: Set, mod: Mod, modDictionary: Map ) { 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, racesList: List, + armorTypes: Set, mod: Mod, modDictionary: Map ) { 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): Set { + private fun saveWeapons(modFolderData: String, mod: Mod, armorTypes: Set, modDictionary: Map): Set { 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 { diff --git a/src/main/kotlin/com/dowstats/service/w40k/SergantRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/SergantRgdExtractService.kt index 81c55ea..4f9b38a 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/SergantRgdExtractService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/SergantRgdExtractService.kt @@ -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, modFolderData: String, buildCost: BuildCost, - armorTypes: List, + armorTypes: Set, ): Pair> { 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): 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): MassData { val massDataRgd = unitData .getRgdTableByName("special_attack_physics_ext") diff --git a/src/main/kotlin/com/dowstats/service/w40k/UnitRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/UnitRgdExtractService.kt index 00a86e7..598ac8b 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/UnitRgdExtractService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/UnitRgdExtractService.kt @@ -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, - armorTypes: List, + armorTypes: Set, modUnitsFull: Map>, ): 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): 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): 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): MoraleData { val moraleData = squadData.getRgdTableByName("squad_morale_ext") val max = moraleData?.getDoubleByName("max") diff --git a/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt index f497126..cf98e3f 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt @@ -52,9 +52,7 @@ class WeaponRgdExtractService @Autowired constructor( val haveEquipButton: Boolean ) - fun extractToWeaponEntity(weaponFileName: String, weaponData: List, mod: Mod, modFolderData: String, modDictionary: Map): Weapon? { - - val armorTypes = armorTypeRepository.findAll().toSet() + fun extractToWeaponEntity(weaponFileName: String, weaponData: List, mod: Mod, armorTypes: Set, modFolderData: String, modDictionary: Map): 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 } diff --git a/src/main/resources/db/0.0.2/data/armor_types.json b/src/main/resources/db/0.0.2/data/armor_types.json new file mode 100644 index 0000000..4db0b53 --- /dev/null +++ b/src/main/resources/db/0.0.2/data/armor_types.json @@ -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 + } + } + ] + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/db/0.0.2/schema/armor_types.json b/src/main/resources/db/0.0.2/schema/armor_types.json new file mode 100644 index 0000000..91e6859 --- /dev/null +++ b/src/main/resources/db/0.0.2/schema/armor_types.json @@ -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" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.2/schema/buildings.json b/src/main/resources/db/0.0.2/schema/buildings.json new file mode 100644 index 0000000..3588766 --- /dev/null +++ b/src/main/resources/db/0.0.2/schema/buildings.json @@ -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" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.2/schema/buildings_units.json b/src/main/resources/db/0.0.2/schema/buildings_units.json new file mode 100644 index 0000000..b7df54a --- /dev/null +++ b/src/main/resources/db/0.0.2/schema/buildings_units.json @@ -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" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.2/schema/sergeants.json b/src/main/resources/db/0.0.2/schema/sergeants.json new file mode 100644 index 0000000..4fffd46 --- /dev/null +++ b/src/main/resources/db/0.0.2/schema/sergeants.json @@ -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" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.2/schema/units.json b/src/main/resources/db/0.0.2/schema/units.json index 47877d4..a26a2f5 100644 --- a/src/main/resources/db/0.0.2/schema/units.json +++ b/src/main/resources/db/0.0.2/schema/units.json @@ -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" + } + } + ] + } } ] } diff --git a/src/main/resources/db/0.0.2/schema/weapons.json b/src/main/resources/db/0.0.2/schema/weapons.json new file mode 100644 index 0000000..d0783a5 --- /dev/null +++ b/src/main/resources/db/0.0.2/schema/weapons.json @@ -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" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/changelog-master.json b/src/main/resources/db/changelog-master.json index 5e9721c..69dd6b9 100644 --- a/src/main/resources/db/changelog-master.json +++ b/src/main/resources/db/changelog-master.json @@ -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" + } } ] }