Add jump feature

This commit is contained in:
anibus 2026-08-01 21:44:52 +03:00
parent f21aeba92e
commit 6ab6073d13
13 changed files with 448 additions and 2 deletions

View File

@ -0,0 +1,30 @@
package com.dowstats.controllers
import com.dowstats.data.rgd.RgdData
import com.dowstats.service.w40k.RgdParserService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
import java.io.DataInputStream
@RestController
@RequestMapping("api/v1/rgd")
class RgdController @Autowired constructor(
val rgdParserService: RgdParserService,
) {
@PostMapping(
value = ["/parse"],
consumes = [MediaType.APPLICATION_OCTET_STREAM_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
fun parseRgdBytes(@RequestBody body: ByteArray): List<RgdData> {
return rgdParserService.parseRgdFileStream(DataInputStream(body.inputStream()))
}
}

View File

@ -0,0 +1,19 @@
package com.dowstats.data.dto.controllers
data class JumpDto(
val chargeRegeneration: Double?,
val goUpTime: Double?,
val jumpDistanceMax: Double?,
val breakdownTime: Double?,
val chargeJumpCostMin: Double?,
val jumpHeight: Double?,
val chargeMax: Double?,
val setupTime: Double?,
val chargeJumpCostMax: Double?,
val goDownTime: Double?,
val isTeleport: Boolean,
val jumpTimeMax: Double?,
val jumpTimeMin: Double?,
val jumpMinHeight: Double?,
val requirements: RequirementDto?,
)

View File

@ -65,4 +65,5 @@ data class UnitFullDto(
var affectedData: AffectedDataDto,
val requirements: RequirementDto?,
val hotkey: String?,
val jumps: JumpDto?,
)

View File

@ -14,6 +14,7 @@ import com.dowstats.data.entities.deathexplosion.DeathExplosion
import com.dowstats.data.entities.research.Research
import com.dowstats.data.entities.research.ResearchModifiers
import com.dowstats.data.entities.weapon.Weapon
import com.dowstats.data.entities.unit.Jump
import jakarta.persistence.*
@ -176,7 +177,10 @@ class DowUnit {
@OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
var unitRequirements: MutableSet<UnitRequirements> = mutableSetOf()
fun toDto(weapons: Set<WeaponSlotDto>, sergeantsDtos: Set<SergeantDto>, requirementDto: RequirementDto?): UnitFullDto =
@OneToOne(mappedBy = "unit", cascade = [CascadeType.ALL])
var jump: Jump? = null
fun toDto(weapons: Set<WeaponSlotDto>, sergeantsDtos: Set<SergeantDto>, requirementDto: RequirementDto?, jumpsDto: JumpDto? = null): UnitFullDto =
UnitFullDto(
id!!,
race?.toDto(),
@ -241,6 +245,7 @@ class DowUnit {
),
requirementDto,
uiHotkeyName,
jumpsDto,
)
}

View File

@ -0,0 +1,36 @@
package com.dowstats.data.entities.unit
import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.*
@Entity
@Table(name = "jumps")
class Jump {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
@ManyToOne
@JoinColumn(name = "unit_id", nullable = false)
@JsonIgnore
var unit: DowUnit? = null
var chargeRegeneration: Double? = null
var goUpTime: Double? = null
var jumpDistanceMax: Double? = null
var breakdownTime: Double? = null
var chargeJumpCostMin: Double? = null
var jumpHeight: Double? = null
var chargeMax: Double? = null
var setupTime: Double? = null
var chargeJumpCostMax: Double? = null
var goDownTime: Double? = null
var teleport: Boolean = false
var jumpTimeMax: Double? = null
var jumpTimeMin: Double? = null
var jumpMinHeight: Double? = null
@OneToMany(mappedBy = "jump", cascade = [CascadeType.ALL])
var jumpRequirements: MutableSet<JumpRequirements> = mutableSetOf()
}

View File

@ -0,0 +1,18 @@
package com.dowstats.data.entities.unit
import com.dowstats.data.entities.RequirementBase
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 = "jump_requirements")
class JumpRequirements : RequirementBase() {
@ManyToOne
@JoinColumn(name = "jump_id", nullable = false)
@JsonIgnore
var jump: Jump? = null
}

View File

@ -1,5 +1,6 @@
package com.dowstats.service.datamaps
import com.dowstats.data.dto.controllers.JumpDto
import com.dowstats.data.dto.controllers.RaceUnits
import com.dowstats.data.dto.controllers.SergeantDto
import com.dowstats.data.dto.controllers.UnitFullDto
@ -37,10 +38,31 @@ class DowUnitMappingService @Autowired constructor(
unitWeapon.toWeaponSlotDto()
}?.toSet() ?: emptySet()
val jumpsDto = unit.jump?.let { jump ->
JumpDto(
jump.chargeRegeneration,
jump.goUpTime,
jump.jumpDistanceMax,
jump.breakdownTime,
jump.chargeJumpCostMin,
jump.jumpHeight,
jump.chargeMax,
jump.setupTime,
jump.chargeJumpCostMax,
jump.goDownTime,
jump.teleport,
jump.jumpTimeMax,
jump.jumpTimeMin,
jump.jumpMinHeight,
requirementsMappingComponent.getRequirements(jump.jumpRequirements.toList(), unit.modId!!)
)
}
return unitRepository.findById(unitId).get().toDto(
weapons,
unit.sergeants?.map { getSergeantFullDto(it) }?.toSet() ?: emptySet(),
requirementDto
requirementDto,
jumpsDto
)
}

View File

@ -10,6 +10,8 @@ import com.dowstats.data.entities.ModifiersBase.ModifiersBaseCompanion.filterUse
import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.sergant.Sergeant
import com.dowstats.data.entities.unit.DowUnit
import com.dowstats.data.entities.unit.Jump
import com.dowstats.data.entities.unit.JumpRequirements
import com.dowstats.data.entities.unit.UnitModifiers
import com.dowstats.data.entities.unit.UnitMoraleBrakeModifiers
import com.dowstats.data.entities.unit.UnitRequirements
@ -159,6 +161,7 @@ class UnitRgdExtractService @Autowired constructor(
unit.unitModifiers = geUnitModifiers(unit, unitData, squadData).toMutableSet()
unit.unitRequirements = getUnitRequirements(unit, squadData).toMutableSet()
unit.jump = getJump(unit, squadData)
unit.abilities = commonParseRgdService.getAbilities(unitData, abilities).toMutableSet()
unit.deathExplosions.addAll(deathExplosionRgdExtractService.getDeathExplosions(unitData, armorTypes).toMutableSet())
@ -439,4 +442,41 @@ class UnitRgdExtractService @Autowired constructor(
} else null
} ?: emptyList()
}
private fun getJump(unit: DowUnit, squadData: List<RgdData>): Jump? {
val squadJumpExt = squadData.getRgdTableByName("squad_jump_ext") ?: return null
return Jump().also { jump ->
jump.unit = unit
jump.chargeRegeneration = squadJumpExt.getDoubleByName("charge_regeneration")
jump.goUpTime = squadJumpExt.getDoubleByName("go_up_time")
jump.jumpDistanceMax = squadJumpExt.getDoubleByName("jump_distance_max")
jump.breakdownTime = squadJumpExt.getDoubleByName("breakdown_time")
jump.chargeJumpCostMin = squadJumpExt.getDoubleByName("charge_jump_cost_min")
jump.jumpHeight = squadJumpExt.getDoubleByName("jump_height")
jump.chargeMax = squadJumpExt.getDoubleByName("charge_max")
jump.setupTime = squadJumpExt.getDoubleByName("setup_time")
jump.chargeJumpCostMax = squadJumpExt.getDoubleByName("charge_jump_cost_max")
jump.goDownTime = squadJumpExt.getDoubleByName("go_down_time")
jump.teleport = squadJumpExt.getBooleanByName("teleport") ?: false
jump.jumpTimeMax = squadJumpExt.getDoubleByName("jump_time_max")
jump.jumpTimeMin = squadJumpExt.getDoubleByName("jump_time_min")
jump.jumpMinHeight = squadJumpExt.getDoubleByName("jump_min_height")
// Parse jump requirements
val requirements = squadJumpExt.getRgdTableByName("requirements")
jump.jumpRequirements = requirements?.mapNotNull { r ->
if (r.name.contains("required_")) {
val rTable = r.value as List<RgdData>
if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
JumpRequirements().also {
it.jump = jump
it.reference = rTable.getStringByName("\$REF")
it.value = commonParseRgdService.getRequirementReference(it.reference, rTable, unit.filenameSquad!!, log)
}
}
} else null
}?.toMutableSet() ?: mutableSetOf()
}
}
}

View File

@ -0,0 +1,18 @@
CREATE OR REPLACE PROCEDURE delete_campaign_entities(mod_id_p bigint)
LANGUAGE SQL
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 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 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 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 '%\_dxp3');
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 '%\_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 '%\_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%');
$$;

View File

@ -0,0 +1,29 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add is_hide to mod table",
"author": "anibus",
"changes": [
{
"addColumn": {
"columns": [
{
"column": {
"name": "is_hide",
"type": "boolean",
"defaultValue": "false"
},
"constraints": {
"nullable": false
}
}
],
"tableName": "mods"
}
}
]
}
}
]
}

View File

@ -0,0 +1,61 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add jump_requirements table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "jump_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": "jump_id",
"type": "int",
"constraints": {
"nullable": false
}
}
}
]
}
},
{
"addForeignKeyConstraint": {
"baseColumnNames": "jump_id",
"baseTableName": "jump_requirements",
"constraintName": "fk_jump_requirements_jumps",
"referencedColumnNames": "id",
"referencedTableName": "jumps",
"onDelete": "CASCADE"
}
}
]
}
}
]
}

View File

@ -0,0 +1,152 @@
{
"databaseChangeLog": [
{
"changeSet": {
"id": "Add jumps table",
"author": "anibus",
"changes": [
{
"createTable": {
"tableName": "jumps",
"columns": [
{
"column": {
"name": "id",
"type": "int",
"autoIncrement": true,
"constraints": {
"primaryKey": true,
"nullable": false
}
}
},
{
"column": {
"name": "unit_id",
"type": "int",
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "charge_regeneration",
"type": "float"
}
},
{
"column": {
"name": "go_up_time",
"type": "float"
}
},
{
"column": {
"name": "jump_distance_max",
"type": "float"
}
},
{
"column": {
"name": "breakdown_time",
"type": "float"
}
},
{
"column": {
"name": "charge_jump_cost_min",
"type": "float"
}
},
{
"column": {
"name": "jump_height",
"type": "float"
}
},
{
"column": {
"name": "charge_max",
"type": "float"
}
},
{
"column": {
"name": "setup_time",
"type": "float"
}
},
{
"column": {
"name": "charge_jump_cost_max",
"type": "float"
}
},
{
"column": {
"name": "go_down_time",
"type": "float"
}
},
{
"column": {
"name": "teleport",
"type": "boolean",
"value": false,
"constraints": {
"nullable": false
}
}
},
{
"column": {
"name": "jump_time_max",
"type": "float"
}
},
{
"column": {
"name": "jump_time_min",
"type": "float"
}
},
{
"column": {
"name": "jump_min_height",
"type": "float"
}
}
]
}
},
{
"addForeignKeyConstraint": {
"baseColumnNames": "unit_id",
"baseTableName": "jumps",
"constraintName": "fk_jumps_units",
"referencedColumnNames": "id",
"referencedTableName": "units",
"onDelete": "CASCADE"
}
}
]
}
},
{
"changeSet": {
"id": "Add unique constraint on jumps.unit_id",
"author": "anibus",
"changes": [
{
"addUniqueConstraint": {
"columnNames": "unit_id",
"tableName": "jumps",
"constraintName": "uk_jumps_unit_id"
}
}
]
}
}
]
}

View File

@ -461,6 +461,21 @@
"include": {
"file": "db/0.0.6/schema/column_types.json"
}
},
{
"include": {
"file": "db/0.0.6/schema/add_is_hide_to_mods.json"
}
},
{
"include": {
"file": "db/0.0.6/schema/jumps.json"
}
},
{
"include": {
"file": "db/0.0.6/schema/jump_requirements.json"
}
}
]
}