diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/ResearchEffectDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/ResearchEffectDto.kt new file mode 100644 index 0000000..b79f641 --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/ResearchEffectDto.kt @@ -0,0 +1,11 @@ +package com.dowstats.data.dto.controllers + +/** + * Описывает как исследование влияет на конкретное поле оружия + * (урон, точность, перезарядка и т.д.). + */ +data class ResearchEffectDto( + val field: String, + val usageType: String?, + val modifierValue: Double?, +) diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/WeaponDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/WeaponDto.kt index 6015991..469ada8 100644 --- a/src/main/kotlin/com/dowstats/data/dto/controllers/WeaponDto.kt +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/WeaponDto.kt @@ -36,4 +36,5 @@ data class WeaponDto( val modifiers: List, val requirements: RequirementDto?, val hotkey: String?, + val researches: List, ) diff --git a/src/main/kotlin/com/dowstats/data/dto/controllers/WeaponResearchDto.kt b/src/main/kotlin/com/dowstats/data/dto/controllers/WeaponResearchDto.kt new file mode 100644 index 0000000..4b6a27d --- /dev/null +++ b/src/main/kotlin/com/dowstats/data/dto/controllers/WeaponResearchDto.kt @@ -0,0 +1,13 @@ +package com.dowstats.data.dto.controllers + +import com.dowstats.data.dto.controllers.research.ResearchShortDto + +/** + * Описывает влияние конкретного исследования (research) на оружие: + * список эффектов по полям оружия (урон, точность, перезарядка и т.д.). + */ +data class WeaponResearchDto( + val research: ResearchShortDto, + val researchRequired: List = emptyList(), + val effects: List, +) diff --git a/src/main/kotlin/com/dowstats/data/entities/weapon/Weapon.kt b/src/main/kotlin/com/dowstats/data/entities/weapon/Weapon.kt index 794c030..884f7bb 100644 --- a/src/main/kotlin/com/dowstats/data/entities/weapon/Weapon.kt +++ b/src/main/kotlin/com/dowstats/data/entities/weapon/Weapon.kt @@ -2,6 +2,7 @@ package com.dowstats.data.entities.weapon import com.dowstats.data.dto.controllers.RequirementDto import com.dowstats.data.dto.controllers.WeaponDto +import com.dowstats.data.dto.controllers.WeaponResearchDto import com.dowstats.data.dto.controllers.WeaponShortDto import com.dowstats.data.entities.ability.Ability import com.dowstats.data.entities.sergant.SergeantWeapon @@ -107,7 +108,7 @@ class Weapon { @OneToMany(mappedBy="weapon", cascade = [(CascadeType.ALL)]) var weaponPiercings: List = listOf() - fun toDto(requirementDto: RequirementDto?) = WeaponDto( + fun toDto(requirementDto: RequirementDto?, researches: List) = WeaponDto( id!!, filename!!, name, @@ -140,6 +141,7 @@ class Weapon { weaponModifiers.map { mod -> mod.toDto().copy(maxLifeTime = mod.maxLifeTime) }, requirementDto, uiHotkeyName, + researches, ) fun toShortDto() = WeaponShortDto( diff --git a/src/main/kotlin/com/dowstats/service/datamaps/WeaponResearchEffectMappingComponent.kt b/src/main/kotlin/com/dowstats/service/datamaps/WeaponResearchEffectMappingComponent.kt new file mode 100644 index 0000000..1ad0b98 --- /dev/null +++ b/src/main/kotlin/com/dowstats/service/datamaps/WeaponResearchEffectMappingComponent.kt @@ -0,0 +1,117 @@ +package com.dowstats.service.datamaps + +import com.dowstats.Metadata.Requirements +import com.dowstats.data.dto.controllers.ResearchEffectDto +import com.dowstats.data.dto.controllers.WeaponResearchDto +import com.dowstats.data.dto.controllers.research.ResearchShortDto +import com.dowstats.data.entities.research.Research +import com.dowstats.data.entities.weapon.Weapon +import com.dowstats.data.repositories.ResearchRepository +import org.springframework.stereotype.Component + +/** + * Builds detailed information about how researches affect a weapon's fields. + * Maps research modifier references to weapon fields. + */ +@Component +class WeaponResearchEffectMappingComponent( + val researchRepository: ResearchRepository, +) { + + /** + * Mapping from modifier reference name (without modifiers prefix and .lua suffix) + * to the corresponding weapon field name. + */ + private val referenceToField: Map = mapOf( + "accuracy_moving_reduction_weapon_modifier" to "accuracyReductionMoving", + "accuracy_ranged_weapon_modifier" to "accuracy", + "accuracy_weapon_modifier" to "accuracy", + "armour_piercing_weapon_modifier" to "armourPiercing", + "max_damage_weapon_modifier" to "maxDamage", + "max_range_weapon_modifier" to "maxRange", + "min_damage_weapon_modifier" to "minDamage", + "reload_time_weapon_modifier" to "reloadTime", + "setup_time_weapon_modifier" to "setupTime", + ) + + /** + * Builds a list of WeaponResearchDto for a given weapon, + * describing how each affecting research modifies the weapon's fields. + */ + fun buildEffects(weapon: Weapon): List { + val weaponTarget = weapon.filename?.replace(".rgd", "") ?: return emptyList() + + return weapon.affectedResearches.mapNotNull { research -> + val effects = research.researchModifiers + .filter { modifier -> + modifier.target == weaponTarget && modifier.reference != null + } + .mapNotNull { modifier -> + val refName = extractRefName(modifier.reference!!) + val field = referenceToField[refName] ?: return@mapNotNull null + + ResearchEffectDto( + field = field, + usageType = modifier.usageType, + modifierValue = modifier.value, + ) + } + + if (effects.isEmpty()) return@mapNotNull null + + WeaponResearchDto( + research = research.toResearchShortDto(), + researchRequired = getRequiredResearches(research), + effects = effects, + ) + } + } + + /** + * Returns researches required by the given research (its prerequisites). + * Handles both "required_research" (excluding "must not be complete" ones, + * since they are mutually exclusive rather than required) + * and "required_research_either" requirements. + */ + private fun getRequiredResearches(research: Research): List { + val modId = research.modId ?: return emptyList() + + val required = research.researchRequirements + .filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_RESEARCH } + .filter { it.value?.split(";")?.last() != "true" } + .mapNotNull { req -> + req.value?.split(";")?.first() + ?.replace(".lua", ".rgd") + ?.replace("research\\", "") + ?.let { if (it.endsWith(".rgd")) it else "$it.rgd" } + ?.let { researchRepository.findFirstByModIdAndFilename(modId, it)?.toResearchShortDto() } + } + + val requiredEither = research.researchRequirements + .filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIRED_RESEARCH_EITHER } + .flatMap { req -> + req.value?.split(";").orEmpty().mapNotNull { r -> + val researchFileName = r.split("\\").last().replace(".lua", ".rgd").let { resFile -> + if (resFile.endsWith(".rgd")) resFile else "$resFile.rgd" + } + researchRepository.findFirstByModIdAndFilename(modId, researchFileName)?.toResearchShortDto() + } + } + + return (required + requiredEither).distinct() + } + + private fun String.getPureReference(): String = + this.split("\\").last() + .replace(".lua", "") + .replace(".rgd", "") + + /** + * Extracts the short modifier name from a full reference path. + * e.g. modifiers/max_damage_weapon_modifier.lua -> max_damage_weapon_modifier + */ + private fun extractRefName(reference: String): String = + reference.split("\\").first() + .replace(".lua", "") + .replace(".rgd", "") +} \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/service/datamaps/WeaponService.kt b/src/main/kotlin/com/dowstats/service/datamaps/WeaponService.kt index 284be33..cf60e7d 100644 --- a/src/main/kotlin/com/dowstats/service/datamaps/WeaponService.kt +++ b/src/main/kotlin/com/dowstats/service/datamaps/WeaponService.kt @@ -9,6 +9,7 @@ import org.springframework.stereotype.Service class WeaponService @Autowired constructor( val weaponRepository: WeaponRepository, val requirementsMappingComponent: RequirementsMappingComponent, + val weaponResearchEffectMappingComponent: WeaponResearchEffectMappingComponent, ) { fun getWeaponDto(id: Long, modId: Long): WeaponDto { @@ -16,7 +17,8 @@ class WeaponService @Autowired constructor( val requirements = requirementsMappingComponent .getRequirements(weapon.weaponRequirements.toList(), modId) - return weapon.toDto(requirements) + val researches = weaponResearchEffectMappingComponent.buildEffects(weapon) + return weapon.toDto(requirements, researches) } diff --git a/src/main/resources/db/0.0.6/schema/add_index_to_researches.json b/src/main/resources/db/0.0.6/schema/add_index_to_researches.json new file mode 100644 index 0000000..c3ec569 --- /dev/null +++ b/src/main/resources/db/0.0.6/schema/add_index_to_researches.json @@ -0,0 +1,30 @@ +{ + "databaseChangeLog": [ + { + "changeSet": { + "id": "Add index on mod_id and filename to researches", + "author": "anibus", + "changes": [ + { + "createIndex": { + "indexName": "idx_researches_mod_id_filename", + "tableName": "researches", + "columns": [ + { + "column": { + "name": "mod_id" + } + }, + { + "column": { + "name": "filename" + } + } + ] + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/changelog-master.json b/src/main/resources/db/changelog-master.json index e285261..05b535e 100644 --- a/src/main/resources/db/changelog-master.json +++ b/src/main/resources/db/changelog-master.json @@ -476,6 +476,11 @@ "include": { "file": "db/0.0.6/schema/jump_requirements.json" } + }, + { + "include": { + "file": "db/0.0.6/schema/add_index_to_researches.json" + } } ] } diff --git a/src/test/kotlin/com/example/dowstats/service/ModDiffPrinterScript.kt b/src/test/kotlin/com/example/dowstats/service/ModDiffPrinterScript.kt index 42e8135..7d542b6 100644 --- a/src/test/kotlin/com/example/dowstats/service/ModDiffPrinterScript.kt +++ b/src/test/kotlin/com/example/dowstats/service/ModDiffPrinterScript.kt @@ -18,7 +18,7 @@ class ModDiffPrinterScript { val rgdService = RgdService() - val prevVersion = "wanila2.8.0" + val prevVersion = "wanila2.9.1" val currentVersion = "wanila" val spaceMarinesPath = "space_marines"