diff --git a/pom.xml b/pom.xml index 4dc63c7..38e2d2e 100644 --- a/pom.xml +++ b/pom.xml @@ -106,6 +106,12 @@ kotlin-test 1.7.22 + + io.mockk + mockk-jvm + 1.13.17 + test + diff --git a/src/main/kotlin/com/dowstats/controllers/AssetsController.kt b/src/main/kotlin/com/dowstats/controllers/AssetsController.kt index 9867fd0..907a602 100644 --- a/src/main/kotlin/com/dowstats/controllers/AssetsController.kt +++ b/src/main/kotlin/com/dowstats/controllers/AssetsController.kt @@ -14,9 +14,11 @@ class AssetsController @Autowired constructor( val iconService: IconsService, ) { - @GetMapping("/icon/{raceIconFolder}/{imageName}") - fun getUnits(@PathVariable raceIconFolder: String, + @GetMapping("/icon/{modName}/{raceIconFolder}/{imageName}") + fun getUnits( + @PathVariable modName: String, + @PathVariable raceIconFolder: String, @PathVariable imageName: String,): ByteArray? { - return iconService.returnIcon(raceIconFolder, imageName) + return iconService.returnIcon(modName, raceIconFolder, imageName) } } \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/controllers/BuildingsController.kt b/src/main/kotlin/com/dowstats/controllers/BuildingsController.kt new file mode 100644 index 0000000..c4bc2ed --- /dev/null +++ b/src/main/kotlin/com/dowstats/controllers/BuildingsController.kt @@ -0,0 +1,47 @@ +package com.dowstats.controllers + +import com.dowstats.data.dto.controllers.RaceBuildings +import com.dowstats.data.dto.controllers.RaceUnits +import com.dowstats.data.entities.Building +import com.dowstats.data.entities.DowUnit +import com.dowstats.data.repositories.BuildingRepository +import com.dowstats.data.repositories.UnitRepository +import com.dowstats.service.datamaps.DowBuildingMappingService +import com.dowstats.service.datamaps.DowUnitMappingService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + + +@RestController +@RequestMapping("api/v1/buildings") +class BuildingsController @Autowired constructor( + val dowBuildingMappingService: DowBuildingMappingService, + val buildingRepository: BuildingRepository, +) { + + @GetMapping("/{modId}/{raceId}") + fun getBuildingsByModAndRace(@PathVariable modId: Long, + @PathVariable raceId: String): RaceBuildings { + return dowBuildingMappingService.findBuildingsByModAndRace(modId, raceId) + } + + @GetMapping("/mod/{modId}") + fun getUnitsByMod(@PathVariable modId: Long,): List { + return dowBuildingMappingService.findBuildingsByMod(modId) + } + + + @GetMapping("/{buildingId}") + fun getById(@PathVariable buildingId: Long): Building { + return buildingRepository.findById(buildingId).get() + } + + @DeleteMapping + fun removeAll() { + buildingRepository.deleteAll() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/dto/BuildingDataToSave.kt b/src/main/kotlin/com/dowstats/data/dto/BuildingDataToSave.kt new file mode 100644 index 0000000..ac22e60 --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/dto/BuildingDataToSave.kt @@ -0,0 +1,11 @@ +package com.dowstats.data.dto + +import com.dowstats.data.entities.Building +import com.dowstats.data.entities.DowUnit +import com.dowstats.data.entities.Sergeant +import com.dowstats.data.entities.Weapon + +data class BuildingDataToSave( + val building: Building, + val buildingWeapons: Set, +) \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/dto/UnitDataToSave.kt b/src/main/kotlin/com/dowstats/data/dto/UnitDataToSave.kt index a76778d..8c13d59 100644 --- a/src/main/kotlin/com/dowstats/data/dto/UnitDataToSave.kt +++ b/src/main/kotlin/com/dowstats/data/dto/UnitDataToSave.kt @@ -4,8 +4,14 @@ import com.dowstats.data.entities.DowUnit import com.dowstats.data.entities.Sergeant import com.dowstats.data.entities.Weapon +data class WeaponsData( + val hardpoint: Int, + val hardpointOrder: Int, + val weapon: Weapon, +) + data class UnitDataToSave( val unit: DowUnit, - val unitWeapons: Set, + val unitWeapons: List?, val sergeants: List>>?, ) \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/BuildingDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/BuildingDto.kt new file mode 100644 index 0000000..81e2846 --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/BuildingDto.kt @@ -0,0 +1,16 @@ +package com.dowstats.data.dto.controllers + +import com.dowstats.data.entities.Race + +data class RaceBuildings( + val race: Race, + val buildings: List, +) + +data class BuildingDto( + val name: String, + val icon: String, + val id: Long, + val armourTypeName: String, + val canDetect: Boolean, +) \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/entities/AddonModifiers.kt b/src/main/kotlin/com/dowstats/data/entities/AddonModifiers.kt new file mode 100644 index 0000000..1bb9ea5 --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/entities/AddonModifiers.kt @@ -0,0 +1,23 @@ +package com.dowstats.data.entities + +import com.fasterxml.jackson.annotation.JsonIgnore +import jakarta.persistence.* + + +@Entity +@Table(name = "addon_modifiers") +class AddonModifiers { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + + @ManyToOne + @JoinColumn(name = "addon_id", nullable = false) + @JsonIgnore + var addon: BuildingAddon? = null + + var references: String? = null + var usageType: String? = null + var value: String? = null +} diff --git a/src/main/kotlin/com/dowstats/data/entities/AddonRequires.kt b/src/main/kotlin/com/dowstats/data/entities/AddonRequires.kt new file mode 100644 index 0000000..d148516 --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/entities/AddonRequires.kt @@ -0,0 +1,25 @@ +package com.dowstats.data.entities + +import com.fasterxml.jackson.annotation.JsonIgnore +import jakarta.persistence.* + + +@Entity +@Table(name = "addon_requires") +class AddonRequires { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + + @ManyToOne + @JoinColumn(name = "addon_id", nullable = false) + @JsonIgnore + var addon: BuildingAddon? = null + + var references: String? = null + + var replaceWhenDone: Boolean = false + + var value: String? = null +} diff --git a/src/main/kotlin/com/dowstats/data/entities/Building.kt b/src/main/kotlin/com/dowstats/data/entities/Building.kt new file mode 100644 index 0000000..d744b36 --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/entities/Building.kt @@ -0,0 +1,50 @@ +package com.dowstats.data.entities + +import jakarta.persistence.* + + +@Entity +@Table(name = "buildings") +class Building { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + + @ManyToOne + @JoinColumn(name = "race_id", nullable = false) + var race: Race? = null + + @ManyToOne + @JoinColumn(name = "armour_type_id", nullable = false) + var armorType: ArmorType? = null + + @ManyToOne + @JoinColumn(name = "armour_type_2_id") + var armorType2: ArmorType? = null + + var name: String? = null + var description: String? = null + var filename: String? = null + var buildCostRequisition: Double? = null + var buildCostPower: Double? = null + var buildCostPopulation: Double? = null + var buildCostFaith: Double? = null + var buildCostSouls: Double? = null + var buildCostTime: Int? = null + var health: Int? = 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 + + @OneToMany(mappedBy = "building", cascade = [CascadeType.ALL]) + var addons: MutableSet? = null + + @OneToMany(mappedBy = "building", cascade = [CascadeType.ALL]) + var weapons: MutableSet? = null + + +} diff --git a/src/main/kotlin/com/dowstats/data/entities/BuildingAddon.kt b/src/main/kotlin/com/dowstats/data/entities/BuildingAddon.kt new file mode 100644 index 0000000..76cc9df --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/entities/BuildingAddon.kt @@ -0,0 +1,38 @@ +package com.dowstats.data.entities + +import com.fasterxml.jackson.annotation.JsonIgnore +import jakarta.persistence.* + + +@Entity +@Table(name = "building_addons") +class BuildingAddon { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null + + @ManyToOne + @JoinColumn(name = "building_id", nullable = false) + @JsonIgnore + var building: Building? = null + + var name: String? = null + var description: String? = null + var filename: String? = null + var addonCostRequisition: Double? = null + var addonCostPower: Double? = null + var addonCostPopulation: Double? = null + var addonCostFaith: Double? = null + var addonCostSouls: Double? = null + var addonCostTime: Int? = null + + @OneToMany(mappedBy = "addon", cascade = [CascadeType.ALL]) + var addonModifiers: MutableSet? = null + + @OneToMany(mappedBy = "addon", cascade = [CascadeType.ALL]) + var addonRequires: MutableSet? = null + + var icon: String? = null + +} diff --git a/src/main/kotlin/com/dowstats/data/entities/BuildingWeapon.kt b/src/main/kotlin/com/dowstats/data/entities/BuildingWeapon.kt new file mode 100644 index 0000000..6e5d25e --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/entities/BuildingWeapon.kt @@ -0,0 +1,42 @@ +package com.dowstats.data.entities + +import com.fasterxml.jackson.annotation.JsonIgnore +import jakarta.persistence.* +import java.io.Serializable + + +@Embeddable +class BuildingWeaponKey : Serializable { + @Column(name = "building_id") + var buildingId: Long? = null + + @Column(name = "weapon_id") + var weaponId: Long? = null + + @Column(nullable = false) + var hardpoint: Int = 0 + + @Column(nullable = false) + var hardpointOrder: Int = 0 +} + +@Entity +@Table(name = "buildings_weapons") +class BuildingWeapon { + + @EmbeddedId + @JsonIgnore + var buildingWeaponKey: BuildingWeaponKey? = null + + @ManyToOne + @MapsId("buildingId") + @JoinColumn(name = "building_id") + @JsonIgnore + var building: Building? = null + + @ManyToOne + @MapsId("weaponId") + @JoinColumn(name = "weapon_id") + var weapon: Weapon? = null + +} diff --git a/src/main/kotlin/com/dowstats/data/entities/SergantWeapon.kt b/src/main/kotlin/com/dowstats/data/entities/SergantWeapon.kt index 5b0ced2..505d059 100644 --- a/src/main/kotlin/com/dowstats/data/entities/SergantWeapon.kt +++ b/src/main/kotlin/com/dowstats/data/entities/SergantWeapon.kt @@ -13,6 +13,11 @@ class SergeantWeaponKey : Serializable { @Column(name = "weapon_id") var weaponId: Long? = null + @Column(nullable = false) + var hardpoint: Int = 0 + + @Column(nullable = false) + var hardpointOrder: Int = 0 } @Entity @@ -33,11 +38,4 @@ class SergeantWeapon { @MapsId("weaponId") @JoinColumn(name = "weapon_id") var weapon: Weapon? = null - - @Column(nullable = false) - var hardpoint: Int = 0 - - @Column(nullable = false) - var hardpointOrder: Int = 0 - } diff --git a/src/main/kotlin/com/dowstats/data/entities/UnitWeapon.kt b/src/main/kotlin/com/dowstats/data/entities/UnitWeapon.kt index 6689782..1a85d53 100644 --- a/src/main/kotlin/com/dowstats/data/entities/UnitWeapon.kt +++ b/src/main/kotlin/com/dowstats/data/entities/UnitWeapon.kt @@ -13,6 +13,12 @@ class UnitWeaponKey : Serializable { @Column(name = "weapon_id") var weaponId: Long? = null + @Column(name = "hardpoint") + var hardpoint: Int = 0 + + @Column(nullable = false) + var hardpointOrder: Int = 0 + } @Entity @@ -34,10 +40,4 @@ class UnitWeapon { @JoinColumn(name = "weapon_id") var weapon: Weapon? = null - @Column(nullable = false) - var hardpoint: Int = 0 - - @Column(nullable = false) - var hardpointOrder: Int = 0 - } diff --git a/src/main/kotlin/com/dowstats/data/entities/Weapon.kt b/src/main/kotlin/com/dowstats/data/entities/Weapon.kt index e5485a3..85d4892 100644 --- a/src/main/kotlin/com/dowstats/data/entities/Weapon.kt +++ b/src/main/kotlin/com/dowstats/data/entities/Weapon.kt @@ -18,6 +18,7 @@ class Weapon { var costTimeSeconds: Int? = null var accuracy: Double? = null var reloadTime: Double? = null + var minRange: Double? = null var maxRange: Double? = null var setupTime: Double? = null var accuracyReductionMoving: Double? = null @@ -25,6 +26,9 @@ class Weapon { var maxDamage: Double? = null var minDamageValue: Double? = null var moraleDamage: Double? = null + var damageRadius: Double? = null + var throwForceMin: Double? = null + var throwForceMax: Double? = null var isMeleeWeapon: Boolean = true var canAttackAir: Boolean = true var canAttackGround: Boolean = true diff --git a/src/main/kotlin/com/dowstats/data/repositories/AddonRepository.kt b/src/main/kotlin/com/dowstats/data/repositories/AddonRepository.kt new file mode 100644 index 0000000..1e3ccc9 --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/repositories/AddonRepository.kt @@ -0,0 +1,10 @@ +package com.dowstats.data.repositories + +import com.dowstats.data.entities.Building +import com.dowstats.data.entities.BuildingAddon +import com.dowstats.data.entities.DowUnit +import com.dowstats.data.entities.Race +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.CrudRepository + +interface AddonRepository : CrudRepository \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/repositories/BuildingRepository.kt b/src/main/kotlin/com/dowstats/data/repositories/BuildingRepository.kt new file mode 100644 index 0000000..989c651 --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/repositories/BuildingRepository.kt @@ -0,0 +1,19 @@ +package com.dowstats.data.repositories + +import com.dowstats.data.entities.Building +import com.dowstats.data.entities.DowUnit +import com.dowstats.data.entities.Race +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.CrudRepository + +interface BuildingRepository : CrudRepository{ + @Query(""" + select b, a1, a2 + from Building b + left join fetch ArmorType a1 on b.armorType.id = a1.id + left join fetch ArmorType a2 on b.armorType2.id = a2.id + where b.modId = :modId + and (:race is null or b.race = :race) + """) + fun findByModIdAndRace(modId: Long, race: Race?): List +} \ 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 new file mode 100644 index 0000000..698cd91 --- /dev/null +++ b/src/main/kotlin/com/dowstats/service/datamaps/DowBuildingMappingService.kt @@ -0,0 +1,65 @@ +package com.dowstats.service.datamaps + +import com.dowstats.data.dto.controllers.BuildingDto +import com.dowstats.data.dto.controllers.RaceBuildings +import com.dowstats.data.dto.controllers.RaceUnits +import com.dowstats.data.dto.controllers.UnitDto +import com.dowstats.data.entities.Building +import com.dowstats.data.entities.DowUnit +import com.dowstats.data.entities.Race +import com.dowstats.data.repositories.BuildingRepository +import com.dowstats.data.repositories.RaceRepository +import com.dowstats.data.repositories.UnitRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service + +@Service +class DowBuildingMappingService @Autowired constructor( + val buildingRepository: BuildingRepository, + val raceRepository: RaceRepository +) { + + fun findBuildingsByMod(modId: Long): List { + return getAllBuildings(modId).groupBy { it.race }.mapNotNull {raceUnits -> + RaceBuildings(raceUnits.key ?: throw Exception("Race is null"), raceUnits.value.toBuildingDto()) + } + } + + fun findBuildingsByModAndRace(modId: Long, race: String): RaceBuildings { + val buildings = getAllBuildings(modId, race) + val raceEntity = race.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") } + return RaceBuildings(raceEntity, buildings.toBuildingDto()) + } + + + + private fun List.toBuildingDto(): List = + this.mapNotNull { + val name = it.name ?: it.filename + val icon = it.icon + if (name == null || icon == null) null else BuildingDto(name, icon, it.id!!, + it.armorType?.name!!, + (it.detectRadius ?: 0) > 0) + } + + + private fun getAllBuildings(modId: Long, race: String? = null): List { + val raceEntity = race?.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") } + return filterCompanyUnits(buildingRepository.findByModIdAndRace(modId, raceEntity)) + } + + private fun filterCompanyUnits(buildings: List): List = + buildings.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 + && 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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/service/datamaps/DowUnitService.kt b/src/main/kotlin/com/dowstats/service/datamaps/DowUnitMappingService.kt similarity index 100% rename from src/main/kotlin/com/dowstats/service/datamaps/DowUnitService.kt rename to src/main/kotlin/com/dowstats/service/datamaps/DowUnitMappingService.kt diff --git a/src/main/kotlin/com/dowstats/service/integrations/ModStorageIntegrationService.kt b/src/main/kotlin/com/dowstats/service/integrations/ModStorageIntegrationService.kt index 92ab5f5..f753d92 100644 --- a/src/main/kotlin/com/dowstats/service/integrations/ModStorageIntegrationService.kt +++ b/src/main/kotlin/com/dowstats/service/integrations/ModStorageIntegrationService.kt @@ -54,14 +54,19 @@ class ModStorageIntegrationService( .sortedBy { it.id } newVersions.map { toSave -> - downloadAndExtractMod(toSave.technicalName, toSave.version) - val savedMod = modRepository.save(Mod().also { - it.version = toSave.version - it.isBeta = toSave.isBeta - it.name = "Dowstats balance mod" - it.technicalName = toSave.technicalName - }) - modParserService.parceModFilesAndSaveToDb(savedMod) + try{ + downloadAndExtractMod(toSave.technicalName, toSave.version) + val savedMod = modRepository.save(Mod().also { + it.version = toSave.version + it.isBeta = toSave.isBeta + it.name = "Dowstats balance mod" + it.technicalName = toSave.technicalName + }) + modParserService.parceModFilesAndSaveToDb(savedMod) + } catch (e: Exception) { + log.error("Error while download and extract mod", e) + } + } } diff --git a/src/main/kotlin/com/dowstats/service/w40k/BuildingAddonRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/BuildingAddonRgdExtractService.kt new file mode 100644 index 0000000..7566a4e --- /dev/null +++ b/src/main/kotlin/com/dowstats/service/w40k/BuildingAddonRgdExtractService.kt @@ -0,0 +1,116 @@ +package com.dowstats.service.w40k + +import com.dowstats.data.dto.BuildCost +import com.dowstats.data.entities.* +import com.dowstats.data.rgd.RgdData +import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName +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 BuildingAddonRgdExtractService @Autowired constructor( + private val modAttribPathService: ModAttribPathService, + private val iconsService: IconsService, +) { + + val log = LoggerFactory.getLogger(BuildingAddonRgdExtractService::class.java) + + data class AddonTexts( + val name: String?, + val description: String?, + ) + + fun extractToAddonEntity( + fileName: String, + modDictionary: Map, + addonRgdData: List, + modFolderData: String, + building: Building, + mod: Mod, + ): BuildingAddon { + + val addon = BuildingAddon() + + val nameAndDescription = getAddonNameAndDescription(addonRgdData, modDictionary) + addon.name = nameAndDescription.name + addon.description = nameAndDescription.description + addon.building = building + addon.filename = fileName + + val buildCost = getAddonCost(addonRgdData) + addon.addonCostRequisition = buildCost.requisition + addon.addonCostPower = buildCost.power + addon.addonCostPopulation = buildCost.population + addon.addonCostFaith = buildCost.faith + addon.addonCostSouls = buildCost.souls + addon.addonCostTime = buildCost.time + + val addonIcon = convertIconAndReturnPath(addonRgdData, modFolderData, mod.name) + addon.icon = addonIcon + + + return addon + } + + + private fun getAddonCost(addonData: List): BuildCost { + + val cost = addonData.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 getAddonNameAndDescription(addonData: List, modDictionary: Map): AddonTexts { + val uiInfo = addonData.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 AddonTexts(name, description) + } + + + private fun convertIconAndReturnPath(buildingData: List, modFolderData: String, modName: String?): String? { + val iconPathInMod = buildingData + .getRgdTableByName("ui_info") + ?.getStringByName("icon_name") + ?.replace("/", File.separator) + + + val tgaIconPath = iconPathInMod?.let { + val modIcon = modAttribPathService.getIconPath(modFolderData, it) + if(Path.of(modIcon).exists()) modIcon else + modAttribPathService.getIconPath(modAttribPathService.pathToWanilaData, it) + } + + return tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it, modName) } + } + +} diff --git a/src/main/kotlin/com/dowstats/service/w40k/BuildingRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/BuildingRgdExtractService.kt new file mode 100644 index 0000000..162b9a4 --- /dev/null +++ b/src/main/kotlin/com/dowstats/service/w40k/BuildingRgdExtractService.kt @@ -0,0 +1,239 @@ +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, + buildingData: List, + weapons: Set, + race: String, + modFolderData: String, + mod: Mod, + races: List, + armorTypes: List, + 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") + 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 { + it?.hardpoint = weaponData.hardpoint + it?.hardpointOrder = weaponData.hardpointOrder + } + }.orEmpty().toSet() + + building.modId = mod.id + + return BuildingDataToSave(building, buildingWeapons) + } + + + private fun getBuildingArmourType( + unitData: List, + armorTypes: Iterable, + 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): 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, modDictionary: Map): 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): 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): Double? = unitData + .getRgdTableByName("sight_ext") + ?.getDoubleByName("sight_radius") + + private fun getDetectRadius(unitData: List): Double? = unitData + .getRgdTableByName("sight_ext") + ?.getDoubleByName("keen_sight_radius") + + + private fun getBuildingWeapon(buildingData: List?): List? = 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 + hardpointTable.getRgdTableByName("weapon_table")?.let { + it.mapNotNull { weapon -> + (weapon.value as? List)?.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, modFolderData: String, modName: String?): String? { + val iconPathInMod = buildingData + .getRgdTableByName("ui_ext") + ?.getRgdTableByName("ui_info") + ?.getStringByName("icon_name") + ?.replace("/", File.separator) + + + val tgaIconPath = iconPathInMod?.let { + val modIcon = modAttribPathService.getIconPath(modFolderData, it) + if(Path.of(modIcon).exists()) modIcon else + modAttribPathService.getIconPath(modAttribPathService.pathToWanilaData, it) + } + + return tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it, modName) } + } + + + private fun getAddons(buildingData: List): List? = buildingData + .getRgdTableByName("addon_ext") + ?.getRgdTableByName("addons") + ?.mapNotNull { addon -> + if (addon.value != "") { + addon.value.toString() + .replace("addons\\", "") + .replace(".lua", "") + .plus(".rgd") + } else null + } +} + diff --git a/src/main/kotlin/com/dowstats/service/w40k/IconsService.kt b/src/main/kotlin/com/dowstats/service/w40k/IconsService.kt index b9101c3..e5eb2f7 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/IconsService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/IconsService.kt @@ -20,7 +20,7 @@ class IconsService @Autowired constructor( * @param pathToTgaIcon - путь до иконки * @return путь до сконвертированной иконки */ - fun convertTgaToJpegImage(iconPathInMod: String, pathToTgaIcon: String): String? { + fun convertTgaToJpegImage(iconPathInMod: String, pathToTgaIcon: String, modName: String? = null): String? { try{ val image: BufferedImage = try { ImageIO.read(File(pathToTgaIcon)) @@ -28,7 +28,10 @@ class IconsService @Autowired constructor( ImageIO.read(File(pathToTgaIcon.lowercase())) } - val pathToSave = "${storageConfig.iconsStorage.replace("/", File.separator)}${File.separator}${iconPathInMod.replace("\\", File.separator)}.png" + val modFolder = modName?.let { "${File.separator}$modName" } ?: "" + + val pathToSave = "${storageConfig.iconsStorage.replace("/", File.separator)}$modFolder" + + "${File.separator}${iconPathInMod.replace("\\", File.separator)}.png" val directoryToSave = File(pathToSave.split(File.separator).dropLast(1).joinToString (File.separator)) if(!directoryToSave.exists()) directoryToSave.mkdirs() @@ -45,8 +48,8 @@ class IconsService @Autowired constructor( } - fun returnIcon(raceIconFolder: String, iconName: String): ByteArray? { - val pathToIcon = "${storageConfig.iconsStorage.replace("/", File.separator)}${File.separator}$raceIconFolder${File.separator}$iconName" + fun returnIcon(modName: String, raceIconFolder: String, iconName: String): ByteArray? { + val pathToIcon = "${storageConfig.iconsStorage.replace("/", File.separator)}${File.separator}$modName${File.separator}$raceIconFolder${File.separator}$iconName" return File(pathToIcon).readBytes() } diff --git a/src/main/kotlin/com/dowstats/service/w40k/ModAttribPathService.kt b/src/main/kotlin/com/dowstats/service/w40k/ModAttribPathService.kt index aaebe08..195f1ef 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/ModAttribPathService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/ModAttribPathService.kt @@ -21,6 +21,9 @@ class ModAttribPathService @Autowired constructor( fun getWeaponAttribsPath(modFolderData: String): String = "$modFolderData${File.separator}attrib${File.separator}weapon" + fun getAddonAttribsPath(modFolderData: String): String = + "$modFolderData${File.separator}attrib${File.separator}addons" + fun getSbpsAttribsFolderPath(modFolderData: String): String = "$modFolderData${File.separator}attrib${File.separator}sbps${File.separator}races${File.separator}" diff --git a/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt b/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt index 4b1223c..269d9dd 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt @@ -24,6 +24,8 @@ class ModParserService @Autowired constructor( val weaponRgdExtractService: WeaponRgdExtractService, val weaponRepository: WeaponRepository, val modAttribPathService: ModAttribPathService, + val buildingRepository: BuildingRepository, + val buildingExtractService: BuildingRgdExtractService, ) { val defaultDictionary: MutableMap = mutableMapOf() @@ -53,8 +55,6 @@ class ModParserService @Autowired constructor( log.info("Start parse mod files ${mod.technicalName}:${mod.version}") - val modId = mod.id!! - val modFolderData = modAttribPathService.getModFolderData(mod.technicalName!!, mod.version!!) val racesList = Files.walk(Path(modAttribPathService.getSbpsAttribsFolderPath(modFolderData)), 1) .toList() @@ -62,10 +62,9 @@ class ModParserService @Autowired constructor( .filter { Files.isDirectory(it) }.map { it.name }.toList() racesList.forEach{ - unitRepository.deleteAllByModIdAndRaceId(modId, it) + unitRepository.deleteAllByModIdAndRaceId(mod.id!!, it) } - val modDictionary: MutableMap = mutableMapOf() log.info("Extract dictionaries from $modFolderData") @@ -73,7 +72,10 @@ class ModParserService @Autowired constructor( it.bufferedReader(Charsets.UTF_8).lines().forEach { val kv = it.split("\\s+".toRegex()) if (kv.size < 2) return@forEach - val key = kv.first().filter { it.isDigit() }.toInt() + val key = try {kv.first().filter { it.isDigit() }.toInt()} catch(e: Exception) { + log.warn("Cant' get key ${kv.first()} for dict") + 0 + } val value = kv.drop(1).joinToString(" ").replace("\u0000","") modDictionary[key] = value } @@ -81,25 +83,20 @@ class ModParserService @Autowired constructor( val enrichedModDictionary = modDictionary + defaultDictionary - val weapons = saveWeapons(modFolderData, modId, enrichedModDictionary) - saveUnits(modFolderData, weapons, racesList, modId, enrichedModDictionary) + val weapons = saveWeapons(modFolderData, mod, enrichedModDictionary) + saveUnits(modFolderData, weapons, racesList, mod, enrichedModDictionary) + + saveBuildings(modFolderData, weapons, racesList, mod, enrichedModDictionary) } - fun saveUnits(modFolderData: String, weapons: Set, racesList: List, modId: Long, modDictionary: Map) { + fun saveUnits(modFolderData: String, weapons: Set, racesList: List, mod: Mod, modDictionary: Map) { val races = raceRepository.findAll().toList() val armorTypes = armorTypeRepository.findAll().toList() racesList.forEach { raceFolder -> - if( raceRepository.findById(raceFolder) == null){ - val race = Race().also { it.id = raceFolder; it.name = raceFolder } - raceRepository.save(race) - } - - println(raceFolder) - val classicRgdDataSquads = rgdParserService.parseFolderToRgdFiles(modAttribPathService.getSbpsAttribsPath(modAttribPathService.pathToWanilaData, raceFolder)) val modRgdDataSquads = @@ -136,7 +133,7 @@ class ModParserService @Autowired constructor( weapons, raceFolder, modFolderData, - modId, + mod, races, armorTypes, modUnitsFull @@ -148,18 +145,18 @@ class ModParserService @Autowired constructor( try { val unit = unitRepository.save(unitDataToSave.unit) - unit.weapons = unitDataToSave.unitWeapons.map {weapon -> + unit.weapons = unitDataToSave.unitWeapons?.map {weapon -> UnitWeapon().also { it.unit = unit - it.weapon = weapon - it.hardpoint = weapon.hardpoint - it.hardpointOrder = weapon.hardpointOrder + it.weapon = weapon.weapon it.unitWeaponKey = UnitWeaponKey().also { it.unitId = unit.id - it.weaponId = weapon.id + it.weaponId = weapon.weapon?.id + it.hardpoint = weapon.hardpoint + it.hardpointOrder = weapon.hardpointOrder } } - }.toMutableSet() + }?.toMutableSet() unitRepository.save(unit) @@ -171,11 +168,12 @@ class ModParserService @Autowired constructor( SergeantWeapon().also { it.sergeant = sergeant it.weapon = weapon - it.hardpoint = weapon.hardpoint - it.hardpointOrder = weapon.hardpointOrder + it.sergeantWeaponKey = SergeantWeaponKey().also { swk -> swk.sergeantId = sergeant.id swk.weaponId = weapon.id + swk.hardpoint = weapon.hardpoint + swk.hardpointOrder = weapon.hardpointOrder } } }.toMutableSet() @@ -188,7 +186,80 @@ class ModParserService @Autowired constructor( } } - fun saveWeapons(modFolderData: String, modId: Long, modDictionary: Map): Set { + fun saveBuildings(modFolderData: String, + weapons: Set, + racesList: List, + mod: Mod, modDictionary: Map) { + + val races = raceRepository.findAll().toList() + val armorTypes = armorTypeRepository.findAll().toList() + + val addonsRgdData = getAddonsRgdData(modFolderData) + + racesList.forEach { raceFolder -> + val classicRgdDataStructures = + rgdParserService.parseFolderToRgdFiles(modAttribPathService.geBuildingAttribsPath(modAttribPathService.pathToWanilaData, raceFolder)) + val modRgdDataStructures = + rgdParserService.parseFolderToRgdFiles(modAttribPathService.geBuildingAttribsPath(modFolderData, raceFolder)) + + val modStructuresFull = classicRgdDataStructures + modRgdDataStructures + + modStructuresFull.forEach { structure -> + val structureDataToSave = + try { + buildingExtractService.extractToBuildingEntity( + structure.key, + modDictionary, + structure.value, + weapons, + raceFolder, + modFolderData, + mod, + races, + armorTypes, + addonsRgdData, + ) + } catch (e: Exception) { + log.error("Can't extract ${structure.key}", e) + return@forEach + } + + try { + val building = buildingRepository.save(structureDataToSave.building) + building.weapons = structureDataToSave.buildingWeapons.map {weapon -> + BuildingWeapon().also { + it.building = building + it.weapon = weapon + it.buildingWeaponKey = BuildingWeaponKey().also { + it.buildingId = building.id + it.weaponId = weapon.id + it.hardpoint = weapon.hardpoint + it.hardpointOrder = weapon.hardpointOrder + } + } + }.toMutableSet() + buildingRepository.save(building) + + } catch (e: Exception) { + throw e + } + } + + + } + } + + fun getAddonsRgdData(modFolderData: String): Map> { + + val classicAddons = rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/addons") + + val modAddons = + rgdParserService.parseFolderToRgdFiles(modAttribPathService.getAddonAttribsPath(modFolderData)) + + return classicAddons + modAddons + } + + fun saveWeapons(modFolderData: String, mod: Mod, modDictionary: Map): Set { val classicWeapons = rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/weapon") @@ -198,7 +269,7 @@ class ModParserService @Autowired constructor( val allWeapons = classicWeapons + modWeapons val weaponsToSave = allWeapons.mapNotNull { - weaponRgdExtractService.extractToWeaponEntity(it.key, it.value, modId, modFolderData, modDictionary) + weaponRgdExtractService.extractToWeaponEntity(it.key, it.value, mod, 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 bfaf514..794a3bf 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/SergantRgdExtractService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/SergantRgdExtractService.kt @@ -48,6 +48,7 @@ class SergeantRgdExtractService @Autowired constructor( fun extractToSergeantEntity( fileName: String, modDictionary: Map, + mod: Mod, sergeantData: List, weapons: Set, modFolderData: String, @@ -84,7 +85,7 @@ class SergeantRgdExtractService @Autowired constructor( sergeant.mass = massData.mass sergeant.upTime = massData.upTime - val unitIcon = convertSergeantIconAndReturnPath(sergeantData, modFolderData) + val unitIcon = convertSergeantIconAndReturnPath(sergeantData, modFolderData, mod.name) sergeant.icon = unitIcon val sergeantWeapons = getSergeantWeapons(sergeantData)?.mapNotNull { weaponData -> @@ -183,7 +184,7 @@ class SergeantRgdExtractService @Autowired constructor( } else null }?.flatten() - private fun convertSergeantIconAndReturnPath(sergeantData: List, modFolderData: String): String? { + private fun convertSergeantIconAndReturnPath(sergeantData: List, modFolderData: String, modName: String?): String? { val iconPathInMod = sergeantData .getRgdTableByName("ui_ext") ?.getRgdTableByName("ui_info") @@ -197,7 +198,7 @@ class SergeantRgdExtractService @Autowired constructor( modAttribPathService.getIconPath(modAttribPathService.pathToWanilaData, it) } - return tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it) } + return tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it, modName) } } } diff --git a/src/main/kotlin/com/dowstats/service/w40k/UnitRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/UnitRgdExtractService.kt index cfd7660..6c7d88e 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/UnitRgdExtractService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/UnitRgdExtractService.kt @@ -2,6 +2,7 @@ package com.dowstats.service.w40k import com.dowstats.data.dto.BuildCost import com.dowstats.data.dto.UnitDataToSave +import com.dowstats.data.dto.WeaponsData import com.dowstats.data.entities.* import com.dowstats.data.rgd.RgdData import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName @@ -36,12 +37,6 @@ class UnitRgdExtractService @Autowired constructor( val regeneration: Double?, ) - data class WeaponsData( - val hardpoint: Int, - val hardpointOrder: Int, - val weaponFilename: String, - ) - data class SergeantData( val filePath: String, val cost: BuildCost, @@ -65,7 +60,7 @@ class UnitRgdExtractService @Autowired constructor( weapons: Set, race: String, modFolderData: String, - modId: Long, + mod: Mod, races: List, armorTypes: List, modUnitsFull: Map>, @@ -131,10 +126,11 @@ class UnitRgdExtractService @Autowired constructor( val sergeantsEntities: List>>? = sergeantsData.first?.mapNotNull { sergeantData -> val sergeantFile = sergeantData.filePath.split("\\").last().replace(".lua", ".rgd") val sergeantRgdData = modUnitsFull[sergeantFile] - if(sergeantRgdData != null){ + if (sergeantRgdData != null) { sergeantRgdExtractService.extractToSergeantEntity( sergeantFile, modDictionary, + mod, sergeantRgdData, weapons, modFolderData, @@ -146,19 +142,12 @@ class UnitRgdExtractService @Autowired constructor( unit.maxSergeants = sergeantsData.second - val unitIcon = convertIconAndReturnPath(squadData, modFolderData) + val unitIcon = convertIconAndReturnPath(mod.name, squadData, modFolderData) unit.icon = unitIcon - val unitWeapons = getUnitWeapons(unitData)?.mapNotNull { weaponData -> - weapons.find { - it.filename == weaponData.weaponFilename + ".rgd" - }.also { - it?.hardpoint = weaponData.hardpoint - it?.hardpointOrder = weaponData.hardpointOrder - } - }.orEmpty().toSet() + val unitWeapons = getUnitWeapons(unitData, weapons) - unit.modId = modId + unit.modId = mod.id return UnitDataToSave(unit, unitWeapons, sergeantsEntities) } @@ -212,16 +201,28 @@ class UnitRgdExtractService @Autowired constructor( val nameRef = uiInfo?.getStringByName("screen_name_id")?.replace("$", "") - val name = nameRef?.let { try{modDictionary[it.toInt()]} catch (e: Exception) { null } } + 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 } } + ?.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) { + descriptionRefs?.map { modDictionary[it.toInt()] }?.joinToString("\n") + } catch (e: Exception) { log.warn("Error parsing ui description", e) null } @@ -321,20 +322,34 @@ class UnitRgdExtractService @Autowired constructor( private fun getReinforceTime(reinforceData: List?): Int? = reinforceData ?.getIntByName("time_seconds") - private fun getUnitWeapons(reinforceData: List?): List? = reinforceData + private fun getUnitWeapons(reinforceData: List?, weapons: Set): List? = reinforceData ?.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 + + + hardpointTable.getRgdTableByName("weapon_table")?.let { it.mapNotNull { weapon -> (weapon.value as? List)?.getStringByName("weapon")?.let { if (it != "") { - WeaponsData(hardpointValue, - weapon.name.replace("weapon_", "").toInt(), - it.replace("weapon\\", "").replace(".lua", "")) + val weaponFileName = it.replace("weapon\\", "").replace(".lua", ".rgd") + val weaponEntity = weapons.find { + it.filename == weaponFileName + } + if(weaponEntity == null){ + log.warn("Can't find weapon $weaponFileName") + null + } else { + WeaponsData( + hardpointValue, + weapon.name.replace("weapon_", "").toInt(), + weaponEntity + ) + } } else null } } @@ -350,34 +365,35 @@ class UnitRgdExtractService @Autowired constructor( val maxSergeants = squadTable?.getIntByName("max_leaders") val sergeantsData = squadTable?.mapNotNull { sergeantData -> - if (sergeantData.name.contains("leader_")) { - val sergeantRgdTable = sergeantData.value as List + if (sergeantData.name.contains("leader_")) { + val sergeantRgdTable = sergeantData.value as List - val sergeantLeaderFilePath = sergeantRgdTable.getRgdTableByName("leader")?.getStringByName("type") + val sergeantLeaderFilePath = sergeantRgdTable.getRgdTableByName("leader")?.getStringByName("type") - if(sergeantLeaderFilePath == null || sergeantLeaderFilePath == "") null else { + if (sergeantLeaderFilePath == null || sergeantLeaderFilePath == "") null else { - val cost = sergeantRgdTable.getRgdTableByName("cost_time") - val costResources = cost?.getRgdTableByName("cost") + val cost = sergeantRgdTable.getRgdTableByName("cost_time") + val costResources = cost?.getRgdTableByName("cost") - SergeantData(sergeantLeaderFilePath, - BuildCost( - costResources?.getDoubleByName("requisition"), - costResources?.getDoubleByName("power"), - costResources?.getDoubleByName("population"), - costResources?.getDoubleByName("faith"), - costResources?.getDoubleByName("souls"), - cost?.getIntByName("time_seconds"), - ) + SergeantData( + sergeantLeaderFilePath, + BuildCost( + costResources?.getDoubleByName("requisition"), + costResources?.getDoubleByName("power"), + costResources?.getDoubleByName("population"), + costResources?.getDoubleByName("faith"), + costResources?.getDoubleByName("souls"), + cost?.getIntByName("time_seconds"), ) - } - } else null - } + ) + } + } else null + } return Pair(sergeantsData, maxSergeants) } - private fun convertIconAndReturnPath(squadData: List, modFolderData: String): String? { + private fun convertIconAndReturnPath(modName: String?, squadData: List, modFolderData: String): String? { val iconPathInMod = squadData .getRgdTableByName("squad_ui_ext") ?.getRgdTableByName("ui_info") @@ -387,11 +403,11 @@ class UnitRgdExtractService @Autowired constructor( val tgaIconPath = iconPathInMod?.let { val modIcon = modAttribPathService.getIconPath(modFolderData, it) - if(Path.of(modIcon).exists()) modIcon else + if (Path.of(modIcon).exists()) modIcon else modAttribPathService.getIconPath(modAttribPathService.pathToWanilaData, it) } - return tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it) } + return tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it, modName) } } } diff --git a/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt index c83e5be..a70d3bc 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt @@ -1,6 +1,7 @@ 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 @@ -32,9 +33,12 @@ class WeaponRgdExtractService @Autowired constructor( val seconds: Int? ) - data class ArmourDamage( + 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, @@ -47,14 +51,14 @@ class WeaponRgdExtractService @Autowired constructor( val haveEquipButton: Boolean ) - fun extractToWeaponEntity(weaponFileName: String, weaponData: List, modId: Long, modFolderData: String, modDictionary: Map): Weapon? { + fun extractToWeaponEntity(weaponFileName: String, weaponData: List, mod: Mod, modFolderData: String, modDictionary: Map): Weapon? { val armorTypes = armorTypeRepository.findAll().toSet() val weapon = Weapon() weapon.filename = weaponFileName - val weaponUiData = getWeaponNameAndDescription(weaponData, modDictionary, modFolderData) + val weaponUiData = getWeaponNameAndDescription(weaponData, modDictionary, modFolderData, mod) weapon.name = weaponUiData.name weapon.icon = weaponUiData.iconPath weapon.description = weaponUiData.description @@ -67,21 +71,26 @@ class WeaponRgdExtractService @Autowired constructor( weapon.costTimeSeconds = cost.seconds ?: 0 weapon.accuracy = weaponData.getDoubleByName("accuracy") 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") ?: false - weapon.canAttackGround = weaponData.getBooleanByName("can_attack_ground_units") ?: false + weapon.canAttackAir = weaponData.getBooleanByName("can_attack_air_units") ?: true + weapon.canAttackGround = weaponData.getBooleanByName("can_attack_ground_units") ?: true - val armourDamage = getArmourDamage(weaponData, armorTypes, weapon) - weapon.minDamageValue = armourDamage.minDamageValue - weapon.minDamage = armourDamage.minDamage - weapon.maxDamage = armourDamage.maxDamage - weapon.moraleDamage = armourDamage.moraleDamage - weapon.weaponPiercings = armourDamage.armourPiercing - weapon.modId = modId + 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 @@ -97,8 +106,15 @@ class WeaponRgdExtractService @Autowired constructor( return BuildCost(requisition, power, costTime) } - private fun getArmourDamage(weaponData: List, armorTypes: Set, thisWeapon: Weapon): ArmourDamage { - val armourDamage = weaponData.getRgdTableByName("area_effect") + private fun getAreaEffectData(weaponData: List, armorTypes: Set, thisWeapon: Weapon): AreaEffectData { + val areaEffect = weaponData.getRgdTableByName("area_effect") + val damageRadius = areaEffect?.getRgdTableByName("area_effect_information")?.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")!! @@ -106,6 +122,7 @@ class WeaponRgdExtractService @Autowired constructor( val minDamageValue = armourDamage.getDoubleByName("min_damage_value")!! val moraleDamage = armourDamage.getDoubleByName("morale_damage")!! + val defaultArmourPiercing = armourDamage.getDoubleByName("armour_piercing") val weaponDmgMap: Map = armourDamage.getRgdTableByName("armour_piercing_types")!!.mapNotNull { armour_piercing -> @@ -125,11 +142,11 @@ class WeaponRgdExtractService @Autowired constructor( weaponArmourPiercing } - return ArmourDamage(minDamage, maxDamage, minDamageValue, moraleDamage, armoursPiercing) + return AreaEffectData(minDamage, maxDamage, damageRadius, forceMin, forceMax, minDamageValue, moraleDamage, armoursPiercing) } - private fun getWeaponNameAndDescription(weaponData: List, modDictionary: Map, modFolderData: String): WeaponUiInfo { + 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("$", "") @@ -156,7 +173,7 @@ class WeaponRgdExtractService @Autowired constructor( if(Path.of(modIcon).exists()) modIcon else modAttribPathService.getIconPath(modAttribPathService.pathToWanilaData, it) } - tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPath, it) } + tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPath, it, mod.name) } } catch (e: Exception) { log.error("Error parsing ui icon path", e) null diff --git a/src/main/resources/db/0.0.1/schema/addon_modifiers.json b/src/main/resources/db/0.0.1/schema/addon_modifiers.json new file mode 100644 index 0000000..2a86160 --- /dev/null +++ b/src/main/resources/db/0.0.1/schema/addon_modifiers.json @@ -0,0 +1,63 @@ +{ + "databaseChangeLog": [ + { + "changeSet": { + "id": "Add buildings table", + "author": "anibus", + "changes": [ + { + "createTable": { + "tableName": "addon_modifiers", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoIncrement": true, + "constraints": { + "primaryKey": true, + "nullable": false + } + } + },{ + "column": { + "name": "references", + "type": "varchar(255)" + } + },{ + "column": { + "name": "usage_type", + "type": "varchar(255)" + } + },{ + "column": { + "name": "value", + "type": "number" + } + },{ + "column": { + "name": "addon_id", + "type": "int", + "constraints": { + "nullable": false + } + } + } + ] + } + }, + { + "addForeignKeyConstraint": + { + "baseColumnNames": "addon_id", + "baseTableName": "addon_modifiers", + "constraintName": "fk_building_addons_addon_modifiers", + "referencedColumnNames": "id", + "referencedTableName": "building_addons" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.1/schema/addon_requires.json b/src/main/resources/db/0.0.1/schema/addon_requires.json new file mode 100644 index 0000000..4fe22a2 --- /dev/null +++ b/src/main/resources/db/0.0.1/schema/addon_requires.json @@ -0,0 +1,63 @@ +{ + "databaseChangeLog": [ + { + "changeSet": { + "id": "Add buildings table", + "author": "anibus", + "changes": [ + { + "createTable": { + "tableName": "addon_requires", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoIncrement": true, + "constraints": { + "primaryKey": true, + "nullable": false + } + } + },{ + "column": { + "name": "references", + "type": "varchar(255)" + } + },{ + "column": { + "name": "value", + "type": "varchar(255)" + } + },{ + "column": { + "name": "replace_when_done", + "type": "boolean" + } + },{ + "column": { + "name": "addon_id", + "type": "int", + "constraints": { + "nullable": false + } + } + } + ] + } + }, + { + "addForeignKeyConstraint": + { + "baseColumnNames": "addon_id", + "baseTableName": "addon_requires", + "constraintName": "fk_building_addons_addon_requires", + "referencedColumnNames": "id", + "referencedTableName": "building_addons" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.1/schema/building_addons.json b/src/main/resources/db/0.0.1/schema/building_addons.json new file mode 100644 index 0000000..d380367 --- /dev/null +++ b/src/main/resources/db/0.0.1/schema/building_addons.json @@ -0,0 +1,104 @@ +{ + "databaseChangeLog": [ + { + "changeSet": { + "id": "Add buildings table", + "author": "anibus", + "changes": [ + { + "createTable": { + "tableName": "building_addons", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoIncrement": true, + "constraints": { + "primaryKey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "filename", + "type": "varchar(255)", + "constraints": { + "nullable": false + } + } + },{ + "column": { + "name": "name", + "type": "varchar(255)" + } + },{ + "column": { + "name": "description", + "type": "varchar(5000)" + } + }, + { + "column": { + "name": "addon_cost_requisition", + "type": "number" + } + },{ + "column": { + "name": "addon_cost_power", + "type": "number" + } + },{ + "column": { + "name": "addon_cost_population", + "type": "number" + } + },{ + "column": { + "name": "addon_cost_faith", + "type": "number" + } + },{ + "column": { + "name": "addon_cost_souls", + "type": "number" + } + },{ + "column": { + "name": "addon_cost_time", + "type": "int" + } + },{ + "column": { + "name": "icon", + "type": "varchar(128)" + } + }, + { + "column": { + "name": "building_id", + "type": "int", + "constraints": { + "nullable": false + } + } + } + ] + } + }, + { + "addForeignKeyConstraint": + { + "baseColumnNames": "building_id", + "baseTableName": "building_addons", + "constraintName": "fk_building_addons_buildings", + "referencedColumnNames": "id", + "referencedTableName": "buildings" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.1/schema/buildings.json b/src/main/resources/db/0.0.1/schema/buildings.json new file mode 100644 index 0000000..8748a3f --- /dev/null +++ b/src/main/resources/db/0.0.1/schema/buildings.json @@ -0,0 +1,170 @@ +{ + "databaseChangeLog": [ + { + "changeSet": { + "id": "Add buildings table", + "author": "anibus", + "changes": [ + { + "createTable": { + "tableName": "buildings", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoIncrement": true, + "constraints": { + "primaryKey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "filename", + "type": "varchar(255)", + "constraints": { + "nullable": false + } + } + },{ + "column": { + "name": "name", + "type": "varchar(4096)" + } + },{ + "column": { + "name": "description", + "type": "varchar(5000)" + } + }, + { + "column": { + "name": "build_cost_requisition", + "type": "number" + } + },{ + "column": { + "name": "build_cost_power", + "type": "number" + } + },{ + "column": { + "name": "build_cost_population", + "type": "number" + } + },{ + "column": { + "name": "build_cost_faith", + "type": "number" + } + },{ + "column": { + "name": "build_cost_souls", + "type": "number" + } + },{ + "column": { + "name": "build_cost_time", + "type": "int" + } + },{ + "column": { + "name": "health", + "type": "int" + } + },{ + "column": { + "name": "health_regeneration", + "type": "number" + } + },{ + "column": { + "name": "race_id", + "type": "varchar(50)", + "constraints": { + "nullable":false + } + } + },{ + "column": { + "name": "armour_type_id", + "type": "varchar(50)", + "constraints": { + "nullable":false + } + } + },{ + "column": { + "name": "armour_type_2_id", + "type": "varchar(50)" + } + },{ + "column": { + "name": "sight_radius", + "type": "int" + } + },{ + "column": { + "name": "detect_radius", + "type": "int" + } + },{ + "column": { + "name": "repair_max", + "type": "int" + } + },{ + "column": { + "name": "mod_id", + "type": "int", + "constraints": { + "nullable": false + } + } + }, + { + "column": { + "name": "icon", + "type": "varchar(128)" + } + } + ] + } + }, + { + "addForeignKeyConstraint": + { + "baseColumnNames": "race_id", + "baseTableName": "buildings", + "constraintName": "fk_buildings_races", + "referencedColumnNames": "id", + "referencedTableName": "races" + } + }, + { + "addForeignKeyConstraint": + { + "baseColumnNames": "armour_type_id", + "baseTableName": "buildings", + "constraintName": "fk_buildings_armor_types", + "referencedColumnNames": "id", + "referencedTableName": "armor_types" + } + }, + { + "addForeignKeyConstraint": + { + "baseColumnNames": "mod_id", + "baseTableName": "buildings", + "constraintName": "fk_buildings_mods", + "referencedColumnNames": "id", + "referencedTableName": "mods" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.1/schema/buildings_weapons.json b/src/main/resources/db/0.0.1/schema/buildings_weapons.json new file mode 100644 index 0000000..b34ef9d --- /dev/null +++ b/src/main/resources/db/0.0.1/schema/buildings_weapons.json @@ -0,0 +1,82 @@ +{ + "databaseChangeLog": [ + { + "changeSet": { + "id": "Add buildings_weapons table", + "author": "anibus", + "changes": [ + { + "createTable": { + "tableName": "buildings_weapons", + "columns": [ + { + "column": { + "name": "building_id", + "type": "int", + "constraints": { + "nullable": false + } + } + }, + { + "column": { + "name": "weapon_id", + "type": "int", + "constraints": { + "nullable": false + } + } + }, + { + "column": { + "name": "hardpoint", + "type": "int", + "constraints": { + "nullable": false + } + } + }, + { + "column": { + "name": "hardpoint_order", + "type": "int", + "constraints": { + "nullable": false + } + } + } + ] + } + }, + { + "addUniqueConstraint": { + "columnNames": "building_id, weapon_id, hardpoint, hardpoint_order", + "constraintName": "uc_building_weapon_hardpoint_hardpoint_order", + "tableName": "buildings_weapons" + } + }, + { + "addForeignKeyConstraint": + { + "baseColumnNames": "building_id", + "baseTableName": "buildings_weapons", + "constraintName": "fk_buildings_buildings_weapons", + "referencedColumnNames": "id", + "referencedTableName": "buildings" + } + }, + { + "addForeignKeyConstraint": + { + "baseColumnNames": "weapon_id", + "baseTableName": "buildings_weapons", + "constraintName": "fk_weapons_buildings_weapons", + "referencedColumnNames": "id", + "referencedTableName": "weapons" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.1/schema/sergants_weapons.json b/src/main/resources/db/0.0.1/schema/sergants_weapons.json index e853607..6111a91 100644 --- a/src/main/resources/db/0.0.1/schema/sergants_weapons.json +++ b/src/main/resources/db/0.0.1/schema/sergants_weapons.json @@ -50,8 +50,8 @@ }, { "addUniqueConstraint": { - "columnNames": "sergeant_id, weapon_id", - "constraintName": "uc_sergeant_weapon", + "columnNames": "sergeant_id, weapon_id, hardpoint, hardpoint_order", + "constraintName": "uc_sergeant_weapon_hardpoint_hardpoint_order", "tableName": "sergeants_weapons" } }, diff --git a/src/main/resources/db/0.0.1/schema/units.json b/src/main/resources/db/0.0.1/schema/units.json index 5bc032c..de6eccb 100644 --- a/src/main/resources/db/0.0.1/schema/units.json +++ b/src/main/resources/db/0.0.1/schema/units.json @@ -31,7 +31,7 @@ },{ "column": { "name": "name", - "type": "varchar(255)" + "type": "varchar(4096)" } },{ "column": { @@ -215,12 +215,6 @@ "name": "icon", "type": "varchar(128)" } - }, - { - "column": { - "name": "hotkey_name", - "type": "varchar(64)" - } } ] } diff --git a/src/main/resources/db/0.0.1/schema/units_weapons.json b/src/main/resources/db/0.0.1/schema/units_weapons.json index 7d952ce..35d27d5 100644 --- a/src/main/resources/db/0.0.1/schema/units_weapons.json +++ b/src/main/resources/db/0.0.1/schema/units_weapons.json @@ -50,8 +50,8 @@ }, { "addUniqueConstraint": { - "columnNames": "unit_id, weapon_id", - "constraintName": "uc_unit_weapon", + "columnNames": "unit_id, weapon_id, hardpoint, hardpoint_order", + "constraintName": "uc_unit_weapon_hardpoint_hardpoint_order", "tableName": "units_weapons" } }, diff --git a/src/main/resources/db/0.0.1/schema/weapons.json b/src/main/resources/db/0.0.1/schema/weapons.json index 31745bf..7bb1d8d 100644 --- a/src/main/resources/db/0.0.1/schema/weapons.json +++ b/src/main/resources/db/0.0.1/schema/weapons.json @@ -31,7 +31,7 @@ },{ "column": { "name": "name", - "type": "varchar(255)" + "type": "varchar(4096)" } },{ "column": { @@ -94,6 +94,11 @@ "nullable": false } } + },{ + "column": { + "name": "min_range", + "type": "number" + } },{ "column": { "name": "max_range", @@ -131,6 +136,30 @@ "nullable": false } } + },{ + "column": { + "name": "damage_radius", + "type": "number", + "constraints": { + "nullable": false + } + } + },{ + "column": { + "name": "throw_force_min", + "type": "number", + "constraints": { + "nullable": false + } + } + },{ + "column": { + "name": "throw_force_max", + "type": "number", + "constraints": { + "nullable": false + } + } }, { "column": { diff --git a/src/main/resources/db/changelog-master.json b/src/main/resources/db/changelog-master.json index e20e599..b6d3c23 100644 --- a/src/main/resources/db/changelog-master.json +++ b/src/main/resources/db/changelog-master.json @@ -21,10 +21,30 @@ "include": { "file": "db/0.0.1/schema/sergants.json" } + },{ + "include": { + "file": "db/0.0.1/schema/buildings.json" + } },{ "include": { "file": "db/0.0.1/schema/weapons.json" } + },{ + "include": { + "file": "db/0.0.1/schema/buildings_weapons.json" + } + },{ + "include": { + "file": "db/0.0.1/schema/building_addons.json" + } + },{ + "include": { + "file": "db/0.0.1/schema/addon_modifiers.json" + } + },{ + "include": { + "file": "db/0.0.1/schema/addon_requires.json" + } },{ "include": { "file": "db/0.0.1/schema/units_weapons.json" diff --git a/src/test/kotlin/com/example/dowstats/service/w40k/BuildingRgdExtractServiceTest.kt b/src/test/kotlin/com/example/dowstats/service/w40k/BuildingRgdExtractServiceTest.kt new file mode 100644 index 0000000..bf94000 --- /dev/null +++ b/src/test/kotlin/com/example/dowstats/service/w40k/BuildingRgdExtractServiceTest.kt @@ -0,0 +1,50 @@ +import com.dowstats.configuration.StorageConfig +import com.dowstats.data.entities.ArmorType +import com.dowstats.data.entities.Mod +import com.dowstats.data.entities.Race +import com.dowstats.service.w40k.* +import io.mockk.junit5.MockKExtension +import io.mockk.mockk +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import java.io.DataInputStream +import java.io.File +import kotlin.test.assertEquals + +@ExtendWith(MockKExtension::class) +class BuildingRgdExtractServiceTest { + + val modAttribPathService = ModAttribPathService(StorageConfig()) + val iconService = mockk() + val addonRgdExtractService = mockk() + + val buildingRgdExtractService = BuildingRgdExtractService(modAttribPathService, iconService, addonRgdExtractService) + + val rgdService = RgdParserService() + + @Test + fun `Should correct parse building`() { + + val buildingRgdFile = File("src/test/resources/rgd/waagh_banner/ork_waagh_banner.rgd") + + val rgdData = rgdService.parseRgdFileStream(DataInputStream(buildingRgdFile.inputStream())) + + val res = buildingRgdExtractService.extractToBuildingEntity("Waagh banner", emptyMap(), rgdData, emptySet(), "orks", "/modFolder", Mod(), listOf( + Race().also { + it.id = "orks" + it.name = "Орки" + } + ), listOf( + ArmorType().also { + it.id = "building_low" + it.name = "Лёгкие здания" + } + ), + emptyMap()) + + assertEquals(170.toDouble(), res.building.buildCostRequisition) + assertEquals(emptySet(), res.buildingWeapons) + + } + +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..48fd808 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,27 @@ +spring.datasource.url=jdbc:postgresql://localhost:5433/wiki-db +spring.datasource.username=wiki-application +spring.datasource.password=hQXmQLJkKXdbIgBx +spring.datasource.driver-class-name=org.postgresql.Driver +spring.liquibase.change-log=classpath:db/changelog-master.json + +spring.servlet.multipart.enabled=true +spring.servlet.multipart.max-file-size=500MB +spring.servlet.multipart.max-request-size=500MB + +spring.jpa.database=postgresql +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect + + +hibernate.jdbc.batch_size = 1000 + +server.port=8082 + +steam.api.key=1DDFA1A35D907D5F5C20418A8D1885C1 + +storage.key=3F82D04CC274897B2552E8798F82F7A1 +storage.take-last=2 +storage.mod-storage=/home/cnb/mods +storage.wanila-storage=/home/cnb/wanila +storage.icons-storage=/home/cnb/icons diff --git a/src/test/resources/rgd/waagh_banner/ork_waagh_banner.rgd b/src/test/resources/rgd/waagh_banner/ork_waagh_banner.rgd new file mode 100644 index 0000000..52e47d3 Binary files /dev/null and b/src/test/resources/rgd/waagh_banner/ork_waagh_banner.rgd differ