- Add death explosions info

- Add mob bonus info
- Reworked abilities, add child objects
- Add unit morale break behaviour
This commit is contained in:
anibus 2026-03-14 15:04:21 +03:00
parent 42283639fa
commit b0081aae16
58 changed files with 1737 additions and 151 deletions

View File

@ -5,18 +5,25 @@ object Metadata {
const val USER_ROLE = "USER" const val USER_ROLE = "USER"
object Requirements { object Requirements {
val REFERENCE_REQUIREMENT_NONE = "requirements\\required_none.lua" val REFERENCE_REQUIREMENT_NONE = "required_none"
val REFERENCE_REQUIREMENT_POP = "requirements\\required_total_pop.lua" val REFERENCE_REQUIREMENT_MOB_BONUS = "required_mobvalue"
val REFERENCE_REQUIREMENT_ADDON = "requirements\\local_required_addon.lua" val REFERENCE_REQUIREMENT_STRUCTURE_EXCLUSIVE = "required_structure_exclusive"
val REFERENCE_GLOBAL_REQUIREMENT_ADDON = "requirements\\global_required_addon.lua" val REFERENCE_LOCAL_REQUIRED_ADDON_EXCLUSIVE = "local_required_addon_exclusive"
val REFERENCE_REQUIREMENT_STRUCTURE_EITHER = "requirements\\required_structure_either.lua" val REFERENCE_REQUIREMENT_SQUAD = "required_squad"
val REFERENCE_REQUIREMENT_STRUCTURE = "requirements\\required_structure.lua" val REFERENCE_REQUIRED_RESEARCH_EITHER = "required_research_either"
val REFERENCE_REQUIREMENT_RESEARCH = "requirements\\required_research.lua" val REFERENCE_REQUIREMENT_POP = "required_total_pop"
val REFERENCE_REQUIREMENT_OWNERSHIP = "requirements\\required_ownership.lua" val REFERENCE_REQUIREMENT_ADDON = "local_required_addon"
val REFERENCE_REQUIREMENT_CAP = "requirements\\required_cap.lua" val REFERENCE_GLOBAL_REQUIREMENT_ADDON = "global_required_addon"
val REFERENCE_REQUIREMENT_SQUAD_CAP = "requirements\\required_squad_cap.lua" val REFERENCE_REQUIREMENT_STRUCTURE_EITHER = "required_structure_either"
val REFERENCE_REQUIREMENT_CUM_SQUAD_CAP = "requirements\\required_cumulative_squad_cap.lua" val REFERENCE_REQUIREMENT_STRUCTURE = "required_structure"
val REFERENCE_REQUIREMENT_STRUCTURE_RATIO = "requirements\\required_structure_ratio.lua" val REFERENCE_REQUIREMENT_RESEARCH = "required_research"
val REFERENCE_REQUIREMENT_OWNERSHIP = "required_ownership"
val REFERENCE_REQUIREMENT_STRUCTURE_RATIO = "required_structure_ratio"
// выводится в юнита
val REFERENCE_REQUIREMENT_CAP = "required_cap"
val REFERENCE_REQUIREMENT_SQUAD_CAP = "required_squad_cap"
val REFERENCE_REQUIREMENT_CUM_SQUAD_CAP = "required_cumulative_squad_cap"
} }
} }

View File

@ -19,7 +19,7 @@ class ModVersionsController @Autowired constructor(
) { ) {
@GetMapping @GetMapping
fun getModVersions(req: HttpServletRequest): List<Mod> { fun getModVersions(): List<Mod> {
return modRepository.findAll().toList() return modRepository.findAll().toList()
} }

View File

@ -1,7 +1,5 @@
package com.dowstats.data.dto package com.dowstats.data.dto
import com.dowstats.data.entities.weapon.WeaponModifiers
data class AreaEffect( data class AreaEffect(
val minDamage: Double, val minDamage: Double,
val maxDamage: Double, val maxDamage: Double,

View File

@ -1,8 +0,0 @@
package com.dowstats.data.dto.controllers
import java.math.BigDecimal
data class AbilityArmorPiercingDto(
val armorType: ArmorTypeDto?,
val piercingValue: BigDecimal,
)

View File

@ -2,7 +2,7 @@ package com.dowstats.data.dto.controllers
import java.math.BigDecimal import java.math.BigDecimal
data class WeaponArmorPiercingDto( data class ArmorPiercingDto(
val armorType: ArmorTypeDto?, val armorType: ArmorTypeDto?,
val piercingValue: BigDecimal, val piercingValue: BigDecimal,
) )

View File

@ -0,0 +1,17 @@
package com.dowstats.data.dto.controllers
data class DeathExplosionDto(
val id: Long?,
var globalChance: Double?,
var chance: Double?,
val minDamage: Double?,
val maxDamage: Double?,
val minDamageValue: Double?,
val moraleDamage: Double?,
val damageRadius: Double?,
val throwForceMin: Double?,
val throwForceMax: Double?,
val modId: Long?,
val modifiers: List<ModifierDto>,
val armorPiercing: List<ArmorPiercingDto>,
)

View File

@ -8,10 +8,15 @@ data class RequirementDto (
val requirementBuildings: Set<BuildingShortDto>, val requirementBuildings: Set<BuildingShortDto>,
val requirementBuildingsEither: Set<BuildingShortDto>, val requirementBuildingsEither: Set<BuildingShortDto>,
val requirementResearches: Set<RequirementResearchDto>, val requirementResearches: Set<RequirementResearchDto>,
val requirementSquads: Set<RequirementSquadsDto>,
val requirementResearchesEither: Set<RequirementResearchDto>,
val requireAddon: BuildingAddonShortDto?, val requireAddon: BuildingAddonShortDto?,
val requirementsGlobalAddons: Set<BuildingAddonShortDto>?, val requirementsGlobalAddons: Set<BuildingAddonShortDto>?,
val requiredTotalPop: Int?, val requiredTotalPop: Int?,
val limitByBuilding: RequirementLimitByBuildingDto?, val limitByBuilding: RequirementLimitByBuildingDto?,
val mobBonus: RequirementMobBonusDto?,
val requirementStructureExclusive: BuildingShortDto?,
val requirementAddonExclusive: BuildingAddonShortDto?,
val requiredOwnership: Set<String>, val requiredOwnership: Set<String>,
val replaceWhenDone: Boolean = false, val replaceWhenDone: Boolean = false,
) )
@ -21,7 +26,17 @@ data class RequirementResearchDto(
val researchMustNotBeComplete: Boolean, val researchMustNotBeComplete: Boolean,
) )
data class RequirementSquadsDto(
val squad: EntityCompressDto,
val count: Int,
)
data class RequirementLimitByBuildingDto( data class RequirementLimitByBuildingDto(
val building: BuildingShortDto, val building: BuildingShortDto,
val limit: Int, val limit: Int,
) )
data class RequirementMobBonusDto(
val mobvalueRequired: Double,
val proximityRequired: Int,
)

View File

@ -20,6 +20,7 @@ data class SergeantDto(
val faithIncome: Double?, val faithIncome: Double?,
val powerIncome: Double?, val powerIncome: Double?,
val requisitionIncome: Double?, val requisitionIncome: Double?,
val mobValue: Double?,
val health: Int?, val health: Int?,
val armour: Double?, val armour: Double?,
val healthRegeneration: Double?, val healthRegeneration: Double?,
@ -31,6 +32,7 @@ data class SergeantDto(
val sightRadius: Int?, val sightRadius: Int?,
val detectRadius: Int?, val detectRadius: Int?,
val icon: String?, val icon: String?,
val deathExplosions: Set<DeathExplosionDto>,
val weapons: List<WeaponSlotDto>?, val weapons: List<WeaponSlotDto>?,
val abilities: Set<AbilityShortDto>?, val abilities: Set<AbilityShortDto>?,
val affectedResearches: Set<ResearchShortDto>, val affectedResearches: Set<ResearchShortDto>,

View File

@ -23,6 +23,7 @@ data class UnitFullDto(
val faithIncome: Double?, val faithIncome: Double?,
val powerIncome: Double?, val powerIncome: Double?,
val requisitionIncome: Double?, val requisitionIncome: Double?,
val mobValue: Double?,
val capInfantry: Int?, val capInfantry: Int?,
val capSupport: Int?, val capSupport: Int?,
val squadStartSize: Int?, val squadStartSize: Int?,
@ -52,6 +53,7 @@ data class UnitFullDto(
val maxSergeants: Int?, val maxSergeants: Int?,
val icon: String?, val icon: String?,
val modId: Long, val modId: Long,
val deathExplosions: Set<DeathExplosionDto>,
val sergeants: Set<SergeantDto>?, val sergeants: Set<SergeantDto>?,
val weapons: Set<WeaponSlotDto>?, val weapons: Set<WeaponSlotDto>?,
val abilities: Set<AbilityShortDto>?, val abilities: Set<AbilityShortDto>?,
@ -59,6 +61,7 @@ data class UnitFullDto(
val affectedResearches: Set<ResearchShortDto>, val affectedResearches: Set<ResearchShortDto>,
val affectedAddons: Set<BuildingAddonShortDto>, val affectedAddons: Set<BuildingAddonShortDto>,
val modifiers: List<ModifierDto>, val modifiers: List<ModifierDto>,
val moraleBrakeModifiers: List<ModifierDto>,
var affectedData: AffectedDataDto, var affectedData: AffectedDataDto,
val requirements: RequirementDto?, val requirements: RequirementDto?,
val hotkey: String?, val hotkey: String?,

View File

@ -30,7 +30,7 @@ data class WeaponDto(
val icon: String?, val icon: String?,
val showInReinforce: Boolean, val showInReinforce: Boolean,
val modId: Long?, val modId: Long?,
val weaponArmorPiercing: List<WeaponArmorPiercingDto>, val weaponArmorPiercing: List<ArmorPiercingDto>,
val affectedResearches: Set<ResearchShortDto>, val affectedResearches: Set<ResearchShortDto>,
val affectedAddons: Set<BuildingAddonShortDto>, val affectedAddons: Set<BuildingAddonShortDto>,
val modifiers: List<ModifierDto>, val modifiers: List<ModifierDto>,

View File

@ -1,11 +1,32 @@
package com.dowstats.data.dto.controllers.ability package com.dowstats.data.dto.controllers.ability
import com.dowstats.data.dto.controllers.AbilityArmorPiercingDto
import com.dowstats.data.dto.controllers.AffectedDataDto import com.dowstats.data.dto.controllers.AffectedDataDto
import com.dowstats.data.dto.controllers.ArmorPiercingDto
import com.dowstats.data.dto.controllers.ArmorTypeDto import com.dowstats.data.dto.controllers.ArmorTypeDto
import com.dowstats.data.dto.controllers.DeathExplosionDto
import com.dowstats.data.dto.controllers.ModifierDto import com.dowstats.data.dto.controllers.ModifierDto
import com.dowstats.data.dto.controllers.RequirementDto import com.dowstats.data.dto.controllers.RequirementDto
data class AbilityEnvironmentDto(
var id: Long,
var modId: Long,
var filename: String,
var name: String?,
var description: String?,
var icon: String?,
var health: Int?,
var armour: Double?,
var healthRegeneration: Double?,
var moveSpeed: Int?,
var sightRadius: Int?,
var detectRadius: Int?,
var lifetime: Double?,
val armorType: ArmorTypeDto?,
val armorType2: ArmorTypeDto?,
val abilities: Set<AbilityShortDto>,
val deathExplosions: Set<DeathExplosionDto>,
)
data class AbilityDto( data class AbilityDto(
val id: Long?, val id: Long?,
val modId: Long?, val modId: Long?,
@ -26,7 +47,6 @@ data class AbilityDto(
val rechargeTimerGlobal: Boolean?, val rechargeTimerGlobal: Boolean?,
val refreshTime: Double?, val refreshTime: Double?,
val durationTime: Double?, val durationTime: Double?,
val spawnedEntityName: String?,
val radius: Double?, val radius: Double?,
val minDamage: Double?, val minDamage: Double?,
val minDamageValue: Double?, val minDamageValue: Double?,
@ -41,5 +61,6 @@ data class AbilityDto(
val requirements: RequirementDto?, val requirements: RequirementDto?,
var affectedData: AffectedDataDto, var affectedData: AffectedDataDto,
val targetFilter: List<ArmorTypeDto>, val targetFilter: List<ArmorTypeDto>,
val piercings: List<AbilityArmorPiercingDto> val piercings: List<ArmorPiercingDto>,
val abilityEnvironment: AbilityEnvironmentDto?,
) )

View File

@ -30,6 +30,7 @@ data class BuildingFullDto(
var repairMax: Int?, var repairMax: Int?,
var icon: String?, var icon: String?,
var modId: Long?, var modId: Long?,
val deathExplosions: Set<DeathExplosionDto>,
var addons: List<BuildingAddonDto>?, var addons: List<BuildingAddonDto>?,
val abilities: Set<AbilityShortDto>?, val abilities: Set<AbilityShortDto>?,
var researches: List<ResearchShortDto>?, var researches: List<ResearchShortDto>?,

View File

@ -56,8 +56,6 @@ open class ModifiersBase {
this.probabilityOfApplying = rgdData.getDoubleByName("probability_of_applying") this.probabilityOfApplying = rgdData.getDoubleByName("probability_of_applying")
return this return this
} }
}
}
} }

View File

@ -1,12 +1,13 @@
package com.dowstats.data.entities.ability package com.dowstats.data.entities.ability
import com.dowstats.data.dto.controllers.AbilityArmorPiercingDto
import com.dowstats.data.dto.controllers.AffectedDataDto import com.dowstats.data.dto.controllers.AffectedDataDto
import com.dowstats.data.dto.controllers.ArmorPiercingDto
import com.dowstats.data.dto.controllers.EntityCompressDtoObject.compressDto import com.dowstats.data.dto.controllers.EntityCompressDtoObject.compressDto
import com.dowstats.data.dto.controllers.RequirementDto import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.SergeantUnitShortDtoObject.toShortDtoSet import com.dowstats.data.dto.controllers.SergeantUnitShortDtoObject.toShortDtoSet
import com.dowstats.data.dto.controllers.WeaponUnitShortDtoObject.toShortDtoSet import com.dowstats.data.dto.controllers.WeaponUnitShortDtoObject.toShortDtoSet
import com.dowstats.data.dto.controllers.ability.AbilityDto import com.dowstats.data.dto.controllers.ability.AbilityDto
import com.dowstats.data.dto.controllers.ability.AbilityEnvironmentDto
import com.dowstats.data.dto.controllers.ability.AbilityShortDto import com.dowstats.data.dto.controllers.ability.AbilityShortDto
import com.dowstats.data.entities.Activation import com.dowstats.data.entities.Activation
import com.dowstats.data.entities.AreaEffect import com.dowstats.data.entities.AreaEffect
@ -24,7 +25,6 @@ import java.math.BigDecimal
@Table(name = "abilities") @Table(name = "abilities")
class Ability { class Ability {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null var id: Long? = null
@ -120,7 +120,7 @@ class Ability {
fun toShortDto() = AbilityShortDto(id, name, filename, icon, uiIndexHint, activation?.name) fun toShortDto() = AbilityShortDto(id, name, filename, icon, uiIndexHint, activation?.name)
fun toDto(requirements: RequirementDto?, childAbility: AbilityDto?): AbilityDto = AbilityDto( fun toDto(requirements: RequirementDto?, childAbility: AbilityDto?, abilityEnvironmentDto: AbilityEnvironmentDto?): AbilityDto = AbilityDto(
id = id, id = id,
modId = modId, modId = modId,
filename = filename, filename = filename,
@ -140,7 +140,6 @@ class Ability {
rechargeTimerGlobal = rechargeTimerGlobal, rechargeTimerGlobal = rechargeTimerGlobal,
refreshTime = refreshTime, refreshTime = refreshTime,
durationTime = durationTime, durationTime = durationTime,
spawnedEntityName = spawnedEntityName,
radius = radius, radius = radius,
minDamage = minDamage, minDamage = minDamage,
minDamageValue = minDamageValue, minDamageValue = minDamageValue,
@ -160,7 +159,8 @@ class Ability {
affectedOnWeapons.toShortDtoSet(), affectedOnWeapons.toShortDtoSet(),
), ),
targetFilter = abilityTargetFilters.map { it.toDto() }, targetFilter = abilityTargetFilters.map { it.toDto() },
piercings = abilityPiercings.map { AbilityArmorPiercingDto(it.armorType?.toDto(), it.piercingValue ?: BigDecimal.ZERO) } piercings = abilityPiercings.map { ArmorPiercingDto(it.armorType?.toDto(), it.piercingValue ?: BigDecimal.ZERO) },
abilityEnvironmentDto,
) )
} }

View File

@ -0,0 +1,74 @@
package com.dowstats.data.entities.ability
import com.dowstats.data.dto.controllers.ability.AbilityEnvironmentDto
import com.dowstats.data.entities.ArmorType
import com.dowstats.data.entities.deathexplosion.DeathExplosion
import jakarta.persistence.*
@Entity
@Table(name = "ability_environments")
class AbilityEnvironment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
var modId: Long? = null
var filename: String? = null
var name: String? = null
var description: String? = null
var icon: String? = null
var health: Int? = null
var armour: Double? = null
var healthRegeneration: Double? = null
var moveSpeed: Int? = null
var sightRadius: Int? = null
var detectRadius: Int? = null
var lifetime: Double? = null
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "armour_type_id")
var armorType: ArmorType? = null
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "armour_type_2_id")
var armorType2: ArmorType? = null
@ManyToMany(cascade = [CascadeType.ALL])
@JoinTable(name = "ability_environment_abilities",
joinColumns = [JoinColumn(name = "ability_environment_id")],
inverseJoinColumns = [JoinColumn(name = "ability_id")])
var abilities: MutableSet<Ability> = mutableSetOf()
@OneToMany(cascade = [CascadeType.ALL])
@JoinTable(name = "ability_environment_death_explosion",
joinColumns = [JoinColumn(name = "ability_environment_id")],
inverseJoinColumns = [JoinColumn(name = "death_explosion_id")])
var deathExplosions: MutableSet<DeathExplosion> = mutableSetOf()
fun toDto(): AbilityEnvironmentDto = AbilityEnvironmentDto(
id!!,
modId!!,
filename!!,
name,
description,
icon,
health,
armour,
healthRegeneration,
moveSpeed,
sightRadius,
detectRadius,
lifetime,
armorType?.toDto(),
armorType2?.toDto(),
abilities.map { it.toShortDto() }.toSet(),
deathExplosions.filter { (it.chance ?: 0.0) > 0.0 }.map { it.toDto(modId!!) }.toSet(),
)
}

View File

@ -12,4 +12,5 @@ class AbilityRequirements : RequirementBase() {
@JoinColumn(name = "ability_id", nullable = false) @JoinColumn(name = "ability_id", nullable = false)
@JsonIgnore @JsonIgnore
var ability: Ability? = null var ability: Ability? = null
} }

View File

@ -18,6 +18,7 @@ import com.dowstats.data.entities.sergant.Sergeant
import com.dowstats.data.entities.weapon.Weapon import com.dowstats.data.entities.weapon.Weapon
import com.dowstats.data.entities.weapon.Weapon.HardpointPosition import com.dowstats.data.entities.weapon.Weapon.HardpointPosition
import com.dowstats.data.entities.addon.BuildingAddon import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.deathexplosion.DeathExplosion
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
import jakarta.persistence.* import jakarta.persistence.*
@ -67,6 +68,7 @@ class Building {
var faithIncome: Double? = null var faithIncome: Double? = null
var powerIncome: Double? = null var powerIncome: Double? = null
var requisitionIncome: Double? = null var requisitionIncome: Double? = null
var buildingLimit: Int? = null
@OneToMany(mappedBy = "building", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "building", cascade = [CascadeType.ALL])
var addons: MutableSet<BuildingAddon>? = null var addons: MutableSet<BuildingAddon>? = null
@ -77,6 +79,12 @@ class Building {
@OneToMany(mappedBy = "building", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "building", cascade = [CascadeType.ALL])
var modifiers: MutableSet<BuildingModifiers> = mutableSetOf() var modifiers: MutableSet<BuildingModifiers> = mutableSetOf()
@OneToMany(cascade = [CascadeType.ALL])
@JoinTable(name = "building_death_explosion",
joinColumns = [JoinColumn(name = "building_id")],
inverseJoinColumns = [JoinColumn(name = "death_explosion_id")])
var deathExplosions: MutableSet<DeathExplosion> = mutableSetOf()
@ManyToMany @ManyToMany
@JoinTable(name = "buildings_units", @JoinTable(name = "buildings_units",
joinColumns = [JoinColumn(name = "building_id")], joinColumns = [JoinColumn(name = "building_id")],
@ -188,8 +196,9 @@ class Building {
repairMax, repairMax,
icon, icon,
modId!!, modId!!,
buildingAddons, deathExplosions.map { it.toDto(modId!!) }.toSet(),
abilities.map { it.toShortDto() }.toSet(), buildingAddons?.sortedBy { it.name },
abilities.map { it.toShortDto() }.sortedBy { it.name }.toSet(),
researches, researches,
modifiers.map { it.toDto() }, modifiers.map { it.toDto() },
AffectedDataDto( AffectedDataDto(

View File

@ -0,0 +1,48 @@
package com.dowstats.data.entities.deathexplosion
import com.dowstats.data.dto.controllers.ArmorPiercingDto
import com.dowstats.data.dto.controllers.DeathExplosionDto
import jakarta.persistence.*
import java.math.BigDecimal
@Entity
@Table(name = "death_explosions")
class DeathExplosion {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
var globalChance: Double? = null
var chance: Double? = null
var minDamage: Double? = null
var maxDamage: Double? = null
var minDamageValue: Double? = null
var moraleDamage: Double? = null
var radius: Double? = null
var throwForceMin: Double? = null
var throwForceMax: Double? = null
@OneToMany(mappedBy = "deathExplosion", cascade = [CascadeType.ALL])
var explosionModifiers: MutableSet<DeathExplosionModifiers> = mutableSetOf()
@OneToMany(mappedBy="deathExplosion", cascade = [(CascadeType.ALL)])
var explosionPiercings: MutableSet<DeathExplosionArmorPiercing> = mutableSetOf()
fun toDto(modId: Long): DeathExplosionDto = DeathExplosionDto(
id!!,
globalChance,
chance,
minDamage,
maxDamage,
minDamageValue,
moraleDamage,
radius,
throwForceMin,
throwForceMax,
modId,
explosionModifiers.map { it.toDto() },
explosionPiercings.map { ArmorPiercingDto(it.armorType?.toDto(), it.piercingValue ?: BigDecimal.ZERO) }
)
}

View File

@ -0,0 +1,28 @@
package com.dowstats.data.entities.deathexplosion
import com.dowstats.data.entities.ArmorType
import com.fasterxml.jackson.annotation.JsonIgnore
import java.math.BigDecimal
import jakarta.persistence.*
@Entity
@Table(name = "death_explosion_armors_piercing")
class DeathExplosionArmorPiercing {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
@ManyToOne
@JsonIgnore
@JoinColumn(name = "death_explosion_id")
var deathExplosion: DeathExplosion? = null
@ManyToOne
@JoinColumn(name = "armor_type_id")
var armorType: ArmorType? = null
var piercingValue: BigDecimal? = null
}

View File

@ -0,0 +1,19 @@
package com.dowstats.data.entities.deathexplosion
import com.dowstats.data.entities.ModifiersBase
import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.*
@Entity
@Table(name = "death_explosion_modifiers")
class DeathExplosionModifiers : ModifiersBase() {
@ManyToOne
@JoinColumn(name = "death_explosion_id", nullable = false)
@JsonIgnore
var deathExplosion: DeathExplosion? = null
var maxLifeTime: Double? = null
}

View File

@ -12,6 +12,7 @@ import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.weapon.Weapon.HardpointPosition import com.dowstats.data.entities.weapon.Weapon.HardpointPosition
import com.dowstats.data.entities.addon.BuildingAddon import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.building.Building import com.dowstats.data.entities.building.Building
import com.dowstats.data.entities.deathexplosion.DeathExplosion
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
import com.dowstats.data.entities.unit.DowUnit import com.dowstats.data.entities.unit.DowUnit
import com.dowstats.data.entities.weapon.Weapon import com.dowstats.data.entities.weapon.Weapon
@ -64,6 +65,7 @@ class Sergeant {
var faithIncome: Double? = null var faithIncome: Double? = null
var powerIncome: Double? = null var powerIncome: Double? = null
var requisitionIncome: Double? = null var requisitionIncome: Double? = null
var mobValue: Double? = null
var modId: Long? = null var modId: Long? = null
@ -73,6 +75,12 @@ class Sergeant {
@OneToMany(mappedBy = "sergeant", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "sergeant", cascade = [CascadeType.ALL])
var weapons: MutableSet<SergeantWeapon>? = null var weapons: MutableSet<SergeantWeapon>? = null
@OneToMany(cascade = [CascadeType.ALL])
@JoinTable(name = "sergeant_death_explosion",
joinColumns = [JoinColumn(name = "sergeant_id")],
inverseJoinColumns = [JoinColumn(name = "death_explosion_id")])
var deathExplosions: MutableSet<DeathExplosion> = mutableSetOf()
@ManyToMany @ManyToMany
@JoinTable(name = "sergeant_abilities", @JoinTable(name = "sergeant_abilities",
joinColumns = [JoinColumn(name = "sergeant_id")], joinColumns = [JoinColumn(name = "sergeant_id")],
@ -163,6 +171,7 @@ class Sergeant {
faithIncome, faithIncome,
powerIncome, powerIncome,
requisitionIncome, requisitionIncome,
mobValue,
health, health,
armour, armour,
healthRegeneration, healthRegeneration,
@ -174,8 +183,9 @@ class Sergeant {
sightRadius, sightRadius,
detectRadius, detectRadius,
icon, icon,
deathExplosions.map { it.toDto(modId!!) }.toSet(),
weapons.toList(), weapons.toList(),
abilities.sortedWith ( compareBy<Ability> { it.activation?.id }.thenBy{ it.uiIndexHint.let { 0 - it } }).map { it.toShortDto() }.toSet(), abilities.sortedWith ( compareBy<Ability> { it.activation?.id }.thenBy{ it.uiIndexHint.let { 0 - it } }.thenBy{ it.name}).map { it.toShortDto() }.toSet(),
affectedResearches.map { it.toResearchShortDto() }.toSet(), affectedResearches.map { it.toResearchShortDto() }.toSet(),
sergeantModifiers.map { it.toDto() }.sortedBy { it.target }, sergeantModifiers.map { it.toDto() }.sortedBy { it.target },
AffectedDataDto( AffectedDataDto(

View File

@ -10,6 +10,7 @@ import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.sergant.Sergeant import com.dowstats.data.entities.sergant.Sergeant
import com.dowstats.data.entities.addon.BuildingAddon import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.building.Building import com.dowstats.data.entities.building.Building
import com.dowstats.data.entities.deathexplosion.DeathExplosion
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
import com.dowstats.data.entities.research.ResearchModifiers import com.dowstats.data.entities.research.ResearchModifiers
import com.dowstats.data.entities.weapon.Weapon import com.dowstats.data.entities.weapon.Weapon
@ -79,6 +80,7 @@ class DowUnit {
var faithIncome: Double? = null var faithIncome: Double? = null
var powerIncome: Double? = null var powerIncome: Double? = null
var requisitionIncome: Double? = null var requisitionIncome: Double? = null
var mobValue: Double? = null
var haveReinforceMenu: Boolean = false var haveReinforceMenu: Boolean = false
var uiIndexHint: Int = 0 var uiIndexHint: Int = 0
var uiHotkeyName: String? = null var uiHotkeyName: String? = null
@ -91,12 +93,19 @@ class DowUnit {
@OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
var weapons: MutableSet<UnitWeapon>? = null var weapons: MutableSet<UnitWeapon>? = null
@OneToMany(cascade = [CascadeType.ALL])
@JoinTable(name = "unit_death_explosion",
joinColumns = [JoinColumn(name = "unit_id")],
inverseJoinColumns = [JoinColumn(name = "death_explosion_id")])
var deathExplosions: MutableSet<DeathExplosion> = mutableSetOf()
@ManyToMany @ManyToMany
@JoinTable(name = "unit_abilities", @JoinTable(name = "unit_abilities",
joinColumns = [JoinColumn(name = "unit_id")], joinColumns = [JoinColumn(name = "unit_id")],
inverseJoinColumns = [JoinColumn(name = "ability_id")]) inverseJoinColumns = [JoinColumn(name = "ability_id")])
var abilities: MutableSet<Ability> = mutableSetOf() var abilities: MutableSet<Ability> = mutableSetOf()
@ManyToMany @ManyToMany
@JoinTable(name = "researches_affected_units", @JoinTable(name = "researches_affected_units",
joinColumns = [JoinColumn(name = "unit_id")], joinColumns = [JoinColumn(name = "unit_id")],
@ -161,6 +170,9 @@ class DowUnit {
@OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
var unitModifiers: MutableSet<UnitModifiers> = mutableSetOf() var unitModifiers: MutableSet<UnitModifiers> = mutableSetOf()
@OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
var unitMoraleBrakeModifiers: MutableSet<UnitMoraleBrakeModifiers> = mutableSetOf()
@OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
var unitRequirements: MutableSet<UnitRequirements> = mutableSetOf() var unitRequirements: MutableSet<UnitRequirements> = mutableSetOf()
@ -182,6 +194,7 @@ class DowUnit {
faithIncome, faithIncome,
powerIncome, powerIncome,
requisitionIncome, requisitionIncome,
mobValue,
capInfantry, capInfantry,
capSupport, capSupport,
squadStartSize, squadStartSize,
@ -211,13 +224,15 @@ class DowUnit {
maxSergeants, maxSergeants,
icon, icon,
modId!!, modId!!,
deathExplosions.map { it.toDto(modId!!) }.toSet(),
sergeantsDtos, sergeantsDtos,
weapons, weapons,
abilities.sortedWith ( compareBy<Ability> { it.activation?.id }.thenBy{ it.uiIndexHint.let { 0 - it } }).map { it.toShortDto() }.toSet(), abilities.sortedWith ( compareBy<Ability> { it.activation?.id }.thenBy{ it.uiIndexHint.let { 0 - it } }.thenBy{ it.name}.thenBy{ it.filename}).map { it.toShortDto() }.toSet(),
haveReinforceMenu, haveReinforceMenu,
affectedResearches.map { it.toResearchShortDto() }.toSet(), affectedResearches.map { it.toResearchShortDto() }.toSet(),
affectedAddons.map { it.toShortDto() }.toSet(), affectedAddons.map { it.toShortDto() }.toSet(),
unitModifiers.map { it.toDto() }.sortedBy { it.target }, unitModifiers.map { it.toDto() }.sortedBy { it.target },
unitMoraleBrakeModifiers.map { it.toDto() }.sortedBy { it.target },
AffectedDataDto( AffectedDataDto(
affectedOnUnits.map { it.compressDto() }.toSet(), affectedOnUnits.map { it.compressDto() }.toSet(),
affectedOnSergeants.toShortDtoSet(), affectedOnSergeants.toShortDtoSet(),

View File

@ -0,0 +1,19 @@
package com.dowstats.data.entities.unit
import com.dowstats.data.dto.controllers.ModifierDto
import com.dowstats.data.entities.ModifiersBase
import com.dowstats.data.entities.building.Building
import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.*
@Entity
@Table(name = "unit_morale_break_modifiers")
class UnitMoraleBrakeModifiers : ModifiersBase() {
@ManyToOne
@JoinColumn(name = "unit_id", nullable = false)
@JsonIgnore
var unit: DowUnit? = null
}

View File

@ -1,6 +1,6 @@
package com.dowstats.data.entities.weapon package com.dowstats.data.entities.weapon
import com.dowstats.data.dto.controllers.WeaponArmorPiercingDto import com.dowstats.data.dto.controllers.ArmorPiercingDto
import com.dowstats.data.entities.ArmorType import com.dowstats.data.entities.ArmorType
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import java.math.BigDecimal import java.math.BigDecimal
@ -26,7 +26,7 @@ class WeaponArmorPiercing {
var piercingValue: BigDecimal? = null var piercingValue: BigDecimal? = null
fun toDto() = WeaponArmorPiercingDto( fun toDto() = ArmorPiercingDto(
armorType?.toDto(), armorType?.toDto(),
piercingValue?:BigDecimal.ZERO, piercingValue?:BigDecimal.ZERO,
) )

View File

@ -0,0 +1,15 @@
package com.dowstats.data.repositories
import com.dowstats.data.entities.unit.DowUnit
import com.dowstats.data.entities.Race
import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.ability.AbilityEnvironment
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.*
interface AbilityEnvironmentRepository : CrudRepository<AbilityEnvironment, Long> {
fun findAllByModId(modId: Long): List<AbilityEnvironment>
fun deleteAllByModId(modId: Long)
}

View File

@ -10,5 +10,7 @@ interface AbilityRepository : CrudRepository<Ability, Long> {
fun findAllByModId(modId: Long): List<Ability> fun findAllByModId(modId: Long): List<Ability>
fun findFirstByModIdAndFilenameIn(modId: Long, fileNames: List<String>): Ability
fun deleteAllByModId(modId: Long) fun deleteAllByModId(modId: Long)
} }

View File

@ -2,6 +2,7 @@ package com.dowstats.data.repositories
import com.dowstats.data.entities.unit.DowUnit import com.dowstats.data.entities.unit.DowUnit
import com.dowstats.data.entities.Race import com.dowstats.data.entities.Race
import com.dowstats.data.entities.addon.BuildingAddon
import org.springframework.data.jpa.repository.Query import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.* import org.springframework.data.repository.*
@ -18,5 +19,7 @@ interface UnitRepository : CrudRepository<DowUnit, Long> {
""") """)
fun findByModIdAndRace(modId: Long, race: Race?): List<DowUnit> fun findByModIdAndRace(modId: Long, race: Race?): List<DowUnit>
fun findFirstByModIdAndFilenameSquad(modId: Long, fileName: String): DowUnit?
fun deleteAllByModIdAndRaceId(modId: Long, raceId: String) fun deleteAllByModIdAndRaceId(modId: Long, raceId: String)
} }

View File

@ -2,8 +2,9 @@ package com.dowstats.service.datamaps
import com.dowstats.data.dto.controllers.RequirementDto import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.ability.AbilityDto import com.dowstats.data.dto.controllers.ability.AbilityDto
import com.dowstats.data.dto.controllers.research.ResearchDto
import com.dowstats.data.entities.ability.Ability import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.ability.AbilityEnvironment
import com.dowstats.data.repositories.AbilityEnvironmentRepository
import com.dowstats.data.repositories.AbilityRepository import com.dowstats.data.repositories.AbilityRepository
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -11,6 +12,7 @@ import org.springframework.stereotype.Service
@Service @Service
class AbilityService @Autowired constructor( class AbilityService @Autowired constructor(
val abilityRepository: AbilityRepository, val abilityRepository: AbilityRepository,
val abilityEnvironmentRepository: AbilityEnvironmentRepository,
val requirementsMappingComponent: RequirementsMappingComponent, val requirementsMappingComponent: RequirementsMappingComponent,
) { ) {
@ -20,17 +22,52 @@ class AbilityService @Autowired constructor(
.getRequirements(ability.abilityRequirements.toList() , .getRequirements(ability.abilityRequirements.toList() ,
modId) modId)
if(ability.childAbilityName == null) return ability.toDto(requirements, null) else { val allAbilityEnvironments = abilityEnvironmentRepository.findAllByModId(modId)
val allAbilities = abilityRepository.findAllByModId(modId)
return toDtoWithChild(requirements, allAbilities, ability) val spawnedAbilityEnvironment: AbilityEnvironment? =
getSpawnedAbilityEnvironment(allAbilityEnvironments, ability.spawnedEntityName)
if(ability.childAbilityName == null || ability.childAbilityName == "") return ability.toDto(requirements, null, spawnedAbilityEnvironment?.toDto()) else {
return toDtoWithChild(requirements, modId,allAbilityEnvironments, ability)
} }
} }
fun toDtoWithChild(requirements: RequirementDto?, allAbilities: List<Ability>, ability: Ability): AbilityDto = fun toDtoWithChild(requirements: RequirementDto?,
if(ability.childAbilityName == null || ability.childAbilityName == "") ability.toDto(requirements, null) else { modId: Long,
ability.toDto(requirements, allAbilityEnvironments: List<AbilityEnvironment>,
toDtoWithChild(requirements, allAbilities, ability: Ability): AbilityDto {
allAbilities.first { it.filename?.replace(".rgd", "") == ability.childAbilityName })
val spawnedAbilityEnvironment: AbilityEnvironment? =
getSpawnedAbilityEnvironment(allAbilityEnvironments, ability.spawnedEntityName)
return if (ability.childAbilityName == null || ability.childAbilityName == "")
ability.toDto(requirements, null, spawnedAbilityEnvironment?.toDto())
else {
val searchChild = ability.childAbilityName?.replace(".lua", "")?.replace("abilities\\", "")!!
ability.toDto(
requirements,
toDtoWithChild(
requirements, modId, allAbilityEnvironments,
abilityRepository.findFirstByModIdAndFilenameIn(modId, listOf(searchChild, searchChild + ".rgd") )),
spawnedAbilityEnvironment?.toDto()
) )
} }
} }
private fun getSpawnedAbilityEnvironment(allAbilityEnvironments: List<AbilityEnvironment>, spawnedEntityName: String?): AbilityEnvironment? {
val spawnedObject = spawnedEntityName?.split("\\")?.last()
?.replace(".lua", "")
?.replace(".rgd", "")
return spawnedObject?.let {
allAbilityEnvironments.firstOrNull {
it.filename?.split("\\")?.last()
?.replace(".lua", "")
?.replace(".rgd", "") == spawnedObject
}
}
}
}

View File

@ -36,11 +36,11 @@ class DowBuildingMappingService @Autowired constructor(
val buildingAddons = building.addons?.sortedBy { it.uiIndexHint }?.map { addon -> val buildingAddons = building.addons?.sortedBy { it.uiIndexHint }?.map { addon ->
val replaceWhenDone = addon.addonRequirements?.find { it.reference == Requirements.REFERENCE_REQUIREMENT_ADDON }?.replaceWhenDone ?: false val replaceWhenDone = addon.addonRequirements?.find { it.reference ==Requirements.REFERENCE_REQUIREMENT_ADDON }
?.replaceWhenDone ?: false
val requirement = requirementsMappingComponent val requirement = requirementsMappingComponent
.getRequirements(addon.addonRequirements?.toList() ?: emptyList(), .getRequirements(addon.addonRequirements?.toList() ?: emptyList(),
building.modId!!, building.modId!!
building.addons?.toList() ?: emptyList()
) )
addon.toDto(requirement?.copy( addon.toDto(requirement?.copy(

View File

@ -1,37 +1,41 @@
package com.dowstats.service.datamaps package com.dowstats.service.datamaps
import com.dowstats.Metadata.Requirements import com.dowstats.Metadata.Requirements
import com.dowstats.data.dto.controllers.EntityCompressDtoObject.compressDto
import com.dowstats.data.dto.controllers.RequirementDto import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.RequirementLimitByBuildingDto import com.dowstats.data.dto.controllers.RequirementLimitByBuildingDto
import com.dowstats.data.dto.controllers.RequirementMobBonusDto
import com.dowstats.data.dto.controllers.RequirementResearchDto import com.dowstats.data.dto.controllers.RequirementResearchDto
import com.dowstats.data.dto.controllers.RequirementSquadsDto
import com.dowstats.data.entities.RequirementBase import com.dowstats.data.entities.RequirementBase
import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.repositories.AddonRepository import com.dowstats.data.repositories.AddonRepository
import com.dowstats.data.repositories.BuildingRepository import com.dowstats.data.repositories.BuildingRepository
import com.dowstats.data.repositories.ResearchRepository import com.dowstats.data.repositories.ResearchRepository
import com.dowstats.data.repositories.UnitRepository
import com.dowstats.service.datamaps.CommonMapping.toBuildingShortDto import com.dowstats.service.datamaps.CommonMapping.toBuildingShortDto
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class RequirementsMappingComponent( class RequirementsMappingComponent(
val addonRepository: AddonRepository, val addonRepository: AddonRepository,
val unitRepository: UnitRepository,
val buildingRepository: BuildingRepository, val buildingRepository: BuildingRepository,
val researchRepository: ResearchRepository, val researchRepository: ResearchRepository,
) { ) {
fun getRequirements(requirements : List<RequirementBase>, modId: Long, thisBuildingAddons: List<BuildingAddon> = emptyList()) : RequirementDto? { fun getRequirements(requirements : List<RequirementBase>, modId: Long) : RequirementDto? {
val requireCap = val requireCap =
requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_POP }?.value?.toDouble() requirements.find { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_POP }?.value?.toDouble()
?.toInt()?.let { if(it == 0) null else it } ?.toInt()?.let { if(it == 0) null else it }
val requiredOwnership = val requiredOwnership =
requirements.filter { it.reference == Requirements.REFERENCE_REQUIREMENT_OWNERSHIP } requirements.filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_OWNERSHIP }
.mapNotNull { rro -> .mapNotNull { rro ->
rro.value rro.value
}.toSet() }.toSet()
val requirementAddon = val requirementAddon =
requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_ADDON }?.value?.let { requirements.find { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_ADDON }?.value?.let {
val addonFileName = it.split("\\").last().replace(".lua", ".rgd") val addonFileName = it.split("\\").last().replace(".lua", ".rgd")
addonFileName.let { addonFileName.let {
addonRepository.findFirstByModIdAndFilename( addonRepository.findFirstByModIdAndFilename(
@ -41,8 +45,24 @@ class RequirementsMappingComponent(
} }
} }
val requirementSquads =
requirements.filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_SQUAD}.mapNotNull {req ->
val unitName = req.value?.split(";")?.first()?.split("\\")?.last()?.replace(".lua", ".rgd")
unitName?.let {
unitRepository.findFirstByModIdAndFilenameSquad(
modId,
it.split("\\").last().replace(".lua", ".rgd")
)?.compressDto()?.let {
RequirementSquadsDto(
it,
req.value?.split(";")?.last()?.toInt() ?: 1
)
}
}
}.toSet()
val requirementAddonGlobal = val requirementAddonGlobal =
requirements.filter { it.reference == Requirements.REFERENCE_GLOBAL_REQUIREMENT_ADDON } requirements.filter { it.reference?.getPureReference() == Requirements.REFERENCE_GLOBAL_REQUIREMENT_ADDON }
.mapNotNull { rgra -> .mapNotNull { rgra ->
val addonFileName = rgra.value?.split("\\")?.last()?.replace(".lua", ".rgd") val addonFileName = rgra.value?.split("\\")?.last()?.replace(".lua", ".rgd")
addonFileName?.let { addonFileName?.let {
@ -54,14 +74,14 @@ class RequirementsMappingComponent(
}.toSet() }.toSet()
val requirementBuildings = val requirementBuildings =
requirements.filter { it.reference == Requirements.REFERENCE_REQUIREMENT_STRUCTURE }.mapNotNull { requirements.filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_STRUCTURE }.mapNotNull {
val buildingFileName = it.value?.split("\\")?.last()?.replace(".lua", ".rgd") val buildingFileName = it.value?.split("\\")?.last()?.replace(".lua", ".rgd")
buildingFileName?.let { buildingFileName?.let {
buildingRepository.findByModIdAndFilename(modId, buildingFileName) buildingRepository.findByModIdAndFilename(modId, buildingFileName)
} }
}.distinct().toBuildingShortDto().toSet() }.distinct().toBuildingShortDto().toSet()
val limitByBuilding = requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_STRUCTURE_RATIO }?.let { val limitByBuilding = requirements.find { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_STRUCTURE_RATIO }?.let {
val buildingFileName = it.value?.split(";")?.first()?.replace(".lua", ".rgd") val buildingFileName = it.value?.split(";")?.first()?.replace(".lua", ".rgd")
val limitPerBuilding = it.value?.split(";")?.last()?.toInt() ?: 0 val limitPerBuilding = it.value?.split(";")?.last()?.toInt() ?: 0
@ -76,7 +96,7 @@ class RequirementsMappingComponent(
} }
val requirementBuildingsEither = val requirementBuildingsEither =
requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EITHER }?.let { requirements.find { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EITHER }?.let {
it.value?.split(";")?.mapNotNull { bPath -> it.value?.split(";")?.mapNotNull { bPath ->
buildingRepository.findByModIdAndFilename( buildingRepository.findByModIdAndFilename(
modId, modId,
@ -86,7 +106,7 @@ class RequirementsMappingComponent(
}?.toBuildingShortDto()?.toSet() ?: emptySet() }?.toBuildingShortDto()?.toSet() ?: emptySet()
val requirementResearches = val requirementResearches =
requirements.filter { it.reference == Requirements.REFERENCE_REQUIREMENT_RESEARCH }.mapNotNull { requirements.filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_RESEARCH }.mapNotNull {
val research = it.value?.split(";")?.first() val research = it.value?.split(";")?.first()
val mustNotBeCompleteStr = it.value?.split(";")?.last() val mustNotBeCompleteStr = it.value?.split(";")?.last()
val researchFileName = research?.split("\\")?.last()?.replace(".lua", ".rgd")?.let { val researchFileName = research?.split("\\")?.last()?.replace(".lua", ".rgd")?.let {
@ -100,23 +120,87 @@ class RequirementsMappingComponent(
} }
}.toSet() }.toSet()
return if (requireCap != null || val requirementResearchEither =
requirementAddon != null || requirements.filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIRED_RESEARCH_EITHER }.mapNotNull {
requirementBuildings.isNotEmpty() || it.value?.split(";")?.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()?.let {
RequirementResearchDto(it, false)
}
}
}.flatten().toSet()
val requirementMobBonus =
requirements.find { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_MOB_BONUS }?.let {
RequirementMobBonusDto(
it.value?.split(";")?.first()?.toDouble() ?: 0.0,
runCatching {
it.value?.split(";")?.last()?.toInt()
}.getOrNull() ?: 0
)
}
val requirementStructureExclusive =
requirements.find { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EXCLUSIVE }?.let {
it.value?.let { bPath ->
buildingRepository.findByModIdAndFilename(
modId,
bPath.split("\\").last().replace(".lua", ".rgd")
)
}
}?.toBuildingShortDto()
val requirementAddonExclusive =
requirements.find { it.reference?.getPureReference() == Requirements.REFERENCE_LOCAL_REQUIRED_ADDON_EXCLUSIVE }?.let {
it.value?.let { addonPath ->
addonRepository.findFirstByModIdAndFilename(
modId,
addonPath.split("\\").last().replace(".lua", ".rgd")
)
}
}?.toShortDto()
return if (requirementBuildings.isNotEmpty() ||
requirementBuildingsEither.isNotEmpty() || requirementBuildingsEither.isNotEmpty() ||
requirementResearches.isNotEmpty() || requirementResearches.isNotEmpty() ||
requirementAddonGlobal.isNotEmpty() requirementSquads.isNotEmpty() ||
requirementResearchEither.isNotEmpty() ||
requirementAddon != null ||
requirementAddonGlobal.isNotEmpty() ||
requireCap != null ||
limitByBuilding != null ||
requirementMobBonus != null ||
requirementStructureExclusive != null ||
requirementAddonExclusive != null ||
requiredOwnership.isNotEmpty()
) { ) {
RequirementDto( RequirementDto(
requirementBuildings, requirementBuildings,
requirementBuildingsEither, requirementBuildingsEither,
requirementResearches, requirementResearches,
requirementSquads,
requirementResearchEither,
requirementAddon, requirementAddon,
requirementAddonGlobal, requirementAddonGlobal,
requireCap, requireCap,
limitByBuilding, limitByBuilding,
requirementMobBonus,
requirementStructureExclusive,
requirementAddonExclusive,
requiredOwnership requiredOwnership
) )
} else null } else null
} }
fun String.getPureReference(): String =
this.split("\\").last()
.replace(".lua", "")
.replace(".rgd", "")
} }

View File

@ -39,7 +39,7 @@ class ModStorageIntegrationService(
fun requestAvailableMods() { fun requestAvailableMods() {
log.info("Requesting available mods") log.info("Requesting available mods")
val urlString = "http://crosspick.ru/dow_stats_client/dow_stats_balance_mod/mods.txt" val urlString = "http://89.108.83.108/dow_stats_client/dow_stats_balance_mod/mods.txt"
val connection = URL(urlString).openConnection() val connection = URL(urlString).openConnection()
connection.setRequestProperty("X-Key", modStorageConfig.key) connection.setRequestProperty("X-Key", modStorageConfig.key)
val inputStream: InputStream = connection.getInputStream() val inputStream: InputStream = connection.getInputStream()
@ -183,7 +183,7 @@ class ModStorageIntegrationService(
private fun downloadAndExtractMod(modTechName: String, version: String) { private fun downloadAndExtractMod(modTechName: String, version: String) {
log.info("Downloading mod $modTechName") log.info("Downloading mod $modTechName")
val urlString = "http://crosspick.ru/dow_stats_client/dow_stats_balance_mod/$modTechName.zip" val urlString = "http://89.108.83.108/dow_stats_client/dow_stats_balance_mod/$modTechName.zip"
val connection = URL(urlString).openConnection() val connection = URL(urlString).openConnection()
connection.setRequestProperty("X-Key", modStorageConfig.key) connection.setRequestProperty("X-Key", modStorageConfig.key)
val inputStream: InputStream = connection.getInputStream() val inputStream: InputStream = connection.getInputStream()

View File

@ -0,0 +1,90 @@
package com.dowstats.service.w40k
import com.dowstats.data.entities.ArmorType
import com.dowstats.data.entities.Mod
import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.ability.AbilityEnvironment
import com.dowstats.data.rgd.RgdData
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
@Service
class AbilityEnvironmentRgdExtractService @Autowired constructor(
private val commonParseRgdService: CommonParseRgdService,
private val deathExplosionRgdExtractService: DeathExplosionRgdExtractService,
) {
val log = LoggerFactory.getLogger(AbilityEnvironmentRgdExtractService::class.java)
fun extractToAbilityEnvironmentEntity(
fileName: String,
abilityEnvironmentData: List<RgdData>,
mod: Mod,
modFolderData: String,
abilities: Set<Ability>,
armorTypes: Set<ArmorType>,
modDictionary: Map<Int, String>
): AbilityEnvironment? {
val abilityEnvironment = AbilityEnvironment()
abilityEnvironment.filename = fileName
abilityEnvironment.modId = mod.id
val uiInfo = abilityEnvironmentData.getRgdTableByName("ui_ext")?.getRgdTableByName("ui_info")
val uiData = uiInfo?.let {
commonParseRgdService.getUiInfo(
uiInfo,
modDictionary,
modFolderData,
mod,
log,
)
}
abilityEnvironment.name = uiData?.name
abilityEnvironment.description = uiData?.description
abilityEnvironment.icon = uiData?.iconPath
val healthExt = abilityEnvironmentData.getRgdTableByName("health_ext")
abilityEnvironment.health = healthExt?.getDoubleByName("hitpoints")?.toInt()
abilityEnvironment.armour = healthExt?.getDoubleByName("armour")?.minus(100)
abilityEnvironment.healthRegeneration = healthExt?.getDoubleByName("regeneration_rate")
abilityEnvironment.moveSpeed = getSpeed(abilityEnvironmentData)
abilityEnvironment.sightRadius = getSightRadius(abilityEnvironmentData)
abilityEnvironment.detectRadius = getDetectRadius(abilityEnvironmentData)
abilityEnvironment.lifetime = getLifetime(abilityEnvironmentData)
abilityEnvironment.armorType = commonParseRgdService.getArmorType(abilityEnvironmentData, armorTypes, "type_armour")
abilityEnvironment.armorType2 = commonParseRgdService.getArmorType(abilityEnvironmentData, armorTypes, "type_armour_2")
abilityEnvironment.abilities.addAll(commonParseRgdService.getAbilities(abilityEnvironmentData, abilities).toMutableSet())
abilityEnvironment.deathExplosions.addAll(deathExplosionRgdExtractService.getDeathExplosions(abilityEnvironmentData, armorTypes).toMutableSet())
return abilityEnvironment
}
private fun getSpeed(abilityEnvironmentData: List<RgdData>): Int? = abilityEnvironmentData
.getRgdTableByName("moving_ext")
?.getDoubleByName("speed_max")
?.toInt()
private fun getSightRadius(abilityEnvironmentData: List<RgdData>): Int? = abilityEnvironmentData
.getRgdTableByName("sight_ext")
?.getDoubleByName("sight_radius")
?.toInt()
private fun getDetectRadius(abilityEnvironmentData: List<RgdData>): Int? = abilityEnvironmentData
.getRgdTableByName("sight_ext")
?.getDoubleByName("keen_sight_radius")
?.toInt()
private fun getLifetime(abilityEnvironmentData: List<RgdData>): Double? = abilityEnvironmentData
.getRgdTableByName("suicide_ext")
?.getDoubleByName("lifetime")
}

View File

@ -77,7 +77,7 @@ class AbilityRgdExtractService @Autowired constructor(
ability.refreshTime = abilityData.getDoubleByName("refresh_time") ability.refreshTime = abilityData.getDoubleByName("refresh_time")
ability.durationTime = abilityData.getDoubleByName("duration_time") ability.durationTime = abilityData.getDoubleByName("duration_time")
ability.spawnedEntityName = abilityData.getStringByName("spawn_entity_name") ability.spawnedEntityName = abilityData.getStringByName("spawned_entity_name")
// Damage & Area Effect // Damage & Area Effect
@ -153,7 +153,9 @@ class AbilityRgdExtractService @Autowired constructor(
return abilityData.getRgdTableByName("requirements")?.mapNotNull { r -> return abilityData.getRgdTableByName("requirements")?.mapNotNull { r ->
if (r.name.contains("required_")) { if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData> val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else { if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true)
null
else {
AbilityRequirements().also { AbilityRequirements().also {
it.ability = ability it.ability = ability
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -79,7 +79,7 @@ class BuildingAddonRgdExtractService @Autowired constructor(
return requirements?.mapNotNull { r -> return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) { if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData> val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else { if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
AddonRequirements().also { AddonRequirements().also {
it.addon = addon it.addon = addon
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -26,6 +26,7 @@ import org.springframework.stereotype.Service
class BuildingRgdExtractService @Autowired constructor( class BuildingRgdExtractService @Autowired constructor(
private val addonRgdExtractService: BuildingAddonRgdExtractService, private val addonRgdExtractService: BuildingAddonRgdExtractService,
private val commonParseRgdService: CommonParseRgdService, private val commonParseRgdService: CommonParseRgdService,
private val deathExplosionRgdExtractService: DeathExplosionRgdExtractService,
) { ) {
val log = LoggerFactory.getLogger(BuildingRgdExtractService::class.java) val log = LoggerFactory.getLogger(BuildingRgdExtractService::class.java)
@ -54,7 +55,6 @@ class BuildingRgdExtractService @Autowired constructor(
race: Race, race: Race,
modFolderData: String, modFolderData: String,
mod: Mod, mod: Mod,
races: List<Race>,
armorTypes: Set<ArmorType>, armorTypes: Set<ArmorType>,
addonsRgdData: Map<String, List<RgdData>>, addonsRgdData: Map<String, List<RgdData>>,
): BuildingDataToSave { ): BuildingDataToSave {
@ -62,8 +62,8 @@ class BuildingRgdExtractService @Autowired constructor(
val building = Building() val building = Building()
building.race = race building.race = race
building.armorType = getBuildingArmourType(buildingData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type") building.armorType = commonParseRgdService.getArmorType(buildingData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type")
building.armorType2 = getBuildingArmourType(buildingData, armorTypes, "type_armour_2") building.armorType2 = commonParseRgdService.getArmorType(buildingData, armorTypes, "type_armour_2")
building.filename = fileName building.filename = fileName
val uiExt = buildingData.getRgdTableByName("ui_ext") ?: throw Exception("Could not find ui_ext at $fileName") val uiExt = buildingData.getRgdTableByName("ui_ext") ?: throw Exception("Could not find ui_ext at $fileName")
@ -100,6 +100,8 @@ class BuildingRgdExtractService @Autowired constructor(
building.repairMax = healthData.maxRepaires building.repairMax = healthData.maxRepaires
building.buildingLimit = getLimit(buildingData)
building.units = getUnits(buildingData, units).toMutableSet() building.units = getUnits(buildingData, units).toMutableSet()
building.researches = getResearches(buildingData, researches).toMutableSet() building.researches = getResearches(buildingData, researches).toMutableSet()
@ -109,6 +111,8 @@ class BuildingRgdExtractService @Autowired constructor(
building.abilities = commonParseRgdService.getAbilities(buildingData, abilities).toMutableSet() building.abilities = commonParseRgdService.getAbilities(buildingData, abilities).toMutableSet()
building.deathExplosions.addAll(deathExplosionRgdExtractService.getDeathExplosions(buildingData, armorTypes).toMutableSet())
val addons = getAddons(buildingData) val addons = getAddons(buildingData)
building.addons = addons?.mapNotNull {addonFileName -> building.addons = addons?.mapNotNull {addonFileName ->
val addonRgdData = addonsRgdData[addonFileName] val addonRgdData = addonsRgdData[addonFileName]
@ -134,17 +138,16 @@ class BuildingRgdExtractService @Autowired constructor(
return BuildingDataToSave(building, buildingWeapons) return BuildingDataToSave(building, buildingWeapons)
} }
private fun getLimit(buildingData: List<RgdData>): Int? {
val requirements = buildingData.getRgdTableByName("requirement_ext")
?.getRgdTableByName("requirements")
private fun getBuildingArmourType( return requirements?.map {
unitData: List<RgdData>, if (it.type == 100) {
armorTypes: Iterable<ArmorType>, val reqirementData = it.value as List<RgdData>
armorTypeTableName: String reqirementData.find {it.name == "max_cap"}?.value as Double?
): ArmorType? { } else null
val armorType = unitData.getRgdTableByName("type_ext") }?.filterNotNull()?.firstOrNull()?.toInt()
?.getRgdTableByName(armorTypeTableName)
?.getStringByName("\$REF")
return armorTypes.find { it.id == armorType?.replace("type_armour\\tp_", "")?.replace(".lua", "") }
} }
private fun getHealthData(buildingData: List<RgdData>): HealthData { private fun getHealthData(buildingData: List<RgdData>): HealthData {
@ -245,7 +248,7 @@ class BuildingRgdExtractService @Autowired constructor(
return requirements?.mapNotNull { r -> return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) { if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData> val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else { if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
BuildingRequirements().also { BuildingRequirements().also {
it.building = building it.building = building
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -5,6 +5,7 @@ import com.dowstats.data.dto.AreaEffect
import com.dowstats.data.dto.BuildCost import com.dowstats.data.dto.BuildCost
import com.dowstats.data.dto.ResourceIncome import com.dowstats.data.dto.ResourceIncome
import com.dowstats.data.dto.UiInfo import com.dowstats.data.dto.UiInfo
import com.dowstats.data.entities.ArmorType
import com.dowstats.data.entities.Mod import com.dowstats.data.entities.Mod
import com.dowstats.data.entities.ability.Ability import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.rgd.RgdData import com.dowstats.data.rgd.RgdData
@ -24,7 +25,8 @@ class CommonParseRgdService @Autowired constructor(
) { ) {
fun getRequirementReference(ref: String?, rTable: List<RgdData>, fileName: String, log: Logger): String? = when (ref) { fun getRequirementReference(ref: String?, rTable: List<RgdData>, fileName: String, log: Logger): String? =
when (ref?.getPureReference()) {
Requirements.REFERENCE_REQUIREMENT_POP -> rTable.getDoubleByName("population_required").toString() Requirements.REFERENCE_REQUIREMENT_POP -> rTable.getDoubleByName("population_required").toString()
Requirements.REFERENCE_REQUIREMENT_ADDON -> rTable.getStringByName("addon_name") Requirements.REFERENCE_REQUIREMENT_ADDON -> rTable.getStringByName("addon_name")
Requirements.REFERENCE_GLOBAL_REQUIREMENT_ADDON -> rTable.getStringByName("global_addon_name") Requirements.REFERENCE_GLOBAL_REQUIREMENT_ADDON -> rTable.getStringByName("global_addon_name")
@ -33,12 +35,20 @@ class CommonParseRgdService @Autowired constructor(
Requirements.REFERENCE_REQUIREMENT_STRUCTURE -> rTable.getStringByName("structure_name") Requirements.REFERENCE_REQUIREMENT_STRUCTURE -> rTable.getStringByName("structure_name")
Requirements.REFERENCE_REQUIREMENT_RESEARCH -> rTable.getStringByName("research_name") + ";" + Requirements.REFERENCE_REQUIREMENT_RESEARCH -> rTable.getStringByName("research_name") + ";" +
rTable.getBooleanByName("research_must_not_be_complete") rTable.getBooleanByName("research_must_not_be_complete")
Requirements.REFERENCE_REQUIRED_RESEARCH_EITHER -> rTable.getStringByName("research_name_either") + ";" +
rTable.getStringByName("research_name_or")
Requirements.REFERENCE_REQUIREMENT_OWNERSHIP -> rTable.getStringByName("own_name") Requirements.REFERENCE_REQUIREMENT_OWNERSHIP -> rTable.getStringByName("own_name")
Requirements.REFERENCE_REQUIREMENT_CAP -> rTable.getIntByName("max_cap").toString() Requirements.REFERENCE_REQUIREMENT_CAP -> rTable.getIntByName("max_cap").toString()
Requirements.REFERENCE_REQUIREMENT_SQUAD_CAP -> rTable.getIntByName("max_squad_cap").toString() Requirements.REFERENCE_REQUIREMENT_SQUAD_CAP -> rTable.getIntByName("max_squad_cap").toString()
Requirements.REFERENCE_REQUIREMENT_CUM_SQUAD_CAP -> rTable.getIntByName("max_cumulative_squad_cap").toString() Requirements.REFERENCE_REQUIREMENT_CUM_SQUAD_CAP -> rTable.getIntByName("max_cumulative_squad_cap").toString()
Requirements.REFERENCE_REQUIREMENT_STRUCTURE_RATIO -> rTable.getStringByName("required_structure_name") + ";" + Requirements.REFERENCE_REQUIREMENT_STRUCTURE_RATIO -> rTable.getStringByName("required_structure_name") + ";" +
rTable.getIntByName("this_structure_count").toString() rTable.getIntByName("this_structure_count").toString()
Requirements.REFERENCE_REQUIREMENT_MOB_BONUS -> rTable.getDoubleByName("mobvalue_required").toString() + ";" +
rTable.getIntByName("proximity_required").toString()
Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EXCLUSIVE -> rTable.getStringByName("structure_name_exclusive")
Requirements.REFERENCE_LOCAL_REQUIRED_ADDON_EXCLUSIVE -> rTable.getStringByName("mutually_exclusive_with")
Requirements.REFERENCE_REQUIREMENT_SQUAD -> rTable.getStringByName("squad_name") + ";" +
rTable.getIntByName("min_count")
else -> { else -> {
log.warn("Unknown requirement reference $ref at $fileName") log.warn("Unknown requirement reference $ref at $fileName")
null null
@ -104,6 +114,21 @@ class CommonParseRgdService @Autowired constructor(
return UiInfo(name, description, icon) return UiInfo(name, description, icon)
} }
fun getArmorType(
entityData: List<RgdData>,
armorTypes: Iterable<ArmorType>,
armorTypeTableName: String
): ArmorType? {
val armorType = entityData.getRgdTableByName("type_ext")
?.getRgdTableByName(armorTypeTableName)
?.getStringByName("\$REF")
return armorTypes.find { it.id == armorType
?.replace("TYPE_Armour\\tp_", "")
?.replace("type_armour\\tp_", "")
?.replace(".lua", "") }
}
fun getAreaEffectData(areaEffectData: List<RgdData>): AreaEffect { fun getAreaEffectData(areaEffectData: List<RgdData>): AreaEffect {
val areaEffectInformation = areaEffectData.getRgdTableByName("area_effect_information") val areaEffectInformation = areaEffectData.getRgdTableByName("area_effect_information")
@ -163,7 +188,7 @@ class CommonParseRgdService @Autowired constructor(
) )
} }
fun getAbilities(modelData: List<RgdData>?, abilities: Set<Ability>): List<Ability> = modelData fun getAbilities(entityData: List<RgdData>?, abilities: Set<Ability>): List<Ability> = entityData
?.getRgdTableByName("ability_ext") ?.getRgdTableByName("ability_ext")
?.getRgdTableByName("abilities") ?.getRgdTableByName("abilities")
?.mapNotNull { ability -> ?.mapNotNull { ability ->
@ -175,6 +200,10 @@ class CommonParseRgdService @Autowired constructor(
} else null } else null
} ?: emptyList() } ?: emptyList()
fun String.getPureReference(): String =
this.split("\\").last()
.replace(".lua", "")
.replace(".rgd", "")
} }

View File

@ -0,0 +1,93 @@
package com.dowstats.service.w40k
import com.dowstats.data.dto.AreaEffect
import com.dowstats.data.entities.ArmorType
import com.dowstats.data.entities.ModifiersBase.ModifiersBaseCompanion.fillDataFromRgdTable
import com.dowstats.data.entities.ModifiersBase.ModifiersBaseCompanion.filterUseless
import com.dowstats.data.entities.deathexplosion.DeathExplosion
import com.dowstats.data.entities.deathexplosion.DeathExplosionArmorPiercing
import com.dowstats.data.entities.deathexplosion.DeathExplosionModifiers
import com.dowstats.data.entities.weapon.WeaponArmorPiercing
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.math.BigDecimal
import kotlin.collections.get
import kotlin.toBigDecimal
@Service
class DeathExplosionRgdExtractService @Autowired constructor(
private val commonParseRgdService: CommonParseRgdService,
) {
val log = LoggerFactory.getLogger(DeathExplosionRgdExtractService::class.java)
fun getDeathExplosions(
entityData: List<RgdData>,
armorTypes: Set<ArmorType>,
): List<DeathExplosion> {
val rootExplosion = entityData.getRgdTableByName("death_explosion_ext")
val globalChance = rootExplosion?.getDoubleByName("chance")
fun getExplosionModifiers(explosionPartRoot: List<RgdData>?, deathExplosion: DeathExplosion): List<DeathExplosionModifiers> =
explosionPartRoot
?.getRgdTableByName("area_effect")
?.getRgdTableByName("weapon_damage")
?.getRgdTableByName("modifiers")
?.mapNotNull { m ->
if (m.name.contains("modifier_")) {
val mTable = m.value as List<RgdData>
val modifier = mTable.getRgdTableByName("modifier")
val maxLifetime = mTable.getDoubleByName("max_lifetime")
if (modifier?.getStringByName("\$REF") == "modifiers\\no_modifier.lua" || modifier == null) null else {
DeathExplosionModifiers().also {
it.deathExplosion = deathExplosion
it.maxLifeTime = maxLifetime
}.fillDataFromRgdTable(modifier)
}
} else null
}?.filterUseless() ?: emptyList()
fun getExplosionArmorPiercings(areaEffectData: AreaEffect, deathExplosion: DeathExplosion): List<DeathExplosionArmorPiercing> = armorTypes.map {
val deathExplosionArmorPiercing = DeathExplosionArmorPiercing()
deathExplosionArmorPiercing.deathExplosion = deathExplosion
deathExplosionArmorPiercing.armorType = it
val piercingValue =
(areaEffectData.weaponDmgMap[it.id] ?: areaEffectData.defaultArmorPiercing).toBigDecimal()
deathExplosionArmorPiercing.piercingValue =
piercingValue.let { pv -> if (pv <= BigDecimal(100)) pv else BigDecimal(100) }
deathExplosionArmorPiercing
}
return rootExplosion?.getRgdTableByName("death_explosions")
?.mapNotNull { explosion ->
if (explosion.name.contains("death_explosion")) {
val explosionPartRoot = explosion.value as List<RgdData>
val chance = explosionPartRoot.getDoubleByName("chance")
val areaEffect = explosionPartRoot.getRgdTableByName("area_effect")?.let { commonParseRgdService.getAreaEffectData(it) }
DeathExplosion().also {
it.globalChance = globalChance
it.chance = chance
it.minDamage = areaEffect?.minDamage
it.maxDamage = areaEffect?.maxDamage
it.minDamageValue = areaEffect?.minDamageValue
it.moraleDamage = areaEffect?.moraleDamage
it.radius = areaEffect?.damageRadius
it.throwForceMin = areaEffect?.throwForceMin
it.throwForceMax = areaEffect?.throwForceMax
it.explosionModifiers += getExplosionModifiers(explosionPartRoot, it)
areaEffect?.let{ ae ->
it.explosionPiercings += getExplosionArmorPiercings(ae, it)
}
}
} else null
} ?: emptyList()
}
}

View File

@ -29,6 +29,9 @@ class ModAttribPathService @Autowired constructor(
fun getAbilitiesAttribsPath(modFolderData: String): String = fun getAbilitiesAttribsPath(modFolderData: String): String =
"$modFolderData${File.separator}attrib${File.separator}abilities" "$modFolderData${File.separator}attrib${File.separator}abilities"
fun getAbilityEnvironmentsAttribsPath(modFolderData: String): String =
"$modFolderData${File.separator}attrib${File.separator}ebps${File.separator}environment${File.separator}gameplay"
fun getResearchAttribsPath(modFolderData: String): String = fun getResearchAttribsPath(modFolderData: String): String =
"$modFolderData${File.separator}attrib${File.separator}research" "$modFolderData${File.separator}attrib${File.separator}research"

View File

@ -2,6 +2,7 @@ package com.dowstats.service.w40k
import com.dowstats.data.entities.* import com.dowstats.data.entities.*
import com.dowstats.data.entities.ability.Ability import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.ability.AbilityEnvironment
import com.dowstats.data.entities.building.BuildingWeapon import com.dowstats.data.entities.building.BuildingWeapon
import com.dowstats.data.entities.building.BuildingWeaponKey import com.dowstats.data.entities.building.BuildingWeaponKey
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
@ -25,24 +26,26 @@ import kotlin.io.path.name
@Service @Service
class ModParserService @Autowired constructor( class ModParserService @Autowired constructor(
val rgdParserService: RgdParserService, private val rgdParserService: RgdParserService,
val unitRgdExtractService: UnitRgdExtractService, private val unitRgdExtractService: UnitRgdExtractService,
val researchRgdExtractService: ResearchRgdExtractService, private val researchRgdExtractService: ResearchRgdExtractService,
val raceRepository: RaceRepository, private val raceRepository: RaceRepository,
val armorTypeRepository: ArmorTypeRepository, private val armorTypeRepository: ArmorTypeRepository,
val areaEffectRepository: AreaEffectRepository, private val areaEffectRepository: AreaEffectRepository,
val areaFilterRepository: AreaFilterRepository, private val areaFilterRepository: AreaFilterRepository,
val activationRepository: ActivationRepository, private val activationRepository: ActivationRepository,
val unitRepository: UnitRepository, private val unitRepository: UnitRepository,
val sergeantRepository: SergeantRepository, private val sergeantRepository: SergeantRepository,
val abilityRgdExtractService: AbilityRgdExtractService, private val abilityRgdExtractService: AbilityRgdExtractService,
val abilityRepository: AbilityRepository, private val abilityEnvironmentRgdExtractService: AbilityEnvironmentRgdExtractService,
val weaponRgdExtractService: WeaponRgdExtractService, private val abilityEnvironmentRepository: AbilityEnvironmentRepository,
val weaponRepository: WeaponRepository, private val abilityRepository: AbilityRepository,
val modAttribPathService: ModAttribPathService, private val weaponRgdExtractService: WeaponRgdExtractService,
val buildingRepository: BuildingRepository, private val weaponRepository: WeaponRepository,
val buildingExtractService: BuildingRgdExtractService, private val modAttribPathService: ModAttribPathService,
val postParsingService: PostParsingService, private val buildingRepository: BuildingRepository,
private val buildingExtractService: BuildingRgdExtractService,
private val postParsingService: PostParsingService,
private val researchRepository: ResearchRepository, private val researchRepository: ResearchRepository,
private val modRepository: ModRepository, private val modRepository: ModRepository,
) { ) {
@ -103,10 +106,13 @@ class ModParserService @Autowired constructor(
val weapons = saveWeapons(modFolderData, mod, armorTypes, enrichedModDictionary) val weapons = saveWeapons(modFolderData, mod, armorTypes, enrichedModDictionary)
val abilities = saveAbilities(modFolderData, mod, activations, areaEffects, areaFilters, armorTypes, enrichedModDictionary) val abilities = saveAbilities(modFolderData, mod, activations, areaEffects, areaFilters, armorTypes, enrichedModDictionary)
val researches = saveResearches(modFolderData, mod, enrichedModDictionary) val researches = saveResearches(modFolderData, mod, enrichedModDictionary)
saveUnits(modFolderData, weapons, abilities, racesList, armorTypes, mod, enrichedModDictionary) saveUnits(modFolderData, weapons, abilities, racesList, armorTypes, mod, enrichedModDictionary)
saveBuildings(modFolderData, weapons, abilities, researches, racesList, armorTypes, mod, enrichedModDictionary) saveBuildings(modFolderData, weapons, abilities, researches, racesList, armorTypes, mod, enrichedModDictionary)
saveAbilityEnvironments(modFolderData, mod, abilities, armorTypes, enrichedModDictionary)
modRepository.removeCampaignEntities(mod.id!!) modRepository.removeCampaignEntities(mod.id!!)
postParsingService.bindResearch(mod) postParsingService.bindResearch(mod)
log.info("Complete parse mod ${mod.technicalName}:${mod.version}") log.info("Complete parse mod ${mod.technicalName}:${mod.version}")
@ -367,7 +373,6 @@ class ModParserService @Autowired constructor(
race, race,
modFolderData, modFolderData,
mod, mod,
races,
armorTypes, armorTypes,
addonsRgdData, addonsRgdData,
) )
@ -408,7 +413,7 @@ class ModParserService @Autowired constructor(
private fun getAddonsRgdData(modFolderData: String): Map<String, List<RgdData>> { private fun getAddonsRgdData(modFolderData: String): Map<String, List<RgdData>> {
val classicAddons = val classicAddons =
rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/addons") rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}${File.separator}attrib${File.separator}addons")
val modAddons = val modAddons =
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getAddonAttribsPath(modFolderData)) rgdParserService.parseFolderToRgdFiles(modAttribPathService.getAddonAttribsPath(modFolderData))
@ -419,7 +424,7 @@ class ModParserService @Autowired constructor(
private fun saveWeapons(modFolderData: String, mod: Mod, armorTypes: Set<ArmorType>, modDictionary: Map<Int, String>): Set<Weapon> { private fun saveWeapons(modFolderData: String, mod: Mod, armorTypes: Set<ArmorType>, modDictionary: Map<Int, String>): Set<Weapon> {
val classicWeapons = val classicWeapons =
rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/weapon") rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}${File.separator}attrib${File.separator}weapon")
val modWeapons = val modWeapons =
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getWeaponAttribsPath(modFolderData)) rgdParserService.parseFolderToRgdFiles(modAttribPathService.getWeaponAttribsPath(modFolderData))
@ -445,7 +450,7 @@ class ModParserService @Autowired constructor(
armorTypes: Set<ArmorType>, modDictionary: Map<Int, String>): Set<Ability> { armorTypes: Set<ArmorType>, modDictionary: Map<Int, String>): Set<Ability> {
val classicAbilities = val classicAbilities =
rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/abilities") rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}${File.separator}attrib${File.separator}abilities")
val modAbilities = val modAbilities =
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getAbilitiesAttribsPath(modFolderData)) rgdParserService.parseFolderToRgdFiles(modAttribPathService.getAbilitiesAttribsPath(modFolderData))
@ -470,6 +475,34 @@ class ModParserService @Autowired constructor(
} }
} }
private fun saveAbilityEnvironments(modFolderData: String,
mod: Mod,
abilities: Set<Ability>,
armorTypes: Set<ArmorType>,
modDictionary: Map<Int, String>): Set<AbilityEnvironment> {
val classicAbilityEnvironments =
rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}${File.separator}attrib${File.separator}ebps${File.separator}environment${File.separator}gameplay")
val modAbilityEnvironments =
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getAbilityEnvironmentsAttribsPath(modFolderData))
val allAbilityEnvironments = classicAbilityEnvironments + modAbilityEnvironments
val abilityEnvironmentsToSave = allAbilityEnvironments.mapNotNull {
abilityEnvironmentRgdExtractService.extractToAbilityEnvironmentEntity(it.key, it.value, mod,
modFolderData,
abilities,
armorTypes,
modDictionary,
)
}
return try {
abilityEnvironmentRepository.saveAll(abilityEnvironmentsToSave).toSet()
} catch (e: Exception) {
throw e
}
}
} }

View File

@ -77,7 +77,7 @@ class ResearchRgdExtractService @Autowired constructor(
return requirements?.mapNotNull { r -> return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) { if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData> val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else { if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
ResearchRequirements().also { ResearchRequirements().also {
it.research = research it.research = research
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -22,6 +22,7 @@ import org.springframework.stereotype.Service
@Service @Service
class SergeantRgdExtractService @Autowired constructor( class SergeantRgdExtractService @Autowired constructor(
private val commonParseRgdService: CommonParseRgdService, private val commonParseRgdService: CommonParseRgdService,
private val deathExplosionRgdExtractService: DeathExplosionRgdExtractService,
) { ) {
val log = LoggerFactory.getLogger(SergeantRgdExtractService::class.java) val log = LoggerFactory.getLogger(SergeantRgdExtractService::class.java)
@ -66,8 +67,8 @@ class SergeantRgdExtractService @Autowired constructor(
sergeant.buildCostSouls = buildCost.souls sergeant.buildCostSouls = buildCost.souls
sergeant.buildCostTime = buildCost.time sergeant.buildCostTime = buildCost.time
sergeant.armorType = getUnitArmorType(sergeantData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type") sergeant.armorType = commonParseRgdService.getArmorType(sergeantData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type")
sergeant.armorType2 = getUnitArmorType(sergeantData, armorTypes, "type_armour_2") sergeant.armorType2 = commonParseRgdService.getArmorType(sergeantData, armorTypes, "type_armour_2")
sergeant.filename = fileName sergeant.filename = fileName
val uiExt = sergeantData.getRgdTableByName("ui_ext") ?: throw Exception("Could not find ui_ext at $fileName") val uiExt = sergeantData.getRgdTableByName("ui_ext") ?: throw Exception("Could not find ui_ext at $fileName")
@ -94,6 +95,8 @@ class SergeantRgdExtractService @Autowired constructor(
sergeant.powerIncome = incomeData.power sergeant.powerIncome = incomeData.power
sergeant.requisitionIncome = incomeData.requisition sergeant.requisitionIncome = incomeData.requisition
sergeant.mobValue = getMobValue(sergeantData)
sergeant.sightRadius = getSightRadius(sergeantData)?.toInt() sergeant.sightRadius = getSightRadius(sergeantData)?.toInt()
sergeant.detectRadius = getDetectRadius(sergeantData)?.toInt() sergeant.detectRadius = getDetectRadius(sergeantData)?.toInt()
@ -108,6 +111,8 @@ class SergeantRgdExtractService @Autowired constructor(
sergeant.abilities = commonParseRgdService.getAbilities(sergeantData, abilities).toMutableSet() sergeant.abilities = commonParseRgdService.getAbilities(sergeantData, abilities).toMutableSet()
sergeant.deathExplosions.addAll(deathExplosionRgdExtractService.getDeathExplosions(sergeantData, armorTypes).toMutableSet())
val sergeantWeapons = getSergeantWeapons(sergeantData)?.mapNotNull { weaponData -> val sergeantWeapons = getSergeantWeapons(sergeantData)?.mapNotNull { weaponData ->
weapons.find { weapons.find {
it.filename == weaponData.weaponFilename + ".rgd" it.filename == weaponData.weaponFilename + ".rgd"
@ -119,19 +124,6 @@ class SergeantRgdExtractService @Autowired constructor(
return Pair(sergeant, sergeantWeapons) return Pair(sergeant, sergeantWeapons)
} }
private fun getUnitArmorType(
unitData: List<RgdData>,
armorTypes: Iterable<ArmorType>,
armorTypeTableName: String
): ArmorType? {
val armorType = unitData.getRgdTableByName("type_ext")
?.getRgdTableByName(armorTypeTableName)
?.getStringByName("\$REF")
return armorTypes.find { it.id == armorType?.replace("type_armour\\tp_", "")?.replace(".lua", "") }
}
private fun getHealthAndMoraleDeathPenaltyData(unitData: List<RgdData>): HealthAndMoraleDeathData { private fun getHealthAndMoraleDeathPenaltyData(unitData: List<RgdData>): HealthAndMoraleDeathData {
val healthExt = unitData.getRgdTableByName("health_ext") val healthExt = unitData.getRgdTableByName("health_ext")
return HealthAndMoraleDeathData( return HealthAndMoraleDeathData(
@ -142,6 +134,10 @@ class SergeantRgdExtractService @Autowired constructor(
) )
} }
private fun getMobValue(squadData: List<RgdData>): Double? =
squadData.getRgdTableByName("mob_ext")
?.getDoubleByName("mob_value")
private fun getMassData(unitData: List<RgdData>): MassData { private fun getMassData(unitData: List<RgdData>): MassData {
val massDataRgd = unitData val massDataRgd = unitData
@ -199,7 +195,7 @@ class SergeantRgdExtractService @Autowired constructor(
return requirements?.mapNotNull { r -> return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) { if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData> val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else { if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
SergeantRequirements().also { SergeantRequirements().also {
it.sergeant = sergeant it.sergeant = sergeant
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -11,9 +11,9 @@ import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.sergant.Sergeant import com.dowstats.data.entities.sergant.Sergeant
import com.dowstats.data.entities.unit.DowUnit import com.dowstats.data.entities.unit.DowUnit
import com.dowstats.data.entities.unit.UnitModifiers import com.dowstats.data.entities.unit.UnitModifiers
import com.dowstats.data.entities.unit.UnitMoraleBrakeModifiers
import com.dowstats.data.entities.unit.UnitRequirements import com.dowstats.data.entities.unit.UnitRequirements
import com.dowstats.data.entities.weapon.Weapon import com.dowstats.data.entities.weapon.Weapon
import com.dowstats.data.entities.weapon.WeaponModifiers
import com.dowstats.data.rgd.RgdData import com.dowstats.data.rgd.RgdData
import com.dowstats.data.rgd.RgdDataUtil.getBooleanByName import com.dowstats.data.rgd.RgdDataUtil.getBooleanByName
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
@ -28,6 +28,7 @@ import org.springframework.stereotype.Service
class UnitRgdExtractService @Autowired constructor( class UnitRgdExtractService @Autowired constructor(
private val commonParseRgdService: CommonParseRgdService, private val commonParseRgdService: CommonParseRgdService,
private val sergeantRgdExtractService: SergeantRgdExtractService, private val sergeantRgdExtractService: SergeantRgdExtractService,
private val deathExplosionRgdExtractService: DeathExplosionRgdExtractService,
) { ) {
val log = LoggerFactory.getLogger(UnitRgdExtractService::class.java) val log = LoggerFactory.getLogger(UnitRgdExtractService::class.java)
@ -49,6 +50,7 @@ class UnitRgdExtractService @Autowired constructor(
val max: Double?, val max: Double?,
val broken: Double?, val broken: Double?,
val regeneration: Double?, val regeneration: Double?,
val unitMoraleBrakeModifiers: List<UnitMoraleBrakeModifiers>,
) )
data class SergeantData( data class SergeantData(
@ -82,8 +84,8 @@ class UnitRgdExtractService @Autowired constructor(
val unitRace = races.find { it.id == race } ?: throw Exception("Cant get race $race") val unitRace = races.find { it.id == race } ?: throw Exception("Cant get race $race")
unit.race = unitRace unit.race = unitRace
unit.armorType = getUnitArmorType(unitData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type") unit.armorType = commonParseRgdService.getArmorType(unitData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type")
unit.armorType2 = getUnitArmorType(unitData, armorTypes, "type_armour_2") unit.armorType2 = commonParseRgdService.getArmorType(unitData, armorTypes, "type_armour_2")
val squadUiInfo = squadData.getRgdTableByName("squad_ui_ext") val squadUiInfo = squadData.getRgdTableByName("squad_ui_ext")
?.getRgdTableByName("ui_info") ?: throw Exception("Could not find ui_info at $fileNameSquad") ?.getRgdTableByName("ui_info") ?: throw Exception("Could not find ui_info at $fileNameSquad")
@ -130,10 +132,11 @@ class UnitRgdExtractService @Autowired constructor(
unit.powerIncome = incomeData.power unit.powerIncome = incomeData.power
unit.requisitionIncome = incomeData.requisition unit.requisitionIncome = incomeData.requisition
val moraleData = getMoraleData(squadData) val moraleData = getMoraleData(unit, squadData)
unit.moraleMax = moraleData.max?.toInt() unit.moraleMax = moraleData.max?.toInt()
unit.moraleBroken = moraleData.broken?.toInt() unit.moraleBroken = moraleData.broken?.toInt()
unit.moraleRegeneration = moraleData.regeneration?.toInt() unit.moraleRegeneration = moraleData.regeneration?.toInt()
unit.unitMoraleBrakeModifiers = moraleData.unitMoraleBrakeModifiers.toMutableSet()
unit.moveSpeed = getUnitSpeed(unitData)?.toInt() unit.moveSpeed = getUnitSpeed(unitData)?.toInt()
unit.sightRadius = getSightRadius(unitData)?.toInt() unit.sightRadius = getSightRadius(unitData)?.toInt()
@ -152,10 +155,14 @@ class UnitRgdExtractService @Autowired constructor(
unit.reinforceCostSouls = reinforceCostData.souls unit.reinforceCostSouls = reinforceCostData.souls
unit.reinforceTime = reinforceCostData.time unit.reinforceTime = reinforceCostData.time
unit.mobValue = getMobValue(unitData)
unit.unitModifiers = geUnitModifiers(unit, unitData, squadData).toMutableSet() unit.unitModifiers = geUnitModifiers(unit, unitData, squadData).toMutableSet()
unit.unitRequirements = getUnitRequirements(unit, squadData).toMutableSet() unit.unitRequirements = getUnitRequirements(unit, squadData).toMutableSet()
unit.abilities = commonParseRgdService.getAbilities(unitData, abilities).toMutableSet() unit.abilities = commonParseRgdService.getAbilities(unitData, abilities).toMutableSet()
unit.deathExplosions.addAll(deathExplosionRgdExtractService.getDeathExplosions(unitData, armorTypes).toMutableSet())
val sergeantsData = getSergeantsData(squadData) val sergeantsData = getSergeantsData(squadData)
@ -194,21 +201,6 @@ class UnitRgdExtractService @Autowired constructor(
return baseUnitPath?.split("\\")?.last()?.replace(".lua", ".rgd") return baseUnitPath?.split("\\")?.last()?.replace(".lua", ".rgd")
} }
private fun getUnitArmorType(
unitData: List<RgdData>,
armorTypes: Iterable<ArmorType>,
armorTypeTableName: String
): ArmorType? {
val armorType = unitData.getRgdTableByName("type_ext")
?.getRgdTableByName(armorTypeTableName)
?.getStringByName("\$REF")
return armorTypes.find { it.id == armorType
?.replace("TYPE_Armour\\tp_", "")
?.replace("type_armour\\tp_", "")
?.replace(".lua", "") }
}
private fun getBuildCost(unitData: List<RgdData>, squadData: List<RgdData>): BuildCost { private fun getBuildCost(unitData: List<RgdData>, squadData: List<RgdData>): BuildCost {
val cost = commonParseRgdService.getBuildCost(unitData.getRgdTableByName("cost_ext")?.getRgdTableByName("time_cost")) val cost = commonParseRgdService.getBuildCost(unitData.getRgdTableByName("cost_ext")?.getRgdTableByName("time_cost"))
@ -284,13 +276,30 @@ class UnitRgdExtractService @Autowired constructor(
} }
private fun getMoraleData(squadData: List<RgdData>): MoraleData { private fun getMoraleData(unit: DowUnit, squadData: List<RgdData>): MoraleData {
val moraleData = squadData.getRgdTableByName("squad_morale_ext") val moraleData = squadData.getRgdTableByName("squad_morale_ext")
val max = moraleData?.getDoubleByName("max") val max = moraleData?.getDoubleByName("max")
val broken = moraleData?.getDoubleByName("broken_min_morale") val broken = moraleData?.getDoubleByName("broken_min_morale")
val regeneration = moraleData?.getDoubleByName("rate_per_second") val regeneration = moraleData?.getDoubleByName("rate_per_second")
return MoraleData(max, broken, regeneration)
val unitMoraleBreakModifiers = moraleData?.getRgdTableByName("broken_modifiers")?.mapNotNull { m ->
if (m.name.contains("modifier_")) {
val mTable = m.value as List<RgdData>
if (mTable.getStringByName("\$REF") == "modifiers\\no_modifier.lua" ||
mTable.getStringByName("\$REF") == "modifiers\\morale_break_event.lua") null else {
UnitMoraleBrakeModifiers().also {
it.unit = unit
}.fillDataFromRgdTable(mTable)
} }
} else null
} ?: emptyList()
return MoraleData(max, broken, regeneration, unitMoraleBreakModifiers)
}
private fun getMobValue(unitData: List<RgdData>): Double? =
unitData.getRgdTableByName("mob_ext")
?.getDoubleByName("mob_value")
private fun getMassData(unitData: List<RgdData>): MassData { private fun getMassData(unitData: List<RgdData>): MassData {
val massDataRgd = unitData val massDataRgd = unitData
@ -420,7 +429,7 @@ class UnitRgdExtractService @Autowired constructor(
return requirements?.mapNotNull { r -> return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) { if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData> val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else { if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
UnitRequirements().also { UnitRequirements().also {
it.unit = unit it.unit = unit
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -117,7 +117,7 @@ class WeaponRgdExtractService @Autowired constructor(
if (r.name.contains("required_")) { if (r.name.contains("required_")) {
try { try {
val rTable = r.value as List<RgdData> val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else { if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
WeaponRequirements().also { WeaponRequirements().also {
it.weapon = weapon it.weapon = weapon
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -0,0 +1,10 @@
CREATE OR REPLACE PROCEDURE delete_mod_data(mod_id_p bigint)
LANGUAGE SQL
AS $$
DELETE FROM units WHERE mod_id = mod_id_p;
DELETE FROM buildings WHERE mod_id = mod_id_p;
DELETE FROM weapons WHERE mod_id = mod_id_p;
DELETE FROM abilities WHERE mod_id = mod_id_p;
DELETE FROM researches WHERE mod_id = mod_id_p;
DELETE FROM ability_environments WHERE mod_id = mod_id_p;
$$;

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add ability_environment_abilities table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "ability_environment_abilities",
"columns": [
{
"column": {
"name": "ability_environment_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "ability_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "ability_environment_id, ability_id",
"constraintName": "uc_ability_environment_abilities",
"tableName": "ability_environment_abilities"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "ability_environment_id",
"baseTableName": "ability_environment_abilities",
"constraintName": "fk_ability_environment_ability_environment_abilities",
"referencedColumnNames": "id",
"referencedTableName": "ability_environments",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "ability_id",
"baseTableName": "ability_environment_abilities",
"constraintName": "fk_abilities_ability_environment_abilities",
"referencedColumnNames": "id",
"referencedTableName": "abilities",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add ability_environment_death_explosion table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "ability_environment_death_explosion",
"columns": [
{
"column": {
"name": "ability_environment_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "death_explosion_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "ability_environment_id, death_explosion_id",
"constraintName": "uc_ability_environment_death_explosion",
"tableName": "ability_environment_death_explosion"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "ability_environment_id",
"baseTableName": "ability_environment_death_explosion",
"constraintName": "fk_ability_environment_ability_environment_death_explosion",
"referencedColumnNames": "id",
"referencedTableName": "ability_environments",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "death_explosion_id",
"baseTableName": "ability_environment_death_explosion",
"constraintName": "fk_death_explosion_ability_environment_death_explosion",
"referencedColumnNames": "id",
"referencedTableName": "death_explosions",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,109 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add ability environments table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "ability_environments",
"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": "health",
"type": "int"
}
},{
"column": {
"name": "health_regeneration",
"type": "number"
}
},{
"column": {
"name": "lifetime",
"type": "number"
}
},{
"column": {
"name": "armour",
"type": "number"
}
},{
"column": {
"name": "armour_type_id",
"type": "varchar(50)"
}
},{
"column": {
"name": "armour_type_2_id",
"type": "varchar(50)"
}
},{
"column": {
"name": "move_speed",
"type": "int"
}
},{
"column": {
"name": "sight_radius",
"type": "int"
}
},{
"column": {
"name": "detect_radius",
"type": "int"
}
},
{
"column": {
"name": "mod_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "icon",
"type": "varchar(128)"
}
}
]
}
}
]
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add building_death_explosion table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "building_death_explosion",
"columns": [
{
"column": {
"name": "building_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "death_explosion_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "building_id, death_explosion_id",
"constraintName": "uc_building_death_explosion",
"tableName": "building_death_explosion"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "building_id",
"baseTableName": "building_death_explosion",
"constraintName": "fk_buildings_building_death_explosion",
"referencedColumnNames": "id",
"referencedTableName": "buildings",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "death_explosion_id",
"baseTableName": "building_death_explosion",
"constraintName": "fk_death_explosions_building_death_explosion",
"referencedColumnNames": "id",
"referencedTableName": "death_explosions",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,25 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add limit column to buildings",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "building_limit",
"type": "int"
}
}
],
"tableName": "buildings"
}
}
]
}
}
]
}

View File

@ -0,0 +1,76 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add death_explosion_armors_piercing table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "death_explosion_armors_piercing",
"columns": [{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},
{
"column": {
"name": "death_explosion_id",
"type": "int",
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},
{
"column": {
"name": "armor_type_id",
"type": "varchar(50)",
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},{
"column": {
"name": "piercing_value",
"type": "number",
"constraints": {
"nullable": false
}
}
},{
"addForeignKeyConstraint":
{
"constraintName": "fk_death_explosion_death_explosion_armors_piercing",
"baseColumnNames": "death_explosion_id",
"baseTableName": "death_explosion_armors_piercing",
"referencedColumnNames": "id",
"referencedTableName": "death_explosions",
"onDelete": "CASCADE"
}
},{
"addForeignKeyConstraint":
{
"constraintName": "fk_armor_types_death_explosion_armors_piercing",
"baseColumnNames": "armor_type_id",
"baseTableName": "death_explosion_armors_piercing",
"referencedColumnNames": "id",
"referencedTableName": "armor_types"
}
}
]
}
}
]
}
}
]
}

View File

@ -0,0 +1,89 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add death_explosion_modifiers table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "death_explosion_modifiers",
"columns": [
{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},{
"column": {
"name": "reference",
"type": "varchar(255)"
}
},{
"column": {
"name": "usage_type",
"type": "varchar(255)"
}
},{
"column": {
"name": "value",
"type": "number"
}
},{
"column": {
"name": "target",
"type": "varchar(255)"
}
},{
"column": {
"name": "probability_of_applying",
"type": "number"
}
},{
"column": {
"name": "exclusive",
"type": "boolean"
}
},{
"column": {
"name": "application_type",
"type": "varchar(255)"
}
},{
"column": {
"name": "max_life_time",
"type": "number"
}
},{
"column": {
"name": "death_explosion_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "death_explosion_id",
"baseTableName": "death_explosion_modifiers",
"constraintName": "fk_death_explosion_death_explosion_modifiers",
"referencedColumnNames": "id",
"referencedTableName": "death_explosions",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,81 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add death_explosions table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "death_explosions",
"columns": [
{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},{
"column": {
"name": "order",
"type": "number"
}
},{
"column": {
"name": "global_chance",
"type": "number"
}
},{
"column": {
"name": "chance",
"type": "number"
}
},{
"column": {
"name": "radius",
"type": "number"
}
},{
"column": {
"name": "min_damage",
"type": "number"
}
},{
"column": {
"name": "min_damage_value",
"type": "number"
}
},{
"column": {
"name": "max_damage",
"type": "number"
}
},{
"column": {
"name": "morale_damage",
"type": "number"
}
},{
"column": {
"name": "throw_force_min",
"type": "number"
}
},
{
"column": {
"name": "throw_force_max",
"type": "number"
}
}
]
}
}
]
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add sergeant_death_explosion table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "sergeant_death_explosion",
"columns": [
{
"column": {
"name": "sergeant_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "death_explosion_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "sergeant_id, death_explosion_id",
"constraintName": "uc_sergeant_death_explosion",
"tableName": "sergeant_death_explosion"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "sergeant_id",
"baseTableName": "sergeant_death_explosion",
"constraintName": "fk_sergeant_id_sergeant_death_explosion",
"referencedColumnNames": "id",
"referencedTableName": "sergeants",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "death_explosion_id",
"baseTableName": "sergeant_death_explosion",
"constraintName": "fk_death_explosion_sergeant_death_explosion",
"referencedColumnNames": "id",
"referencedTableName": "death_explosions",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,25 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add mob bonus columns to sergeants",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "mob_value",
"type": "number"
}
}
],
"tableName": "sergeants"
}
}
]
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add unit_death_explosion table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "unit_death_explosion",
"columns": [
{
"column": {
"name": "unit_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "death_explosion_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "unit_id, death_explosion_id",
"constraintName": "uc_unit_death_explosion",
"tableName": "unit_death_explosion"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "unit_id",
"baseTableName": "unit_death_explosion",
"constraintName": "fk_units_unit_death_explosion",
"referencedColumnNames": "id",
"referencedTableName": "units",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "death_explosion_id",
"baseTableName": "unit_death_explosion",
"constraintName": "fk_death_explosions_unit_death_explosion",
"referencedColumnNames": "id",
"referencedTableName": "death_explosions",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,84 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add unit_morale_break_modifiers table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "unit_morale_break_modifiers",
"columns": [
{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},{
"column": {
"name": "reference",
"type": "varchar(255)"
}
},{
"column": {
"name": "usage_type",
"type": "varchar(255)"
}
},{
"column": {
"name": "value",
"type": "number"
}
},{
"column": {
"name": "target",
"type": "varchar(255)"
}
},{
"column": {
"name": "probability_of_applying",
"type": "number"
}
},{
"column": {
"name": "exclusive",
"type": "boolean"
}
},{
"column": {
"name": "application_type",
"type": "varchar(255)"
}
},{
"column": {
"name": "unit_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "unit_id",
"baseTableName": "unit_morale_break_modifiers",
"constraintName": "fk_units_unit_morale_break_modifiers",
"referencedColumnNames": "id",
"referencedTableName": "units",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,25 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add mob bonus to units",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "mob_value",
"type": "number"
}
}
],
"tableName": "units"
}
}
]
}
}
]
}

View File

@ -389,6 +389,62 @@
"include": { "include": {
"file": "db/0.0.4/data/area_filter.json" "file": "db/0.0.4/data/area_filter.json"
} }
},{
"include": {
"file": "db/0.0.5/schema/buildings.json"
}
},{
"include": {
"file": "db/0.0.5/schema/unit_morale_break_modifiers.json"
}
},{
"include": {
"file": "db/0.0.5/schema/ability_environments.json"
}
},{
"include": {
"file": "db/0.0.5/schema/ability_environment_abilities.json"
}
},{
"include": {
"file": "db/0.0.5/schema/death_explosions.json"
}
},{
"include": {
"file": "db/0.0.5/schema/death_explosion_modifiers.json"
}
},{
"include": {
"file": "db/0.0.5/schema/death_explosion_armors_piercing.json"
}
},{
"include": {
"file": "db/0.0.5/schema/unit_death_explosion.json"
}
},{
"include": {
"file": "db/0.0.5/schema/sergeant_death_explosion.json"
}
},{
"include": {
"file": "db/0.0.5/schema/building_death_explosion.json"
}
},{
"include": {
"file": "db/0.0.5/schema/ability_environment_death_explosion.json"
}
},{
"include": {
"file": "db/0.0.5/schema/units.json"
}
},{
"include": {
"file": "db/0.0.5/schema/sergeants.json"
}
},{
"include": {
"file": "db/0.0.5/procedures/delete_mod_data_procedure.sql"
}
} }
] ]
} }