Global update

- add requirements for units, sergeants, buildings and weapons
- add uiIndexHint for units, buildings, researches and addons
- add addon affects
- improve campaign entities clear
- add dds converter definitive edition
- add all files lowercase function before mod parse
- add addon modifiers info
- add advanced build menu grid
This commit is contained in:
Anibus 2025-10-11 01:23:24 +03:00
parent 2d941bd29c
commit 9b12eb3f51
64 changed files with 1599 additions and 148 deletions

View File

@ -89,6 +89,11 @@
<artifactId>imageio-tga</artifactId> <artifactId>imageio-tga</artifactId>
<version>3.10.1</version> <version>3.10.1</version>
</dependency> </dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-dds</artifactId>
<version>3.12.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>

View File

@ -5,12 +5,18 @@ 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_POP = "requirements\\required_total_pop.lua" val REFERENCE_REQUIREMENT_POP = "requirements\\required_total_pop.lua"
val REFERENCE_REQUIREMENT_ADDON = "requirements\\local_required_addon.lua" val REFERENCE_REQUIREMENT_ADDON = "requirements\\local_required_addon.lua"
val REFERENCE_GLOBAL_REQUIREMENT_ADDON = "requirements\\global_required_addon.lua" val REFERENCE_GLOBAL_REQUIREMENT_ADDON = "requirements\\global_required_addon.lua"
val REFERENCE_REQUIREMENT_STRUCTURE_EITHER = "requirements\\required_structure_either.lua" val REFERENCE_REQUIREMENT_STRUCTURE_EITHER = "requirements\\required_structure_either.lua"
val REFERENCE_REQUIREMENT_STRUCTURE = "requirements\\required_structure.lua" val REFERENCE_REQUIREMENT_STRUCTURE = "requirements\\required_structure.lua"
val REFERENCE_REQUIREMENT_RESEARCH = "requirements\\required_research.lua" val REFERENCE_REQUIREMENT_RESEARCH = "requirements\\required_research.lua"
val REFERENCE_REQUIREMENT_OWNERSHIP = "requirements\\required_ownership.lua"
val REFERENCE_REQUIREMENT_CAP = "requirements\\required_cap.lua"
val REFERENCE_REQUIREMENT_SQUAD_CAP = "requirements\\required_squad_cap.lua"
val REFERENCE_REQUIREMENT_CUM_SQUAD_CAP = "requirements\\required_cumulative_squad_cap.lua"
val REFERENCE_REQUIREMENT_STRUCTURE_RATIO = "requirements\\required_structure_ratio.lua"
} }
} }

View File

@ -34,7 +34,7 @@ class UnitsController @Autowired constructor(
@GetMapping("/{unitId}") @GetMapping("/{unitId}")
fun getById(@PathVariable unitId: Long): UnitFullDto { fun getById(@PathVariable unitId: Long): UnitFullDto {
return unitsRepo.findById(unitId).get().toDto() return dowUnitMappingService.getUnitFullDto(unitId)
} }
@DeleteMapping @DeleteMapping

View File

@ -0,0 +1,9 @@
package com.dowstats.data.dto.controllers
data class AffectedDataDto(
val affectedUnits: Set<EntityCompressDto>,
val affectedSergeants: Set<SergeantUnitShortDto>,
val affectedBuildings: Set<EntityCompressDto>,
val affectedWeapons: Set<WeaponUnitShortDto>,
)

View File

@ -11,10 +11,17 @@ data class RequirementDto (
val requireAddon: BuildingAddonShortDto?, val requireAddon: BuildingAddonShortDto?,
val requirementsGlobalAddons: Set<BuildingAddonShortDto>?, val requirementsGlobalAddons: Set<BuildingAddonShortDto>?,
val requiredTotalPop: Int?, val requiredTotalPop: Int?,
val limitByBuilding: RequirementLimitByBuildingDto?,
val requiredOwnership: Set<String>,
val replaceWhenDone: Boolean = false, val replaceWhenDone: Boolean = false,
) )
data class RequirementResearchDto( data class RequirementResearchDto(
val researchShortDto: ResearchShortDto, val researchShortDto: ResearchShortDto,
val researchMustNotBeComplete: Boolean, val researchMustNotBeComplete: Boolean,
)
data class RequirementLimitByBuildingDto(
val building: BuildingShortDto,
val limit: Int,
) )

View File

@ -30,4 +30,5 @@ data class SergeantDto(
val icon: String?, val icon: String?,
val weapons: List<WeaponSlotDto>?, val weapons: List<WeaponSlotDto>?,
val affectedResearches: Set<ResearchShortDto>, val affectedResearches: Set<ResearchShortDto>,
val requirements: RequirementDto?,
) )

View File

@ -1,5 +1,8 @@
package com.dowstats.data.dto.controllers package com.dowstats.data.dto.controllers
import com.dowstats.data.dto.controllers.EntityCompressDtoObject.compressDto
import com.dowstats.data.entities.Sergeant
data class SergeantUnitShortDto( data class SergeantUnitShortDto(
val id: Long?, val id: Long?,
val filename: String, val filename: String,
@ -7,3 +10,16 @@ data class SergeantUnitShortDto(
val icon: String?, val icon: String?,
val unit: EntityCompressDto val unit: EntityCompressDto
) )
object SergeantUnitShortDtoObject{
fun MutableSet<Sergeant>.toShortDtoSet() =
this.map {
SergeantUnitShortDto(
it.id!!,
it.filename!!,
it.name,
it.icon,
it.unit!!.compressDto()
)
}.toSet()
}

View File

@ -1,5 +1,6 @@
package com.dowstats.data.dto.controllers package com.dowstats.data.dto.controllers
import com.dowstats.data.dto.controllers.building.BuildingAddonShortDto
import com.dowstats.data.dto.controllers.research.ResearchShortDto import com.dowstats.data.dto.controllers.research.ResearchShortDto
@ -51,5 +52,8 @@ data class UnitFullDto(
val modId: Long, val modId: Long,
val sergeants: Set<SergeantDto>?, val sergeants: Set<SergeantDto>?,
val weapons: Set<WeaponSlotDto>?, val weapons: Set<WeaponSlotDto>?,
val haveReinforceMenu: Boolean,
val affectedResearches: Set<ResearchShortDto>, val affectedResearches: Set<ResearchShortDto>,
val affectedAddons: Set<BuildingAddonShortDto>,
val requirements: RequirementDto?,
) )

View File

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

View File

@ -1,5 +1,6 @@
package com.dowstats.data.dto.controllers package com.dowstats.data.dto.controllers
import com.dowstats.data.dto.controllers.building.BuildingAddonShortDto
import com.dowstats.data.dto.controllers.research.ResearchShortDto import com.dowstats.data.dto.controllers.research.ResearchShortDto
data class WeaponDto( data class WeaponDto(
@ -27,8 +28,10 @@ data class WeaponDto(
val canAttackAir: Boolean, val canAttackAir: Boolean,
val canAttackGround: Boolean, val canAttackGround: Boolean,
val icon: String?, val icon: String?,
val haveEquipButton: Boolean, val showInReinforce: Boolean,
val modId: Long?, val modId: Long?,
val weaponArmorPiercing: List<WeaponArmorPiercingDto>, val weaponArmorPiercing: List<WeaponArmorPiercingDto>,
val affectedResearches: Set<ResearchShortDto>, val affectedResearches: Set<ResearchShortDto>,
val affectedAddons: Set<BuildingAddonShortDto>,
val requirements: RequirementDto?,
) )

View File

@ -1,5 +1,8 @@
package com.dowstats.data.dto.controllers package com.dowstats.data.dto.controllers
import com.dowstats.data.dto.controllers.EntityCompressDtoObject.compressDto
import com.dowstats.data.entities.Weapon
data class WeaponUnitShortDto( data class WeaponUnitShortDto(
val id: Long?, val id: Long?,
val filename: String, val filename: String,
@ -8,3 +11,27 @@ data class WeaponUnitShortDto(
val sergeants: Set<SergeantUnitShortDto>, val sergeants: Set<SergeantUnitShortDto>,
val buildings: Set<EntityCompressDto> val buildings: Set<EntityCompressDto>
) )
object WeaponUnitShortDtoObject{
fun MutableSet<Weapon>.toShortDtoSet() =
this.map {
WeaponUnitShortDto(
it.id!!,
it.filename!!,
it.name,
it.unitsUse?.mapNotNull {
it.unit?.compressDto()
}?.toSet() ?: emptySet(),
it.sergeantsUse?.map {
SergeantUnitShortDto(
it.sergeant!!.id!!,
it.sergeant!!.filename!!,
it.sergeant!!.name,
it.sergeant!!.icon,
it.sergeant!!.unit!!.compressDto()
)
}?.toSet() ?: emptySet(),
it.buildingUse?.map { it.building!!.compressDto() }?.toSet() ?: emptySet()
)
}.toSet()
}

View File

@ -1,7 +1,6 @@
package com.dowstats.data.dto.controllers.building package com.dowstats.data.dto.controllers.building
import com.dowstats.data.dto.controllers.ModifierDto import com.dowstats.data.dto.controllers.*
import com.dowstats.data.dto.controllers.RequirementDto
data class BuildingAddonDto( data class BuildingAddonDto(
val id: Long?, val id: Long?,
@ -15,6 +14,7 @@ data class BuildingAddonDto(
val addonCostSouls: Double?, val addonCostSouls: Double?,
val addonCostTime: Int?, val addonCostTime: Int?,
val addonModifiers: Set<ModifierDto>, val addonModifiers: Set<ModifierDto>,
val affectedData: AffectedDataDto,
val addonRequirement: RequirementDto?, val addonRequirement: RequirementDto?,
val icon: String?, val icon: String?,
) )

View File

@ -1,9 +1,6 @@
package com.dowstats.data.dto.controllers.building package com.dowstats.data.dto.controllers.building
import com.dowstats.data.dto.controllers.ArmorTypeDto import com.dowstats.data.dto.controllers.*
import com.dowstats.data.dto.controllers.RaceDto
import com.dowstats.data.dto.controllers.UnitShortDto
import com.dowstats.data.dto.controllers.WeaponSlotDto
import com.dowstats.data.dto.controllers.research.ResearchDto import com.dowstats.data.dto.controllers.research.ResearchDto
import com.dowstats.data.dto.controllers.research.ResearchShortDto import com.dowstats.data.dto.controllers.research.ResearchShortDto
@ -32,9 +29,10 @@ data class BuildingFullDto(
var repairMax: Int?, var repairMax: Int?,
var icon: String?, var icon: String?,
var modId: Long?, var modId: Long?,
var addons: Set<BuildingAddonDto>?, var addons: List<BuildingAddonDto>?,
var researches: List<ResearchDto>?, var researches: List<ResearchDto>?,
val units: Set<UnitShortDto>, val units: List<UnitShortDto>,
val weapons: Set<WeaponSlotDto>?, val weapons: Set<WeaponSlotDto>,
val requirements: RequirementDto?,
val affectedResearches: Set<ResearchShortDto>, val affectedResearches: Set<ResearchShortDto>,
) )

View File

@ -6,6 +6,7 @@ import com.dowstats.data.dto.controllers.UnitShortDto
data class RaceBuildings( data class RaceBuildings(
val race: RaceDto, val race: RaceDto,
val buildings: List<BuildingShortDto>, val buildings: List<BuildingShortDto>,
val buildingsAdvanced: List<BuildingShortDto>,
) )
data class BuildingShortDto( data class BuildingShortDto(
@ -13,7 +14,7 @@ data class BuildingShortDto(
val icon: String, val icon: String,
val id: Long, val id: Long,
val filename: String, val filename: String,
val units: Set<UnitShortDto>, val units: List<UnitShortDto>,
val armourTypeName: String, val armourTypeName: String,
val canDetect: Boolean, val canDetect: Boolean,
) )

View File

@ -15,10 +15,8 @@ data class ResearchDto(
val costTime: Int?, val costTime: Int?,
val modifiers: List<ModifierDto>, val modifiers: List<ModifierDto>,
val requirements: RequirementDto?, val requirements: RequirementDto?,
val affectedUnits: Set<EntityCompressDto>, val affectedData: AffectedDataDto,
val affectedSergeants: Set<SergeantUnitShortDto>,
val affectedBuildings: Set<EntityCompressDto>,
val affectedWeapons: Set<WeaponUnitShortDto>,
val icon: String?, val icon: String?,
val uiIndexHint : Int,
val modId: Long?, val modId: Long?,
) )

View File

@ -1,5 +1,7 @@
package com.dowstats.data.entities package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.WeaponSlotDto
import com.dowstats.data.dto.controllers.building.BuildingAddonDto import com.dowstats.data.dto.controllers.building.BuildingAddonDto
import com.dowstats.data.dto.controllers.building.BuildingFullDto import com.dowstats.data.dto.controllers.building.BuildingFullDto
import com.dowstats.data.dto.controllers.research.ResearchDto import com.dowstats.data.dto.controllers.research.ResearchDto
@ -7,6 +9,7 @@ import com.dowstats.data.entities.DowUnitObject.toUnitDto
import com.dowstats.data.entities.Weapon.HardpointPosition import com.dowstats.data.entities.Weapon.HardpointPosition
import com.dowstats.data.entities.addon.BuildingAddon import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
import com.dowstats.data.entities.research.ResearchRequirements
import jakarta.persistence.* import jakarta.persistence.*
@ -47,6 +50,8 @@ class Building {
var sightRadius: Int? = null var sightRadius: Int? = null
var detectRadius: Int? = null var detectRadius: Int? = null
var repairMax: Int? = null var repairMax: Int? = null
var uiIndexHint: Int = 0
var advancedBuildOption: Boolean = false
var icon: String? = null var icon: String? = null
var modId: Long? = null var modId: Long? = null
var faithIncome: Double? = null var faithIncome: Double? = null
@ -77,10 +82,19 @@ class Building {
inverseJoinColumns = [JoinColumn(name = "research_id")]) inverseJoinColumns = [JoinColumn(name = "research_id")])
var affectedResearches: MutableSet<Research> = mutableSetOf() var affectedResearches: MutableSet<Research> = mutableSetOf()
@ManyToMany
@JoinTable(name = "addon_affected_buildings",
joinColumns = [JoinColumn(name = "building_id")],
inverseJoinColumns = [JoinColumn(name = "addon_id")])
var affectedAddons: MutableSet<BuildingAddon> = mutableSetOf()
@OneToMany(mappedBy = "building", cascade = [CascadeType.ALL])
var buildingRequirements: MutableSet<BuildingRequirements> = mutableSetOf()
@Transient @Transient
var weaponHardpoints: MutableSet<HardpointPosition> = mutableSetOf() var weaponHardpoints: MutableSet<HardpointPosition> = mutableSetOf()
fun toDto(buildingAddons: Set<BuildingAddonDto>?, researches: List<ResearchDto>?): BuildingFullDto = fun toDto(buildingAddons: List<BuildingAddonDto>?, researches: List<ResearchDto>?, weapons: Set<WeaponSlotDto>, requirement: RequirementDto?): BuildingFullDto =
BuildingFullDto( BuildingFullDto(
id!!, id!!,
race?.toDto(), race?.toDto(),
@ -108,8 +122,9 @@ class Building {
modId!!, modId!!,
buildingAddons, buildingAddons,
researches, researches,
units?.toList()?.toUnitDto() ?: emptySet(), units?.toList()?.sortedBy { it.uiIndexHint }?.toUnitDto() ?: emptyList(),
weapons?.map { it.toWeaponSlotDto() }?.toSet(), weapons,
requirement,
affectedResearches.map { it.toResearchShortDto() }.toSet(), affectedResearches.map { it.toResearchShortDto() }.toSet(),
) )

View File

@ -0,0 +1,18 @@
package com.dowstats.data.entities
import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.Entity
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.Table
@Entity
@Table(name = "building_requirements")
class BuildingRequirements: RequirementBase() {
@ManyToOne
@JoinColumn(name = "building_id", nullable = false)
@JsonIgnore
var building: Building? = null
}

View File

@ -1,5 +1,6 @@
package com.dowstats.data.entities package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.WeaponSlotDto import com.dowstats.data.dto.controllers.WeaponSlotDto
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.* import jakarta.persistence.*
@ -40,10 +41,10 @@ class BuildingWeapon {
@JoinColumn(name = "weapon_id") @JoinColumn(name = "weapon_id")
var weapon: Weapon? = null var weapon: Weapon? = null
fun toWeaponSlotDto() = WeaponSlotDto( fun toWeaponSlotDto(requirementDto: RequirementDto?) = WeaponSlotDto(
buildingWeaponKey!!.hardpoint, buildingWeaponKey!!.hardpoint,
buildingWeaponKey!!.hardpointOrder, buildingWeaponKey!!.hardpointOrder,
weapon?.toDto()!! weapon?.toDto(requirementDto)!!
) )
} }

View File

@ -1,7 +1,7 @@
package com.dowstats.data.entities package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.UnitFullDto import com.dowstats.data.dto.controllers.*
import com.dowstats.data.dto.controllers.UnitShortDto import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
import jakarta.persistence.* import jakarta.persistence.*
@ -69,6 +69,8 @@ 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 haveReinforceMenu: Boolean = false
var uiIndexHint: Int = 0
var icon: String? = null var icon: String? = null
var modId: Long? = null var modId: Long? = null
@ -84,8 +86,16 @@ class DowUnit {
inverseJoinColumns = [JoinColumn(name = "research_id")]) inverseJoinColumns = [JoinColumn(name = "research_id")])
var affectedResearches: MutableSet<Research> = mutableSetOf() var affectedResearches: MutableSet<Research> = mutableSetOf()
@ManyToMany
@JoinTable(name = "addon_affected_units",
joinColumns = [JoinColumn(name = "unit_id")],
inverseJoinColumns = [JoinColumn(name = "addon_id")])
var affectedAddons: MutableSet<BuildingAddon> = mutableSetOf()
fun toDto(): UnitFullDto = @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
var unitRequirements: MutableSet<UnitRequirements> = mutableSetOf()
fun toDto(weapons: Set<WeaponSlotDto>, sergeantsDtos: Set<SergeantDto>, requirementDto: RequirementDto?): UnitFullDto =
UnitFullDto( UnitFullDto(
id!!, id!!,
race?.toDto(), race?.toDto(),
@ -132,20 +142,23 @@ class DowUnit {
maxSergeants, maxSergeants,
icon, icon,
modId!!, modId!!,
sergeants?.map { it.toDto() }?.toSet(), sergeantsDtos,
weapons?.map { it.toWeaponSlotDto() }?.toSet(), weapons,
haveReinforceMenu,
affectedResearches.map { it.toResearchShortDto() }.toSet(), affectedResearches.map { it.toResearchShortDto() }.toSet(),
affectedAddons.map { it.toShortDto() }.toSet(),
requirementDto,
) )
} }
object DowUnitObject { object DowUnitObject {
fun List<DowUnit>.toUnitDto(): Set<UnitShortDto> = fun List<DowUnit>.toUnitDto(): List<UnitShortDto> =
this.mapNotNull { this.mapNotNull {
val name = it.name ?: it.filenameSquad val name = it.name ?: it.filenameSquad
val icon = it.icon val icon = it.icon
if (name == null || icon == null) null else UnitShortDto(name, icon, it.id!!, if (name == null || icon == null) null else UnitShortDto(name, icon, it.id!!,
it.armorType?.name!!, it.armorType?.name!!,
(it.detectRadius ?: 0) > 0) (it.detectRadius ?: 0) > 0)
}.toSet() }
} }

View File

@ -1,7 +1,10 @@
package com.dowstats.data.entities package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.SergeantDto import com.dowstats.data.dto.controllers.SergeantDto
import com.dowstats.data.dto.controllers.WeaponSlotDto
import com.dowstats.data.entities.Weapon.HardpointPosition import com.dowstats.data.entities.Weapon.HardpointPosition
import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.* import jakarta.persistence.*
@ -65,7 +68,16 @@ class Sergeant {
inverseJoinColumns = [JoinColumn(name = "research_id")]) inverseJoinColumns = [JoinColumn(name = "research_id")])
var affectedResearches: MutableSet<Research> = mutableSetOf() var affectedResearches: MutableSet<Research> = mutableSetOf()
fun toDto(): SergeantDto = @ManyToMany
@JoinTable(name = "addon_affected_sergeants",
joinColumns = [JoinColumn(name = "sergeant_id")],
inverseJoinColumns = [JoinColumn(name = "addon_id")])
var affectedAddons: MutableSet<BuildingAddon> = mutableSetOf()
@OneToMany(mappedBy = "sergeant", cascade = [CascadeType.ALL])
var sergeantRequirements: MutableSet<SergeantRequirements> = mutableSetOf()
fun toDto(weapons: Set<WeaponSlotDto>, requirements: RequirementDto?,): SergeantDto =
SergeantDto( SergeantDto(
id!!, id!!,
armorType?.toDto(), armorType?.toDto(),
@ -91,7 +103,8 @@ class Sergeant {
sightRadius, sightRadius,
detectRadius, detectRadius,
icon, icon,
weapons?.map { it.toWeaponSlotDto() }, weapons.toList(),
affectedResearches.map { it.toResearchShortDto() }.toSet() affectedResearches.map { it.toResearchShortDto() }.toSet(),
requirements
) )
} }

View File

@ -1,5 +1,6 @@
package com.dowstats.data.entities package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.WeaponSlotDto import com.dowstats.data.dto.controllers.WeaponSlotDto
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import java.io.Serializable import java.io.Serializable
@ -40,9 +41,9 @@ class SergeantWeapon {
@JoinColumn(name = "weapon_id") @JoinColumn(name = "weapon_id")
var weapon: Weapon? = null var weapon: Weapon? = null
fun toWeaponSlotDto() = WeaponSlotDto( fun toWeaponSlotDto(requirement: RequirementDto?) = WeaponSlotDto(
sergeantWeaponKey!!.hardpoint, sergeantWeaponKey!!.hardpoint,
sergeantWeaponKey!!.hardpointOrder, sergeantWeaponKey!!.hardpointOrder,
weapon?.toDto()!! weapon?.toDto(requirement)!!
) )
} }

View File

@ -0,0 +1,18 @@
package com.dowstats.data.entities
import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.Entity
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.Table
@Entity
@Table(name = "sergeant_requirements")
class SergeantRequirements: RequirementBase() {
@ManyToOne
@JoinColumn(name = "sergeant_id", nullable = false)
@JsonIgnore
var sergeant: Sergeant? = null
}

View File

@ -0,0 +1,18 @@
package com.dowstats.data.entities
import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.Entity
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.Table
@Entity
@Table(name = "unit_requirements")
class UnitRequirements: RequirementBase() {
@ManyToOne
@JoinColumn(name = "unit_id", nullable = false)
@JsonIgnore
var unit: DowUnit? = null
}

View File

@ -1,5 +1,6 @@
package com.dowstats.data.entities package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.WeaponSlotDto import com.dowstats.data.dto.controllers.WeaponSlotDto
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import java.io.Serializable import java.io.Serializable
@ -41,10 +42,10 @@ class UnitWeapon {
@JoinColumn(name = "weapon_id") @JoinColumn(name = "weapon_id")
var weapon: Weapon? = null var weapon: Weapon? = null
fun toWeaponSlotDto() = WeaponSlotDto( fun toWeaponSlotDto(requirement: RequirementDto?) = WeaponSlotDto(
unitWeaponKey!!.hardpoint, unitWeaponKey!!.hardpoint,
unitWeaponKey!!.hardpointOrder, unitWeaponKey!!.hardpointOrder,
weapon?.toDto()!! weapon?.toDto(requirement)!!
) )
} }

View File

@ -1,6 +1,8 @@
package com.dowstats.data.entities package com.dowstats.data.entities
import com.dowstats.data.dto.controllers.RequirementDto
import com.dowstats.data.dto.controllers.WeaponDto import com.dowstats.data.dto.controllers.WeaponDto
import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
import jakarta.persistence.* import jakarta.persistence.*
@ -35,7 +37,7 @@ class Weapon {
var canAttackAir: Boolean = true var canAttackAir: Boolean = true
var canAttackGround: Boolean = true var canAttackGround: Boolean = true
var icon: String? = null var icon: String? = null
var haveEquipButton: Boolean = true var showInReinforce: Boolean = true
var modId: Long? = null var modId: Long? = null
@ManyToMany @ManyToMany
@ -44,6 +46,12 @@ class Weapon {
inverseJoinColumns = [JoinColumn(name = "research_id")]) inverseJoinColumns = [JoinColumn(name = "research_id")])
var affectedResearches: MutableSet<Research> = mutableSetOf() var affectedResearches: MutableSet<Research> = mutableSetOf()
@ManyToMany
@JoinTable(name = "addon_affected_weapons",
joinColumns = [JoinColumn(name = "weapon_id")],
inverseJoinColumns = [JoinColumn(name = "addon_id")])
var affectedAddons: MutableSet<BuildingAddon> = mutableSetOf()
@OneToMany(mappedBy = "weapon", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "weapon", cascade = [CascadeType.ALL])
var unitsUse: MutableSet<UnitWeapon>? = null var unitsUse: MutableSet<UnitWeapon>? = null
@ -56,11 +64,14 @@ class Weapon {
// for many-to-many persistance // for many-to-many persistance
data class HardpointPosition(val weaponId: Long, val hardpoint: Int, val order: Int) data class HardpointPosition(val weaponId: Long, val hardpoint: Int, val order: Int)
@OneToMany(mappedBy = "weapon", cascade = [CascadeType.ALL])
var weaponRequirements: MutableSet<WeaponRequirements> = mutableSetOf()
@OneToMany(mappedBy="weapon", fetch = FetchType.EAGER, cascade = [(CascadeType.ALL)])
@OneToMany(mappedBy="weapon", cascade = [(CascadeType.ALL)])
var weaponPiercings: List<WeaponArmorPiercing> = listOf() var weaponPiercings: List<WeaponArmorPiercing> = listOf()
fun toDto() = WeaponDto( fun toDto(requirementDto: RequirementDto?) = WeaponDto(
id!!, id!!,
filename!!, filename!!,
name, name,
@ -85,9 +96,11 @@ class Weapon {
canAttackAir, canAttackAir,
canAttackGround, canAttackGround,
icon, icon,
haveEquipButton, showInReinforce,
modId, modId,
weaponPiercings.map { it.toDto() }, weaponPiercings.map { it.toDto() },
affectedResearches.map { it.toResearchShortDto() }.toSet(), affectedResearches.map { it.toResearchShortDto() }.toSet(),
affectedAddons.map { it.toShortDto() }.toSet(),
requirementDto,
) )
} }

View File

@ -14,12 +14,12 @@ class WeaponArmorPiercing {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null var id: Long? = null
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne
@JsonIgnore @JsonIgnore
@JoinColumn(name = "weapon_id") @JoinColumn(name = "weapon_id")
var weapon: Weapon? = null var weapon: Weapon? = null
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne
@JoinColumn(name = "armor_type_id") @JoinColumn(name = "armor_type_id")
var armorType: ArmorType? = null var armorType: ArmorType? = null

View File

@ -0,0 +1,18 @@
package com.dowstats.data.entities
import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.Entity
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.Table
@Entity
@Table(name = "weapon_requirements")
class WeaponRequirements: RequirementBase() {
@ManyToOne
@JoinColumn(name = "weapon_id", nullable = false)
@JsonIgnore
var weapon: Weapon? = null
}

View File

@ -19,6 +19,7 @@ class AddonModifiers {
var addon: BuildingAddon? = null var addon: BuildingAddon? = null
var reference: String? = null var reference: String? = null
var target: String? = null
var usageType: String? = null var usageType: String? = null
var value: Double? = null var value: Double? = null
@ -28,6 +29,7 @@ class AddonModifiers {
reference = reference, reference = reference,
usageType = usageType, usageType = usageType,
value = value, value = value,
target = target,
) )
} }
} }

View File

@ -1,9 +1,17 @@
package com.dowstats.data.entities.addon package com.dowstats.data.entities.addon
import com.dowstats.data.dto.controllers.AffectedDataDto
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.SergeantUnitShortDto
import com.dowstats.data.dto.controllers.SergeantUnitShortDtoObject.toShortDtoSet
import com.dowstats.data.dto.controllers.WeaponUnitShortDtoObject.toShortDtoSet
import com.dowstats.data.dto.controllers.building.BuildingAddonDto import com.dowstats.data.dto.controllers.building.BuildingAddonDto
import com.dowstats.data.dto.controllers.building.BuildingAddonShortDto import com.dowstats.data.dto.controllers.building.BuildingAddonShortDto
import com.dowstats.data.entities.Building import com.dowstats.data.entities.Building
import com.dowstats.data.entities.DowUnit
import com.dowstats.data.entities.Sergeant
import com.dowstats.data.entities.Weapon
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.* import jakarta.persistence.*
@ -32,11 +40,36 @@ class BuildingAddon {
var addonCostTime: Int? = null var addonCostTime: Int? = null
@OneToMany(mappedBy = "addon", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "addon", cascade = [CascadeType.ALL])
var addonModifiers: MutableSet<AddonModifiers>? = null var addonModifiers: MutableSet<AddonModifiers> = mutableSetOf()
@OneToMany(mappedBy = "addon", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "addon", cascade = [CascadeType.ALL])
var addonRequirements: MutableSet<AddonRequirements>? = null var addonRequirements: MutableSet<AddonRequirements>? = null
@ManyToMany
@JoinTable(name = "addon_affected_units",
joinColumns = [JoinColumn(name = "addon_id")],
inverseJoinColumns = [JoinColumn(name = "unit_id")])
var affectedUnits: MutableSet<DowUnit> = mutableSetOf()
@ManyToMany
@JoinTable(name = "addon_affected_sergeants",
joinColumns = [JoinColumn(name = "addon_id")],
inverseJoinColumns = [JoinColumn(name = "sergeant_id")])
var affectedSergeants: MutableSet<Sergeant> = mutableSetOf()
@ManyToMany
@JoinTable(name = "addon_affected_buildings",
joinColumns = [JoinColumn(name = "addon_id")],
inverseJoinColumns = [JoinColumn(name = "building_id")])
var affectedBuildings: MutableSet<Building> = mutableSetOf()
@ManyToMany
@JoinTable(name = "addon_affected_weapons",
joinColumns = [JoinColumn(name = "addon_id")],
inverseJoinColumns = [JoinColumn(name = "weapon_id")])
var affectedWeapons: MutableSet<Weapon> = mutableSetOf()
var uiIndexHint: Int = 0
var icon: String? = null var icon: String? = null
var modId: Long? = null var modId: Long? = null
@ -52,6 +85,12 @@ class BuildingAddon {
addonCostSouls = addonCostSouls, addonCostSouls = addonCostSouls,
addonCostTime = addonCostTime, addonCostTime = addonCostTime,
addonModifiers = addonModifiers?.sortedBy { it.reference }?.map { it.toDto() }?.toSet() ?: emptySet(), addonModifiers = addonModifiers?.sortedBy { it.reference }?.map { it.toDto() }?.toSet() ?: emptySet(),
affectedData = AffectedDataDto(
affectedUnits.map { it.compressDto() }.toSet(),
affectedSergeants.toShortDtoSet(),
affectedBuildings.map { it.compressDto() }.toSet(),
affectedWeapons.toShortDtoSet(),
),
addonRequirement = addonRequirementDto, addonRequirement = addonRequirementDto,
icon = icon icon = icon
) )

View File

@ -1,10 +1,11 @@
package com.dowstats.data.entities.research package com.dowstats.data.entities.research
import com.dowstats.data.dto.controllers.AffectedDataDto
import com.dowstats.data.dto.controllers.EntityCompressDtoObject.compressDto import com.dowstats.data.dto.controllers.EntityCompressDtoObject.compressDto
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
import com.dowstats.data.dto.controllers.SergeantUnitShortDto import com.dowstats.data.dto.controllers.SergeantUnitShortDtoObject.toShortDtoSet
import com.dowstats.data.dto.controllers.WeaponUnitShortDto import com.dowstats.data.dto.controllers.WeaponUnitShortDtoObject.toShortDtoSet
import com.dowstats.data.dto.controllers.research.ResearchDto import com.dowstats.data.dto.controllers.research.ResearchDto
import com.dowstats.data.dto.controllers.research.ResearchShortDto import com.dowstats.data.dto.controllers.research.ResearchShortDto
import com.dowstats.data.entities.Building import com.dowstats.data.entities.Building
@ -30,6 +31,7 @@ class Research {
var costFaith: Double? = null var costFaith: Double? = null
var costSouls: Double? = null var costSouls: Double? = null
var costTime: Int? = null var costTime: Int? = null
var uiIndexHint: Int = 0
var icon: String? = null var icon: String? = null
var modId: Long? = null var modId: Long? = null
@ -37,7 +39,7 @@ class Research {
var researchModifiers: MutableSet<ResearchModifiers> = mutableSetOf() var researchModifiers: MutableSet<ResearchModifiers> = mutableSetOf()
@OneToMany(mappedBy = "research", cascade = [CascadeType.ALL]) @OneToMany(mappedBy = "research", cascade = [CascadeType.ALL])
var addonRequirements: MutableSet<ResearchRequirements> = mutableSetOf() var researchRequirements: MutableSet<ResearchRequirements> = mutableSetOf()
@ManyToMany @ManyToMany
@JoinTable(name = "researches_affected_units", @JoinTable(name = "researches_affected_units",
@ -80,6 +82,7 @@ class Research {
id = id!!, id = id!!,
name = name, name = name,
icon = icon, icon = icon,
uiIndexHint = uiIndexHint,
filename = filename, filename = filename,
description = description, description = description,
costRequisition = costRequisition, costRequisition = costRequisition,
@ -97,41 +100,12 @@ class Research {
) )
}.sortedBy { it.target }, }.sortedBy { it.target },
requirements = requirements, requirements = requirements,
affectedUnits = affectedUnits.map { it.compressDto() }.toSet(), affectedData = AffectedDataDto(
affectedSergeants = affectedSergeants.map { affectedUnits.map { it.compressDto() }.toSet(),
SergeantUnitShortDto( affectedSergeants.toShortDtoSet(),
it.id!!, affectedBuildings.map { it.compressDto() }.toSet(),
it.filename!!, affectedWeapons.toShortDtoSet(),
it.name, ),
it.icon,
it.unit!!.compressDto()
)
}.toSet(),
affectedBuildings = affectedBuildings.map { it.compressDto() }.toSet(),
affectedWeapons = affectedWeapons.mapNotNull {
if(it.unitsUse!!.isEmpty() && it.sergeantsUse!!.isEmpty() && it.buildingUse!!.isEmpty()) {
null
} else
WeaponUnitShortDto(
it.id!!,
it.filename!!,
it.name,
it.unitsUse?.mapNotNull {
it.unit?.compressDto()
}?.toSet() ?: emptySet(),
it.sergeantsUse?.map {
SergeantUnitShortDto(
it.sergeant!!.id!!,
it.sergeant!!.filename!!,
it.sergeant!!.name,
it.sergeant!!.icon,
it.sergeant!!.unit!!.compressDto()
)
}?.toSet() ?: emptySet(),
it.buildingUse?.map { it.building!!.compressDto() }?.toSet() ?: emptySet()
)
}.toSet(),
modId = modId, modId = modId,
) )
} }

View File

@ -1,8 +1,10 @@
package com.dowstats.data.repositories package com.dowstats.data.repositories
import com.dowstats.data.entities.addon.BuildingAddon import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.research.Research
import org.springframework.data.repository.CrudRepository import org.springframework.data.repository.CrudRepository
interface AddonRepository : CrudRepository<BuildingAddon, Long>{ interface AddonRepository : CrudRepository<BuildingAddon, Long>{
fun findAllByModId(modId: Long): List<BuildingAddon>
fun findFirstByModIdAndFilename(modId: Long, fileName: String): BuildingAddon? fun findFirstByModIdAndFilename(modId: Long, fileName: String): BuildingAddon?
} }

View File

@ -7,18 +7,20 @@ import com.dowstats.data.entities.DowUnitObject.toUnitDto
object CommonMapping { object CommonMapping {
fun List<Building>.toBuildingShortDto(): List<BuildingShortDto> = fun List<Building>.toBuildingShortDto(): List<BuildingShortDto> =
this this
.sortedWith(compareBy<Building> { it.units?.isEmpty() }.thenBy{ it.buildCostRequisition?.let { 0 - it } }) .sortedBy { it.uiIndexHint }
.mapNotNull { .mapNotNull {it.toBuildingShortDto() }
val name = it.name ?: it.filename?.replace(".rgd", "")?.replace("_", " ")
val icon = it.icon fun Building.toBuildingShortDto(): BuildingShortDto? {
if (name == null || icon == null) null else BuildingShortDto( val name = this.name ?: this.filename?.replace(".rgd", "")?.replace("_", " ")
name, icon, it.id!!, val icon = this.icon
it.filename!!, return if (name == null || icon == null) null else BuildingShortDto(
it.units?.toList()?.toUnitDto() ?: emptySet(), name, icon, this.id!!,
it.armorType?.name!!, this.filename!!,
(it.detectRadius ?: 0) > 0 this.units?.toList()?.sortedBy { it.uiIndexHint }?.toUnitDto() ?: emptyList(),
) this.armorType?.name!!,
} (this.detectRadius ?: 0) > 0,
)
}
fun isNotCompanyEntity(fileName: String): Boolean = fun isNotCompanyEntity(fileName: String): Boolean =
!fileName.contains("_dxp3.") && !fileName.contains("_dxp3.") &&

View File

@ -6,7 +6,6 @@ import com.dowstats.data.dto.controllers.building.RaceBuildings
import com.dowstats.data.entities.Building import com.dowstats.data.entities.Building
import com.dowstats.data.repositories.BuildingRepository import com.dowstats.data.repositories.BuildingRepository
import com.dowstats.data.repositories.RaceRepository import com.dowstats.data.repositories.RaceRepository
import com.dowstats.service.datamaps.CommonMapping.isNotCompanyEntity
import com.dowstats.service.datamaps.CommonMapping.toBuildingShortDto import com.dowstats.service.datamaps.CommonMapping.toBuildingShortDto
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -19,20 +18,23 @@ class DowBuildingMappingService @Autowired constructor(
) { ) {
fun findBuildingsByMod(modId: Long): List<RaceBuildings> { fun findBuildingsByMod(modId: Long): List<RaceBuildings> {
return getAllBuildings(modId).groupBy { it.race }.mapNotNull { raceUnits -> return getAllBuildings(modId).groupBy { it.race }.mapNotNull { raceBuildings ->
RaceBuildings(raceUnits.key?.toDto() ?: throw Exception("Race is null"), raceUnits.value.toBuildingShortDto()) RaceBuildings(raceBuildings.key?.toDto() ?: throw Exception("Race is null"),
raceBuildings.value.filter { !it.advancedBuildOption }.toBuildingShortDto(),
raceBuildings.value.filter { it.advancedBuildOption }.toBuildingShortDto(),)
} }
} }
fun findBuildingsByModAndRace(modId: Long, race: String): RaceBuildings { fun findBuildingsByModAndRace(modId: Long, race: String): RaceBuildings {
val buildings = getAllBuildings(modId, race) val buildings = getAllBuildings(modId, race)
val raceEntity = race.let { raceRepository.findById(race) ?: throw Exception("Race $race not found") } val raceEntity = race.let { raceRepository.findById(race) ?: throw Exception("Race $race not found") }
return RaceBuildings(raceEntity.toDto(), buildings.toBuildingShortDto()) return RaceBuildings(raceEntity.toDto(), buildings.filter { !it.advancedBuildOption }.toBuildingShortDto(),
buildings.filter { it.advancedBuildOption }.toBuildingShortDto())
} }
fun mapToDto(building: Building): BuildingFullDto { fun mapToDto(building: Building): BuildingFullDto {
val buildingAddons = building.addons?.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
@ -45,18 +47,27 @@ class DowBuildingMappingService @Autowired constructor(
replaceWhenDone = replaceWhenDone, replaceWhenDone = replaceWhenDone,
requirementBuildings = requirement.requirementBuildings.filter { it.id != building.id }.toSet()) requirementBuildings = requirement.requirementBuildings.filter { it.id != building.id }.toSet())
) )
}?.sortedBy { it.name }?.toSet() }
val researches = building.researches?.map {research -> val researches = building.researches?.map {research ->
val requiremens = requirementsMappingComponent val requirements = requirementsMappingComponent
.getRequirements(research.addonRequirements.toList() ?: emptyList(), .getRequirements(research.researchRequirements.toList() ?: emptyList(),
building.modId!!, building.modId!!)
building.addons?.toList() ?: emptyList() research.toResearchDto(requirements)
) }?.sortedBy { it.uiIndexHint }
research.toResearchDto(requiremens)
}?.sortedBy { it.name }
return building.toDto(buildingAddons, researches) val requirementDto = requirementsMappingComponent
.getRequirements(building.buildingRequirements.toList(),
building.modId!!)
val weapons = building.weapons?.map { buildingWeapon ->
val requirements = requirementsMappingComponent
.getRequirements(buildingWeapon.weapon?.weaponRequirements?.toList() ?: emptyList(),
building.modId!!)
buildingWeapon.toWeaponSlotDto(requirements)
}?.toSet() ?: emptySet()
return building.toDto(buildingAddons, researches, weapons, requirementDto)
} }

View File

@ -1,9 +1,12 @@
package com.dowstats.service.datamaps package com.dowstats.service.datamaps
import com.dowstats.data.dto.controllers.RaceUnits import com.dowstats.data.dto.controllers.RaceUnits
import com.dowstats.data.dto.controllers.SergeantDto
import com.dowstats.data.dto.controllers.UnitFullDto
import com.dowstats.data.entities.DowUnit import com.dowstats.data.entities.DowUnit
import com.dowstats.data.entities.DowUnitObject.toUnitDto import com.dowstats.data.entities.DowUnitObject.toUnitDto
import com.dowstats.data.entities.Race import com.dowstats.data.entities.Race
import com.dowstats.data.entities.Sergeant
import com.dowstats.data.repositories.RaceRepository import com.dowstats.data.repositories.RaceRepository
import com.dowstats.data.repositories.UnitRepository import com.dowstats.data.repositories.UnitRepository
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
@ -12,7 +15,8 @@ import org.springframework.stereotype.Service
@Service @Service
class DowUnitMappingService @Autowired constructor( class DowUnitMappingService @Autowired constructor(
val unitRepository: UnitRepository, val unitRepository: UnitRepository,
val raceRepository: RaceRepository val raceRepository: RaceRepository,
val requirementsMappingComponent: RequirementsMappingComponent,
) { ) {
fun findUnitsByMod(modId: Long): List<RaceUnits> { fun findUnitsByMod(modId: Long): List<RaceUnits> {
@ -22,12 +26,51 @@ class DowUnitMappingService @Autowired constructor(
} }
} }
fun getUnitFullDto(unitId: Long): UnitFullDto {
val unit = unitRepository.findById(unitId).get()
val requirementDto = requirementsMappingComponent
.getRequirements(unit.unitRequirements.toList(),
unit.modId!!,
)
val weapons = unit.weapons?.map { unitWeapon ->
val requirements = requirementsMappingComponent
.getRequirements(unitWeapon.weapon?.weaponRequirements?.toList() ?: emptyList(),
unit.modId!!,
)
unitWeapon.toWeaponSlotDto(requirements)
}?.toSet() ?: emptySet()
return unitRepository.findById(unitId).get().toDto(
weapons,
unit.sergeants?.map { getSergeantFullDto(it) }?.toSet() ?: emptySet(),
requirementDto
)
}
fun findUnitsByModAndRace(modId: Long, race: String): RaceUnits { fun findUnitsByModAndRace(modId: Long, race: String): RaceUnits {
val units = getAllUnits(modId, race) val units = getAllUnits(modId, race)
val raceEntity = race.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") } val raceEntity = race.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") }
return toRaceUnitsDto(units, raceEntity) return toRaceUnitsDto(units, raceEntity)
} }
private fun getSergeantFullDto(sergeant: Sergeant): SergeantDto {
val requirementDto = requirementsMappingComponent
.getRequirements(sergeant.sergeantRequirements.toList(),
sergeant.modId!!,
)
val weapons = sergeant.weapons?.map { sergeantWeapon ->
val requirements = requirementsMappingComponent
.getRequirements(sergeantWeapon.weapon?.weaponRequirements?.toList() ?: emptyList(),
sergeant.modId!!,
)
sergeantWeapon.toWeaponSlotDto(requirements)
}?.toSet() ?: emptySet()
return sergeant.toDto(weapons, requirementDto)
}
private fun toRaceUnitsDto(units: List<DowUnit>, race: Race): RaceUnits { private fun toRaceUnitsDto(units: List<DowUnit>, race: Race): RaceUnits {
val infantry = units val infantry = units
.filter { it.capInfantry?.let { it > 0 } ?: false || it.reinforceCostPopulation?.let { it > 0 } ?: false } .filter { it.capInfantry?.let { it > 0 } ?: false || it.reinforceCostPopulation?.let { it > 0 } ?: false }

View File

@ -2,6 +2,7 @@ package com.dowstats.service.datamaps
import com.dowstats.Metadata.Requirements import com.dowstats.Metadata.Requirements
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.RequirementResearchDto import com.dowstats.data.dto.controllers.RequirementResearchDto
import com.dowstats.data.entities.RequirementBase import com.dowstats.data.entities.RequirementBase
import com.dowstats.data.entities.addon.BuildingAddon import com.dowstats.data.entities.addon.BuildingAddon
@ -23,11 +24,22 @@ class RequirementsMappingComponent(
requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_POP }?.value?.toDouble() requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_POP }?.value?.toDouble()
?.toInt() ?.toInt()
val requiredOwnership =
requirements.filter { it.reference == Requirements.REFERENCE_REQUIREMENT_OWNERSHIP }
.mapNotNull { rro ->
rro.value
}.toSet()
val requirementAddon = val requirementAddon =
requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_ADDON }?.value?.let { requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_ADDON }?.value?.let {
thisBuildingAddons.find { addon -> addon.filename == it.split("\\").last().replace(".lua", ".rgd") } val addonFileName = it.split("\\").last().replace(".lua", ".rgd")
}?.toShortDto() addonFileName.let {
addonRepository.findFirstByModIdAndFilename(
modId,
it.split("\\").last().replace(".lua", ".rgd")
)?.toShortDto()
}
}
val requirementAddonGlobal = val requirementAddonGlobal =
requirements.filter { it.reference == Requirements.REFERENCE_GLOBAL_REQUIREMENT_ADDON } requirements.filter { it.reference == Requirements.REFERENCE_GLOBAL_REQUIREMENT_ADDON }
@ -49,6 +61,20 @@ class RequirementsMappingComponent(
} }
}.distinct().toBuildingShortDto().toSet() }.distinct().toBuildingShortDto().toSet()
val limitByBuilding = requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_STRUCTURE_RATIO }?.let {
val buildingFileName = it.value?.split(";")?.first()?.replace(".lua", ".rgd")
val limitPerBuilding = it.value?.split(";")?.last()?.toInt() ?: 0
buildingFileName
?.let {buildingRepository.findByModIdAndFilename(modId, it.split("\\").last().replace(".lua", ".rgd"))?.toBuildingShortDto()}
?.let {
RequirementLimitByBuildingDto(
it,
limitPerBuilding
)
}
}
val requirementBuildingsEither = val requirementBuildingsEither =
requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EITHER }?.let { requirements.find { it.reference == Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EITHER }?.let {
it.value?.split(";")?.mapNotNull { bPath -> it.value?.split(";")?.mapNotNull { bPath ->
@ -87,7 +113,9 @@ class RequirementsMappingComponent(
requirementResearches, requirementResearches,
requirementAddon, requirementAddon,
requirementAddonGlobal, requirementAddonGlobal,
requireCap requireCap,
limitByBuilding,
requiredOwnership
) )
} else null } else null
} }

View File

@ -69,6 +69,7 @@ class ModStorageIntegrationService(
it.name = "Dowstats balance mod" it.name = "Dowstats balance mod"
it.technicalName = toSave.technicalName it.technicalName = toSave.technicalName
}) })
convertAllToLowercase(savedMod)
modParserService.parseModFilesAndSaveToDb(savedMod) modParserService.parseModFilesAndSaveToDb(savedMod)
} catch (e: Exception) { } catch (e: Exception) {
log.error("Error while download and extract mod", e) log.error("Error while download and extract mod", e)
@ -94,6 +95,7 @@ class ModStorageIntegrationService(
val mod = modRepository.findById(modId).orElseThrow { IllegalArgumentException("Mod not found") } val mod = modRepository.findById(modId).orElseThrow { IllegalArgumentException("Mod not found") }
modRepository.clearModData(modId) modRepository.clearModData(modId)
checkSgaAndExtract(mod) checkSgaAndExtract(mod)
convertAllToLowercase(mod)
modParserService.parseModFilesAndSaveToDb(mod) modParserService.parseModFilesAndSaveToDb(mod)
} }
@ -141,6 +143,44 @@ class ModStorageIntegrationService(
} }
} }
private fun convertAllToLowercase(mod: Mod){
val iconDirectory = "${storageConfig.modStorage.replace("/", File.separator)}${File.separator}${mod.technicalName}${mod.version}${File.separator}Data${File.separator}art${File.separator}ui${File.separator}ingame"
File(iconDirectory).listFiles()?.forEach { raceIconDirectory ->
raceIconDirectory.listFiles()?.forEach { iconFile ->
if(iconFile.name != iconFile.name.lowercase()){
iconFile.renameTo(File( raceIconDirectory.path + File.separator + iconFile.name.lowercase()))
}
}
}
val attribDirectory = "${storageConfig.modStorage.replace("/", File.separator)}${File.separator}${mod.technicalName}${mod.version}${File.separator}Data${File.separator}attrib"
File(attribDirectory).listFiles()?.forEach { attribFolder ->
attribFolder.listFiles()?.forEach { attribFile ->
if(attribFile.name != attribFile.name.lowercase()){
attribFile.renameTo(File( attribFolder.path + File.separator + attribFile.name.lowercase()))
}
}
}
val spbsDirectory = "${storageConfig.modStorage.replace("/", File.separator)}${File.separator}${mod.technicalName}${mod.version}${File.separator}Data${File.separator}attrib${File.separator}sbps${File.separator}races"
File(spbsDirectory).listFiles()?.forEach { raceFolder ->
raceFolder.listFiles()?.forEach { spbsFile ->
if(spbsFile.name != spbsFile.name.lowercase()){
spbsFile.renameTo(File( raceFolder.path + File.separator + spbsFile.name.lowercase()))
}
}
}
val ebpsDirectory = "${storageConfig.modStorage.replace("/", File.separator)}${File.separator}${mod.technicalName}${mod.version}${File.separator}Data${File.separator}attrib${File.separator}ebps${File.separator}races"
File(ebpsDirectory).listFiles()?.forEach { raceFolder ->
raceFolder.listFiles()?.forEach { typeFolder ->
typeFolder.listFiles()?.forEach { ebpsFile ->
if(ebpsFile.name != ebpsFile.name.lowercase()){
ebpsFile.renameTo(File( raceFolder.path + File.separator + ebpsFile.name.lowercase()))
}
}
}
}
}
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://crosspick.ru/dow_stats_client/dow_stats_balance_mod/$modTechName.zip"

View File

@ -2,6 +2,7 @@ package com.dowstats.service.postparsing
import com.dowstats.configuration.StorageConfig import com.dowstats.configuration.StorageConfig
import com.dowstats.data.entities.* import com.dowstats.data.entities.*
import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
import com.dowstats.data.repositories.* import com.dowstats.data.repositories.*
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -17,6 +18,7 @@ import kotlin.io.path.exists
@Service @Service
class PostParsingService @Autowired constructor( class PostParsingService @Autowired constructor(
val researchRepository: ResearchRepository, val researchRepository: ResearchRepository,
val addonRepository: AddonRepository,
val unitRepository: UnitRepository, val unitRepository: UnitRepository,
val sergeantRepository: SergeantRepository, val sergeantRepository: SergeantRepository,
val buildingRepository: BuildingRepository, val buildingRepository: BuildingRepository,
@ -29,6 +31,7 @@ class PostParsingService @Autowired constructor(
try { try {
val modId = mod.id!! val modId = mod.id!!
val researches = researchRepository.findAllByModId(modId) val researches = researchRepository.findAllByModId(modId)
val addons = addonRepository.findAllByModId(modId)
val units = unitRepository.findByModIdAndRace(modId, null) val units = unitRepository.findByModIdAndRace(modId, null)
val sergeants = sergeantRepository.findAllByModId(modId) val sergeants = sergeantRepository.findAllByModId(modId)
val buildings = buildingRepository.findByModIdAndRace(modId, null) val buildings = buildingRepository.findByModIdAndRace(modId, null)
@ -39,6 +42,11 @@ class PostParsingService @Autowired constructor(
bindResearchBuildings(buildings, researches) bindResearchBuildings(buildings, researches)
bindResearchWeapons(weapons, researches) bindResearchWeapons(weapons, researches)
bindAddonUnits(units, addons)
bindAddonSergeants(sergeants, addons)
bindAddonBuildings(buildings, addons)
bindAddonWeapons(weapons, addons)
} catch (e: Exception) { } catch (e: Exception) {
log.warn("Error occurred during bind researches", e) log.warn("Error occurred during bind researches", e)
} }
@ -104,4 +112,64 @@ class PostParsingService @Autowired constructor(
weaponRepository.saveAll(weaponsWithBindings) weaponRepository.saveAll(weaponsWithBindings)
log.info("Successfully bind researches to weapons") log.info("Successfully bind researches to weapons")
} }
private fun bindAddonUnits(units: List<DowUnit>, addons: List<BuildingAddon>) {
val unitsWithBindings: List<DowUnit> = addons.flatMap { addon ->
addon.addonModifiers.mapNotNull { modifier ->
units.find {
it.filenameSquad?.replace(".rgd", "") == modifier.target ||
it.filenameUnit?.replace(".rgd", "") == modifier.target
}
?.let {
it.affectedAddons.add(addon)
it
}
}
}
unitRepository.saveAll(unitsWithBindings)
log.info("Successfully bind addons to units")
}
private fun bindAddonSergeants(sergeants: List<Sergeant>, addons: List<BuildingAddon>) {
val sergeantsWithBindings: List<Sergeant> = addons.flatMap { addon ->
addon.addonModifiers.mapNotNull { modifier ->
sergeants.find { it.filename.replace(".rgd", "") == modifier.target }?.let {
it.affectedAddons.add(addon)
it
}
}
}
sergeantRepository.saveAll(sergeantsWithBindings)
log.info("Successfully bind addons to sergeants")
}
private fun bindAddonBuildings(buildings: List<Building>, addons: List<BuildingAddon>) {
val buildingsWithBindings: List<Building> = addons.flatMap { addon ->
addon.addonModifiers.mapNotNull { modifier ->
buildings.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
it.affectedAddons.add(addon)
it
}
}
}
buildingRepository.saveAll(buildingsWithBindings)
log.info("Successfully bind addons to buildings")
}
private fun bindAddonWeapons(weapons: List<Weapon>, addons: List<BuildingAddon>) {
val weaponsWithBindings: List<Weapon> = addons.flatMap { addon ->
addon.addonModifiers.mapNotNull { modifier ->
weapons.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
it.affectedAddons.add(addon)
it
}
}
}
weaponRepository.saveAll(weaponsWithBindings)
log.info("Successfully bind addons to weapons")
}
} }

View File

@ -1,5 +1,6 @@
package com.dowstats.service.w40k package com.dowstats.service.w40k
import com.dowstats.Metadata
import com.dowstats.data.entities.* import com.dowstats.data.entities.*
import com.dowstats.data.entities.addon.AddonModifiers import com.dowstats.data.entities.addon.AddonModifiers
import com.dowstats.data.entities.addon.AddonRequirements import com.dowstats.data.entities.addon.AddonRequirements
@ -7,6 +8,7 @@ import com.dowstats.data.entities.addon.BuildingAddon
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
import com.dowstats.data.rgd.RgdDataUtil.getIntByName
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
import com.dowstats.data.rgd.RgdDataUtil.getStringByName import com.dowstats.data.rgd.RgdDataUtil.getStringByName
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -35,6 +37,7 @@ class BuildingAddonRgdExtractService @Autowired constructor(
val uiInfo = commonParseRgdService.getUiInfo(uiInfoRgd, modDictionary, modFolderData, mod, log) val uiInfo = commonParseRgdService.getUiInfo(uiInfoRgd, modDictionary, modFolderData, mod, log)
addon.name = uiInfo.name addon.name = uiInfo.name
addon.description = uiInfo.description addon.description = uiInfo.description
addon.uiIndexHint = addonRgdData.getIntByName("ui_index_hint") ?: 0
addon.icon = uiInfo.iconPath addon.icon = uiInfo.iconPath
addon.filename = fileName addon.filename = fileName
@ -62,6 +65,7 @@ class BuildingAddonRgdExtractService @Autowired constructor(
AddonModifiers().also { AddonModifiers().also {
it.addon = addon it.addon = addon
it.reference = mTable.getStringByName("\$REF") it.reference = mTable.getStringByName("\$REF")
it.target = mTable.getStringByName("target_type_name")
it.usageType = mTable.getRgdTableByName("usage_type")?.getStringByName("\$REF") it.usageType = mTable.getRgdTableByName("usage_type")?.getStringByName("\$REF")
it.value = mTable.getDoubleByName("value") it.value = mTable.getDoubleByName("value")
} }
@ -75,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") == "requirements\\required_none.lua") null else { if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else {
AddonRequirements().also { AddonRequirements().also {
it.addon = addon it.addon = addon
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -1,9 +1,11 @@
package com.dowstats.service.w40k package com.dowstats.service.w40k
import com.dowstats.Metadata
import com.dowstats.data.dto.BuildingDataToSave import com.dowstats.data.dto.BuildingDataToSave
import com.dowstats.data.entities.* import com.dowstats.data.entities.*
import com.dowstats.data.entities.research.Research import com.dowstats.data.entities.research.Research
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.getDoubleByName import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
import com.dowstats.data.rgd.RgdDataUtil.getIntByName import com.dowstats.data.rgd.RgdDataUtil.getIntByName
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
@ -61,6 +63,8 @@ class BuildingRgdExtractService @Autowired constructor(
building.name = buildingUiData.name building.name = buildingUiData.name
building.description = buildingUiData.description building.description = buildingUiData.description
building.icon = buildingUiData.iconPath building.icon = buildingUiData.iconPath
building.uiIndexHint = buildingData.getRgdTableByName("ui_ext")?.getIntByName("ui_index_hint") ?: 0
building.advancedBuildOption = buildingData.getRgdTableByName("structure_buildable_ext")?.getBooleanByName("advanced_build_option") ?: false
val buildCost = commonParseRgdService.getBuildCost(buildingData.getRgdTableByName("cost_ext")?.getRgdTableByName("time_cost")) val buildCost = commonParseRgdService.getBuildCost(buildingData.getRgdTableByName("cost_ext")?.getRgdTableByName("time_cost"))
building.buildCostRequisition = buildCost.requisition building.buildCostRequisition = buildCost.requisition
@ -89,6 +93,8 @@ class BuildingRgdExtractService @Autowired constructor(
building.units = getUnits(buildingData, units).toMutableSet() building.units = getUnits(buildingData, units).toMutableSet()
building.researches = getResearches(buildingData, researches).toMutableSet() building.researches = getResearches(buildingData, researches).toMutableSet()
building.buildingRequirements = getBuildingRequirements(building, buildingData).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]
@ -202,4 +208,20 @@ class BuildingRgdExtractService @Autowired constructor(
} else null } else null
} ?: emptyList() } ?: emptyList()
} }
private fun getBuildingRequirements(building: Building, buildingData: List<RgdData>): List<BuildingRequirements> {
val requirements = buildingData.getRgdTableByName("requirement_ext")?.getRgdTableByName("requirements")
return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else {
BuildingRequirements().also {
it.building = building
it.reference = rTable.getStringByName("\$REF")
it.value = commonParseRgdService.getRequirementReference(it.reference, rTable, building.filename!!, log)
}
}
} else null
} ?: emptyList()
}
} }

View File

@ -9,6 +9,7 @@ import com.dowstats.data.entities.Mod
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
import com.dowstats.data.rgd.RgdDataUtil.getIntByName
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
import com.dowstats.data.rgd.RgdDataUtil.getStringByName import com.dowstats.data.rgd.RgdDataUtil.getStringByName
import org.slf4j.Logger import org.slf4j.Logger
@ -26,13 +27,17 @@ class CommonParseRgdService @Autowired constructor(
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")
Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EITHER -> rTable.getStringByName("structure_name_or") + ";" + rTable.getStringByName( Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EITHER -> rTable.getStringByName("structure_name_or") + ";" +
"structure_name_either" rTable.getStringByName("structure_name_either")
)
Requirements.REFERENCE_REQUIREMENT_STRUCTURE -> rTable.getStringByName("structure_name") Requirements.REFERENCE_REQUIREMENT_STRUCTURE -> rTable.getStringByName("structure_name")
Requirements.REFERENCE_REQUIREMENT_RESEARCH -> rTable.getStringByName("research_name")+ ";" + rTable.getBooleanByName( Requirements.REFERENCE_REQUIREMENT_RESEARCH -> rTable.getStringByName("research_name") + ";" +
"research_must_not_be_complete" rTable.getBooleanByName("research_must_not_be_complete")
) Requirements.REFERENCE_REQUIREMENT_OWNERSHIP -> rTable.getStringByName("own_name")
Requirements.REFERENCE_REQUIREMENT_CAP -> rTable.getIntByName("max_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_STRUCTURE_RATIO -> rTable.getStringByName("required_structure_name") + ";" +
rTable.getIntByName("this_structure_count").toString()
else -> { else -> {
log.warn("Unknown requirement reference $ref at $fileName") log.warn("Unknown requirement reference $ref at $fileName")
null null
@ -64,7 +69,7 @@ class CommonParseRgdService @Autowired constructor(
fun getUiInfo(uiInfoRgdData: List<RgdData>, modDictionary: Map<Int, String>, modFolderData: String, mod: Mod, log: Logger): UiInfo { fun getUiInfo(uiInfoRgdData: List<RgdData>, modDictionary: Map<Int, String>, modFolderData: String, mod: Mod, log: Logger): UiInfo {
val nameRef = uiInfoRgdData?.getStringByName("screen_name_id")?.replace("$", "") val nameRef = uiInfoRgdData?.getStringByName("screen_name_id")?.replace("$", "")
val name = nameRef?.let { try { modDictionary[it.toInt()] } catch (e: Exception) { null } } val name = nameRef?.let { try { modDictionary[it.toInt()] } catch (e: Exception) { it } }
val descriptionRefs = uiInfoRgdData.getRgdTableByName("help_text_list") val descriptionRefs = uiInfoRgdData.getRgdTableByName("help_text_list")
?.mapNotNull{try { ?.mapNotNull{try {
@ -77,7 +82,7 @@ class CommonParseRgdService @Autowired constructor(
?.sortedBy { try { it.toInt() } catch (e: Exception) { 0 } } ?.sortedBy { try { it.toInt() } catch (e: Exception) { 0 } }
val description = try { val description = try {
descriptionRefs?.map { try { modDictionary[it.toInt()] } catch (e: Exception) { "" } }?.joinToString ( "\n" ) descriptionRefs?.map { try { modDictionary[it.toInt()] } catch (e: Exception) { it } }?.joinToString ( "\n" )
} catch(e:Exception) { } catch(e:Exception) {
log.warn("Error parsing ui description weapon $name", e) log.warn("Error parsing ui description weapon $name", e)
null null
@ -89,7 +94,7 @@ class CommonParseRgdService @Autowired constructor(
iconPath?.let { modAttribPathService.getIconPath(modFolderData, iconPath) } iconPath?.let { modAttribPathService.getIconPath(modFolderData, iconPath) }
?.let { iconsService.convertTgaToJpegImage(iconPath, it, mod.name) } ?.let { iconsService.convertTgaToJpegImage(iconPath, it, mod.name) }
} catch (e: Exception) { } catch (e: Exception) {
log.error("Error parsing ui icon path for weapon $name", e) log.error("Error parsing ui icon path for $name", e)
null null
} }

View File

@ -6,9 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.File import java.io.File
import java.nio.file.Path
import javax.imageio.ImageIO import javax.imageio.ImageIO
import kotlin.io.path.exists
@Service @Service
@ -22,19 +20,29 @@ class IconsService @Autowired constructor(
* @param pathToTgaIcon - путь до иконки * @param pathToTgaIcon - путь до иконки
* @return путь до сконвертированной иконки * @return путь до сконвертированной иконки
*/ */
fun convertTgaToJpegImage(iconPathInMod: String, pathToTgaIcon: String, modName: String? = null): String? { fun convertTgaToJpegImage(iconPathInMod: String, pathToTgaIcon: String, modName: String? = null, convertHere: Boolean = false): String? {
try{ try{
val image: BufferedImage = if(File(pathToTgaIcon).exists()) { val image: BufferedImage = if(File(pathToTgaIcon).exists()) {
ImageIO.read(File(pathToTgaIcon)) try {
ImageIO.read(File(pathToTgaIcon))
} catch (e: NoClassDefFoundError) {
log.warn("Can't convert icon $iconPathInMod", e)
return null
}
} else if (File(pathToTgaIcon.lowercase()).exists()) { } else if (File(pathToTgaIcon.lowercase()).exists()) {
ImageIO.read(File(pathToTgaIcon.lowercase())) ImageIO.read(File(pathToTgaIcon.lowercase()))
} else return null } else return null
val modFolder = modName?.let { "${File.separator}$modName" } ?: "" val modFolder = modName?.let { "${File.separator}$modName" } ?: ""
val pathToSave = "${storageConfig.iconsStorage.replace("/", File.separator)}$modFolder" + val pathToSave = if(!convertHere){
"${File.separator}${iconPathInMod.replace("/", File.separator).replace("\\", File.separator)}.png" "${storageConfig.iconsStorage.replace("/", File.separator)}$modFolder" +
"${File.separator}${iconPathInMod.lowercase().replace("/", File.separator).replace("\\", File.separator)}.png"
} else {
pathToTgaIcon.replace(".dds", ".tga")
}
val directoryToSave = File(pathToSave.split(File.separator).dropLast(1).joinToString (File.separator)) val directoryToSave = File(pathToSave.split(File.separator).dropLast(1).joinToString (File.separator))
if(!directoryToSave.exists()) directoryToSave.mkdirs() if(!directoryToSave.exists()) directoryToSave.mkdirs()
@ -53,7 +61,7 @@ class IconsService @Autowired constructor(
} }
fun returnIcon(modName: String, raceIconFolder: String, iconName: String): ByteArray? { fun returnIcon(modName: String, raceIconFolder: String, iconName: String): ByteArray? {
val pathToIcon = "${storageConfig.iconsStorage.replace("/", File.separator)}${File.separator}$modName${File.separator}$raceIconFolder${File.separator}$iconName" val pathToIcon = "${storageConfig.iconsStorage.replace("/", File.separator)}${File.separator}$modName${File.separator}$raceIconFolder${File.separator}${iconName.lowercase()}"
return File(pathToIcon).readBytes() return File(pathToIcon).readBytes()
} }

View File

@ -46,13 +46,17 @@ class ModAttribPathService @Autowired constructor(
fun getIconPath(modFolderData: String, iconPath: String): String { fun getIconPath(modFolderData: String, iconPath: String): String {
val iconValidEndPath = iconPath.replace("\\", File.separator).replace("/", File.separator) val iconValidEndPath = iconPath.replace("\\", File.separator).replace("/", File.separator).replace(".tga", "")
val pathToIcon = "${File.separator}art${File.separator}ui${File.separator}ingame${File.separator}$iconValidEndPath.tga" val pathToIcon = "${File.separator}art${File.separator}ui${File.separator}ingame${File.separator}$iconValidEndPath.tga"
val modIcon = "$modFolderData$pathToIcon" val modIcon = "$modFolderData$pathToIcon"
val lowercaseModIcon = "$modFolderData${pathToIcon.lowercase()}"
val wanilaModIcon = "$pathToWanilaData$pathToIcon"
return if (Path.of(modIcon).exists()) modIcon else return if (Path.of(modIcon).exists()) modIcon else
"$pathToWanilaData$pathToIcon" if (Path.of(lowercaseModIcon).exists()) lowercaseModIcon else
wanilaModIcon
} }
} }

View File

@ -1,11 +1,13 @@
package com.dowstats.service.w40k package com.dowstats.service.w40k
import com.dowstats.Metadata
import com.dowstats.data.entities.* import com.dowstats.data.entities.*
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.research.ResearchRequirements import com.dowstats.data.entities.research.ResearchRequirements
import com.dowstats.data.rgd.RgdData import com.dowstats.data.rgd.RgdData
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
import com.dowstats.data.rgd.RgdDataUtil.getIntByName
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
import com.dowstats.data.rgd.RgdDataUtil.getStringByName import com.dowstats.data.rgd.RgdDataUtil.getStringByName
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -34,6 +36,7 @@ class ResearchRgdExtractService @Autowired constructor(
val researchUiInfo = commonParseRgdService.getUiInfo(uiInfo, modDictionary, modFolderData, mod, log) val researchUiInfo = commonParseRgdService.getUiInfo(uiInfo, modDictionary, modFolderData, mod, log)
research.name = researchUiInfo.name research.name = researchUiInfo.name
research.description = researchUiInfo.description research.description = researchUiInfo.description
research.uiIndexHint = researchData.getIntByName("ui_index_hint") ?: 0
research.icon = researchUiInfo.iconPath research.icon = researchUiInfo.iconPath
val cost = commonParseRgdService.getBuildCost(researchData.getRgdTableByName("time_cost")) val cost = commonParseRgdService.getBuildCost(researchData.getRgdTableByName("time_cost"))
@ -44,7 +47,7 @@ class ResearchRgdExtractService @Autowired constructor(
research.costSouls = cost.souls research.costSouls = cost.souls
research.costTime = cost.time research.costTime = cost.time
research.researchModifiers = getResearchModifiers(research, researchData).toMutableSet() research.researchModifiers = getResearchModifiers(research, researchData).toMutableSet()
research.addonRequirements = getResearchRequirements(research, researchData).toMutableSet() research.researchRequirements = getResearchRequirements(research, researchData).toMutableSet()
research.modId = mod.id research.modId = mod.id
@ -74,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") == "requirements\\required_none.lua") null else { if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else {
ResearchRequirements().also { ResearchRequirements().also {
it.research = research it.research = research
it.reference = rTable.getStringByName("\$REF") it.reference = rTable.getStringByName("\$REF")

View File

@ -1,5 +1,6 @@
package com.dowstats.service.w40k package com.dowstats.service.w40k
import com.dowstats.Metadata
import com.dowstats.data.dto.BuildCost import com.dowstats.data.dto.BuildCost
import com.dowstats.data.entities.* import com.dowstats.data.entities.*
import com.dowstats.data.rgd.RgdData import com.dowstats.data.rgd.RgdData
@ -62,7 +63,7 @@ class SergeantRgdExtractService @Autowired constructor(
sergeant.filename = fileName sergeant.filename = fileName
val sergeantUiRgd = sergeantData.getRgdTableByName("ui_ext") val sergeantUiRgd = sergeantData.getRgdTableByName("ui_ext")
?.getRgdTableByName("ui_info") ?: throw Exception("Could not find ui_info at $fileName") ?.getRgdTableByName("ui_info") ?: throw Exception("$fileName")
val sergeantUiInfo = commonParseRgdService.getUiInfo(sergeantUiRgd, modDictionary, modFolderData, mod, log) val sergeantUiInfo = commonParseRgdService.getUiInfo(sergeantUiRgd, modDictionary, modFolderData, mod, log)
sergeant.name = sergeantUiInfo.name sergeant.name = sergeantUiInfo.name
sergeant.description = sergeantUiInfo.description sergeant.description = sergeantUiInfo.description
@ -89,6 +90,8 @@ class SergeantRgdExtractService @Autowired constructor(
sergeant.modId = mod.id!! sergeant.modId = mod.id!!
sergeant.sergeantRequirements = getSergeantRequirements(sergeant, sergeantData).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"
@ -163,4 +166,20 @@ class SergeantRgdExtractService @Autowired constructor(
} else null } else null
}?.flatten() }?.flatten()
private fun getSergeantRequirements(sergeant: Sergeant, sergeantData: List<RgdData>): List<SergeantRequirements> {
val requirements = sergeantData.getRgdTableByName("requirement_ext")?.getRgdTableByName("requirements")
return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else {
SergeantRequirements().also {
it.sergeant = sergeant
it.reference = rTable.getStringByName("\$REF")
it.value = commonParseRgdService.getRequirementReference(it.reference, rTable, sergeant.filename, log)
}
}
} else null
} ?: emptyList()
}
} }

View File

@ -1,5 +1,6 @@
package com.dowstats.service.w40k package com.dowstats.service.w40k
import com.dowstats.Metadata
import com.dowstats.data.dto.BuildCost import com.dowstats.data.dto.BuildCost
import com.dowstats.data.dto.UnitDataToSave import com.dowstats.data.dto.UnitDataToSave
import com.dowstats.data.dto.WeaponsData import com.dowstats.data.dto.WeaponsData
@ -82,6 +83,8 @@ class UnitRgdExtractService @Autowired constructor(
unit.icon = squadUiData.iconPath unit.icon = squadUiData.iconPath
unit.filenameSquad = fileNameSquad unit.filenameSquad = fileNameSquad
unit.filenameUnit = fileNameUnit unit.filenameUnit = fileNameUnit
unit.uiIndexHint = unitData.getRgdTableByName("ui_ext")?.getIntByName("ui_index_hint") ?: 0
unit.haveReinforceMenu = squadData.getRgdTableByName("squad_reinforce_ext") != null
val buildCost = getBuildCost(unitData, squadData) val buildCost = getBuildCost(unitData, squadData)
unit.buildCostRequisition = buildCost.requisition unit.buildCostRequisition = buildCost.requisition
@ -138,6 +141,7 @@ class UnitRgdExtractService @Autowired constructor(
unit.reinforceCostSouls = reinforceCostData.souls unit.reinforceCostSouls = reinforceCostData.souls
unit.reinforceTime = reinforceCostData.time unit.reinforceTime = reinforceCostData.time
unit.unitRequirements = getUnitRequirements(unit, squadData).toMutableSet()
val sergeantsData = getSergeantsData(squadData) val sergeantsData = getSergeantsData(squadData)
@ -360,4 +364,20 @@ class UnitRgdExtractService @Autowired constructor(
return Pair(sergeantsData, maxSergeants) return Pair(sergeantsData, maxSergeants)
} }
private fun getUnitRequirements(unit: DowUnit, squadData: List<RgdData>): List<UnitRequirements> {
val requirements = squadData.getRgdTableByName("squad_requirement_ext")?.getRgdTableByName("requirements")
return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else {
UnitRequirements().also {
it.unit = unit
it.reference = rTable.getStringByName("\$REF")
it.value = commonParseRgdService.getRequirementReference(it.reference, rTable, unit.filenameSquad!!, log)
}
}
} else null
} ?: emptyList()
}
} }

View File

@ -1,13 +1,12 @@
package com.dowstats.service.w40k package com.dowstats.service.w40k
import com.dowstats.data.entities.ArmorType import com.dowstats.Metadata
import com.dowstats.data.entities.Mod import com.dowstats.data.entities.*
import com.dowstats.data.entities.Weapon
import com.dowstats.data.entities.WeaponArmorPiercing
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
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -27,12 +26,12 @@ class WeaponRgdExtractService @Autowired constructor(
weapon.filename = weaponFileName weapon.filename = weaponFileName
val weaponUiInfo = weaponData.getRgdTableByName("ui_info") ?: throw Exception("Could not find ui_info at $weaponFileName") val weaponUiInfo = weaponData.getRgdTableByName("ui_info")
val weaponUiData = commonParseRgdService.getUiInfo(weaponUiInfo, modDictionary, modFolderData, mod, log) val weaponUiData = weaponUiInfo?.let{ commonParseRgdService.getUiInfo(weaponUiInfo, modDictionary, modFolderData, mod, log)}
weapon.name = weaponUiData.name weapon.name = weaponUiData?.name
weapon.icon = weaponUiData.iconPath weapon.icon = weaponUiData?.iconPath
weapon.description = weaponUiData.description weapon.description = weaponUiData?.description
weapon.haveEquipButton = weaponUiInfo.getBooleanByName("no_button")?.let { !it } ?: false weapon.showInReinforce = weaponUiInfo?.getBooleanByName("show_in_reinforce") ?: false
val cost = commonParseRgdService.getBuildCost(weaponData.getRgdTableByName("cost")) val cost = commonParseRgdService.getBuildCost(weaponData.getRgdTableByName("cost"))
weapon.costRequisition = cost.requisition ?: 0.0 weapon.costRequisition = cost.requisition ?: 0.0
@ -70,11 +69,28 @@ class WeaponRgdExtractService @Autowired constructor(
weapon.weaponPiercings = armoursPiercing weapon.weaponPiercings = armoursPiercing
weapon.damageRadius = areaEffectData.damageRadius weapon.damageRadius = areaEffectData.damageRadius
weapon.modId = mod.id weapon.modId = mod.id
weapon.weaponRequirements = getWeaponRequirements(weapon, weaponData).toMutableSet()
return if(weapon.minDamage == 0.0 && weapon.maxDamage == 0.0 && weapon.moraleDamage == 0.0){ return if(weapon.minDamage == 0.0 && weapon.maxDamage == 0.0 && weapon.moraleDamage == 0.0){
null null
} else weapon } else weapon
} }
private fun getWeaponRequirements(weapon: Weapon, weaponData: List<RgdData>): List<WeaponRequirements> {
val requirements = weaponData.getRgdTableByName("requirements")
return requirements?.mapNotNull { r ->
if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF") == Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) null else {
WeaponRequirements().also {
it.weapon = weapon
it.reference = rTable.getStringByName("\$REF")
it.value = commonParseRgdService.getRequirementReference(it.reference, rTable, weapon.filename!!, log)
}
}
} else null
} ?: emptyList()
}
} }

View File

@ -3,9 +3,16 @@ CREATE OR REPLACE PROCEDURE delete_campaign_entities(mod_id_p bigint)
AS $$ AS $$
DELETE FROM units WHERE mod_id = mod_id_p AND (fileName_squad LIKE '%\_dxp3.%' OR fileName_squad LIKE '%\_dxp3.%' OR fileName_squad LIKE '%sp\_eldar\_%' OR fileName_squad LIKE '%\_sp\_%' OR fileName_squad LIKE '%\_sp.%' OR fileName_squad LIKE '%\_nis.%' OR fileName_squad LIKE '%\_interface\_relay.%' OR fileName_squad LIKE '%necron\_tunnel.rgd%' OR fileName_squad LIKE '%\_exarch\_council.%' OR fileName_squad LIKE '%\_dark\_reapers\_base.%' OR fileName_squad LIKE '%eldar\_deep\_strike\_building.rgd%' OR fileName_squad LIKE '%ork\_deep\_strike\_building.rgd%' OR fileName_squad LIKE '%\_caravel\_ai.rgd%' OR fileName_squad LIKE '%sisters\_tanktrap\_ai.rgd%' OR fileName_squad LIKE '%sisters\_hq\_ktgm.rgd%' OR fileName_squad LIKE '%tau\_squad\_slave\_murdered%' OR fileName_squad LIKE '%single\_player\_only%' OR fileName_squad LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR fileName_squad LIKE '%advance\_sp%' OR fileName_squad LIKE '%\_no\_limit.rgd%'); DELETE FROM units WHERE mod_id = mod_id_p AND (fileName_squad LIKE '%\_dxp3.%' OR fileName_squad LIKE '%\_dxp3.%' OR fileName_squad LIKE '%sp\_eldar\_%' OR fileName_squad LIKE '%\_sp\_%' OR fileName_squad LIKE '%\_sp.%' OR fileName_squad LIKE '%\_nis.%' OR fileName_squad LIKE '%\_interface\_relay.%' OR fileName_squad LIKE '%necron\_tunnel.rgd%' OR fileName_squad LIKE '%\_exarch\_council.%' OR fileName_squad LIKE '%\_dark\_reapers\_base.%' OR fileName_squad LIKE '%eldar\_deep\_strike\_building.rgd%' OR fileName_squad LIKE '%ork\_deep\_strike\_building.rgd%' OR fileName_squad LIKE '%\_caravel\_ai.rgd%' OR fileName_squad LIKE '%sisters\_tanktrap\_ai.rgd%' OR fileName_squad LIKE '%sisters\_hq\_ktgm.rgd%' OR fileName_squad LIKE '%tau\_squad\_slave\_murdered%' OR fileName_squad LIKE '%single\_player\_only%' OR fileName_squad LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR fileName_squad LIKE '%advance\_sp%' OR fileName_squad LIKE '%\_no\_limit.rgd%');
DELETE FROM buildings WHERE mod_id = mod_id_p AND (filename LIKE '%\_dxp3.%' OR filename LIKE '%\_dxp3.%' OR filename LIKE '%sp\_eldar\_%' OR filename LIKE '%\_sp\_%' OR filename LIKE '%\_sp.%' OR filename LIKE '%\_nis.%' OR filename LIKE '%\_interface\_relay.%' OR filename LIKE '%necron\_tunnel.rgd%' OR filename LIKE '%\_exarch\_council.%' OR filename LIKE '%\_dark\_reapers\_base.%' OR filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR filename LIKE '%ork\_deep\_strike\_building.rgd%' OR filename LIKE '%\_caravel\_ai.rgd%' OR filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR filename LIKE '%sisters\_hq\_ktgm.rgd%' OR filename LIKE '%tau\_squad\_slave\_murdered%' OR filename LIKE '%single\_player\_only%' OR filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR filename LIKE '%advance\_sp%' OR filename LIKE '%night\_lords\_hq\_aep%' OR filename LIKE '%\_ktgm.rgd%' OR filename LIKE '%salamander\_hq\_aep.rgd%' OR filename LIKE '%\_no\_limit.rgd%'); DELETE FROM buildings WHERE mod_id = mod_id_p AND (filename LIKE '%\_dxp3.%' OR filename LIKE '%\_dxp3.%' OR filename LIKE '%sp\_eldar\_%' OR filename LIKE '%\_sp\_%' OR filename LIKE '%\_sp.%' OR filename LIKE '%\_nis.%' OR filename LIKE '%\_interface\_relay.%' OR filename LIKE '%necron\_tunnel.rgd%' OR filename LIKE '%\_exarch\_council.%' OR filename LIKE '%\_dark\_reapers\_base.%' OR filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR filename LIKE '%ork\_deep\_strike\_building.rgd%' OR filename LIKE '%\_caravel\_ai.rgd%' OR filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR filename LIKE '%sisters\_hq\_ktgm.rgd%' OR filename LIKE '%tau\_squad\_slave\_murdered%' OR filename LIKE '%single\_player\_only%' OR filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR filename LIKE '%advance\_sp%' OR filename LIKE '%night\_lords\_hq\_aep%' OR filename LIKE '%\_ktgm.rgd%' OR filename LIKE '%salamander\_hq\_aep.rgd%' OR filename LIKE '%\_no\_limit.rgd%');
DELETE FROM buildings USING building_requirements WHERE mod_id = mod_id_p AND buildings.id = building_requirements.building_id AND building_requirements.reference = 'requirements\required_cap.lua' AND building_requirements.value = '0';
DELETE FROM weapons WHERE mod_id = mod_id_p AND (filename LIKE '%\_dxp3.%' OR filename LIKE '%\_dxp3.%' OR filename LIKE '%sp\_eldar\_%' OR filename LIKE '%\_sp\_%' OR filename LIKE '%\_sp.%' OR filename LIKE '%\_nis.%' OR filename LIKE '%\_interface\_relay.%' OR filename LIKE '%necron\_tunnel.rgd%' OR filename LIKE '%\_exarch\_council.%' OR filename LIKE '%\_dark\_reapers\_base.%' OR filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR filename LIKE '%ork\_deep\_strike\_building.rgd%' OR filename LIKE '%\_caravel\_ai.rgd%' OR filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR filename LIKE '%sisters\_hq\_ktgm.rgd%' OR filename LIKE '%tau\_squad\_slave\_murdered%' OR filename LIKE '%single\_player\_only%' OR filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR filename LIKE '%advance\_sp%'); DELETE FROM weapons WHERE mod_id = mod_id_p AND (filename LIKE '%\_dxp3.%' OR filename LIKE '%\_dxp3.%' OR filename LIKE '%sp\_eldar\_%' OR filename LIKE '%\_sp\_%' OR filename LIKE '%\_sp.%' OR filename LIKE '%\_nis.%' OR filename LIKE '%\_interface\_relay.%' OR filename LIKE '%necron\_tunnel.rgd%' OR filename LIKE '%\_exarch\_council.%' OR filename LIKE '%\_dark\_reapers\_base.%' OR filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR filename LIKE '%ork\_deep\_strike\_building.rgd%' OR filename LIKE '%\_caravel\_ai.rgd%' OR filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR filename LIKE '%sisters\_hq\_ktgm.rgd%' OR filename LIKE '%tau\_squad\_slave\_murdered%' OR filename LIKE '%single\_player\_only%' OR filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR filename LIKE '%advance\_sp%');
DELETE FROM weapons WHERE EXISTS (SELECT 1 FROM weapons AS w2
LEFT JOIN buildings_weapons ON w2.id = buildings_weapons.weapon_id
LEFT JOIN units_weapons ON w2.id = units_weapons.weapon_id
LEFT JOIN sergeants_weapons ON w2.id = sergeants_weapons.weapon_id
WHERE mod_id = mod_id_p AND weapons.id = w2.id AND buildings_weapons.building_id IS NULL AND units_weapons.unit_id IS NULL AND sergeants_weapons.sergeant_id IS NULL) AND mod_id = mod_id_p;
DELETE FROM researches WHERE mod_id = mod_id_p AND (filename LIKE '%\_dxp3.%' OR filename LIKE '%\_dxp3.%' OR filename LIKE '%sp\_eldar\_%' OR filename LIKE '%\_sp\_%' OR filename LIKE '%\_sp.%' OR filename LIKE '%\_nis.%' OR filename LIKE '%\_interface\_relay.%' OR filename LIKE '%necron\_tunnel.rgd%' OR filename LIKE '%\_exarch\_council.%' OR filename LIKE '%\_dark\_reapers\_base.%' OR filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR filename LIKE '%ork\_deep\_strike\_building.rgd%' OR filename LIKE '%\_caravel\_ai.rgd%' OR filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR filename LIKE '%sisters\_hq\_ktgm.rgd%' OR filename LIKE '%tau\_squad\_slave\_murdered%' OR filename LIKE '%single\_player\_only%' OR filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR filename LIKE '%advance\_sp%'); DELETE FROM researches WHERE mod_id = mod_id_p AND (filename LIKE '%\_dxp3.%' OR filename LIKE '%\_dxp3.%' OR filename LIKE '%sp\_eldar\_%' OR filename LIKE '%\_sp\_%' OR filename LIKE '%\_sp.%' OR filename LIKE '%\_nis.%' OR filename LIKE '%\_interface\_relay.%' OR filename LIKE '%necron\_tunnel.rgd%' OR filename LIKE '%\_exarch\_council.%' OR filename LIKE '%\_dark\_reapers\_base.%' OR filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR filename LIKE '%ork\_deep\_strike\_building.rgd%' OR filename LIKE '%\_caravel\_ai.rgd%' OR filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR filename LIKE '%sisters\_hq\_ktgm.rgd%' OR filename LIKE '%tau\_squad\_slave\_murdered%' OR filename LIKE '%single\_player\_only%' OR filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR filename LIKE '%advance\_sp%');
DELETE FROM research_modifiers USING researches WHERE researches.mod_id = 3 AND researches.id = research_id AND (target LIKE '%\_dxp3%' OR target LIKE '%\_dxp3%' OR target LIKE '%sp\_eldar\_%' OR target LIKE '%\_sp\_%' OR target LIKE '%\_sp.%' OR target LIKE '%\_nis.%' OR target LIKE '%\_interface\_relay.%' OR target LIKE '%necron\_tunnel.rgd%' OR target LIKE '%\_exarch\_council.%' OR target LIKE '%\_dark\_reapers\_base.%' OR target LIKE '%eldar\_deep\_strike\_building.rgd%' OR target LIKE '%ork\_deep\_strike\_building.rgd%' OR target LIKE '%\_caravel\_ai.rgd%' OR target LIKE '%sisters\_tanktrap\_ai.rgd%' OR target LIKE '%sisters\_hq\_ktgm.rgd%' OR target LIKE '%tau\_squad\_slave\_murdered%' OR target LIKE '%single\_player\_only%' OR target LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR target LIKE '%advance\_sp%'); DELETE FROM research_modifiers USING researches WHERE researches.mod_id = mod_id_p AND researches.id = research_id AND (target LIKE '%\_dxp3%' OR target LIKE '%\_dxp3%' OR target LIKE '%sp\_eldar\_%' OR target LIKE '%\_sp\_%' OR target LIKE '%\_sp.%' OR target LIKE '%\_nis.%' OR target LIKE '%\_interface\_relay.%' OR target LIKE '%necron\_tunnel.rgd%' OR target LIKE '%\_exarch\_council.%' OR target LIKE '%\_dark\_reapers\_base.%' OR target LIKE '%eldar\_deep\_strike\_building.rgd%' OR target LIKE '%ork\_deep\_strike\_building.rgd%' OR target LIKE '%\_caravel\_ai.rgd%' OR target LIKE '%sisters\_tanktrap\_ai.rgd%' OR target LIKE '%sisters\_hq\_ktgm.rgd%' OR target LIKE '%tau\_squad\_slave\_murdered%' OR target LIKE '%single\_player\_only%' OR target LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR target LIKE '%\_clone%' OR target LIKE '%\_sp%' OR target LIKE '%\_dxp3');
DELETE FROM building_addons USING buildings WHERE buildings.mod_id = 3 AND buildings.id = building_addons.building_id AND (building_addons.filename LIKE '%\_dxp3%' OR building_addons.filename LIKE '%\_dxp3%' OR building_addons.filename LIKE '%sp\_eldar\_%' OR building_addons.filename LIKE '%\_sp\_%' OR building_addons.filename LIKE '%\_sp.%' OR building_addons.filename LIKE '%\_nis.%' OR building_addons.filename LIKE '%\_interface\_relay.%' OR building_addons.filename LIKE '%necron\_tunnel.rgd%' OR building_addons.filename LIKE '%\_exarch\_council.%' OR building_addons.filename LIKE '%\_dark\_reapers\_base.%' OR building_addons.filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR building_addons.filename LIKE '%ork\_deep\_strike\_building.rgd%' OR building_addons.filename LIKE '%\_caravel\_ai.rgd%' OR building_addons.filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR building_addons.filename LIKE '%sisters\_hq\_ktgm.rgd%' OR building_addons.filename LIKE '%tau\_squad\_slave\_murdered%' OR building_addons.filename LIKE '%single\_player\_only%' OR building_addons.filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR building_addons.filename LIKE '%advance\_sp%' OR building_addons.filename LIKE '%addon\_necron\_hq\_3%'); DELETE FROM addon_modifiers USING building_addons WHERE building_addons.mod_id = mod_id_p AND building_addons.id = addon_modifiers.addon_id AND (target LIKE '%\_dxp3%' OR target LIKE '%\_dxp3%' OR target LIKE '%sp\_eldar\_%' OR target LIKE '%\_sp\_%' OR target LIKE '%\_sp.%' OR target LIKE '%\_nis.%' OR target LIKE '%\_interface\_relay.%' OR target LIKE '%necron\_tunnel.rgd%' OR target LIKE '%\_exarch\_council.%' OR target LIKE '%\_dark\_reapers\_base.%' OR target LIKE '%eldar\_deep\_strike\_building.rgd%' OR target LIKE '%ork\_deep\_strike\_building.rgd%' OR target LIKE '%\_caravel\_ai.rgd%' OR target LIKE '%sisters\_tanktrap\_ai.rgd%' OR target LIKE '%sisters\_hq\_ktgm.rgd%' OR target LIKE '%tau\_squad\_slave\_murdered%' OR target LIKE '%single\_player\_only%' OR target LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR target LIKE '%\_clone%' OR target LIKE '%\_sp%' OR target LIKE '%\_dxp3');
DELETE FROM building_addons USING buildings WHERE buildings.mod_id = mod_id_p AND buildings.id = building_addons.building_id AND (building_addons.filename LIKE '%\_dxp3%' OR building_addons.filename LIKE '%\_dxp3%' OR building_addons.filename LIKE '%sp\_eldar\_%' OR building_addons.filename LIKE '%\_sp\_%' OR building_addons.filename LIKE '%\_sp.%' OR building_addons.filename LIKE '%\_nis.%' OR building_addons.filename LIKE '%\_interface\_relay.%' OR building_addons.filename LIKE '%necron\_tunnel.rgd%' OR building_addons.filename LIKE '%\_exarch\_council.%' OR building_addons.filename LIKE '%\_dark\_reapers\_base.%' OR building_addons.filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR building_addons.filename LIKE '%ork\_deep\_strike\_building.rgd%' OR building_addons.filename LIKE '%\_caravel\_ai.rgd%' OR building_addons.filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR building_addons.filename LIKE '%sisters\_hq\_ktgm.rgd%' OR building_addons.filename LIKE '%tau\_squad\_slave\_murdered%' OR building_addons.filename LIKE '%single\_player\_only%' OR building_addons.filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR building_addons.filename LIKE '%advance\_sp%' OR building_addons.filename LIKE '%addon\_necron\_hq\_3%');
DELETE FROM abilities WHERE mod_id = mod_id_p AND (filename LIKE '%\_dxp3.%' OR filename LIKE '%\_dxp3.%' OR filename LIKE '%sp\_eldar\_%' OR filename LIKE '%\_sp\_%' OR filename LIKE '%\_sp.%' OR filename LIKE '%\_nis.%' OR filename LIKE '%\_interface\_relay.%' OR filename LIKE '%necron\_tunnel.rgd%' OR filename LIKE '%\_exarch\_council.%' OR filename LIKE '%\_dark\_reapers\_base.%' OR filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR filename LIKE '%ork\_deep\_strike\_building.rgd%' OR filename LIKE '%\_caravel\_ai.rgd%' OR filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR filename LIKE '%sisters\_hq\_ktgm.rgd%' OR filename LIKE '%tau\_squad\_slave\_murdered%' OR filename LIKE '%single\_player\_only%' OR filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR filename LIKE '%advance\_sp%'); DELETE FROM abilities WHERE mod_id = mod_id_p AND (filename LIKE '%\_dxp3.%' OR filename LIKE '%\_dxp3.%' OR filename LIKE '%sp\_eldar\_%' OR filename LIKE '%\_sp\_%' OR filename LIKE '%\_sp.%' OR filename LIKE '%\_nis.%' OR filename LIKE '%\_interface\_relay.%' OR filename LIKE '%necron\_tunnel.rgd%' OR filename LIKE '%\_exarch\_council.%' OR filename LIKE '%\_dark\_reapers\_base.%' OR filename LIKE '%eldar\_deep\_strike\_building.rgd%' OR filename LIKE '%ork\_deep\_strike\_building.rgd%' OR filename LIKE '%\_caravel\_ai.rgd%' OR filename LIKE '%sisters\_tanktrap\_ai.rgd%' OR filename LIKE '%sisters\_hq\_ktgm.rgd%' OR filename LIKE '%tau\_squad\_slave\_murdered%' OR filename LIKE '%single\_player\_only%' OR filename LIKE '%space\_marine\_drop\_pod\_building.rgd%' OR filename LIKE '%advance\_sp%');
$$; $$;

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add addon_affected_buildings table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "addon_affected_buildings",
"columns": [
{
"column": {
"name": "addon_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "building_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "addon_id, building_id",
"constraintName": "uc_addon_affected_buildings_addon_id_building_id",
"tableName": "addon_affected_buildings"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "addon_id",
"baseTableName": "addon_affected_buildings",
"constraintName": "fk_addon_addon_affected_buildings",
"referencedColumnNames": "id",
"referencedTableName": "building_addons",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "building_id",
"baseTableName": "addon_affected_buildings",
"constraintName": "fk_buildings_addon_affected_buildings",
"referencedColumnNames": "id",
"referencedTableName": "buildings",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add addon_affected_sergeants table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "addon_affected_sergeants",
"columns": [
{
"column": {
"name": "addon_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "sergeant_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "addon_id, sergeant_id",
"constraintName": "uc_addon_affected_sergeants_addon_id_sergeant_id",
"tableName": "addon_affected_sergeants"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "addon_id",
"baseTableName": "addon_affected_sergeants",
"constraintName": "fk_addon_addon_affected_sergeants",
"referencedColumnNames": "id",
"referencedTableName": "building_addons",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "sergeant_id",
"baseTableName": "addon_affected_sergeants",
"constraintName": "fk_sergeants_addon_affected_sergeants",
"referencedColumnNames": "id",
"referencedTableName": "sergeants",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add addon_affected_units table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "addon_affected_units",
"columns": [
{
"column": {
"name": "addon_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "unit_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "addon_id, unit_id",
"constraintName": "uc_addon_affected_units_addon_id_unit_id",
"tableName": "addon_affected_units"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "addon_id",
"baseTableName": "addon_affected_units",
"constraintName": "fk_addon_addon_affected_units",
"referencedColumnNames": "id",
"referencedTableName": "building_addons",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "unit_id",
"baseTableName": "addon_affected_units",
"constraintName": "fk_units_addon_affected_units",
"referencedColumnNames": "id",
"referencedTableName": "units",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,66 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add addon_affected_weapons table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "addon_affected_weapons",
"columns": [
{
"column": {
"name": "addon_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "weapon_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addUniqueConstraint": {
"columnNames": "addon_id, weapon_id",
"constraintName": "uc_addon_affected_weapons_addon_id_weapon_id",
"tableName": "addon_affected_weapons"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "addon_id",
"baseTableName": "addon_affected_weapons",
"constraintName": "fk_addon_addon_affected_weapons",
"referencedColumnNames": "id",
"referencedTableName": "building_addons",
"onDelete": "CASCADE"
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "weapon_id",
"baseTableName": "addon_affected_weapons",
"constraintName": "fk_weapons_addon_affected_weapons",
"referencedColumnNames": "id",
"referencedTableName": "weapons",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,25 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add target to addon modifiers",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "target",
"type": "varchar(255)"
}
}
],
"tableName": "addon_modifiers"
}
}
]
}
}
]
}

View File

@ -0,0 +1,29 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add ui_index_hint column to building_addons",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "ui_index_hint",
"type": "int",
"value": 0,
"constraints": {
"nullable": false
}
}
}
],
"tableName": "building_addons"
}
}
]
}
}
]
}

View File

@ -0,0 +1,59 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add building_requirements table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "building_requirements",
"columns": [
{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},{
"column": {
"name": "reference",
"type": "varchar(255)"
}
},{
"column": {
"name": "value",
"type": "varchar(255)"
}
},{
"column": {
"name": "building_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "building_id",
"baseTableName": "building_requirements",
"constraintName": "fk_building_requirements_buildings",
"referencedColumnNames": "id",
"referencedTableName": "buildings",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,54 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add ui_index_hint column to buildings",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "ui_index_hint",
"type": "int",
"value": 0,
"constraints": {
"nullable": false
}
}
}
],
"tableName": "buildings"
}
}
]
}
},
{
"changeSet": {
"id": "Add advanced_build_option column to buildings",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "advanced_build_option",
"type": "boolean",
"value": false,
"constraints": {
"nullable": false
}
}
}
],
"tableName": "buildings"
}
}
]
}
}
]
}

View File

@ -94,6 +94,31 @@
} }
] ]
} }
},
{
"changeSet": {
"id": "Add ui_index_hint column to researches",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "ui_index_hint",
"type": "int",
"value": 0,
"constraints": {
"nullable": false
}
}
}
],
"tableName": "researches"
}
}
]
}
} }
] ]
} }

View File

@ -0,0 +1,59 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add sergeant_requirements table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "sergeant_requirements",
"columns": [
{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},{
"column": {
"name": "reference",
"type": "varchar(255)"
}
},{
"column": {
"name": "value",
"type": "varchar(255)"
}
},{
"column": {
"name": "sergeant_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "sergeant_id",
"baseTableName": "sergeant_requirements",
"constraintName": "fk_sergeant_requirements_buildings",
"referencedColumnNames": "id",
"referencedTableName": "sergeants",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,59 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add unit_requirements table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "unit_requirements",
"columns": [
{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},{
"column": {
"name": "reference",
"type": "varchar(255)"
}
},{
"column": {
"name": "value",
"type": "varchar(255)"
}
},{
"column": {
"name": "unit_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "unit_id",
"baseTableName": "unit_requirements",
"constraintName": "fk_unit_requirements_units",
"referencedColumnNames": "id",
"referencedTableName": "units",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -31,6 +31,56 @@
} }
] ]
} }
},
{
"changeSet": {
"id": "Add ui_index_hint column to unit",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "ui_index_hint",
"type": "int",
"value": 0,
"constraints": {
"nullable": false
}
}
}
],
"tableName": "units"
}
}
]
}
},
{
"changeSet": {
"id": "Add have_reinforce_menu column to unit",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "have_reinforce_menu",
"type": "boolean",
"value": false,
"constraints": {
"nullable": false
}
}
}
],
"tableName": "units"
}
}
]
}
} }
] ]
} }

View File

@ -0,0 +1,59 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add weapon_requirements table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "weapon_requirements",
"columns": [
{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},{
"column": {
"name": "reference",
"type": "varchar(255)"
}
},{
"column": {
"name": "value",
"type": "varchar(255)"
}
},{
"column": {
"name": "weapon_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addForeignKeyConstraint":
{
"baseColumnNames": "weapon_id",
"baseTableName": "weapon_requirements",
"constraintName": "fk_weapon_requirements_buildings",
"referencedColumnNames": "id",
"referencedTableName": "weapons",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,19 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Rename addon columns",
"author": "anibus",
"changes": [
{
"renameColumn": {
"newColumnName": "show_in_reinforce",
"oldColumnName": "have_equip_button",
"tableName": "weapons"
}
}
]
}
}
]
}

View File

@ -168,6 +168,55 @@
"include": { "include": {
"file": "db/0.0.3/schema/sergeants.json" "file": "db/0.0.3/schema/sergeants.json"
} }
},
{
"include": {
"file": "db/0.0.3/schema/addon_modifiers.json"
}
},{
"include": {
"file": "db/0.0.3/schema/addon_affected_units.json"
}
},{
"include": {
"file": "db/0.0.3/schema/addon_affected_sergeants.json"
}
},{
"include": {
"file": "db/0.0.3/schema/addon_affected_buildings.json"
}
},{
"include": {
"file": "db/0.0.3/schema/addon_affected_weapons.json"
}
},{
"include": {
"file": "db/0.0.3/schema/building_requirements.json"
}
},{
"include": {
"file": "db/0.0.3/schema/unit_requirements.json"
}
},{
"include": {
"file": "db/0.0.3/schema/sergeant_requirements.json"
}
},{
"include": {
"file": "db/0.0.3/schema/weapon_requirements.json"
}
},{
"include": {
"file": "db/0.0.3/schema/buildings.json"
}
},{
"include": {
"file": "db/0.0.3/schema/building_addons.json"
}
},{
"include": {
"file": "db/0.0.3/schema/weapons.json"
}
} }
] ]
} }

View File

@ -0,0 +1,35 @@
import com.dowstats.configuration.StorageConfig
import com.dowstats.data.rgd.RgdData
import com.dowstats.service.w40k.IconsService
import org.junit.jupiter.api.Test
import java.io.DataInputStream
import java.io.File
import java.io.PrintWriter
import java.math.RoundingMode
import java.nio.ByteBuffer
import java.nio.file.Paths
class IconConvertScript {
@Test
fun testSum() {
val iconsService = IconsService(StorageConfig())
val folder = "D:\\home\\cnb\\mods\\Definitive_Edition2.1.1\\Data\\art\\ui\\swf"
File(folder).walkTopDown().map {fileOrFolder ->
println("Convert ${fileOrFolder.name}")
if(fileOrFolder.isFile ){
println("Convert ${fileOrFolder.name}")
iconsService.convertTgaToJpegImage("" + fileOrFolder.name.replace(".dds", ""), fileOrFolder.path, "Converted")
} else {
null
}
}.toList()
}
}

View File

@ -0,0 +1,144 @@
import com.dowstats.configuration.StorageConfig
import com.dowstats.service.w40k.IconsService
import org.junit.jupiter.api.Test
import java.awt.image.BufferedImage
import java.io.File
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import javax.imageio.ImageIO
class RtxConvertScript {
val headersAndTypesByteSize = 20
var imageBytes: List<Byte> = emptyList()
var width: Int = 0
var height: Int = 0
var format: Int = 0
var numMips: Int = 0
val iconsService = IconsService(StorageConfig())
@Test
fun convertAllRtx(){
val folder = "D:\\home\\cnb\\wanila\\art\\ui\\ingame"
File(folder).walkTopDown().map {fileOrFolder ->
if(fileOrFolder.isFile && fileOrFolder.name.endsWith(".dds") ){
try {
convertFile(fileOrFolder.path)
} catch (e: Exception) {
println(e.message)
}
} else {
null
}
}.toList()
}
fun convertFile(fileName: String) {
convertRtx(fileName)
/*val file = fileName
val data = File(file).readBytes().toList()
getDDSHeader()
File(fileName.replace(".rtx", ".dds")).writeBytes((getDDSHeader() + data.drop(128)).toByteArray())*/
iconsService.convertTgaToJpegImage(fileName, File(fileName).path, "Converted", true)
val image = ImageIO.read(File(fileName.replace(".rtx", ".tga")))
val outputImage = BufferedImage(image.height, image.width, image.type)
val g2d = outputImage.createGraphics()
g2d.rotate(Math.toRadians(180.0), (image.width / 2).toDouble(), (image.height / 2).toDouble())
g2d.drawImage(image, 0 + image.width, 0, -image.width, image.height, null)
g2d.dispose()
ImageIO.write(outputImage, "tga", File(fileName.replace(".dds", ".tga")));
}
fun getDDSHeader(): List<Byte> {
val _DOW_DXT_FLAGS = 0x000A1007
val _ddsF_FOURCC = 0x00000004
val _DOW_DDSCAPS_FLAGS = 0x401008
val signature = "DDS ".toByteArray(Charsets.US_ASCII)
signature + ByteBuffer.allocate(4).putInt(124).array()
val fourCC = when(format) {
8 -> "DXT1"
10 -> "DXT3"
11 -> "DXT5"
else -> "----"
}.toByteArray(Charsets.US_ASCII)
return (signature + to32Bit(124) + to32Bit(_DOW_DXT_FLAGS) + to32Bit(height) + to32Bit(width) + to32Bit(imageBytes.size) + to32Bit(0) + to32Bit(numMips)
+ ByteBuffer.allocate(44).array() + to32Bit(32) + to32Bit(_ddsF_FOURCC) + fourCC
+ ByteBuffer.allocate(20).array() + to32Bit(_DOW_DDSCAPS_FLAGS) + ByteBuffer.allocate(16).array()).toList()
}
fun to32Bit(i: Int): List<Byte> {
return ByteBuffer.allocate(4).putInt(i).array().reversed()
}
private fun convertRtx(fileName: String) {
File(fileName).readBytes().size
var byteArrayParse = File(fileName).readBytes().toList().drop(24)
readHeader(byteArrayParse)
}
private fun readHeader(bytes: List<Byte>, lvl: Int = 0): Int{
val type1 = String(bytes.take(4).toByteArray(), StandardCharsets.UTF_8)
val type2 = String(bytes.subList(4, 8).toByteArray(), StandardCharsets.UTF_8)
val arrSize = bytes.subList(8, 12).reversed().toByteArray().getUIntAt(0)
val bytesSize = bytes.subList(12, 16).reversed().toByteArray().getUIntAt(0)
val headerValueSize = bytes.subList(16, 20).reversed().toByteArray().getUIntAt(0)
val header = String(bytes.subList(headersAndTypesByteSize, headersAndTypesByteSize + headerValueSize.toInt()).toByteArray(), StandardCharsets.UTF_8)
val dataBytes = bytes.subList(headersAndTypesByteSize + headerValueSize.toInt(), headersAndTypesByteSize + headerValueSize.toInt() + bytesSize.toInt())
println("Type1: $type1, Type2: $type2, ArrSize: $arrSize BytesSize: $bytesSize HeaderValueSize: $headerValueSize Header: $header Bytes: ${dataBytes.size}")
var i = 0
if(type1 == "FOLD") {
while (i < bytesSize.toInt()){
val bytesSublist = bytes.subList(headersAndTypesByteSize + headerValueSize.toInt() + i, headersAndTypesByteSize + headerValueSize.toInt() + bytesSize.toInt())
i += readHeader(bytesSublist, lvl + 1)
}
}
if(type2 == "ATTR"){
format = dataBytes.take(4).reversed().toByteArray().getUIntAt(0).toInt()
width = dataBytes.subList(4,8).reversed().toByteArray().getUIntAt(0).toInt()
height = dataBytes.subList(8,12).reversed().toByteArray().getUIntAt(0).toInt()
numMips = dataBytes.subList(12,16).reversed().toByteArray().getUIntAt(0).toInt()
println("Format: $format, Width: $width, Height: $height, NumMips: $numMips")
}
if(type2 == "DATA"){
imageBytes = dataBytes
}
return bytesSize.toInt() + headersAndTypesByteSize
}
private fun ByteArray.getUIntAt(idx: Int): UInt =
((this[idx].toUInt() and 0xFFu) shl 24) or
((this[idx + 1].toUInt() and 0xFFu) shl 16) or
((this[idx + 2].toUInt() and 0xFFu) shl 8) or
(this[idx + 3].toUInt() and 0xFFu)
}