Soulstorm wiki
This commit is contained in:
commit
0248c49aa1
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
src/main/resources/application.properties
|
||||||
|
src/main/resources/application.prod.properties
|
||||||
|
balance_mods
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Soulstorm wiki backend
|
||||||
|
|
||||||
|
## Стек технологий backend:
|
||||||
|
|
||||||
|
- kotlin 1.7
|
||||||
|
- spring 3
|
||||||
6518
RGD_DIC_HASHED.txt
Normal file
6518
RGD_DIC_HASHED.txt
Normal file
File diff suppressed because it is too large
Load Diff
166
pom.xml
Normal file
166
pom.xml
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>com.dowstats</groupId>
|
||||||
|
<artifactId>dow-wiki-backend</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>dow-wiki-backend</name>
|
||||||
|
<description>Down of war wiki</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<kotlin.version>1.8.22</kotlin.version>
|
||||||
|
<spring.version>3.3.1</spring.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.module</groupId>
|
||||||
|
<artifactId>jackson-module-kotlin</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-reflect</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.data</groupId>
|
||||||
|
<artifactId>spring-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.liquibase</groupId>
|
||||||
|
<artifactId>liquibase-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate.orm</groupId>
|
||||||
|
<artifactId>hibernate-core</artifactId>
|
||||||
|
<version>6.6.0.CR1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.module</groupId>
|
||||||
|
<artifactId>jackson-module-kotlin</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-websocket</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-messaging</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
|
<artifactId>imageio-core</artifactId>
|
||||||
|
<version>3.10.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
|
<artifactId>imageio-jpeg</artifactId>
|
||||||
|
<version>3.10.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
|
<artifactId>imageio-tga</artifactId>
|
||||||
|
<version>3.10.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.module</groupId>
|
||||||
|
<artifactId>jackson-module-kotlin</artifactId>
|
||||||
|
<version>2.14.2</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-test -->
|
||||||
|
<dependency>
|
||||||
|
<scope>test</scope>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-test</artifactId>
|
||||||
|
<version>1.7.22</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<version>42.7.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||||
|
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<args>
|
||||||
|
<arg>-Xjsr305=strict</arg>
|
||||||
|
</args>
|
||||||
|
<compilerPlugins>
|
||||||
|
<plugin>spring</plugin>
|
||||||
|
<plugin>jpa</plugin>
|
||||||
|
</compilerPlugins>
|
||||||
|
</configuration>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-maven-allopen</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-maven-noarg</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>16</source>
|
||||||
|
<target>16</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
</build>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
</project>
|
||||||
7
src/main/kotlin/com/dowstats/Metadata.kt
Normal file
7
src/main/kotlin/com/dowstats/Metadata.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package com.dowstats
|
||||||
|
|
||||||
|
object Metadata {
|
||||||
|
|
||||||
|
const val USER_ROLE = "USER"
|
||||||
|
|
||||||
|
}
|
||||||
17
src/main/kotlin/com/dowstats/WikiApplication.kt
Normal file
17
src/main/kotlin/com/dowstats/WikiApplication.kt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.dowstats
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
|
import org.springframework.boot.runApplication
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
|
@ConfigurationPropertiesScan("com.dowstats.configuration")
|
||||||
|
@EnableConfigurationProperties
|
||||||
|
class WikiApplication
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
runApplication<WikiApplication>(*args)
|
||||||
|
}
|
||||||
41
src/main/kotlin/com/dowstats/configuration/SecurityConfig.kt
Normal file
41
src/main/kotlin/com/dowstats/configuration/SecurityConfig.kt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package com.dowstats.configuration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.web.cors.CorsConfiguration
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http.cors {httpSecurityCorsConfigurer ->
|
||||||
|
val configuration = CorsConfiguration()
|
||||||
|
configuration.allowedOrigins = listOf("*")
|
||||||
|
configuration.allowedMethods = listOf("*")
|
||||||
|
configuration.allowedHeaders = listOf("*")
|
||||||
|
val source: UrlBasedCorsConfigurationSource = UrlBasedCorsConfigurationSource()
|
||||||
|
source.registerCorsConfiguration("/**", configuration)
|
||||||
|
httpSecurityCorsConfigurer.configurationSource(source)
|
||||||
|
}
|
||||||
|
.csrf().disable()
|
||||||
|
.authorizeHttpRequests {
|
||||||
|
it.requestMatchers("/admin/**")
|
||||||
|
.hasRole("ADMIN")
|
||||||
|
it.requestMatchers("/api/v1/**")
|
||||||
|
.anonymous()
|
||||||
|
it.requestMatchers("/login")
|
||||||
|
.permitAll()
|
||||||
|
.anyRequest()
|
||||||
|
.authenticated()
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/main/kotlin/com/dowstats/configuration/SteamConfig.kt
Normal file
10
src/main/kotlin/com/dowstats/configuration/SteamConfig.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.dowstats.configuration
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "steam.api")
|
||||||
|
data class SteamConfig (
|
||||||
|
var key: String? = null
|
||||||
|
)
|
||||||
14
src/main/kotlin/com/dowstats/configuration/StorageConfig.kt
Normal file
14
src/main/kotlin/com/dowstats/configuration/StorageConfig.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.dowstats.configuration
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "storage")
|
||||||
|
data class StorageConfig(
|
||||||
|
var key: String? = null,
|
||||||
|
var takeLast: Int = 10,
|
||||||
|
var modStorage: String = "/home/cnb/mods",
|
||||||
|
var wanilaStorage: String = "/home/cnb/wanila",
|
||||||
|
var iconsStorage: String = "/home/cnb/icons",
|
||||||
|
)
|
||||||
22
src/main/kotlin/com/dowstats/controllers/AssetsController.kt
Normal file
22
src/main/kotlin/com/dowstats/controllers/AssetsController.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package com.dowstats.controllers
|
||||||
|
|
||||||
|
import com.dowstats.service.w40k.IconsService
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api/v1/grapics")
|
||||||
|
class AssetsController @Autowired constructor(
|
||||||
|
val iconService: IconsService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping("/icon/{raceIconFolder}/{imageName}")
|
||||||
|
fun getUnits(@PathVariable raceIconFolder: String,
|
||||||
|
@PathVariable imageName: String,): ByteArray? {
|
||||||
|
return iconService.returnIcon(raceIconFolder, imageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.dowstats.controllers
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.Mod
|
||||||
|
import com.dowstats.data.entities.Race
|
||||||
|
import com.dowstats.data.repositories.ModRepository
|
||||||
|
import com.dowstats.data.repositories.RaceRepository
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api/v1/races")
|
||||||
|
@EnableWebSecurity
|
||||||
|
class AvailableRacesController @Autowired constructor(
|
||||||
|
val raceRepository: RaceRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping("/{raceId}")
|
||||||
|
fun getRace(@PathVariable raceId: String): Race? {
|
||||||
|
return raceRepository.findById(raceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/mod/{mod}")
|
||||||
|
fun getModVersions(@PathVariable mod: Long): List<Race> {
|
||||||
|
return raceRepository.findRacesByMod(mod)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
43
src/main/kotlin/com/dowstats/controllers/LoginController.kt
Normal file
43
src/main/kotlin/com/dowstats/controllers/LoginController.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package com.dowstats.controllers
|
||||||
|
|
||||||
|
import com.dowstats.Metadata
|
||||||
|
import com.dowstats.service.user.SteamService
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.security.authentication.RememberMeAuthenticationToken
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.servlet.view.RedirectView
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpSession
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/login")
|
||||||
|
@EnableWebSecurity
|
||||||
|
class LoginController @Autowired constructor(
|
||||||
|
val steamService: SteamService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
fun getLogin(req: HttpServletRequest): RedirectView {
|
||||||
|
|
||||||
|
val steamId = req.parameterMap["openid.identity"]?.get(0)?.split("/")?.last()
|
||||||
|
val user = steamService.updateUserBySteamId(steamId!!)
|
||||||
|
|
||||||
|
// TODO: introspect user
|
||||||
|
|
||||||
|
val authReq = RememberMeAuthenticationToken(steamId, user, listOf(SimpleGrantedAuthority(Metadata.USER_ROLE)))
|
||||||
|
|
||||||
|
val sc = SecurityContextHolder.getContext()
|
||||||
|
sc.authentication = authReq
|
||||||
|
val session: HttpSession = req.getSession(true)
|
||||||
|
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, sc)
|
||||||
|
|
||||||
|
return RedirectView("http://localhost:3000")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.dowstats.controllers
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.Mod
|
||||||
|
import com.dowstats.data.repositories.ModRepository
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api/v1/mods")
|
||||||
|
@EnableWebSecurity
|
||||||
|
class ModVersionsController @Autowired constructor(
|
||||||
|
val modRepository: ModRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
fun getModVersions(req: HttpServletRequest): List<Mod> {
|
||||||
|
return modRepository.findAll().toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{mod}")
|
||||||
|
fun getModById(@PathVariable mod: Long): Mod {
|
||||||
|
return modRepository.findById(mod).get()
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/main/kotlin/com/dowstats/controllers/UnitsController.kt
Normal file
43
src/main/kotlin/com/dowstats/controllers/UnitsController.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package com.dowstats.controllers
|
||||||
|
|
||||||
|
import com.dowstats.data.dto.controllers.RaceUnits
|
||||||
|
import com.dowstats.data.entities.DowUnit
|
||||||
|
import com.dowstats.data.repositories.UnitRepository
|
||||||
|
import com.dowstats.service.datamaps.DowUnitMappingService
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api/v1/units")
|
||||||
|
class UnitsController @Autowired constructor(
|
||||||
|
val dowUnitMappingService: DowUnitMappingService,
|
||||||
|
val unitsRepo: UnitRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping("/{modId}/{raceId}")
|
||||||
|
fun getUnitsByModAndRace(@PathVariable modId: Long,
|
||||||
|
@PathVariable raceId: String): RaceUnits {
|
||||||
|
return dowUnitMappingService.findUnitsByModAndRace(modId, raceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/mod/{modId}")
|
||||||
|
fun getUnitsByMod(@PathVariable modId: Long,): List<RaceUnits> {
|
||||||
|
return dowUnitMappingService.findUnitsByMod(modId)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/{unitId}")
|
||||||
|
fun getById(@PathVariable unitId: Long): DowUnit {
|
||||||
|
return unitsRepo.findById(unitId).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping
|
||||||
|
fun removeAll() {
|
||||||
|
unitsRepo.deleteAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.dowstats.controllers
|
||||||
|
|
||||||
|
import com.dowstats.service.integrations.ModStorageIntegrationService
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
|
import org.springframework.web.multipart.MultipartFile
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api/v1/upload-mod")
|
||||||
|
class UploadCustomModController @Autowired constructor(
|
||||||
|
val modStorageIntegrationService: ModStorageIntegrationService
|
||||||
|
) {
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
fun uploadMod(@RequestParam("file") file: MultipartFile,
|
||||||
|
@RequestParam("name") name: String,
|
||||||
|
@RequestParam("technicalName") technicalName: String,
|
||||||
|
@RequestParam("version") version: String?): String {
|
||||||
|
|
||||||
|
val log = LoggerFactory.getLogger(UploadCustomModController::class.java)
|
||||||
|
|
||||||
|
modStorageIntegrationService.saveModFromRequest(file.bytes.inputStream(), name, technicalName, version?:"1.0.0")
|
||||||
|
log.info("${file.originalFilename} successfull uploaded. Name: $name, version: $version")
|
||||||
|
|
||||||
|
return "Successfull upload mod"
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/main/kotlin/com/dowstats/controllers/UserController.kt
Normal file
26
src/main/kotlin/com/dowstats/controllers/UserController.kt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package com.dowstats.controllers
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.User
|
||||||
|
import com.dowstats.service.user.UserService
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api/v1/user")
|
||||||
|
@EnableWebSecurity
|
||||||
|
class UserController @Autowired constructor(
|
||||||
|
val userService: UserService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@PreAuthorize("hasAnyAuthority('USER')")
|
||||||
|
fun getCurrentUser(req: HttpServletRequest): User {
|
||||||
|
return userService.getCurrentUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/main/kotlin/com/dowstats/data/dto/BuildCost.kt
Normal file
10
src/main/kotlin/com/dowstats/data/dto/BuildCost.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.dowstats.data.dto
|
||||||
|
|
||||||
|
data class BuildCost(
|
||||||
|
val requisition: Double?,
|
||||||
|
val power: Double?,
|
||||||
|
val population: Double?,
|
||||||
|
val faith: Double?,
|
||||||
|
val souls: Double?,
|
||||||
|
val time: Int?,
|
||||||
|
)
|
||||||
11
src/main/kotlin/com/dowstats/data/dto/UnitDataToSave.kt
Normal file
11
src/main/kotlin/com/dowstats/data/dto/UnitDataToSave.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package com.dowstats.data.dto
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.DowUnit
|
||||||
|
import com.dowstats.data.entities.Sergeant
|
||||||
|
import com.dowstats.data.entities.Weapon
|
||||||
|
|
||||||
|
data class UnitDataToSave(
|
||||||
|
val unit: DowUnit,
|
||||||
|
val unitWeapons: Set<Weapon>,
|
||||||
|
val sergeants: List<Pair<Sergeant, Set<Weapon>>>?,
|
||||||
|
)
|
||||||
18
src/main/kotlin/com/dowstats/data/dto/controllers/UnitDto.kt
Normal file
18
src/main/kotlin/com/dowstats/data/dto/controllers/UnitDto.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package com.dowstats.data.dto.controllers
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.Race
|
||||||
|
|
||||||
|
data class RaceUnits(
|
||||||
|
val race: Race,
|
||||||
|
val infantry: List<UnitDto>,
|
||||||
|
val tech: List<UnitDto>,
|
||||||
|
val support: List<UnitDto>,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class UnitDto(
|
||||||
|
val name: String,
|
||||||
|
val icon: String,
|
||||||
|
val id: Long,
|
||||||
|
val armourTypeName: String,
|
||||||
|
val canDetect: Boolean,
|
||||||
|
)
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.dowstats.data.dto.controllers
|
||||||
|
|
||||||
|
data class UserDto(
|
||||||
|
val id: Long,
|
||||||
|
val name: String?,
|
||||||
|
val steamId: String?,
|
||||||
|
val steamAvatarUrl: String?
|
||||||
|
)
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.dowstats.data.dto.integration
|
||||||
|
|
||||||
|
data class AvailableMods(
|
||||||
|
val id: Long,
|
||||||
|
val technicalName: String,
|
||||||
|
val uiName: String,
|
||||||
|
val version: String,
|
||||||
|
val isActual: Boolean,
|
||||||
|
val isPrevious: Boolean,
|
||||||
|
val isBeta: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
14
src/main/kotlin/com/dowstats/data/entities/ArmorType.kt
Normal file
14
src/main/kotlin/com/dowstats/data/entities/ArmorType.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "armor_types")
|
||||||
|
class ArmorType {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
var id: String? = null
|
||||||
|
var name: String? = null
|
||||||
|
}
|
||||||
67
src/main/kotlin/com/dowstats/data/entities/DowUnit.kt
Normal file
67
src/main/kotlin/com/dowstats/data/entities/DowUnit.kt
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "units")
|
||||||
|
class DowUnit {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
var id: Long? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "race_id", nullable = false)
|
||||||
|
var race: Race? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "armour_type_id", nullable = false)
|
||||||
|
var armorType: ArmorType? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "armour_type_2_id")
|
||||||
|
var armorType2: ArmorType? = null
|
||||||
|
|
||||||
|
var name: String? = null
|
||||||
|
var description: String? = null
|
||||||
|
var filename: String? = null
|
||||||
|
var buildCostRequisition: Double? = null
|
||||||
|
var buildCostPower: Double? = null
|
||||||
|
var buildCostPopulation: Double? = null
|
||||||
|
var buildCostFaith: Double? = null
|
||||||
|
var buildCostSouls: Double? = null
|
||||||
|
var buildCostTime: Int? = null
|
||||||
|
var capInfantry: Int? = null
|
||||||
|
var capSupport: Int? = null
|
||||||
|
var squadStartSize: Int? = null
|
||||||
|
var squadMaxSize: Int? = null
|
||||||
|
var squadLimit: Int? = null
|
||||||
|
var health: Int? = null
|
||||||
|
var healthRegeneration: Double? = null
|
||||||
|
var moraleDeathPenalty: Int? = null
|
||||||
|
var moraleMax: Int? = null
|
||||||
|
var moraleBroken: Int? = null
|
||||||
|
var moraleRegeneration: Int? = null
|
||||||
|
var mass: Int? = null
|
||||||
|
var upTime: Double? = null
|
||||||
|
var moveSpeed: Int? = null
|
||||||
|
var sightRadius: Int? = null
|
||||||
|
var detectRadius: Int? = null
|
||||||
|
var reinforceCostRequisition: Int? = null
|
||||||
|
var reinforceCostPower: Int? = null
|
||||||
|
var reinforceCostPopulation: Int? = null
|
||||||
|
var reinforceCostFaith: Int? = null
|
||||||
|
var reinforceTime: Int? = null
|
||||||
|
var maxSergeants: Int? = null
|
||||||
|
var icon: String? = null
|
||||||
|
var modId: Long? = null
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
|
||||||
|
var sergeants: MutableSet<Sergeant>? = null
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
|
||||||
|
var weapons: MutableSet<UnitWeapon>? = null
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
18
src/main/kotlin/com/dowstats/data/entities/Mod.kt
Normal file
18
src/main/kotlin/com/dowstats/data/entities/Mod.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "mods")
|
||||||
|
class Mod {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
var id: Long? = null
|
||||||
|
|
||||||
|
var name: String? = null
|
||||||
|
var version: String? = null
|
||||||
|
var technicalName: String? = null
|
||||||
|
var isBeta: Boolean = true
|
||||||
|
}
|
||||||
14
src/main/kotlin/com/dowstats/data/entities/Race.kt
Normal file
14
src/main/kotlin/com/dowstats/data/entities/Race.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "races")
|
||||||
|
class Race {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
var id: String? = null
|
||||||
|
var name: String? = null
|
||||||
|
|
||||||
|
}
|
||||||
50
src/main/kotlin/com/dowstats/data/entities/Sergant.kt
Normal file
50
src/main/kotlin/com/dowstats/data/entities/Sergant.kt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "sergeants")
|
||||||
|
class Sergeant {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
var id: Long? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "armour_type_id", nullable = false)
|
||||||
|
var armorType: ArmorType? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "armour_type_2_id")
|
||||||
|
var armorType2: ArmorType? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "unit_id", nullable = false)
|
||||||
|
@JsonIgnore
|
||||||
|
var unit: DowUnit? = null
|
||||||
|
|
||||||
|
var name: String? = null
|
||||||
|
var description: String? = null
|
||||||
|
var filename: String? = null
|
||||||
|
var buildCostRequisition: Double? = null
|
||||||
|
var buildCostPower: Double? = null
|
||||||
|
var buildCostPopulation: Double? = null
|
||||||
|
var buildCostFaith: Double? = null
|
||||||
|
var buildCostSouls: Double? = null
|
||||||
|
var buildCostTime: Int? = null
|
||||||
|
var health: Int? = null
|
||||||
|
var healthRegeneration: Double? = null
|
||||||
|
var moraleDeathPenalty: Int? = null
|
||||||
|
var mass: Int? = null
|
||||||
|
var upTime: Double? = null
|
||||||
|
var sightRadius: Int? = null
|
||||||
|
var detectRadius: Int? = null
|
||||||
|
var icon: String? = null
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "sergeant", cascade = [CascadeType.ALL])
|
||||||
|
var weapons: MutableSet<SergeantWeapon>? = null
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
43
src/main/kotlin/com/dowstats/data/entities/SergantWeapon.kt
Normal file
43
src/main/kotlin/com/dowstats/data/entities/SergantWeapon.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
|
import java.io.Serializable
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
class SergeantWeaponKey : Serializable {
|
||||||
|
@Column(name = "sergeant_id")
|
||||||
|
var sergeantId: Long? = null
|
||||||
|
|
||||||
|
@Column(name = "weapon_id")
|
||||||
|
var weaponId: Long? = null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "sergeants_weapons")
|
||||||
|
class SergeantWeapon {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
@JsonIgnore
|
||||||
|
var sergeantWeaponKey: SergeantWeaponKey? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@MapsId("sergeantId")
|
||||||
|
@JoinColumn(name = "sergeant_id")
|
||||||
|
@JsonIgnore
|
||||||
|
var sergeant: Sergeant? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@MapsId("weaponId")
|
||||||
|
@JoinColumn(name = "weapon_id")
|
||||||
|
var weapon: Weapon? = null
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
var hardpoint: Int = 0
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
var hardpointOrder: Int = 0
|
||||||
|
|
||||||
|
}
|
||||||
43
src/main/kotlin/com/dowstats/data/entities/UnitWeapon.kt
Normal file
43
src/main/kotlin/com/dowstats/data/entities/UnitWeapon.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
|
import java.io.Serializable
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
class UnitWeaponKey : Serializable {
|
||||||
|
@Column(name = "unit_id")
|
||||||
|
var unitId: Long? = null
|
||||||
|
|
||||||
|
@Column(name = "weapon_id")
|
||||||
|
var weaponId: Long? = null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "units_weapons")
|
||||||
|
class UnitWeapon {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
@JsonIgnore
|
||||||
|
var unitWeaponKey: UnitWeaponKey? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@MapsId("unitId")
|
||||||
|
@JoinColumn(name = "unit_id")
|
||||||
|
@JsonIgnore
|
||||||
|
var unit: DowUnit? = null
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@MapsId("weaponId")
|
||||||
|
@JoinColumn(name = "weapon_id")
|
||||||
|
var weapon: Weapon? = null
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
var hardpoint: Int = 0
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
var hardpointOrder: Int = 0
|
||||||
|
|
||||||
|
}
|
||||||
18
src/main/kotlin/com/dowstats/data/entities/User.kt
Normal file
18
src/main/kotlin/com/dowstats/data/entities/User.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "users")
|
||||||
|
class User {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
var id: Long? = null
|
||||||
|
|
||||||
|
var name: String? = null
|
||||||
|
|
||||||
|
var steamId: String? = null
|
||||||
|
var avatarUrl: String? = null
|
||||||
|
}
|
||||||
44
src/main/kotlin/com/dowstats/data/entities/Weapon.kt
Normal file
44
src/main/kotlin/com/dowstats/data/entities/Weapon.kt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "weapons")
|
||||||
|
class Weapon {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
var id: Long? = null
|
||||||
|
var filename: String? = null
|
||||||
|
var name: String? = null
|
||||||
|
var description: String? = null
|
||||||
|
var costRequisition: Int? = null
|
||||||
|
var costPower: Int? = null
|
||||||
|
var costTimeSeconds: Int? = null
|
||||||
|
var accuracy: Double? = null
|
||||||
|
var reloadTime: Double? = null
|
||||||
|
var maxRange: Double? = null
|
||||||
|
var setupTime: Double? = null
|
||||||
|
var accuracyReductionMoving: Double? = null
|
||||||
|
var minDamage: Double? = null
|
||||||
|
var maxDamage: Double? = null
|
||||||
|
var minDamageValue: Double? = null
|
||||||
|
var moraleDamage: Double? = null
|
||||||
|
var isMeleeWeapon: Boolean = true
|
||||||
|
var canAttackAir: Boolean = true
|
||||||
|
var canAttackGround: Boolean = true
|
||||||
|
var icon: String? = null
|
||||||
|
var haveEquipButton: Boolean = true
|
||||||
|
var hotkeyName: String? = null
|
||||||
|
var modId: Long? = null
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var hardpoint: Int = 0
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var hardpointOrder: Int = 0
|
||||||
|
|
||||||
|
@OneToMany(mappedBy="weapon", fetch = FetchType.EAGER, cascade = [(CascadeType.ALL)])
|
||||||
|
var weaponPiercings: List<WeaponArmorPiercing> = listOf()
|
||||||
|
}
|
||||||
26
src/main/kotlin/com/dowstats/data/entities/WeaponDamage.kt
Normal file
26
src/main/kotlin/com/dowstats/data/entities/WeaponDamage.kt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package com.dowstats.data.entities
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "weapons_armors_piercing")
|
||||||
|
class WeaponArmorPiercing {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
var id: Long? = null
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JsonIgnore
|
||||||
|
@JoinColumn(name = "weapon_id")
|
||||||
|
var weapon: Weapon? = null
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "armor_type_id")
|
||||||
|
var armorType: ArmorType? = null
|
||||||
|
|
||||||
|
var piercingValue: BigDecimal? = null
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.dowstats.data.repositories
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.ArmorType
|
||||||
|
import com.dowstats.data.entities.Race
|
||||||
|
import com.dowstats.data.entities.User
|
||||||
|
import org.springframework.data.repository.*
|
||||||
|
|
||||||
|
interface ArmorTypeRepository : CrudRepository<ArmorType, Long>
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.dowstats.data.repositories
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.Mod
|
||||||
|
import org.springframework.data.repository.*
|
||||||
|
|
||||||
|
interface ModRepository : CrudRepository<Mod, Long>{
|
||||||
|
fun findByTechnicalNameAndVersion(techName: String, version: String): Mod?
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.dowstats.data.repositories
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.Race
|
||||||
|
import org.springframework.data.repository.*
|
||||||
|
|
||||||
|
interface RacesRepository : CrudRepository<Race, Long>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.dowstats.data.repositories
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.Race
|
||||||
|
import com.dowstats.data.entities.User
|
||||||
|
import org.springframework.data.jpa.repository.Query
|
||||||
|
import org.springframework.data.repository.*
|
||||||
|
|
||||||
|
interface RaceRepository : CrudRepository<Race, Long>{
|
||||||
|
fun findById(id: String): Race?
|
||||||
|
|
||||||
|
@Query("SELECT u.race FROM DowUnit u WHERE u.modId = :modId")
|
||||||
|
fun findRacesByMod(modId: Long): List<Race>
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.dowstats.data.repositories
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.Sergeant
|
||||||
|
import org.springframework.data.repository.CrudRepository
|
||||||
|
|
||||||
|
interface SergeantRepository : CrudRepository<Sergeant, Long>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.dowstats.data.repositories
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.DowUnit
|
||||||
|
import com.dowstats.data.entities.Race
|
||||||
|
import org.springframework.data.jpa.repository.Query
|
||||||
|
import org.springframework.data.repository.*
|
||||||
|
|
||||||
|
interface UnitRepository : CrudRepository<DowUnit, Long> {
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
select d, a1, a2
|
||||||
|
from DowUnit d
|
||||||
|
left join fetch ArmorType a1 on d.armorType.id = a1.id
|
||||||
|
left join fetch ArmorType a2 on d.armorType2.id = a2.id
|
||||||
|
where d.modId = :modId
|
||||||
|
and (:race is null or d.race = :race)
|
||||||
|
""")
|
||||||
|
fun findByModIdAndRace(modId: Long, race: Race?): List<DowUnit>
|
||||||
|
|
||||||
|
fun deleteAllByModIdAndRaceId(modId: Long, raceId: String)
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.dowstats.data.repositories
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.User
|
||||||
|
import org.springframework.data.repository.*
|
||||||
|
|
||||||
|
interface UserRepository : CrudRepository<User, Long>{
|
||||||
|
fun findBySteamId(steamId: String): List<User>
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.dowstats.data.repositories
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.DowUnit
|
||||||
|
import com.dowstats.data.entities.Weapon
|
||||||
|
import org.springframework.data.repository.*
|
||||||
|
|
||||||
|
interface WeaponRepository : CrudRepository<Weapon, Long>{
|
||||||
|
fun deleteAllByModId(modId: Long)
|
||||||
|
}
|
||||||
31
src/main/kotlin/com/dowstats/data/rgd/RgdData.kt
Normal file
31
src/main/kotlin/com/dowstats/data/rgd/RgdData.kt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package com.dowstats.data.rgd
|
||||||
|
|
||||||
|
data class RgdData(
|
||||||
|
val hash: Int,
|
||||||
|
val name: String,
|
||||||
|
val type: Int,
|
||||||
|
val value: Any,
|
||||||
|
)
|
||||||
|
|
||||||
|
object RgdDataUtil{
|
||||||
|
|
||||||
|
fun List<RgdData>.getRgdTableByName(name: String): List<RgdData>? {
|
||||||
|
return this.find { it.name == name }?.value as List<RgdData>?
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<RgdData>.getBooleanByName(name: String): Boolean? {
|
||||||
|
return this.find { it.name == name }?.value as Boolean?
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<RgdData>.getDoubleByName(name: String): Double? {
|
||||||
|
return this.find { it.name == name }?.value as Double?
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<RgdData>.getIntByName(name: String): Int? {
|
||||||
|
return (this.find { it.name == name }?.value as Double?)?.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<RgdData>.getStringByName(name: String): String? {
|
||||||
|
return this.find { it.name == name }?.value as String?
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
package com.dowstats.service.datamaps
|
||||||
|
|
||||||
|
import com.dowstats.data.dto.controllers.RaceUnits
|
||||||
|
import com.dowstats.data.dto.controllers.UnitDto
|
||||||
|
import com.dowstats.data.entities.DowUnit
|
||||||
|
import com.dowstats.data.entities.Race
|
||||||
|
import com.dowstats.data.repositories.RaceRepository
|
||||||
|
import com.dowstats.data.repositories.UnitRepository
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class DowUnitMappingService @Autowired constructor(
|
||||||
|
val unitRepository: UnitRepository,
|
||||||
|
val raceRepository: RaceRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun findUnitsByMod(modId: Long): List<RaceUnits> {
|
||||||
|
val races = raceRepository.findRacesByMod(modId)
|
||||||
|
return getAllUnits(modId).groupBy { it.race }.map {raceUnits ->
|
||||||
|
toRaceUnitsDto(raceUnits.value, races.first { it.id == raceUnits.key?.id } )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findUnitsByModAndRace(modId: Long, race: String): RaceUnits {
|
||||||
|
val units = getAllUnits(modId, race)
|
||||||
|
val raceEntity = race.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") }
|
||||||
|
return toRaceUnitsDto(units, raceEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toRaceUnitsDto(units: List<DowUnit>, race: Race): RaceUnits {
|
||||||
|
val infantry = units
|
||||||
|
.filter { it.capInfantry?.let { it > 0 } ?: false || it.reinforceCostPopulation?.let { it > 0 } ?: false }
|
||||||
|
.toUnitDto()
|
||||||
|
|
||||||
|
val tech = units
|
||||||
|
.filter { it.capSupport?.let { it > 0 } ?: false }
|
||||||
|
.toUnitDto()
|
||||||
|
|
||||||
|
val support = units.filter {
|
||||||
|
it.capInfantry?.let { it == 0 } ?: false && it.reinforceCostPopulation?.let { it == 0 } ?: true &&
|
||||||
|
it.capSupport?.let { it == 0 } ?: false
|
||||||
|
}
|
||||||
|
.toUnitDto()
|
||||||
|
|
||||||
|
return RaceUnits(race, infantry, tech, support)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun List<DowUnit>.toUnitDto(): List<UnitDto> =
|
||||||
|
this.mapNotNull {
|
||||||
|
val name = it.name ?: it.filename
|
||||||
|
val icon = it.icon
|
||||||
|
if (name == null || icon == null) null else UnitDto(name, icon, it.id!!,
|
||||||
|
it.armorType?.name!!,
|
||||||
|
(it.detectRadius ?: 0) > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getAllUnits(modId: Long, race: String? = null): List<DowUnit> {
|
||||||
|
val raceEntity = race?.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") }
|
||||||
|
return filterCompanyUnits(unitRepository.findByModIdAndRace(modId, raceEntity))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterCompanyUnits(units: List<DowUnit>): List<DowUnit> =
|
||||||
|
units.filter {
|
||||||
|
it.filename?.contains("_sp.") != true
|
||||||
|
&& it.filename?.contains("_sp_") != true
|
||||||
|
&& it.filename?.contains("sp_eldar_") != true
|
||||||
|
&& it.filename?.contains("_dxp3.") != true
|
||||||
|
&& it.filename?.contains("_dxp3_") != true
|
||||||
|
&& it.filename?.contains("_nis.") != true
|
||||||
|
&& it.filename?.contains("_exarch_council.") != true
|
||||||
|
&& it.filename?.contains("_dark_reapers_base.") != true
|
||||||
|
&& it.filename?.contains("tau_squad_slave_murdered") != true
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
package com.dowstats.service.integrations
|
||||||
|
|
||||||
|
import com.dowstats.configuration.StorageConfig
|
||||||
|
import com.dowstats.data.dto.integration.AvailableMods
|
||||||
|
import com.dowstats.data.entities.Mod
|
||||||
|
import com.dowstats.data.repositories.ModRepository
|
||||||
|
import com.dowstats.service.w40k.ModParserService
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.util.FileSystemUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.net.URL
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.zip.*
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ModStorageIntegrationService(
|
||||||
|
val modStorageConfig: StorageConfig,
|
||||||
|
val modRepository: ModRepository,
|
||||||
|
val modParserService: ModParserService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val objectMapper = jacksonObjectMapper()
|
||||||
|
val log = LoggerFactory.getLogger(ModStorageIntegrationService::class.java)
|
||||||
|
|
||||||
|
fun requestAvailableMods() {
|
||||||
|
|
||||||
|
log.info("Requesting available mods")
|
||||||
|
val urlString = "http://crosspick.ru/dow_stats_client/dow_stats_balance_mod/mods.txt"
|
||||||
|
val connection = URL(urlString).openConnection()
|
||||||
|
connection.setRequestProperty("X-Key", modStorageConfig.key)
|
||||||
|
val inputStream: InputStream = connection.getInputStream()
|
||||||
|
val modsJson = String(inputStream.readAllBytes(), StandardCharsets.UTF_8)
|
||||||
|
val availableMods: List<AvailableMods> = objectMapper.readValue(modsJson)
|
||||||
|
|
||||||
|
val prevSavedMods = modRepository.findAll()
|
||||||
|
val lastMods = availableMods.take(modStorageConfig.takeLast)
|
||||||
|
|
||||||
|
val lastVersionsFromServer = if (lastMods.all { !it.isBeta }) {
|
||||||
|
lastMods
|
||||||
|
} else {
|
||||||
|
lastMods + availableMods.filter { !it.isBeta }.take(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val newVersions =
|
||||||
|
lastVersionsFromServer.filter { serverVersion -> !prevSavedMods.any { it.technicalName == serverVersion.technicalName } }
|
||||||
|
.sortedBy { it.id }
|
||||||
|
|
||||||
|
newVersions.map { toSave ->
|
||||||
|
downloadAndExtractMod(toSave.technicalName, toSave.version)
|
||||||
|
val savedMod = modRepository.save(Mod().also {
|
||||||
|
it.version = toSave.version
|
||||||
|
it.isBeta = toSave.isBeta
|
||||||
|
it.name = "Dowstats balance mod"
|
||||||
|
it.technicalName = toSave.technicalName
|
||||||
|
})
|
||||||
|
modParserService.parceModFilesAndSaveToDb(savedMod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveModFromRequest(fileStream: InputStream, name: String, techName: String, version: String) {
|
||||||
|
val modDirectoryTo = "$name$version"
|
||||||
|
unzip(fileStream, modDirectoryTo)
|
||||||
|
val uploadingMod = modRepository.findByTechnicalNameAndVersion(techName, version)
|
||||||
|
val savedMod = uploadingMod ?: modRepository.save(Mod().also {
|
||||||
|
it.version = version
|
||||||
|
it.isBeta = false
|
||||||
|
it.name = name
|
||||||
|
it.technicalName = techName
|
||||||
|
})
|
||||||
|
modParserService.parceModFilesAndSaveToDb(savedMod)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun downloadAndExtractMod(modTechName: String, version: String) {
|
||||||
|
log.info("Downloading mod $modTechName")
|
||||||
|
val urlString = "http://crosspick.ru/dow_stats_client/dow_stats_balance_mod/$modTechName.zip"
|
||||||
|
val connection = URL(urlString).openConnection()
|
||||||
|
connection.setRequestProperty("X-Key", modStorageConfig.key)
|
||||||
|
val inputStream: InputStream = connection.getInputStream()
|
||||||
|
val modDirectoryTo = "$modTechName$version"
|
||||||
|
unzip(inputStream, modDirectoryTo)
|
||||||
|
alignBmModFolder(modDirectoryTo, modTechName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unzip(stream: InputStream, modDirectoryTo: String) {
|
||||||
|
val unzipPath = "${modStorageConfig.modStorage}${File.separator}$modDirectoryTo"
|
||||||
|
log.info("Start unzip mod to $modDirectoryTo to '$unzipPath'")
|
||||||
|
ZipInputStream(stream, Charsets.ISO_8859_1).use { zis ->
|
||||||
|
var entry: ZipEntry?
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
while ((zis.nextEntry.also { entry = it }) != null) {
|
||||||
|
val newFile = File(("$unzipPath${File.separator}${entry!!.name}"))
|
||||||
|
if (entry!!.isDirectory) {
|
||||||
|
newFile.mkdirs()
|
||||||
|
} else {
|
||||||
|
File(newFile.getParent()).mkdirs()
|
||||||
|
FileOutputStream(newFile).use { fos ->
|
||||||
|
var length: Int
|
||||||
|
while ((zis.read(buffer).also { length = it }) > 0) {
|
||||||
|
fos.write(buffer, 0, length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("Mod $modDirectoryTo unzip complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun alignBmModFolder(modFolder: String, modName: String) {
|
||||||
|
val unzipPath = "${modStorageConfig.modStorage}${File.separator}$modFolder"
|
||||||
|
FileSystemUtils.copyRecursively(Path.of("$unzipPath${File.separator}$modName"), Path.of(unzipPath))
|
||||||
|
FileSystemUtils.deleteRecursively(Path.of("$unzipPath${File.separator}$modName"))
|
||||||
|
FileSystemUtils.deleteRecursively(Path.of("$unzipPath${File.separator}Engine"))
|
||||||
|
FileSystemUtils.deleteRecursively(Path.of("$unzipPath${File.separator}$modName.module"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.dowstats.service.schedulers
|
||||||
|
|
||||||
|
import com.dowstats.service.integrations.ModStorageIntegrationService
|
||||||
|
import org.apache.commons.logging.LogFactory
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Schedulers (
|
||||||
|
val modStorageIntegrationService: ModStorageIntegrationService
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val log = LogFactory.getLog(javaClass)
|
||||||
|
|
||||||
|
@Scheduled(fixedDelay = 600000)
|
||||||
|
fun synchronizeLastMods() {
|
||||||
|
modStorageIntegrationService.requestAvailableMods()
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/main/kotlin/com/dowstats/service/user/SteamService.kt
Normal file
53
src/main/kotlin/com/dowstats/service/user/SteamService.kt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package com.dowstats.service.user
|
||||||
|
|
||||||
|
import com.dowstats.configuration.SteamConfig
|
||||||
|
import com.dowstats.data.entities.User
|
||||||
|
import com.dowstats.data.repositories.UserRepository
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class SteamService @Autowired constructor(
|
||||||
|
val steamConfig: SteamConfig,
|
||||||
|
val userRepo: UserRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
data class UserData(
|
||||||
|
val name: String,
|
||||||
|
val avatarUrl: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
val mapper = jacksonObjectMapper()
|
||||||
|
|
||||||
|
fun updateUserBySteamId(steamId: String): User {
|
||||||
|
|
||||||
|
val userInfo = getUserData(steamId)
|
||||||
|
|
||||||
|
val userToSaveOrUpdate = userRepo.findBySteamId(steamId).singleOrNull() ?: User().apply {
|
||||||
|
this.avatarUrl = userInfo.avatarUrl
|
||||||
|
this.name = userInfo.name
|
||||||
|
this.steamId = steamId
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return userRepo.save(userToSaveOrUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getUserData(steamId: String): UserData {
|
||||||
|
val userDetails = URL("https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/" +
|
||||||
|
"?key=${steamConfig.key}" +
|
||||||
|
"&steamids=$steamId").readText()
|
||||||
|
|
||||||
|
val model: JsonNode = mapper.readTree(userDetails)
|
||||||
|
|
||||||
|
return UserData(model["response"]["players"].get(0)["personaname"].textValue(),
|
||||||
|
model["response"]["players"].get(0)["avatarmedium"].textValue())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
16
src/main/kotlin/com/dowstats/service/user/UserService.kt
Normal file
16
src/main/kotlin/com/dowstats/service/user/UserService.kt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package com.dowstats.service.user
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.User
|
||||||
|
import com.dowstats.data.repositories.UserRepository
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class UserService {
|
||||||
|
fun getCurrentUser(): User {
|
||||||
|
val sc = SecurityContextHolder.getContext()
|
||||||
|
val user = sc.authentication.principal as User
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/main/kotlin/com/dowstats/service/w40k/IconsService.kt
Normal file
53
src/main/kotlin/com/dowstats/service/w40k/IconsService.kt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.configuration.StorageConfig
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.File
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class IconsService @Autowired constructor(
|
||||||
|
val storageConfig: StorageConfig,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val log = LoggerFactory.getLogger(IconsService::class.java)
|
||||||
|
|
||||||
|
/** Конвертирует иконку tga->jpeg и возвращает путь до неё
|
||||||
|
* @param pathToTgaIcon - путь до иконки
|
||||||
|
* @return путь до сконвертированной иконки
|
||||||
|
*/
|
||||||
|
fun convertTgaToJpegImage(iconPathInMod: String, pathToTgaIcon: String): String? {
|
||||||
|
try{
|
||||||
|
val image: BufferedImage = try {
|
||||||
|
ImageIO.read(File(pathToTgaIcon))
|
||||||
|
} catch (e: Exception){
|
||||||
|
ImageIO.read(File(pathToTgaIcon.lowercase()))
|
||||||
|
}
|
||||||
|
|
||||||
|
val pathToSave = "${storageConfig.iconsStorage.replace("/", File.separator)}${File.separator}${iconPathInMod.replace("\\", File.separator)}.png"
|
||||||
|
|
||||||
|
val directoryToSave = File(pathToSave.split(File.separator).dropLast(1).joinToString (File.separator))
|
||||||
|
if(!directoryToSave.exists()) directoryToSave.mkdirs()
|
||||||
|
|
||||||
|
return if (!ImageIO.write(image, "png", File(pathToSave))) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
pathToSave.replace("${storageConfig.iconsStorage.replace("/", File.separator)}${File.separator}", "")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.warn("Can't convert icon $iconPathInMod", e)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun returnIcon(raceIconFolder: String, iconName: String): ByteArray? {
|
||||||
|
val pathToIcon = "${storageConfig.iconsStorage.replace("/", File.separator)}${File.separator}$raceIconFolder${File.separator}$iconName"
|
||||||
|
return File(pathToIcon).readBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.configuration.StorageConfig
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ModAttribPathService @Autowired constructor(
|
||||||
|
private val storageConfig: StorageConfig,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val pathToWanilaData = storageConfig.wanilaStorage.replace("/", File.separator)
|
||||||
|
|
||||||
|
fun getModFolderData(modTechName: String, modVersion: String) =
|
||||||
|
"${storageConfig.modStorage.replace("/", File.separator)}${File.separator}$modTechName$modVersion${File.separator}Data"
|
||||||
|
|
||||||
|
fun getUcsFolder(modFolderData: String): String =
|
||||||
|
"${modFolderData.replace("Data", "")}Locale${File.separator}English${File.separator}"
|
||||||
|
|
||||||
|
fun getWeaponAttribsPath(modFolderData: String): String =
|
||||||
|
"$modFolderData${File.separator}attrib${File.separator}weapon"
|
||||||
|
|
||||||
|
fun getSbpsAttribsFolderPath(modFolderData: String): String =
|
||||||
|
"$modFolderData${File.separator}attrib${File.separator}sbps${File.separator}races${File.separator}"
|
||||||
|
|
||||||
|
fun getSbpsAttribsPath(modFolderData: String, race: String): String =
|
||||||
|
"${getSbpsAttribsFolderPath(modFolderData)}$race"
|
||||||
|
|
||||||
|
fun getEbpsTroopsAttribsPath(modFolderData: String, race: String): String =
|
||||||
|
"$modFolderData${File.separator}attrib${File.separator}ebps${File.separator}races${File.separator}$race${File.separator}troops"
|
||||||
|
|
||||||
|
fun geBuildingAttribsPath(modFolderData: String, race: String): String =
|
||||||
|
"$modFolderData${File.separator}attrib${File.separator}ebps${File.separator}races${File.separator}$race${File.separator}structures"
|
||||||
|
|
||||||
|
fun getIconPath(modFolderData: String, iconName: String): String =
|
||||||
|
"$modFolderData${File.separator}art${File.separator}ui${File.separator}ingame${File.separator}$iconName.tga"
|
||||||
|
}
|
||||||
211
src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt
Normal file
211
src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.*
|
||||||
|
import com.dowstats.data.repositories.*
|
||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.lang.Exception
|
||||||
|
import jakarta.transaction.Transactional
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.name
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ModParserService @Autowired constructor(
|
||||||
|
val rgdParserService: RgdParserService,
|
||||||
|
val unitRgdExtractService: UnitRgdExtractService,
|
||||||
|
val raceRepository: RaceRepository,
|
||||||
|
val armorTypeRepository: ArmorTypeRepository,
|
||||||
|
val unitRepository: UnitRepository,
|
||||||
|
val sergeantRepository: SergeantRepository,
|
||||||
|
val weaponRgdExtractService: WeaponRgdExtractService,
|
||||||
|
val weaponRepository: WeaponRepository,
|
||||||
|
val modAttribPathService: ModAttribPathService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val defaultDictionary: MutableMap<Int, String> = mutableMapOf()
|
||||||
|
|
||||||
|
init {
|
||||||
|
RgdParserService::class.java.getClassLoader().getResourceAsStream("DXP2.ucs").bufferedReader(Charsets.UTF_8).lines().forEach {
|
||||||
|
val kv = it.split("\\s+".toRegex())
|
||||||
|
if (kv.size < 2) return@forEach
|
||||||
|
val key = kv.first().filter { it.isDigit() }.toInt()
|
||||||
|
val value = kv.drop(1).joinToString(" ").replace("\u0000","")
|
||||||
|
defaultDictionary[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
RgdParserService::class.java.getClassLoader().getResourceAsStream("W40k.ucs").bufferedReader(Charsets.UTF_8).lines().forEach {
|
||||||
|
val kv = it.split("\\s+".toRegex())
|
||||||
|
if (kv.size < 2) return@forEach
|
||||||
|
val key = kv.first().filter { it.isDigit() }.toInt()
|
||||||
|
val value = kv.drop(1).joinToString(" ").replace("\u0000","")
|
||||||
|
defaultDictionary[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val log = LoggerFactory.getLogger(ModParserService::class.java)
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun parceModFilesAndSaveToDb(mod: Mod) {
|
||||||
|
|
||||||
|
log.info("Start parse mod files ${mod.technicalName}:${mod.version}")
|
||||||
|
|
||||||
|
val modId = mod.id!!
|
||||||
|
|
||||||
|
val modFolderData = modAttribPathService.getModFolderData(mod.technicalName!!, mod.version!!)
|
||||||
|
val racesList = Files.walk(Path(modAttribPathService.getSbpsAttribsFolderPath(modFolderData)), 1)
|
||||||
|
.toList()
|
||||||
|
.drop(1)
|
||||||
|
.filter { Files.isDirectory(it) }.map { it.name }.toList()
|
||||||
|
|
||||||
|
racesList.forEach{
|
||||||
|
unitRepository.deleteAllByModIdAndRaceId(modId, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val modDictionary: MutableMap<Int, String> = mutableMapOf()
|
||||||
|
|
||||||
|
log.info("Extract dictionaries from $modFolderData")
|
||||||
|
File(modAttribPathService.getUcsFolder(modFolderData)).listFiles().forEach {
|
||||||
|
it.bufferedReader(Charsets.UTF_8).lines().forEach {
|
||||||
|
val kv = it.split("\\s+".toRegex())
|
||||||
|
if (kv.size < 2) return@forEach
|
||||||
|
val key = kv.first().filter { it.isDigit() }.toInt()
|
||||||
|
val value = kv.drop(1).joinToString(" ").replace("\u0000","")
|
||||||
|
modDictionary[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val enrichedModDictionary = modDictionary + defaultDictionary
|
||||||
|
|
||||||
|
val weapons = saveWeapons(modFolderData, modId, enrichedModDictionary)
|
||||||
|
saveUnits(modFolderData, weapons, racesList, modId, enrichedModDictionary)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveUnits(modFolderData: String, weapons: Set<Weapon>, racesList: List<String>, modId: Long, modDictionary: Map<Int, String>) {
|
||||||
|
|
||||||
|
val races = raceRepository.findAll().toList()
|
||||||
|
val armorTypes = armorTypeRepository.findAll().toList()
|
||||||
|
|
||||||
|
racesList.forEach { raceFolder ->
|
||||||
|
|
||||||
|
if( raceRepository.findById(raceFolder) == null){
|
||||||
|
val race = Race().also { it.id = raceFolder; it.name = raceFolder }
|
||||||
|
raceRepository.save(race)
|
||||||
|
}
|
||||||
|
|
||||||
|
println(raceFolder)
|
||||||
|
|
||||||
|
val classicRgdDataSquads =
|
||||||
|
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getSbpsAttribsPath(modAttribPathService.pathToWanilaData, raceFolder))
|
||||||
|
val modRgdDataSquads =
|
||||||
|
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getSbpsAttribsPath(modFolderData, raceFolder))
|
||||||
|
|
||||||
|
val classicRgdDataUnits =
|
||||||
|
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getEbpsTroopsAttribsPath(modAttribPathService.pathToWanilaData, raceFolder))
|
||||||
|
val modRgdDataUnits =
|
||||||
|
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getEbpsTroopsAttribsPath(modFolderData, raceFolder))
|
||||||
|
|
||||||
|
val modSquadsFull = classicRgdDataSquads + modRgdDataSquads
|
||||||
|
val modUnitsFull = classicRgdDataUnits + modRgdDataUnits
|
||||||
|
|
||||||
|
modSquadsFull.forEach { squadRgdData ->
|
||||||
|
|
||||||
|
val baseUnitName = unitRgdExtractService.getUnitRgdFileNameFromSquadData(squadRgdData.value)
|
||||||
|
?: throw Exception("Can't extract unit name from squad ${squadRgdData.key}")
|
||||||
|
log.info("Start extracting $raceFolder: $baseUnitName")
|
||||||
|
val unitRgdData: List<RgdData> =
|
||||||
|
modUnitsFull[baseUnitName] ?: emptyList()
|
||||||
|
|
||||||
|
if(unitRgdData.isEmpty()){
|
||||||
|
log.warn("Can't find rgd data for unit $baseUnitName")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
val unitDataToSave =
|
||||||
|
try {
|
||||||
|
unitRgdExtractService.extractToUnitEntity(
|
||||||
|
squadRgdData.key,
|
||||||
|
modDictionary,
|
||||||
|
squadRgdData.value,
|
||||||
|
unitRgdData,
|
||||||
|
weapons,
|
||||||
|
raceFolder,
|
||||||
|
modFolderData,
|
||||||
|
modId,
|
||||||
|
races,
|
||||||
|
armorTypes,
|
||||||
|
modUnitsFull
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.error("Can't extract $baseUnitName", e)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val unit = unitRepository.save(unitDataToSave.unit)
|
||||||
|
unit.weapons = unitDataToSave.unitWeapons.map {weapon ->
|
||||||
|
UnitWeapon().also {
|
||||||
|
it.unit = unit
|
||||||
|
it.weapon = weapon
|
||||||
|
it.hardpoint = weapon.hardpoint
|
||||||
|
it.hardpointOrder = weapon.hardpointOrder
|
||||||
|
it.unitWeaponKey = UnitWeaponKey().also {
|
||||||
|
it.unitId = unit.id
|
||||||
|
it.weaponId = weapon.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.toMutableSet()
|
||||||
|
unitRepository.save(unit)
|
||||||
|
|
||||||
|
|
||||||
|
unitDataToSave.sergeants?.forEach { sergeantToSave ->
|
||||||
|
val sergeant = sergeantRepository.save(sergeantToSave.first.also {
|
||||||
|
it.unit = unit
|
||||||
|
})
|
||||||
|
sergeant.weapons = sergeantToSave.second.map {weapon ->
|
||||||
|
SergeantWeapon().also {
|
||||||
|
it.sergeant = sergeant
|
||||||
|
it.weapon = weapon
|
||||||
|
it.hardpoint = weapon.hardpoint
|
||||||
|
it.hardpointOrder = weapon.hardpointOrder
|
||||||
|
it.sergeantWeaponKey = SergeantWeaponKey().also { swk ->
|
||||||
|
swk.sergeantId = sergeant.id
|
||||||
|
swk.weaponId = weapon.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.toMutableSet()
|
||||||
|
sergeantRepository.save(sergeant)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveWeapons(modFolderData: String, modId: Long, modDictionary: Map<Int, String>): Set<Weapon> {
|
||||||
|
|
||||||
|
val classicWeapons = rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/weapon")
|
||||||
|
|
||||||
|
val modWeapons =
|
||||||
|
rgdParserService.parseFolderToRgdFiles(modAttribPathService.getWeaponAttribsPath(modFolderData))
|
||||||
|
|
||||||
|
val allWeapons = classicWeapons + modWeapons
|
||||||
|
|
||||||
|
val weaponsToSave = allWeapons.mapNotNull {
|
||||||
|
weaponRgdExtractService.extractToWeaponEntity(it.key, it.value, modId, modFolderData, modDictionary)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
weaponRepository.saveAll(weaponsToSave).toSet()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
132
src/main/kotlin/com/dowstats/service/w40k/ModsDiffService.kt
Normal file
132
src/main/kotlin/com/dowstats/service/w40k/ModsDiffService.kt
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ModsDiffService @Autowired constructor(
|
||||||
|
val rgdParseService: RgdParserService,
|
||||||
|
val rgdService: RgdService,
|
||||||
|
val modStorageService: ModAttribPathService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val spaceMarinesPath = "space_marines"
|
||||||
|
val chaosPath = "chaos"
|
||||||
|
val eldarPath = "eldar"
|
||||||
|
val orksPath = "orks"
|
||||||
|
val guardPath = "guard"
|
||||||
|
val necronPath = "necrons"
|
||||||
|
val tauPath = "tau"
|
||||||
|
val sistersPath = "sisters"
|
||||||
|
|
||||||
|
val racesWikiPaths =
|
||||||
|
setOf(spaceMarinesPath, chaosPath, eldarPath, orksPath, guardPath, necronPath, tauPath, sistersPath)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun getUnitsAndSquadsDiff(modFolderData: String, oldModFolderData: String? = null): String {
|
||||||
|
|
||||||
|
val oldModFolderPath =
|
||||||
|
if (oldModFolderData != null) oldModFolderData else modStorageService.pathToWanilaData
|
||||||
|
|
||||||
|
var totalDiff = ""
|
||||||
|
|
||||||
|
racesWikiPaths.forEach { racePath ->
|
||||||
|
val rgdDataOld = getRgdDataMapFromFolder(modStorageService.getSbpsAttribsPath(oldModFolderPath, racePath))
|
||||||
|
val rgdDataNew = getRgdDataMapFromFolder(modStorageService.getSbpsAttribsPath(modFolderData, racePath))
|
||||||
|
|
||||||
|
val rgdDataUnitsOld = getRgdDataMapFromFolder(modStorageService.getEbpsTroopsAttribsPath(oldModFolderPath, racePath))
|
||||||
|
val rgdDataUnitsNew = getRgdDataMapFromFolder(modStorageService.getEbpsTroopsAttribsPath(modFolderData, racePath))
|
||||||
|
|
||||||
|
rgdDataNew.forEach { newSquad ->
|
||||||
|
val oldSquad = rgdDataOld[newSquad.key]
|
||||||
|
|
||||||
|
val baseUnitPath = ((newSquad.value?.find { it.name == "squad_loadout_ext" }?.value as List<RgdData>)
|
||||||
|
.find { it.name == "trooper_base" }?.value as List<RgdData>)
|
||||||
|
.find { it.name == "type" }?.value as String
|
||||||
|
val baseUnitName = baseUnitPath.split("\\").last().replace(".lua", ".rgd")
|
||||||
|
|
||||||
|
val newUnit = rgdDataUnitsNew[baseUnitName]
|
||||||
|
val oldUnit = rgdDataUnitsOld[baseUnitName]
|
||||||
|
if (oldSquad != newSquad.value || oldUnit != newUnit) {
|
||||||
|
totalDiff += (newSquad.key.replace(".rgd", "").uppercase() + "\n")
|
||||||
|
totalDiff += rgdService.getRgdDiff(newSquad.value, oldSquad)
|
||||||
|
if (oldSquad != newSquad.value && oldUnit != newUnit) totalDiff += "-\n"
|
||||||
|
totalDiff += rgdService.getRgdDiff(newUnit, oldUnit)
|
||||||
|
totalDiff += "---\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalDiff
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllWeaponsDiff(modFolderData: String, oldModFolderData: String? = null): String {
|
||||||
|
|
||||||
|
val oldModFolderPath =
|
||||||
|
if (oldModFolderData != null) oldModFolderData else modStorageService.pathToWanilaData
|
||||||
|
|
||||||
|
val wanillaRgdData = getRgdDataMapFromFolder(modStorageService.getWeaponAttribsPath(modStorageService.pathToWanilaData))
|
||||||
|
val oldRgdData = getRgdDataMapFromFolder(modStorageService.getWeaponAttribsPath(oldModFolderPath))
|
||||||
|
val newRgdData = getRgdDataMapFromFolder(modStorageService.getWeaponAttribsPath(modFolderData))
|
||||||
|
|
||||||
|
var totalDiff = ""
|
||||||
|
|
||||||
|
newRgdData.forEach {newWeapon ->
|
||||||
|
val oldWeapon = oldRgdData[newWeapon.key]
|
||||||
|
if(oldWeapon != newWeapon.value){
|
||||||
|
totalDiff += (newWeapon.key.replace(".rgd", "").uppercase()) + "\n"
|
||||||
|
if(oldWeapon != null){
|
||||||
|
totalDiff += rgdService.getRgdDiff(newWeapon.value, oldWeapon) + "\n"
|
||||||
|
}else{
|
||||||
|
totalDiff += "NOT EXIST IN PREV VERSION\n"
|
||||||
|
totalDiff += rgdService.getRgdDiff(newWeapon.value, wanillaRgdData[newWeapon.key]) + "\n"
|
||||||
|
}
|
||||||
|
totalDiff += "---\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalDiff
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllBuildingsDiff(modFolderData: String, oldModFolderData: String? = null): String {
|
||||||
|
|
||||||
|
var totalDiff = ""
|
||||||
|
racesWikiPaths.forEach { racePath ->
|
||||||
|
|
||||||
|
val oldModFolderPath =
|
||||||
|
if (oldModFolderData != null) oldModFolderData else modStorageService.pathToWanilaData
|
||||||
|
|
||||||
|
val oldRgdData = getRgdDataMapFromFolder(modStorageService.geBuildingAttribsPath(oldModFolderPath, racePath))
|
||||||
|
val newRgdData = getRgdDataMapFromFolder(modStorageService.geBuildingAttribsPath(modFolderData, racePath))
|
||||||
|
|
||||||
|
newRgdData.forEach { newBuilding ->
|
||||||
|
val oldBuilding = oldRgdData[newBuilding.key]
|
||||||
|
if (oldBuilding != newBuilding.value) {
|
||||||
|
totalDiff += (newBuilding.key.replace(".rgd", "").uppercase()) + "\n"
|
||||||
|
totalDiff += rgdService.getRgdDiff(newBuilding.value, oldBuilding) + "\n"
|
||||||
|
totalDiff += "---\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalDiff
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRgdDataMapFromFolder(folderPath: String): Map<String, List<RgdData>?> =
|
||||||
|
File(folderPath).walkTopDown().map {
|
||||||
|
val rgdData = if (it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")) {
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.sortedBy { it.first }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
}
|
||||||
155
src/main/kotlin/com/dowstats/service/w40k/RgdParserService.kt
Normal file
155
src/main/kotlin/com/dowstats/service/w40k/RgdParserService.kt
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.math.RoundingMode
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class RgdParserService @Autowired constructor(
|
||||||
|
) {
|
||||||
|
|
||||||
|
val log = LoggerFactory.getLogger(WeaponRgdExtractService::class.java)
|
||||||
|
|
||||||
|
val zeroByte: Byte = 0
|
||||||
|
|
||||||
|
val rgdDictionary: MutableMap<Int, String> = mutableMapOf()
|
||||||
|
|
||||||
|
init {
|
||||||
|
RgdParserService::class.java.getClassLoader().getResourceAsStream("RGD_DIC.TXT").bufferedReader(Charsets.UTF_8).lines().forEach {
|
||||||
|
if (it.isNotEmpty() && it[0] != '#') {
|
||||||
|
val kv = it.split('=')
|
||||||
|
val key = kv.first().drop(2).decodeHex().getUIntAt(0).toInt()
|
||||||
|
val value = kv.last()
|
||||||
|
rgdDictionary[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseFolderToRgdFiles(folderPath: String, ignoreFiles: List<String> = emptyList()): Map<String, List<RgdData>> =
|
||||||
|
File(folderPath).walkTopDown().map {fileOrFolder ->
|
||||||
|
val rgdData = if(fileOrFolder.isFile && !ignoreFiles.any { fileOrFolder.name.contains(it) } && fileOrFolder.name.endsWith(".rgd")){
|
||||||
|
log.info("Parce file ${fileOrFolder.name}")
|
||||||
|
parseRgdFileStream(DataInputStream(fileOrFolder.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
fileOrFolder.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.map { it.first to it.second!! }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
|
||||||
|
fun parseRgdFileStream(dataInputStream: DataInputStream): List<RgdData> {
|
||||||
|
val res = dataInputStream.use { it.readAllBytes() }
|
||||||
|
|
||||||
|
val dataInfo = res.copyOfRange(24, 32)
|
||||||
|
|
||||||
|
val chunkBytes = res.drop(32).toByteArray()
|
||||||
|
|
||||||
|
val version = chunkBytes.copyOfRange(0, 4).reversedArray().getUIntAt(0)
|
||||||
|
val chunkLength = chunkBytes.copyOfRange(4, 8).reversedArray().getUIntAt(0)
|
||||||
|
val stringLength = chunkBytes.copyOfRange(8, 12).reversedArray().getUIntAt(0).toInt()
|
||||||
|
|
||||||
|
|
||||||
|
val dataLength =
|
||||||
|
chunkBytes.copyOfRange(16 + stringLength, 16 + stringLength + 4).reversedArray().getUIntAt(0).toInt()
|
||||||
|
|
||||||
|
return handleRgdData(chunkBytes.copyOfRange(20 + stringLength, 20 + stringLength + dataLength).toList())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRgdData(byteList: List<Byte>): List<RgdData> {
|
||||||
|
|
||||||
|
var keyCount = byteList.subList(0,4).getInt()
|
||||||
|
val totalKeyCount = keyCount
|
||||||
|
var offset = 4
|
||||||
|
|
||||||
|
val rgdData = mutableListOf<RgdData>()
|
||||||
|
|
||||||
|
while (keyCount != 0) {
|
||||||
|
|
||||||
|
val hash = byteList.subList(offset, offset + 4).getInt()
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
val valueName = rgdDictionary[hash] ?: hash.toString()
|
||||||
|
|
||||||
|
val type = byteList.subList(offset, offset + 4).getInt()
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
val iOffset = byteList.subList(offset, offset + 4).getInt()
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
val dataOffset = totalKeyCount * (3 * 4) + iOffset + 4
|
||||||
|
|
||||||
|
val data: Any = when (type) {
|
||||||
|
0 -> { // Float
|
||||||
|
val entityData = byteList.subList(dataOffset, dataOffset + 4).toByteArray().reversedArray().getFloat()
|
||||||
|
entityData.toBigDecimal().setScale(2, RoundingMode.HALF_UP).toDouble()
|
||||||
|
}
|
||||||
|
1 -> { // Int
|
||||||
|
val entityData = byteList.subList(dataOffset, dataOffset + 8).getInt()
|
||||||
|
entityData
|
||||||
|
}
|
||||||
|
2 -> { // Bool
|
||||||
|
byteList.subList(dataOffset, dataOffset + 1).first() != 0.toByte()
|
||||||
|
}
|
||||||
|
3 -> { // String
|
||||||
|
var zeroByteOffset = 0
|
||||||
|
while (byteList[dataOffset + zeroByteOffset] != zeroByte) {
|
||||||
|
zeroByteOffset++
|
||||||
|
}
|
||||||
|
byteList.subList(dataOffset, dataOffset + zeroByteOffset).toByteArray().toString(Charsets.UTF_8)
|
||||||
|
}
|
||||||
|
4 -> { // String
|
||||||
|
var zeroByteOffset = 0
|
||||||
|
while (byteList[dataOffset + zeroByteOffset] != zeroByte || byteList[dataOffset + zeroByteOffset + 1] != zeroByte) {
|
||||||
|
zeroByteOffset++
|
||||||
|
}
|
||||||
|
zeroByteOffset++
|
||||||
|
byteList.subList(dataOffset, dataOffset + zeroByteOffset).toByteArray().toString(Charsets.UTF_16LE)
|
||||||
|
}
|
||||||
|
100 -> {
|
||||||
|
handleRgdData(byteList.subList(dataOffset, byteList.size))
|
||||||
|
}
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
rgdData.add(RgdData(hash, valueName, type, data))
|
||||||
|
keyCount--
|
||||||
|
}
|
||||||
|
return rgdData
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ByteArray.getFloat(): Float {
|
||||||
|
val buffer = ByteBuffer.wrap(this)
|
||||||
|
return buffer.float
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<Byte>.getInt(): Int {
|
||||||
|
var result = 0.toUInt()
|
||||||
|
var shift = 0
|
||||||
|
for (byte in this) {
|
||||||
|
result = result or ((byte.toInt().toUInt() and 0xFFu) shl shift)
|
||||||
|
shift += 8
|
||||||
|
}
|
||||||
|
return result.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
private fun String.decodeHex(): ByteArray {
|
||||||
|
check(length % 2 == 0) { "Must have an even length" }
|
||||||
|
return chunked(2)
|
||||||
|
.map { it.toInt(16).toByte() }
|
||||||
|
.toByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/main/kotlin/com/dowstats/service/w40k/RgdService.kt
Normal file
62
src/main/kotlin/com/dowstats/service/w40k/RgdService.kt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.io.PrintWriter
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class RgdService {
|
||||||
|
|
||||||
|
fun getRgdDiff(first: List<RgdData>?, second: List<RgdData>?, path: String = ""): String {
|
||||||
|
var diff = ""
|
||||||
|
first?.sortedBy { it.name }?.forEach{rgdFirst ->
|
||||||
|
val name = rgdFirst.name
|
||||||
|
val rgdSecond = second?.find{it.name == name}
|
||||||
|
if(rgdFirst != rgdSecond){
|
||||||
|
val firstValue = rgdFirst.value
|
||||||
|
when {
|
||||||
|
firstValue is List<*> && rgdSecond?.value is List<*> -> {
|
||||||
|
diff += getRgdDiff(firstValue as List<RgdData>, rgdSecond.value as List<RgdData>, "$path$name\\")
|
||||||
|
}
|
||||||
|
rgdFirst.value is List<*> -> {
|
||||||
|
diff += (path + name + " NEW" + "\n")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
diff += (path + name + ": " + rgdSecond?.value + "->" + rgdFirst.value + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val firstNames = first?.map { it.name }
|
||||||
|
second?.filterNot { firstNames?.contains(it.name) == true }?.forEach {
|
||||||
|
diff += (path + it.name + " REMOVED" + "\n")
|
||||||
|
}
|
||||||
|
return diff
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удалить как будет готова вики
|
||||||
|
fun printRgdDiff(first: List<RgdData>?, second: List<RgdData>?, path: String = "", out: PrintWriter = PrintWriter(System.out)){
|
||||||
|
first?.sortedBy { it.name }?.forEach{rgdFirst ->
|
||||||
|
val name = rgdFirst.name
|
||||||
|
val rgdSecond = second?.find{it.name == name}
|
||||||
|
if(rgdFirst != rgdSecond){
|
||||||
|
val firstValue = rgdFirst.value
|
||||||
|
when {
|
||||||
|
firstValue is List<*> && rgdSecond?.value is List<*> -> {
|
||||||
|
printRgdDiff(firstValue as List<RgdData>, rgdSecond.value as List<RgdData>, "$path$name\\", out)
|
||||||
|
}
|
||||||
|
rgdFirst.value is List<*> -> {
|
||||||
|
out.println(path + name + " NEW")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
out.println(path + name + ": " + rgdSecond?.value + "->" + rgdFirst.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val firstNames = first?.map { it.name }
|
||||||
|
second?.filterNot { firstNames?.contains(it.name) == true }?.forEach {
|
||||||
|
out.println(path + it.name + " REMOVED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,203 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.data.dto.BuildCost
|
||||||
|
import com.dowstats.data.entities.*
|
||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getIntByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class SergeantRgdExtractService @Autowired constructor(
|
||||||
|
private val modAttribPathService: ModAttribPathService,
|
||||||
|
private val iconsService: IconsService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val log = LoggerFactory.getLogger(SergeantRgdExtractService::class.java)
|
||||||
|
|
||||||
|
data class HealthAndMoraleDeathData(
|
||||||
|
|
||||||
|
val hitpoints: Double?,
|
||||||
|
val regeneration: Double?,
|
||||||
|
val moraleDeathPenalty: Double?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class WeaponsData(
|
||||||
|
val hardpoint: Int,
|
||||||
|
val hardpointOrder: Int,
|
||||||
|
val weaponFilename: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MassData(
|
||||||
|
val mass: Int?,
|
||||||
|
val upTime: Double?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SquadTexts(
|
||||||
|
val name: String?,
|
||||||
|
val description: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun extractToSergeantEntity(
|
||||||
|
fileName: String,
|
||||||
|
modDictionary: Map<Int, String>,
|
||||||
|
sergeantData: List<RgdData>,
|
||||||
|
weapons: Set<Weapon>,
|
||||||
|
modFolderData: String,
|
||||||
|
buildCost: BuildCost,
|
||||||
|
armorTypes: List<ArmorType>,
|
||||||
|
): Pair<Sergeant, Set<Weapon>> {
|
||||||
|
|
||||||
|
val sergeant = Sergeant()
|
||||||
|
|
||||||
|
sergeant.buildCostRequisition = buildCost.requisition
|
||||||
|
sergeant.buildCostPower = buildCost.power
|
||||||
|
sergeant.buildCostPopulation = buildCost.population
|
||||||
|
sergeant.buildCostFaith = buildCost.faith
|
||||||
|
sergeant.buildCostSouls = buildCost.souls
|
||||||
|
sergeant.buildCostTime = buildCost.time
|
||||||
|
|
||||||
|
sergeant.armorType = getUnitArmorType(sergeantData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type")
|
||||||
|
sergeant.armorType2 = getUnitArmorType(sergeantData, armorTypes, "type_armour_2")
|
||||||
|
|
||||||
|
val nameAndDescription = getSergeantNameAndDescription(sergeantData, modDictionary)
|
||||||
|
sergeant.name = nameAndDescription.name
|
||||||
|
sergeant.description = nameAndDescription.description
|
||||||
|
sergeant.filename = fileName
|
||||||
|
|
||||||
|
val healthData = getHealthAndMoraleDeathPenaltyData(sergeantData)
|
||||||
|
sergeant.health = healthData.hitpoints?.toInt()
|
||||||
|
sergeant.healthRegeneration = healthData.regeneration
|
||||||
|
sergeant.moraleDeathPenalty = healthData.moraleDeathPenalty?.toInt()
|
||||||
|
|
||||||
|
sergeant.sightRadius = getSightRadius(sergeantData)?.toInt()
|
||||||
|
sergeant.detectRadius = getDetectRadius(sergeantData)?.toInt()
|
||||||
|
|
||||||
|
val massData = getMassData(sergeantData)
|
||||||
|
sergeant.mass = massData.mass
|
||||||
|
sergeant.upTime = massData.upTime
|
||||||
|
|
||||||
|
val unitIcon = convertSergeantIconAndReturnPath(sergeantData, modFolderData)
|
||||||
|
sergeant.icon = unitIcon
|
||||||
|
|
||||||
|
val sergeantWeapons = getSergeantWeapons(sergeantData)?.mapNotNull { weaponData ->
|
||||||
|
weapons.find {
|
||||||
|
it.filename == weaponData.weaponFilename + ".rgd"
|
||||||
|
}.also {
|
||||||
|
it?.hardpoint = weaponData.hardpoint
|
||||||
|
it?.hardpointOrder = weaponData.hardpointOrder
|
||||||
|
}
|
||||||
|
}.orEmpty().toSet()
|
||||||
|
|
||||||
|
return Pair(sergeant, sergeantWeapons)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUnitArmorType(
|
||||||
|
unitData: List<RgdData>,
|
||||||
|
armorTypes: Iterable<ArmorType>,
|
||||||
|
armorTypeTableName: String
|
||||||
|
): ArmorType? {
|
||||||
|
val armorType = unitData.getRgdTableByName("type_ext")
|
||||||
|
?.getRgdTableByName(armorTypeTableName)
|
||||||
|
?.getStringByName("\$REF")
|
||||||
|
|
||||||
|
return armorTypes.find { it.id == armorType?.replace("type_armour\\tp_", "")?.replace(".lua", "") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSergeantNameAndDescription(sergeantData: List<RgdData>, modDictionary: Map<Int, String>): SquadTexts {
|
||||||
|
val uiInfo = sergeantData.getRgdTableByName("ui_ext")
|
||||||
|
?.getRgdTableByName("ui_info")
|
||||||
|
|
||||||
|
|
||||||
|
val nameRef = uiInfo?.getStringByName("screen_name_id")?.replace("$", "")
|
||||||
|
val name = nameRef?.let { try{modDictionary[it.toInt()]} catch (e: Exception) { null } }
|
||||||
|
|
||||||
|
val descriptionRefs = uiInfo?.getRgdTableByName("help_text_list")
|
||||||
|
?.map{(it.value as String).replace("$", "")}
|
||||||
|
?.filter{it != "0" && it != "tables\\text_table.lua" && it != ""}
|
||||||
|
?.sortedBy { try { it.toInt() } catch (e: Exception) { 0 } }
|
||||||
|
|
||||||
|
val description = try {
|
||||||
|
descriptionRefs?.map { modDictionary[it.toInt()] }?.joinToString ( "\n" )
|
||||||
|
} catch(e:Exception) {
|
||||||
|
log.warn("Error parsing ui description", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
return SquadTexts(name, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getHealthAndMoraleDeathPenaltyData(unitData: List<RgdData>): HealthAndMoraleDeathData {
|
||||||
|
val healthExt = unitData.getRgdTableByName("health_ext")
|
||||||
|
return HealthAndMoraleDeathData(
|
||||||
|
healthExt?.getDoubleByName("hitpoints"),
|
||||||
|
healthExt?.getDoubleByName("regeneration_rate"),
|
||||||
|
healthExt?.getDoubleByName("morale_death")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMassData(unitData: List<RgdData>): MassData {
|
||||||
|
val massDataRgd = unitData
|
||||||
|
.getRgdTableByName("special_attack_physics_ext")
|
||||||
|
|
||||||
|
val unitMass = massDataRgd?.getIntByName("mass")
|
||||||
|
val unitUpTime = massDataRgd?.getDoubleByName("get_up_time")
|
||||||
|
|
||||||
|
return MassData(unitMass, unitUpTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSightRadius(unitData: List<RgdData>): Double? = unitData
|
||||||
|
.getRgdTableByName("sight_ext")
|
||||||
|
?.getDoubleByName("sight_radius")
|
||||||
|
|
||||||
|
private fun getDetectRadius(unitData: List<RgdData>): Double? = unitData
|
||||||
|
.getRgdTableByName("sight_ext")
|
||||||
|
?.getDoubleByName("keen_sight_radius")
|
||||||
|
|
||||||
|
private fun getSergeantWeapons(reinforceData: List<RgdData>?): List<WeaponsData>? = reinforceData
|
||||||
|
?.getRgdTableByName("combat_ext")
|
||||||
|
?.getRgdTableByName("hardpoints")
|
||||||
|
?.mapNotNull { hardpoint ->
|
||||||
|
if (hardpoint.name.contains("hardpoint_")) {
|
||||||
|
val hardpointValue = hardpoint.name.replace("hardpoint_", "").toInt()
|
||||||
|
val hardpointTable = hardpoint.value as List<RgdData>
|
||||||
|
hardpointTable.getRgdTableByName("weapon_table")?.let {
|
||||||
|
it.mapNotNull { weapon ->
|
||||||
|
(weapon.value as? List<RgdData>)?.getStringByName("weapon")?.let {
|
||||||
|
if (it != "") {
|
||||||
|
WeaponsData(hardpointValue,
|
||||||
|
weapon.name.replace("weapon_", "").toInt(),
|
||||||
|
it.replace("weapon\\", "").replace(".lua", ""))
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
}?.flatten()
|
||||||
|
|
||||||
|
private fun convertSergeantIconAndReturnPath(sergeantData: List<RgdData>, modFolderData: String): String? {
|
||||||
|
val iconPathInMod = sergeantData
|
||||||
|
.getRgdTableByName("ui_ext")
|
||||||
|
?.getRgdTableByName("ui_info")
|
||||||
|
?.getStringByName("icon_name")
|
||||||
|
?.replace("/", File.separator)
|
||||||
|
|
||||||
|
|
||||||
|
val tgaIconPath = iconPathInMod?.let {
|
||||||
|
val modIcon = modAttribPathService.getIconPath(modFolderData, it)
|
||||||
|
if(Path.of(modIcon).exists()) modIcon else
|
||||||
|
modAttribPathService.getIconPath(modAttribPathService.pathToWanilaData, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,397 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.data.dto.BuildCost
|
||||||
|
import com.dowstats.data.dto.UnitDataToSave
|
||||||
|
import com.dowstats.data.entities.*
|
||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getIntByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class UnitRgdExtractService @Autowired constructor(
|
||||||
|
private val modAttribPathService: ModAttribPathService,
|
||||||
|
private val iconsService: IconsService,
|
||||||
|
private val sergeantRgdExtractService: SergeantRgdExtractService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val log = LoggerFactory.getLogger(UnitRgdExtractService::class.java)
|
||||||
|
|
||||||
|
data class HealthAndMoraleDeathData(
|
||||||
|
val hitpoints: Double?,
|
||||||
|
val regeneration: Double?,
|
||||||
|
val moraleDeathPenalty: Double?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MoraleData(
|
||||||
|
val max: Double?,
|
||||||
|
val broken: Double?,
|
||||||
|
val regeneration: Double?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class WeaponsData(
|
||||||
|
val hardpoint: Int,
|
||||||
|
val hardpointOrder: Int,
|
||||||
|
val weaponFilename: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SergeantData(
|
||||||
|
val filePath: String,
|
||||||
|
val cost: BuildCost,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MassData(
|
||||||
|
val mass: Int?,
|
||||||
|
val upTime: Double?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class UnitTexts(
|
||||||
|
val name: String?,
|
||||||
|
val description: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun extractToUnitEntity(
|
||||||
|
fileName: String,
|
||||||
|
modDictionary: Map<Int, String>,
|
||||||
|
squadData: List<RgdData>,
|
||||||
|
unitData: List<RgdData>,
|
||||||
|
weapons: Set<Weapon>,
|
||||||
|
race: String,
|
||||||
|
modFolderData: String,
|
||||||
|
modId: Long,
|
||||||
|
races: List<Race>,
|
||||||
|
armorTypes: List<ArmorType>,
|
||||||
|
modUnitsFull: Map<String, List<RgdData>>,
|
||||||
|
): UnitDataToSave {
|
||||||
|
|
||||||
|
val unit = DowUnit()
|
||||||
|
val unitRace = races.find { it.id == race } ?: throw Exception("Cant get race $race")
|
||||||
|
|
||||||
|
unit.race = unitRace
|
||||||
|
unit.armorType = getUnitArmorType(unitData, armorTypes, "type_armour") ?: throw Exception("Cant get armor type")
|
||||||
|
unit.armorType2 = getUnitArmorType(unitData, armorTypes, "type_armour_2")
|
||||||
|
|
||||||
|
val nameAndDescription = getUnitNameAndDescription(squadData, modDictionary)
|
||||||
|
unit.name = nameAndDescription.name
|
||||||
|
unit.description = nameAndDescription.description
|
||||||
|
unit.filename = fileName
|
||||||
|
|
||||||
|
val buildCost = getBuildCost(unitData, squadData)
|
||||||
|
unit.buildCostRequisition = buildCost.requisition
|
||||||
|
unit.buildCostPower = buildCost.power
|
||||||
|
unit.buildCostPopulation = buildCost.population
|
||||||
|
unit.buildCostFaith = buildCost.faith
|
||||||
|
unit.buildCostSouls = buildCost.souls
|
||||||
|
unit.buildCostTime = buildCost.time
|
||||||
|
|
||||||
|
val squadCap = getSquadCap(squadData)
|
||||||
|
unit.capInfantry = squadCap.first?.toInt()
|
||||||
|
unit.capSupport = squadCap.second?.toInt()
|
||||||
|
|
||||||
|
val squadSize = getSquadSize(squadData)
|
||||||
|
unit.squadStartSize = squadSize.first?.toInt()
|
||||||
|
unit.squadMaxSize = squadSize.second?.toInt()
|
||||||
|
unit.squadLimit = getSquadLimit(squadData)?.toInt()
|
||||||
|
|
||||||
|
val healthData = getHealthAndMoraleDeathPenaltyData(unitData)
|
||||||
|
unit.health = healthData.hitpoints?.toInt()
|
||||||
|
unit.healthRegeneration = healthData.regeneration
|
||||||
|
unit.moraleDeathPenalty = healthData.moraleDeathPenalty?.toInt()
|
||||||
|
|
||||||
|
val moraleData = getMoraleData(squadData)
|
||||||
|
unit.moraleMax = moraleData.max?.toInt()
|
||||||
|
unit.moraleBroken = moraleData.broken?.toInt()
|
||||||
|
unit.moraleRegeneration = moraleData.regeneration?.toInt()
|
||||||
|
|
||||||
|
unit.moveSpeed = getUnitSpeed(unitData)?.toInt()
|
||||||
|
unit.sightRadius = getSightRadius(unitData)?.toInt()
|
||||||
|
unit.detectRadius = getDetectRadius(unitData)?.toInt()
|
||||||
|
|
||||||
|
val massData = getMassData(unitData)
|
||||||
|
unit.mass = massData.mass
|
||||||
|
unit.upTime = massData.upTime
|
||||||
|
|
||||||
|
val reinforceData = getReinforceRgdData(squadData)
|
||||||
|
val reinforceCostData = reinforceData?.getRgdTableByName("cost")
|
||||||
|
unit.reinforceCostRequisition = getReinforceRequisition(reinforceCostData)
|
||||||
|
unit.reinforceCostPower = getReinforcePower(reinforceCostData)
|
||||||
|
unit.reinforceCostPopulation = getReinforcePopulation(reinforceCostData)
|
||||||
|
unit.reinforceCostFaith = getReinforceFaith(reinforceCostData)
|
||||||
|
unit.reinforceTime = getReinforceTime(reinforceData)
|
||||||
|
|
||||||
|
val sergeantsData = getSergeantsData(squadData)
|
||||||
|
|
||||||
|
val sergeantsEntities: List<Pair<Sergeant, Set<Weapon>>>? = sergeantsData.first?.mapNotNull { sergeantData ->
|
||||||
|
val sergeantFile = sergeantData.filePath.split("\\").last().replace(".lua", ".rgd")
|
||||||
|
val sergeantRgdData = modUnitsFull[sergeantFile]
|
||||||
|
if(sergeantRgdData != null){
|
||||||
|
sergeantRgdExtractService.extractToSergeantEntity(
|
||||||
|
sergeantFile,
|
||||||
|
modDictionary,
|
||||||
|
sergeantRgdData,
|
||||||
|
weapons,
|
||||||
|
modFolderData,
|
||||||
|
sergeantData.cost,
|
||||||
|
armorTypes
|
||||||
|
)
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
unit.maxSergeants = sergeantsData.second
|
||||||
|
|
||||||
|
val unitIcon = convertIconAndReturnPath(squadData, modFolderData)
|
||||||
|
unit.icon = unitIcon
|
||||||
|
|
||||||
|
val unitWeapons = getUnitWeapons(unitData)?.mapNotNull { weaponData ->
|
||||||
|
weapons.find {
|
||||||
|
it.filename == weaponData.weaponFilename + ".rgd"
|
||||||
|
}.also {
|
||||||
|
it?.hardpoint = weaponData.hardpoint
|
||||||
|
it?.hardpointOrder = weaponData.hardpointOrder
|
||||||
|
}
|
||||||
|
}.orEmpty().toSet()
|
||||||
|
|
||||||
|
unit.modId = modId
|
||||||
|
|
||||||
|
return UnitDataToSave(unit, unitWeapons, sergeantsEntities)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUnitRgdFileNameFromSquadData(squadRgdData: List<RgdData>): String? {
|
||||||
|
val baseUnitPath = squadRgdData.getRgdTableByName("squad_loadout_ext")
|
||||||
|
?.getRgdTableByName("trooper_base")
|
||||||
|
?.getStringByName("type")
|
||||||
|
return baseUnitPath?.split("\\")?.last()?.replace(".lua", ".rgd")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUnitArmorType(
|
||||||
|
unitData: List<RgdData>,
|
||||||
|
armorTypes: Iterable<ArmorType>,
|
||||||
|
armorTypeTableName: String
|
||||||
|
): ArmorType? {
|
||||||
|
val armorType = unitData.getRgdTableByName("type_ext")
|
||||||
|
?.getRgdTableByName(armorTypeTableName)
|
||||||
|
?.getStringByName("\$REF")
|
||||||
|
|
||||||
|
return armorTypes.find { it.id == armorType?.replace("type_armour\\tp_", "")?.replace(".lua", "") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBuildCost(unitData: List<RgdData>, squadData: List<RgdData>): BuildCost {
|
||||||
|
|
||||||
|
val cost = unitData.getRgdTableByName("cost_ext")
|
||||||
|
?.getRgdTableByName("time_cost")
|
||||||
|
|
||||||
|
val costResources = cost
|
||||||
|
?.getRgdTableByName("cost")
|
||||||
|
|
||||||
|
val minSquadSize = squadData.getRgdTableByName("squad_loadout_ext")
|
||||||
|
?.getDoubleByName("unit_min")?.toInt()
|
||||||
|
|
||||||
|
fun getCost(cost: Double?) =
|
||||||
|
cost?.let { it * (minSquadSize ?: 1) }
|
||||||
|
|
||||||
|
return BuildCost(
|
||||||
|
getCost(costResources?.getDoubleByName("requisition")),
|
||||||
|
getCost(costResources?.getDoubleByName("power")),
|
||||||
|
getCost(costResources?.getDoubleByName("population")),
|
||||||
|
getCost(costResources?.getDoubleByName("faith")),
|
||||||
|
getCost(costResources?.getDoubleByName("souls")),
|
||||||
|
getCost(cost?.getDoubleByName("time_seconds"))?.toInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUnitNameAndDescription(squadData: List<RgdData>, modDictionary: Map<Int, String>): UnitTexts {
|
||||||
|
val uiInfo = squadData.getRgdTableByName("squad_ui_ext")
|
||||||
|
?.getRgdTableByName("ui_info")
|
||||||
|
|
||||||
|
|
||||||
|
val nameRef = uiInfo?.getStringByName("screen_name_id")?.replace("$", "")
|
||||||
|
val name = nameRef?.let { try{modDictionary[it.toInt()]} catch (e: Exception) { null } }
|
||||||
|
|
||||||
|
val descriptionRefs = uiInfo?.getRgdTableByName("help_text_list")
|
||||||
|
?.map{(it.value as String).replace("$", "")}
|
||||||
|
?.filter{it != "0" && it != "tables\\text_table.lua" && it != ""}
|
||||||
|
?.sortedBy { try { it.toInt() } catch (e: Exception) { 0 } }
|
||||||
|
|
||||||
|
val description = try {
|
||||||
|
descriptionRefs?.map { modDictionary[it.toInt()] }?.joinToString ( "\n" )
|
||||||
|
} catch(e:Exception) {
|
||||||
|
log.warn("Error parsing ui description", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnitTexts(name, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getSquadCap(squadData: List<RgdData>): Pair<Double?, Double?> {
|
||||||
|
|
||||||
|
val squadCap = squadData.getRgdTableByName("squad_cap_ext")
|
||||||
|
|
||||||
|
return Pair(
|
||||||
|
squadCap?.getDoubleByName("squad_cap_usage"),
|
||||||
|
squadCap?.getDoubleByName("support_cap_usage")
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSquadSize(squadData: List<RgdData>): Pair<Double?, Double?> {
|
||||||
|
|
||||||
|
val squadSize = squadData.getRgdTableByName("squad_loadout_ext")
|
||||||
|
|
||||||
|
val unitMin = squadSize?.getDoubleByName("unit_min")
|
||||||
|
val unitMax = squadSize?.getDoubleByName("unit_max")
|
||||||
|
|
||||||
|
return Pair(unitMin, unitMax)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSquadLimit(squadData: List<RgdData>): Double? {
|
||||||
|
val requirements = squadData.getRgdTableByName("squad_requirement_ext")
|
||||||
|
?.getRgdTableByName("requirements")
|
||||||
|
|
||||||
|
return requirements?.map {
|
||||||
|
if (it.type == 100) {
|
||||||
|
val reqirementData = it.value as List<RgdData>
|
||||||
|
reqirementData.find { it.name == "max_squad_cap" || it.name == "max_cumulative_squad_cap" }?.value as Double?
|
||||||
|
} else null
|
||||||
|
}?.filterNotNull()?.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getHealthAndMoraleDeathPenaltyData(unitData: List<RgdData>): HealthAndMoraleDeathData {
|
||||||
|
val healthExt = unitData.getRgdTableByName("health_ext")
|
||||||
|
return HealthAndMoraleDeathData(
|
||||||
|
healthExt?.getDoubleByName("hitpoints"),
|
||||||
|
healthExt?.getDoubleByName("regeneration_rate"),
|
||||||
|
healthExt?.getDoubleByName("morale_death")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMoraleData(squadData: List<RgdData>): MoraleData {
|
||||||
|
val moraleData = squadData.getRgdTableByName("squad_morale_ext")
|
||||||
|
val max = moraleData?.getDoubleByName("max")
|
||||||
|
val broken = moraleData?.getDoubleByName("broken_min_morale")
|
||||||
|
val regeneration = moraleData?.getDoubleByName("rate_per_second")
|
||||||
|
return MoraleData(max, broken, regeneration)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMassData(unitData: List<RgdData>): MassData {
|
||||||
|
val massDataRgd = unitData
|
||||||
|
.getRgdTableByName("special_attack_physics_ext")
|
||||||
|
|
||||||
|
val unitMass = massDataRgd?.getIntByName("mass")
|
||||||
|
val unitUpTime = massDataRgd?.getDoubleByName("get_up_time")
|
||||||
|
|
||||||
|
return MassData(unitMass, unitUpTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUnitSpeed(unitData: List<RgdData>): Double? = unitData
|
||||||
|
.getRgdTableByName("moving_ext")
|
||||||
|
?.getDoubleByName("speed_max")
|
||||||
|
|
||||||
|
private fun getSightRadius(unitData: List<RgdData>): Double? = unitData
|
||||||
|
.getRgdTableByName("sight_ext")
|
||||||
|
?.getDoubleByName("sight_radius")
|
||||||
|
|
||||||
|
private fun getDetectRadius(unitData: List<RgdData>): Double? = unitData
|
||||||
|
.getRgdTableByName("sight_ext")
|
||||||
|
?.getDoubleByName("keen_sight_radius")
|
||||||
|
|
||||||
|
private fun getReinforceRgdData(squadData: List<RgdData>): List<RgdData>? = squadData
|
||||||
|
.getRgdTableByName("squad_reinforce_ext")
|
||||||
|
?.getRgdTableByName("cost")
|
||||||
|
|
||||||
|
private fun getReinforceRequisition(reinforceData: List<RgdData>?): Int? = reinforceData
|
||||||
|
?.getIntByName("requisition")
|
||||||
|
|
||||||
|
private fun getReinforcePower(reinforceData: List<RgdData>?): Int? = reinforceData
|
||||||
|
?.getIntByName("power")
|
||||||
|
|
||||||
|
private fun getReinforcePopulation(reinforceData: List<RgdData>?): Int? = reinforceData
|
||||||
|
?.getIntByName("population")
|
||||||
|
|
||||||
|
private fun getReinforceFaith(reinforceData: List<RgdData>?): Int? = reinforceData
|
||||||
|
?.getIntByName("faith")
|
||||||
|
|
||||||
|
private fun getReinforceTime(reinforceData: List<RgdData>?): Int? = reinforceData
|
||||||
|
?.getIntByName("time_seconds")
|
||||||
|
|
||||||
|
private fun getUnitWeapons(reinforceData: List<RgdData>?): List<WeaponsData>? = reinforceData
|
||||||
|
?.getRgdTableByName("combat_ext")
|
||||||
|
?.getRgdTableByName("hardpoints")
|
||||||
|
?.mapNotNull { hardpoint ->
|
||||||
|
if (hardpoint.name.contains("hardpoint_")) {
|
||||||
|
val hardpointValue = hardpoint.name.replace("hardpoint_", "").toInt()
|
||||||
|
val hardpointTable = hardpoint.value as List<RgdData>
|
||||||
|
hardpointTable.getRgdTableByName("weapon_table")?.let {
|
||||||
|
it.mapNotNull { weapon ->
|
||||||
|
(weapon.value as? List<RgdData>)?.getStringByName("weapon")?.let {
|
||||||
|
if (it != "") {
|
||||||
|
WeaponsData(hardpointValue,
|
||||||
|
weapon.name.replace("weapon_", "").toInt(),
|
||||||
|
it.replace("weapon\\", "").replace(".lua", ""))
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
}?.flatten()
|
||||||
|
|
||||||
|
private fun getSergeantsData(squadData: List<RgdData>?): Pair<List<SergeantData>?, Int?> {
|
||||||
|
|
||||||
|
val squadTable = squadData
|
||||||
|
?.getRgdTableByName("squad_leader_ext")
|
||||||
|
|
||||||
|
val maxSergeants = squadTable?.getIntByName("max_leaders")
|
||||||
|
|
||||||
|
val sergeantsData = squadTable?.mapNotNull { sergeantData ->
|
||||||
|
if (sergeantData.name.contains("leader_")) {
|
||||||
|
val sergeantRgdTable = sergeantData.value as List<RgdData>
|
||||||
|
|
||||||
|
val sergeantLeaderFilePath = sergeantRgdTable.getRgdTableByName("leader")?.getStringByName("type")
|
||||||
|
|
||||||
|
if(sergeantLeaderFilePath == null || sergeantLeaderFilePath == "") null else {
|
||||||
|
|
||||||
|
val cost = sergeantRgdTable.getRgdTableByName("cost_time")
|
||||||
|
val costResources = cost?.getRgdTableByName("cost")
|
||||||
|
|
||||||
|
SergeantData(sergeantLeaderFilePath,
|
||||||
|
BuildCost(
|
||||||
|
costResources?.getDoubleByName("requisition"),
|
||||||
|
costResources?.getDoubleByName("power"),
|
||||||
|
costResources?.getDoubleByName("population"),
|
||||||
|
costResources?.getDoubleByName("faith"),
|
||||||
|
costResources?.getDoubleByName("souls"),
|
||||||
|
cost?.getIntByName("time_seconds"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(sergeantsData, maxSergeants)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertIconAndReturnPath(squadData: List<RgdData>, modFolderData: String): String? {
|
||||||
|
val iconPathInMod = squadData
|
||||||
|
.getRgdTableByName("squad_ui_ext")
|
||||||
|
?.getRgdTableByName("ui_info")
|
||||||
|
?.getStringByName("icon_name")
|
||||||
|
?.replace("/", File.separator)
|
||||||
|
|
||||||
|
|
||||||
|
val tgaIconPath = iconPathInMod?.let {
|
||||||
|
val modIcon = modAttribPathService.getIconPath(modFolderData, it)
|
||||||
|
if(Path.of(modIcon).exists()) modIcon else
|
||||||
|
modAttribPathService.getIconPath(modAttribPathService.pathToWanilaData, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPathInMod, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,169 @@
|
|||||||
|
package com.dowstats.service.w40k
|
||||||
|
|
||||||
|
import com.dowstats.data.entities.ArmorType
|
||||||
|
import com.dowstats.data.entities.Weapon
|
||||||
|
import com.dowstats.data.entities.WeaponArmorPiercing
|
||||||
|
import com.dowstats.data.repositories.ArmorTypeRepository
|
||||||
|
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.getIntByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class WeaponRgdExtractService @Autowired constructor(
|
||||||
|
private val armorTypeRepository: ArmorTypeRepository,
|
||||||
|
private val iconsService: IconsService,
|
||||||
|
private val modAttribPathService: ModAttribPathService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val log = LoggerFactory.getLogger(WeaponRgdExtractService::class.java)
|
||||||
|
|
||||||
|
data class BuildCost(
|
||||||
|
val requisition: Int?,
|
||||||
|
val power: Int?,
|
||||||
|
val seconds: Int?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ArmourDamage(
|
||||||
|
val minDamage: Double,
|
||||||
|
val maxDamage: Double,
|
||||||
|
val minDamageValue: Double,
|
||||||
|
val moraleDamage: Double,
|
||||||
|
val armourPiercing: List<WeaponArmorPiercing>,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class WeaponUiInfo(
|
||||||
|
val name: String?,
|
||||||
|
val description: String?,
|
||||||
|
val iconPath: String?,
|
||||||
|
val haveEquipButton: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
fun extractToWeaponEntity(weaponFileName: String, weaponData: List<RgdData>, modId: Long, modFolderData: String, modDictionary: Map<Int, String>): Weapon? {
|
||||||
|
|
||||||
|
val armorTypes = armorTypeRepository.findAll().toSet()
|
||||||
|
|
||||||
|
val weapon = Weapon()
|
||||||
|
|
||||||
|
weapon.filename = weaponFileName
|
||||||
|
val weaponUiData = getWeaponNameAndDescription(weaponData, modDictionary, modFolderData)
|
||||||
|
weapon.name = weaponUiData.name
|
||||||
|
weapon.icon = weaponUiData.iconPath
|
||||||
|
weapon.description = weaponUiData.description
|
||||||
|
weapon.haveEquipButton = weaponUiData.haveEquipButton
|
||||||
|
weapon.hotkeyName = weaponData.getStringByName("ui_hotkey_name")
|
||||||
|
|
||||||
|
val cost = getCost(weaponData)
|
||||||
|
weapon.costRequisition = cost.requisition ?: 0
|
||||||
|
weapon.costPower = cost.power ?: 0
|
||||||
|
weapon.costTimeSeconds = cost.seconds ?: 0
|
||||||
|
weapon.accuracy = weaponData.getDoubleByName("accuracy")
|
||||||
|
weapon.accuracyReductionMoving = weaponData.getDoubleByName("accuracy_reduction_when_moving")
|
||||||
|
weapon.maxRange = weaponData.getDoubleByName("max_range")
|
||||||
|
weapon.reloadTime = weaponData.getDoubleByName("reload_time")
|
||||||
|
weapon.setupTime = weaponData.getDoubleByName("setup_time")
|
||||||
|
weapon.isMeleeWeapon = weaponData.getBooleanByName("melee_weapon") ?: false
|
||||||
|
weapon.canAttackAir = weaponData.getBooleanByName("can_attack_air_units") ?: false
|
||||||
|
weapon.canAttackGround = weaponData.getBooleanByName("can_attack_ground_units") ?: false
|
||||||
|
|
||||||
|
|
||||||
|
val armourDamage = getArmourDamage(weaponData, armorTypes, weapon)
|
||||||
|
weapon.minDamageValue = armourDamage.minDamageValue
|
||||||
|
weapon.minDamage = armourDamage.minDamage
|
||||||
|
weapon.maxDamage = armourDamage.maxDamage
|
||||||
|
weapon.moraleDamage = armourDamage.moraleDamage
|
||||||
|
weapon.weaponPiercings = armourDamage.armourPiercing
|
||||||
|
weapon.modId = modId
|
||||||
|
|
||||||
|
return if(weapon.minDamage == 0.0 && weapon.maxDamage == 0.0 && weapon.moraleDamage == 0.0){
|
||||||
|
null
|
||||||
|
} else weapon
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCost(weaponData: List<RgdData>): BuildCost {
|
||||||
|
val costTable = weaponData.getRgdTableByName("cost")
|
||||||
|
val costTime = costTable?.getIntByName("time_seconds")
|
||||||
|
val costCost = costTable?.getRgdTableByName("cost")
|
||||||
|
val power = costCost?.getIntByName("power")
|
||||||
|
val requisition = costCost?.getIntByName("requisition")
|
||||||
|
return BuildCost(requisition, power, costTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getArmourDamage(weaponData: List<RgdData>, armorTypes: Set<ArmorType>, thisWeapon: Weapon): ArmourDamage {
|
||||||
|
val armourDamage = weaponData.getRgdTableByName("area_effect")
|
||||||
|
?.getRgdTableByName("weapon_damage")
|
||||||
|
?.getRgdTableByName("armour_damage")!!
|
||||||
|
val minDamage = armourDamage.getDoubleByName("min_damage")!!
|
||||||
|
val maxDamage = armourDamage.getDoubleByName("max_damage")!!
|
||||||
|
val minDamageValue = armourDamage.getDoubleByName("min_damage_value")!!
|
||||||
|
val moraleDamage = armourDamage.getDoubleByName("morale_damage")!!
|
||||||
|
|
||||||
|
val defaultArmourPiercing = armourDamage.getDoubleByName("armour_piercing")
|
||||||
|
val weaponDmgMap: Map<String, Double> =
|
||||||
|
armourDamage.getRgdTableByName("armour_piercing_types")!!.mapNotNull { armour_piercing ->
|
||||||
|
if (armour_piercing.name.contains("entry")) {
|
||||||
|
val entry = armour_piercing.value as List<RgdData>
|
||||||
|
val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","")
|
||||||
|
val dmgValue = entry.getDoubleByName("armour_piercing_value")
|
||||||
|
dmgType!! to dmgValue!!
|
||||||
|
} else null
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
val armoursPiercing = armorTypes.map {
|
||||||
|
val weaponArmourPiercing = WeaponArmorPiercing()
|
||||||
|
weaponArmourPiercing.weapon = thisWeapon
|
||||||
|
weaponArmourPiercing.armorType = it
|
||||||
|
weaponArmourPiercing.piercingValue = (weaponDmgMap[it.id] ?: defaultArmourPiercing)?.toBigDecimal()
|
||||||
|
weaponArmourPiercing
|
||||||
|
}
|
||||||
|
|
||||||
|
return ArmourDamage(minDamage, maxDamage, minDamageValue, moraleDamage, armoursPiercing)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getWeaponNameAndDescription(weaponData: List<RgdData>, modDictionary: Map<Int, String>, modFolderData: String): WeaponUiInfo {
|
||||||
|
val weaponUiInfo = weaponData.getRgdTableByName("ui_info")
|
||||||
|
|
||||||
|
val nameRef = weaponUiInfo?.getStringByName("screen_name_id")?.replace("$", "")
|
||||||
|
val name = nameRef?.let { try { modDictionary[it.toInt()] } catch (e: Exception) { null } }
|
||||||
|
|
||||||
|
val descriptionRefs = weaponUiInfo?.getRgdTableByName("help_text_list")
|
||||||
|
?.map{(it.value as String).replace("$", "")}
|
||||||
|
?.filter{it != "0" && it != "tables\\text_table.lua" && it != ""}
|
||||||
|
?.sortedBy { try { it.toInt() } catch (e: Exception) { 0 } }
|
||||||
|
|
||||||
|
val description = try {
|
||||||
|
descriptionRefs?.map { try { modDictionary[it.toInt()] } catch (e: Exception) { "" } }?.joinToString ( "\n" )
|
||||||
|
} catch(e:Exception) {
|
||||||
|
log.warn("Error parsing ui description", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val icon = try {
|
||||||
|
val iconPath = weaponUiInfo?.getStringByName("icon_name")
|
||||||
|
?.replace("/", File.separator)
|
||||||
|
|
||||||
|
val tgaIconPath = iconPath?.let {
|
||||||
|
val modIcon = modAttribPathService.getIconPath(modFolderData, it)
|
||||||
|
if(Path.of(modIcon).exists()) modIcon else
|
||||||
|
modAttribPathService.getIconPath(modAttribPathService.pathToWanilaData, it)
|
||||||
|
}
|
||||||
|
tgaIconPath?.let { iconsService.convertTgaToJpegImage(iconPath, it) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.error("Error parsing ui icon path", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val haveUpgradeButton = weaponUiInfo?.getBooleanByName("no_button")?.let { !it } ?: false
|
||||||
|
|
||||||
|
return WeaponUiInfo(name, description, icon, haveUpgradeButton)
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/main/resources/DXP2.ucs
Normal file
BIN
src/main/resources/DXP2.ucs
Normal file
Binary file not shown.
6536
src/main/resources/RGD_DIC.TXT
Normal file
6536
src/main/resources/RGD_DIC.TXT
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/main/resources/W40k.ucs
Normal file
BIN
src/main/resources/W40k.ucs
Normal file
Binary file not shown.
268
src/main/resources/db/0.0.1/data/armor_types.json
Normal file
268
src/main/resources/db/0.0.1/data/armor_types.json
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Fill races table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "infantry_low"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Infantry Low"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "infantry_med"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Infantry Medium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "infantry_high"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Infantry High"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "infantry_heavy_med"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Infantry Heavy Medium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "infantry_heavy_high"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Infantry Heavy High"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "commander"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Commander"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "vehicle_low"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Vehicle Low"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "vehicle_med"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Vehicle Medium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "vehicle_high"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Vehicle High"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "air_med"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Air"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "building_low"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Building Low"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "building_med"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Building Medium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "building_high"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Building High"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "monster_med"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Demon Medium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "monster_high"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Demon High"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
344
src/main/resources/db/0.0.1/data/races.json
Normal file
344
src/main/resources/db/0.0.1/data/races.json
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Fill races table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "space_marines"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Space Marines"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "chaos"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Chaos Marines"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "orks"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Orks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "eldar"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Eldar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "guard"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Imperial Guard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "necrons"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Necrons"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "tau"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Tau Empire"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "sisters"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Sisters of Battle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "steel_legion"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Steel legion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "witch_hunters"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Witch hunters"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "ynnari"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Ynnari"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "ynnari"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Ynnari"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "ynnari"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Ynnari"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "ynnari"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Ynnari"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "ynnari"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Ynnari"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "ynnari"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Ynnari"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "ynnari"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Ynnari"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "renegade_guard"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Renegade guard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"insert": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"value": "dark_eldar"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"value": "Dark Eldar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
38
src/main/resources/db/0.0.1/schema/armor_types.json
Normal file
38
src/main/resources/db/0.0.1/schema/armor_types.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add armor types table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "armor_types",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "varchar(50)",
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
73
src/main/resources/db/0.0.1/schema/mods.json
Normal file
73
src/main/resources/db/0.0.1/schema/mods.json
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add mods table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "mods",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"autoIncrement": true,
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"unique": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "version",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"constraints": {
|
||||||
|
"unique": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "technical_name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "is_beta",
|
||||||
|
"type": "boolean",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addUniqueConstraint": {
|
||||||
|
"columnNames": "technical_name, version",
|
||||||
|
"constraintName": "uc_mods_technical_name_version",
|
||||||
|
"tableName": "mods"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
38
src/main/resources/db/0.0.1/schema/races.json
Normal file
38
src/main/resources/db/0.0.1/schema/races.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add races table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "races",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "varchar(50)",
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
159
src/main/resources/db/0.0.1/schema/sergants.json
Normal file
159
src/main/resources/db/0.0.1/schema/sergants.json
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add sergeants table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "sergeants",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"autoIncrement": true,
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "filename",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(5000)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_requisition",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_power",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_population",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_faith",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_souls",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_time",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "health",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "health_regeneration",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "armour_type_id",
|
||||||
|
"type": "varchar(50)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable":false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "armour_type_2_id",
|
||||||
|
"type": "varchar(50)"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "morale_death_penalty",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "mass",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "up_time",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "sight_radius",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "detect_radius",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "icon",
|
||||||
|
"type": "varchar(128)"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "unit_id",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "armour_type_id",
|
||||||
|
"baseTableName": "sergeants",
|
||||||
|
"constraintName": "fk_sergeants_armor_types",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "armor_types"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "unit_id",
|
||||||
|
"baseTableName": "sergeants",
|
||||||
|
"constraintName": "fk_sergeants_units",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "units"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
82
src/main/resources/db/0.0.1/schema/sergants_weapons.json
Normal file
82
src/main/resources/db/0.0.1/schema/sergants_weapons.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add sergeants_weapons table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "sergeants_weapons",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "sergeant_id",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "weapon_id",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "hardpoint",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "hardpoint_order",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addUniqueConstraint": {
|
||||||
|
"columnNames": "sergeant_id, weapon_id",
|
||||||
|
"constraintName": "uc_sergeant_weapon",
|
||||||
|
"tableName": "sergeants_weapons"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "sergeant_id",
|
||||||
|
"baseTableName": "sergeants_weapons",
|
||||||
|
"constraintName": "fk_sergeants_sergeants_weapons",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "sergeants"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "weapon_id",
|
||||||
|
"baseTableName": "sergeants_weapons",
|
||||||
|
"constraintName": "fk_weapons_sergeants_weapons",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "weapons"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
262
src/main/resources/db/0.0.1/schema/units.json
Normal file
262
src/main/resources/db/0.0.1/schema/units.json
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add units table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "units",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"autoIncrement": true,
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "filename",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(5000)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_requisition",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_power",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_population",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_faith",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_souls",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "build_cost_time",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "cap_infantry",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "cap_support",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "squad_start_size",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "squad_max_size",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "squad_limit",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "health",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "health_regeneration",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "race_id",
|
||||||
|
"type": "varchar(50)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable":false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "armour_type_id",
|
||||||
|
"type": "varchar(50)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable":false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "armour_type_2_id",
|
||||||
|
"type": "varchar(50)"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "morale_max",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "morale_broken",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "morale_regeneration",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "morale_death_penalty",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "mass",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "up_time",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "move_speed",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "sight_radius",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "detect_radius",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "reinforce_cost_requisition",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "reinforce_cost_power",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "reinforce_cost_population",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "reinforce_cost_faith",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "reinforce_time",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "max_sergeants",
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "mod_id",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "icon",
|
||||||
|
"type": "varchar(128)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "hotkey_name",
|
||||||
|
"type": "varchar(64)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "race_id",
|
||||||
|
"baseTableName": "units",
|
||||||
|
"constraintName": "fk_units_races",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "races"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "armour_type_id",
|
||||||
|
"baseTableName": "units",
|
||||||
|
"constraintName": "fk_units_armor_types",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "armor_types"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "mod_id",
|
||||||
|
"baseTableName": "units",
|
||||||
|
"constraintName": "fk_units_mods",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "mods"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
82
src/main/resources/db/0.0.1/schema/units_weapons.json
Normal file
82
src/main/resources/db/0.0.1/schema/units_weapons.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add units_weapons table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "units_weapons",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "unit_id",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "weapon_id",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "hardpoint",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "hardpoint_order",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addUniqueConstraint": {
|
||||||
|
"columnNames": "unit_id, weapon_id",
|
||||||
|
"constraintName": "uc_unit_weapon",
|
||||||
|
"tableName": "units_weapons"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "unit_id",
|
||||||
|
"baseTableName": "units_weapons",
|
||||||
|
"constraintName": "fk_units_units_weapons",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "units"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"baseColumnNames": "weapon_id",
|
||||||
|
"baseTableName": "units_weapons",
|
||||||
|
"constraintName": "fk_weapons_units_weapons",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "weapons"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
57
src/main/resources/db/0.0.1/schema/users.json
Normal file
57
src/main/resources/db/0.0.1/schema/users.json
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add users table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "users",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"autoIncrement": true,
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "steam_id",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "avatar_url",
|
||||||
|
"type": "varchar(127)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
199
src/main/resources/db/0.0.1/schema/weapons.json
Normal file
199
src/main/resources/db/0.0.1/schema/weapons.json
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add weapons table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "weapons",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"autoIncrement": true,
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "filename",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(5000)"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "cost_power",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "cost_requisition",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "cost_time_seconds",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "accuracy",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "reload_time",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "setup_time",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "accuracy_reduction_moving",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "max_range",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "min_damage",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "min_damage_value",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "max_damage",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "morale_damage",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "is_melee_weapon",
|
||||||
|
"type": "boolean",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "can_attack_air",
|
||||||
|
"type": "boolean",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "can_attack_ground",
|
||||||
|
"type": "boolean",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "have_equip_button",
|
||||||
|
"type": "boolean",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "icon",
|
||||||
|
"type": "varchar(128)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "hotkey_name",
|
||||||
|
"type": "varchar(64)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "mod_id",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"changeSet": {
|
||||||
|
"id": "Add weapons_armors_piercing table",
|
||||||
|
"author": "anibus",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"createTable": {
|
||||||
|
"tableName": "weapons_armors_piercing",
|
||||||
|
"columns": [{
|
||||||
|
"column": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"autoIncrement": true,
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "weapon_id",
|
||||||
|
"type": "int",
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": {
|
||||||
|
"name": "armor_type_id",
|
||||||
|
"type": "varchar(50)",
|
||||||
|
"constraints": {
|
||||||
|
"primaryKey": true,
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"column": {
|
||||||
|
"name": "piercing_value",
|
||||||
|
"type": "number",
|
||||||
|
"constraints": {
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"constraintName": "fk_weapons_armors_damage_weapons",
|
||||||
|
"baseColumnNames": "weapon_id",
|
||||||
|
"baseTableName": "weapons_armors_damage",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "weapons"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"addForeignKeyConstraint":
|
||||||
|
{
|
||||||
|
"constraintName": "fk_armor_types_armors_damage_weapons",
|
||||||
|
"baseColumnNames": "armor_type_id",
|
||||||
|
"baseTableName": "weapons_armors_damage",
|
||||||
|
"referencedColumnNames": "id",
|
||||||
|
"referencedTableName": "armor_types"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
54
src/main/resources/db/changelog-master.json
Normal file
54
src/main/resources/db/changelog-master.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"databaseChangeLog": [
|
||||||
|
{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/mods.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/races.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/armor_types.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/units.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/sergants.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/weapons.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/units_weapons.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/sergants_weapons.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/weapons_armors_damage.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/schema/users.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/data/armor_types.json"
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"include": {
|
||||||
|
"file": "db/0.0.1/data/races.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import com.dowstats.configuration.StorageConfig
|
||||||
|
import com.dowstats.service.w40k.IconsService
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class InconConvertServiceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun convertIcon() {
|
||||||
|
|
||||||
|
println(mapOf(1 to 3, 2 to 4) + mapOf(3 to 5, 5 to 6))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,380 @@
|
|||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
|
||||||
|
import com.dowstats.service.w40k.RgdParserService
|
||||||
|
import com.dowstats.service.w40k.RgdService
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
class ModParserServiceTest {
|
||||||
|
|
||||||
|
val zeroByte: Byte = 0
|
||||||
|
|
||||||
|
val rgdDictionary: MutableMap<Int, String> = mutableMapOf()
|
||||||
|
|
||||||
|
val rgdParseService = RgdParserService()
|
||||||
|
|
||||||
|
val rgdService = RgdService()
|
||||||
|
|
||||||
|
val prevVersion = "Dowstats_Balance_Mod_1_0_97"
|
||||||
|
val currentVersion = "Dowstats_Balance_Mod_1_0_98"
|
||||||
|
|
||||||
|
val spaceMarinesPath = "space_marines"
|
||||||
|
val chaosPath = "chaos"
|
||||||
|
val eldarPath = "eldar"
|
||||||
|
val orksPath = "orks"
|
||||||
|
val guardPath = "guard"
|
||||||
|
val necronPath = "necrons"
|
||||||
|
val tauPath = "tau"
|
||||||
|
val sistersPath = "sisters"
|
||||||
|
val darkPath = "dark_eldar"
|
||||||
|
|
||||||
|
val racesWikiPaths = setOf(spaceMarinesPath, chaosPath, eldarPath, orksPath, guardPath, necronPath, tauPath, sistersPath)
|
||||||
|
val racesWikiPaths2 = setOf("black_templars")
|
||||||
|
|
||||||
|
fun readDictionary() {
|
||||||
|
File("src/test/resources/RGD_DIC.TXT").forEachLine {
|
||||||
|
if(it.isNotEmpty() && it[0] != '#'){
|
||||||
|
val kv = it.split('=')
|
||||||
|
val key = kv.first().drop(2).decodeHex().getUIntAt(0).toInt()
|
||||||
|
val value = kv.last()
|
||||||
|
rgdDictionary[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUnitsAndSquadsDiff(out: PrintWriter) {
|
||||||
|
|
||||||
|
racesWikiPaths.forEach{racePath ->
|
||||||
|
val rgdDataOld = File("src/main/resources/static/mods/$prevVersion/attrib/sbps/races/$racePath").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val rgdDataNew = File("src/main/resources/static/mods/$currentVersion/attrib/sbps/races/$racePath").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
}else{
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.sortedBy { it.first }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val rgdDataUnitsOld = File("src/main/resources/static/mods/$prevVersion/attrib/ebps/races/$racePath/troops").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
}else{
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val rgdDataUnitsNew = File("src/main/resources/static/mods/$currentVersion/attrib/ebps/races/$racePath/troops").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
}else{
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
/*classicRgdData.mapValues { findStrategicPointCaptureRate( it.value?: listOf()) }.entries
|
||||||
|
.sortedByDescending { it.value }
|
||||||
|
.filter { it.value != null }
|
||||||
|
.filter { !it.key.contains("_sp") }
|
||||||
|
.forEach{println(it.key + " -> " + it.value )}*/
|
||||||
|
|
||||||
|
val changedSquadNames = rgdDataNew.map { }
|
||||||
|
|
||||||
|
rgdDataNew.forEach { newSquad ->
|
||||||
|
val oldSquad = rgdDataOld[newSquad.key]
|
||||||
|
|
||||||
|
val baseUnitPath = ((newSquad.value?.find { it.name == "squad_loadout_ext" }?.value as List<RgdData>)
|
||||||
|
.find { it.name == "trooper_base" }?.value as List<RgdData>)
|
||||||
|
.find { it.name == "type" }?.value as String
|
||||||
|
val baseUnitName = baseUnitPath.split("\\").last().replace(".lua", ".rgd")
|
||||||
|
|
||||||
|
val newUnit = rgdDataUnitsNew[baseUnitName]
|
||||||
|
val oldUnit = rgdDataUnitsOld[baseUnitName]
|
||||||
|
if(oldSquad != newSquad.value || oldUnit != newUnit){
|
||||||
|
out.println(newSquad.key.replace(".rgd", "").uppercase())
|
||||||
|
rgdService.printRgdDiff(newSquad.value, oldSquad, out = out)
|
||||||
|
if(oldSquad != newSquad.value && oldUnit != newUnit) out.println("-")
|
||||||
|
rgdService.printRgdDiff(newUnit, oldUnit, out = out)
|
||||||
|
out.println("---")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UnitAsSquad(val squadData: List<RgdData>?,val uintData: List<RgdData>?)
|
||||||
|
|
||||||
|
fun getAllWeaponsDiff(out: PrintWriter) {
|
||||||
|
|
||||||
|
val wanillaRgdData = File("src/main/resources/static/mods/wanila/attrib/weapon").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val classicRgdData = File("src/main/resources/static/mods/$prevVersion/attrib/weapon").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val newRgdData = File("src/main/resources/static/mods/$currentVersion/attrib/weapon").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.sortedBy { it.first }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
|
||||||
|
newRgdData.forEach {newWeapon ->
|
||||||
|
val oldWeapon = classicRgdData[newWeapon.key]
|
||||||
|
if(oldWeapon != newWeapon.value){
|
||||||
|
out.println(newWeapon.key.replace(".rgd", "").uppercase())
|
||||||
|
if(oldWeapon != null){
|
||||||
|
rgdService.printRgdDiff(newWeapon.value, oldWeapon, out = out)
|
||||||
|
}else{
|
||||||
|
out.println("NOT EXIST IN PREV VERSION")
|
||||||
|
rgdService.printRgdDiff(newWeapon.value, wanillaRgdData[newWeapon.key], out = out)
|
||||||
|
}
|
||||||
|
out.println("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllUnits(): Map<String, UnitAsSquad> {
|
||||||
|
|
||||||
|
return racesWikiPaths.map{racePath ->
|
||||||
|
|
||||||
|
val rgdData42 = File("src/main/resources/static/mods/$currentVersion/attrib/sbps/races/$racePath").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
}else{
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val newUnits = File("src/main/resources/static/mods/$currentVersion/attrib/ebps/races/$racePath/troops").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
}else{
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
rgdData42.map {newSquad ->
|
||||||
|
|
||||||
|
val baseUnitPath = ((newSquad.value?.find { it.name == "squad_loadout_ext" }?.value as List<RgdData>)
|
||||||
|
.find { it.name == "trooper_base" }?.value as List<RgdData>)
|
||||||
|
.find { it.name == "type" }?.value as String
|
||||||
|
val baseUnitName = baseUnitPath.split("\\").last().replace(".lua", ".rgd")
|
||||||
|
|
||||||
|
baseUnitName to UnitAsSquad(newSquad.value, newUnits[baseUnitName])
|
||||||
|
}.toMap()
|
||||||
|
}.fold(emptyMap()){sum, map -> map + sum}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getWeaponRgdMap(): Map<String, List<RgdData>?> {
|
||||||
|
|
||||||
|
return File("src/main/resources/static/mods/$currentVersion/attrib/weapon").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name.replace(".rgd", "") to rgdData
|
||||||
|
}.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllWeaponDamage() {
|
||||||
|
val weaponMap = getWeaponRgdMap()
|
||||||
|
|
||||||
|
getAllUnits().forEach {
|
||||||
|
println(it.key.replace(".rgd",""))
|
||||||
|
it.value.uintData
|
||||||
|
?.getRgdTableByName("combat_ext")
|
||||||
|
?.getRgdTableByName("hardpoints")
|
||||||
|
?.forEach { hardpoint ->
|
||||||
|
if (hardpoint.name.contains("hardpoint")) {
|
||||||
|
val hardpointWeapons = (hardpoint.value as List<RgdData>)?.getRgdTableByName("weapon_table")
|
||||||
|
?.mapNotNull { weapon ->
|
||||||
|
if (weapon.name.contains("weapon")) {
|
||||||
|
val weapon = (weapon.value as List<RgdData>).getStringByName("weapon")
|
||||||
|
if (weapon != "" && weapon?.contains("dummy_weapon") == false) {
|
||||||
|
weapon
|
||||||
|
} else null
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hardpointWeapons?.isNotEmpty() == true) {
|
||||||
|
|
||||||
|
hardpointWeapons.forEach {
|
||||||
|
val weaponId = it.replace("weapon\\", "").replace(".lua", "")
|
||||||
|
println(":" + weaponId + ":")
|
||||||
|
val weaponRgd = weaponMap[weaponId]
|
||||||
|
val reloadTime: Double = weaponRgd?.getDoubleByName("reload_time")!!
|
||||||
|
val accuracy: Double = weaponRgd?.getDoubleByName("accuracy")!!
|
||||||
|
|
||||||
|
val armourDamage = weaponRgd?.getRgdTableByName("area_effect")
|
||||||
|
?.getRgdTableByName("weapon_damage")
|
||||||
|
?.getRgdTableByName("armour_damage")
|
||||||
|
|
||||||
|
val minDamage = armourDamage?.getDoubleByName("min_damage")!!
|
||||||
|
val maxDamage = armourDamage.getDoubleByName("max_damage")!!
|
||||||
|
val avgDamage = (minDamage + maxDamage) / 2
|
||||||
|
|
||||||
|
val weaponDmgMap: Map<String, Double>? =
|
||||||
|
armourDamage
|
||||||
|
?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing ->
|
||||||
|
if (armour_piercing.name.contains("entry")) {
|
||||||
|
val entry = armour_piercing.value as List<RgdData>
|
||||||
|
val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","")
|
||||||
|
val dmgValue = entry.getDoubleByName("armour_piercing_value")
|
||||||
|
dmgType!! to dmgValue!!
|
||||||
|
} else null
|
||||||
|
}?.toMap()?.toSortedMap()
|
||||||
|
println()
|
||||||
|
val dmgK = accuracy * (1/(reloadTime - reloadTime.mod(0.125)))
|
||||||
|
weaponDmgMap?.forEach {
|
||||||
|
val piercingK = it.value / 100
|
||||||
|
val totalDmg = piercingK * dmgK * avgDamage
|
||||||
|
print(it.key + "-" + (Math.round(totalDmg * 100.0) / 100.00).toString() + "|")
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
println("-----")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("============================")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllBuildingsDiff(out: PrintWriter) {
|
||||||
|
racesWikiPaths.forEach { racePath ->
|
||||||
|
|
||||||
|
val classicRgdData =
|
||||||
|
File("src/main/resources/static/mods/$prevVersion/attrib/ebps/races/$racePath/structures").walkTopDown().map {
|
||||||
|
val rgdData = if (it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")) {
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val newRgdData = File("src/main/resources/static/mods/$currentVersion/attrib/ebps/races/$racePath/structures").walkTopDown().map {
|
||||||
|
val rgdData = if (it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")) {
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
|
||||||
|
newRgdData.forEach { newBuilding ->
|
||||||
|
val oldBuilding = classicRgdData[newBuilding.key]
|
||||||
|
if (oldBuilding != newBuilding.value) {
|
||||||
|
out.println(newBuilding.key.replace(".rgd", "").uppercase())
|
||||||
|
rgdService.printRgdDiff(newBuilding.value, oldBuilding, out = out)
|
||||||
|
out.println("---")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun saveAllDataToDb() {
|
||||||
|
// modParserService.saveModDataToDb()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllDiffAndPrintToFiles(){
|
||||||
|
val weaponDiffFileName = "$prevVersion-$currentVersion-weapons.txt"
|
||||||
|
File(weaponDiffFileName).printWriter().use { out ->
|
||||||
|
getAllWeaponsDiff(out)
|
||||||
|
}
|
||||||
|
val unitsDiffFileName = "$prevVersion-$currentVersion-units.txt"
|
||||||
|
File(unitsDiffFileName).printWriter().use { out ->
|
||||||
|
getUnitsAndSquadsDiff(out)
|
||||||
|
}
|
||||||
|
val buildingsFileName = "$prevVersion-$currentVersion-building.txt"
|
||||||
|
File(buildingsFileName).printWriter().use { out ->
|
||||||
|
getAllBuildingsDiff(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun findStrategicPointCaptureRate(rgdDataList: List<RgdData>): Double? {
|
||||||
|
val capRgdData = rgdDataList.find { it.name == "squad_capture_strategic_point_ext" }?.value as List<RgdData>?
|
||||||
|
val canCapture = capRgdData?.find { it.name == "able_to_capture" }?.value == "[1]"
|
||||||
|
val captureRate = capRgdData?.find { it.name == "capture_rate" }
|
||||||
|
return if(canCapture){
|
||||||
|
captureRate?.value as Double?
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findVisionCaptureRate(rgdDataList: List<RgdData>): Double? {
|
||||||
|
val visionRgdData = rgdDataList.find { it.name == "sight_ext" }?.value as List<RgdData>?
|
||||||
|
return visionRgdData?.find { it.name == "sight_radius" }?.value as Double?
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.decodeHex(): ByteArray {
|
||||||
|
check(length % 2 == 0) { "Must have an even length" }
|
||||||
|
return chunked(2)
|
||||||
|
.map { it.toInt(16).toByte() }
|
||||||
|
.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ByteArray.getFloat(): Float {
|
||||||
|
val buffer = ByteBuffer.wrap(this)
|
||||||
|
return buffer.float
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
@ -0,0 +1,159 @@
|
|||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
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 RgdParserServiceTest {
|
||||||
|
|
||||||
|
val zeroByte: Byte = 0
|
||||||
|
|
||||||
|
val rgdDictionary: MutableMap<Int, String> = mutableMapOf()
|
||||||
|
|
||||||
|
fun readDictionary() {
|
||||||
|
File("src/test/resources/RGD_DIC.TXT").forEachLine {
|
||||||
|
if(it.isNotEmpty() && it[0] != '#'){
|
||||||
|
val kv = it.split('=')
|
||||||
|
val key = kv.first().drop(2).decodeHex().getUIntAt(0).toInt()
|
||||||
|
val value = kv.last()
|
||||||
|
rgdDictionary[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.decodeHex(): ByteArray {
|
||||||
|
check(length % 2 == 0) { "Must have an even length" }
|
||||||
|
return chunked(2)
|
||||||
|
.map { it.toInt(16).toByte() }
|
||||||
|
.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSum() {
|
||||||
|
|
||||||
|
readDictionary()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val path = Paths.get("").toAbsolutePath().toString()
|
||||||
|
println(path)
|
||||||
|
val bufferedReader: DataInputStream = DataInputStream(File("src/test/resources/chaos_marine_squad.rgd").inputStream())
|
||||||
|
val res = bufferedReader.use { it.readAllBytes() }
|
||||||
|
|
||||||
|
println("${res.size} bytes")
|
||||||
|
|
||||||
|
val dataInfo = res.copyOfRange(24, 32)
|
||||||
|
println(dataInfo.toString(Charsets.UTF_8))
|
||||||
|
|
||||||
|
val chunkBytes = res.drop(32).toByteArray()
|
||||||
|
|
||||||
|
val version = chunkBytes.copyOfRange(0,4).reversedArray().getUIntAt(0)
|
||||||
|
val chunkLength = chunkBytes.copyOfRange(4,8).reversedArray().getUIntAt(0)
|
||||||
|
val stringLength = chunkBytes.copyOfRange(8,12).reversedArray().getUIntAt(0).toInt()
|
||||||
|
|
||||||
|
println("version: $version; chunkLength: $chunkLength; stringLength: $stringLength")
|
||||||
|
|
||||||
|
//val sString = chunkBytes.copyOfRange(12, 12 + stringLength).toString(Charsets.UTF_8)
|
||||||
|
//val iCRC = chunkBytes.copyOfRange(12 + stringLength, 12 + stringLength + 4).reversedArray().getUIntAt(0)
|
||||||
|
val dataLength = chunkBytes.copyOfRange(16 + stringLength, 16 + stringLength + 4).reversedArray().getUIntAt(0).toInt()
|
||||||
|
|
||||||
|
//println("sString: $sString; iCRC: $iCRC; dataLength: $dataLength")
|
||||||
|
|
||||||
|
fun printRgdData(data: List<RgdData>, out: PrintWriter){
|
||||||
|
data.sortedBy { it.name }.forEach{
|
||||||
|
if(it.value is List<*>){
|
||||||
|
out.println("<details closed>")
|
||||||
|
out.println("<summary>${it.name}</summary>")
|
||||||
|
|
||||||
|
out.println("<ul>")
|
||||||
|
printRgdData(it.value as List<RgdData>, out)
|
||||||
|
out.println("</ul>")
|
||||||
|
|
||||||
|
out.println("</details>")
|
||||||
|
}else{
|
||||||
|
out.println("<li>${it.name} → ${it.value}</li>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRgdData(byteArray: ByteArray): List<RgdData> {
|
||||||
|
|
||||||
|
var keyCount = byteArray.copyOfRange(0,4).reversedArray().getUIntAt(0).toInt()
|
||||||
|
val totalKeyCount = keyCount
|
||||||
|
var offset = 4
|
||||||
|
|
||||||
|
val rgdData = mutableListOf<RgdData>()
|
||||||
|
|
||||||
|
while (keyCount != 0){
|
||||||
|
|
||||||
|
val hash = byteArray.copyOfRange(offset,offset + 4).reversedArray().getUIntAt(0).toInt()
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
val valueName = rgdDictionary[hash] ?: hash.toString()
|
||||||
|
|
||||||
|
val type = byteArray.copyOfRange(offset,offset + 4).reversedArray().getUIntAt(0).toInt()
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
val iOffset = byteArray.copyOfRange(offset,offset + 4).reversedArray().getUIntAt(0).toInt()
|
||||||
|
offset += 4
|
||||||
|
//println("$space iOffset: $iOffset")
|
||||||
|
|
||||||
|
val dataOffset = totalKeyCount * (3 * 4) + iOffset + 4
|
||||||
|
|
||||||
|
val data: Any = when(type){
|
||||||
|
0 -> {// Float
|
||||||
|
val entityData = byteArray.copyOfRange(dataOffset,dataOffset + 4).reversedArray().getFloat()
|
||||||
|
entityData.toBigDecimal().setScale(2, RoundingMode.HALF_UP).toDouble()
|
||||||
|
}
|
||||||
|
1 -> {// Int
|
||||||
|
val entityData = byteArray.copyOfRange(dataOffset,dataOffset + 8).reversedArray().getUIntAt(0)
|
||||||
|
entityData
|
||||||
|
}
|
||||||
|
2 -> {// Bool
|
||||||
|
val entityData = byteArray.copyOfRange(dataOffset,dataOffset + 1).map { it }.toString()
|
||||||
|
entityData
|
||||||
|
}
|
||||||
|
3 -> {// String
|
||||||
|
var zeroByteOffset = 0
|
||||||
|
while(byteArray[dataOffset+zeroByteOffset] != zeroByte){
|
||||||
|
zeroByteOffset++
|
||||||
|
}
|
||||||
|
byteArray.copyOfRange(dataOffset,dataOffset + zeroByteOffset).toString(Charsets.UTF_8)
|
||||||
|
}
|
||||||
|
4 -> {// String
|
||||||
|
var zeroByteOffset = 0
|
||||||
|
while(byteArray[dataOffset+zeroByteOffset] != zeroByte || byteArray[dataOffset+zeroByteOffset+1] != zeroByte){
|
||||||
|
zeroByteOffset++
|
||||||
|
}
|
||||||
|
zeroByteOffset++
|
||||||
|
byteArray.copyOfRange(dataOffset,dataOffset + zeroByteOffset).toString(Charsets.UTF_16LE)
|
||||||
|
}
|
||||||
|
100 -> {
|
||||||
|
handleRgdData(byteArray.copyOfRange(dataOffset, byteArray.size))
|
||||||
|
}
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
rgdData.add(RgdData(hash, valueName, type, data))
|
||||||
|
keyCount--
|
||||||
|
}
|
||||||
|
|
||||||
|
return rgdData
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ByteArray.getFloat(): Float {
|
||||||
|
val buffer = ByteBuffer.wrap(this)
|
||||||
|
return buffer.float
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,216 @@
|
|||||||
|
import com.dowstats.data.rgd.RgdData
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
|
||||||
|
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
|
||||||
|
import com.dowstats.service.w40k.RgdParserService
|
||||||
|
import com.dowstats.service.w40k.RgdService
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
class UnificationResearchTest {
|
||||||
|
|
||||||
|
val zeroByte: Byte = 0
|
||||||
|
|
||||||
|
val rgdDictionary: MutableMap<Int, String> = mutableMapOf()
|
||||||
|
|
||||||
|
val rgdParseService = RgdParserService()
|
||||||
|
|
||||||
|
val rgdService = RgdService()
|
||||||
|
|
||||||
|
val blackTemplars = "black_templars"
|
||||||
|
|
||||||
|
|
||||||
|
val racesWikiPaths = setOf(blackTemplars)
|
||||||
|
|
||||||
|
fun readDictionary() {
|
||||||
|
File("src/test/resources/RGD_DIC.TXT").forEachLine {
|
||||||
|
if(it.isNotEmpty() && it[0] != '#'){
|
||||||
|
val kv = it.split('=')
|
||||||
|
val key = kv.first().drop(2).decodeHex().getUIntAt(0).toInt()
|
||||||
|
val value = kv.last()
|
||||||
|
rgdDictionary[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UnitAsSquad(val squadData: List<RgdData>?,val uintData: List<RgdData>?)
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllUnits(): Map<String, UnitAsSquad> {
|
||||||
|
|
||||||
|
return racesWikiPaths.map{racePath ->
|
||||||
|
|
||||||
|
val rgdData42 = File("src/main/resources/static/mods/unification/attrib/sbps/races/$racePath").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
}else{
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val newUnits = File("src/main/resources/static/mods/unification/attrib/ebps/races/$racePath/troops").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
}else{
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name to rgdData
|
||||||
|
}.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
rgdData42.map {newSquad ->
|
||||||
|
|
||||||
|
val baseUnitPath = ((newSquad.value?.find { it.name == "squad_loadout_ext" }?.value as List<RgdData>)
|
||||||
|
.find { it.name == "trooper_base" }?.value as List<RgdData>)
|
||||||
|
.find { it.name == "type" }?.value as String
|
||||||
|
val baseUnitName = baseUnitPath.split("\\").last().replace(".lua", ".rgd")
|
||||||
|
|
||||||
|
baseUnitName to UnitAsSquad(newSquad.value, newUnits[baseUnitName])
|
||||||
|
}.toMap()
|
||||||
|
}.fold(emptyMap()){sum, map -> map + sum}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getWeaponRgdMap(): Map<String, List<RgdData>?> {
|
||||||
|
|
||||||
|
return File("src/main/resources/static/mods/unification/attrib/weapon").walkTopDown().map {
|
||||||
|
val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){
|
||||||
|
rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream()))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
it.name.replace(".rgd", "") to rgdData
|
||||||
|
}.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllWeaponDamage() {
|
||||||
|
|
||||||
|
val weapons = getWeaponRgdMap()
|
||||||
|
|
||||||
|
|
||||||
|
val weaponsByDeamonDmg = weapons.toList()
|
||||||
|
.sortedByDescending { (key, weaponRgd) ->
|
||||||
|
try {
|
||||||
|
val reloadTime: Double = weaponRgd?.getDoubleByName("reload_time")!!
|
||||||
|
val accuracy: Double = weaponRgd?.getDoubleByName("accuracy")!!
|
||||||
|
|
||||||
|
val armourDamage = weaponRgd?.getRgdTableByName("area_effect")
|
||||||
|
?.getRgdTableByName("weapon_damage")
|
||||||
|
?.getRgdTableByName("armour_damage")
|
||||||
|
|
||||||
|
val minDamage = armourDamage?.getDoubleByName("min_damage")!!
|
||||||
|
val maxDamage = armourDamage.getDoubleByName("max_damage")!!
|
||||||
|
val avgDamage = (minDamage + maxDamage) / 2
|
||||||
|
|
||||||
|
val weaponDmgMap: Map<String, Double>? =
|
||||||
|
armourDamage
|
||||||
|
?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing ->
|
||||||
|
if (armour_piercing.name.contains("entry")) {
|
||||||
|
val entry = armour_piercing.value as List<RgdData>
|
||||||
|
val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","")
|
||||||
|
val dmgValue = entry.getDoubleByName("armour_piercing_value")
|
||||||
|
dmgType!! to dmgValue!!
|
||||||
|
} else null
|
||||||
|
}?.toMap()?.toSortedMap()
|
||||||
|
val dmgK = accuracy * (1/(reloadTime - reloadTime.mod(0.125)))
|
||||||
|
val piercingK = weaponDmgMap?.get("vehicle_low")!! / 100
|
||||||
|
val totalDmg = piercingK * dmgK * avgDamage
|
||||||
|
val res = (Math.round(totalDmg * 100.0) / 100.00)
|
||||||
|
res
|
||||||
|
} catch(exception: Exception) {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter { it.second != null }
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
print("<table><tr><th>name</th>")
|
||||||
|
weaponsByDeamonDmg.values.first()?.getRgdTableByName("area_effect")
|
||||||
|
?.getRgdTableByName("weapon_damage")
|
||||||
|
?.getRgdTableByName("armour_damage")
|
||||||
|
?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing ->
|
||||||
|
if (armour_piercing.name.contains("entry")) {
|
||||||
|
val entry = armour_piercing.value as List<RgdData>
|
||||||
|
val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","")
|
||||||
|
val dmgValue = entry.getDoubleByName("armour_piercing_value")
|
||||||
|
dmgType!! to dmgValue!!
|
||||||
|
} else null
|
||||||
|
}?.toMap()?.toSortedMap()?.forEach {
|
||||||
|
print("<th>${ it.key}</th>")
|
||||||
|
}
|
||||||
|
print("</tr>")
|
||||||
|
|
||||||
|
weaponsByDeamonDmg.forEach { (key, weaponRgd) ->
|
||||||
|
val reloadTime: Double = weaponRgd?.getDoubleByName("reload_time")!!
|
||||||
|
val accuracy: Double = weaponRgd.getDoubleByName("accuracy")!!
|
||||||
|
|
||||||
|
val armourDamage = weaponRgd?.getRgdTableByName("area_effect")
|
||||||
|
?.getRgdTableByName("weapon_damage")
|
||||||
|
?.getRgdTableByName("armour_damage")
|
||||||
|
|
||||||
|
val minDamage = armourDamage?.getDoubleByName("min_damage")!!
|
||||||
|
val maxDamage = armourDamage.getDoubleByName("max_damage")!!
|
||||||
|
val avgDamage = (minDamage + maxDamage) / 2
|
||||||
|
|
||||||
|
val weaponDmgMap: Map<String, Double>? =
|
||||||
|
armourDamage
|
||||||
|
?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing ->
|
||||||
|
if (armour_piercing.name.contains("entry")) {
|
||||||
|
val entry = armour_piercing.value as List<RgdData>
|
||||||
|
val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","")
|
||||||
|
val dmgValue = entry.getDoubleByName("armour_piercing_value")
|
||||||
|
dmgType!! to dmgValue!!
|
||||||
|
} else null
|
||||||
|
}?.toMap()?.toSortedMap()
|
||||||
|
println("<tr><td>$key</td>")
|
||||||
|
val dmgK = accuracy * (1/(reloadTime - reloadTime.mod(0.125)))
|
||||||
|
weaponDmgMap?.forEach {
|
||||||
|
val piercingK = it.value / 100
|
||||||
|
val totalDmg = piercingK * dmgK * avgDamage
|
||||||
|
print("<td>${ (Math.round(totalDmg * 100.0) / 100.00)}</td>")
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
println("</tr>")
|
||||||
|
}
|
||||||
|
println("</table>")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun findStrategicPointCaptureRate(rgdDataList: List<RgdData>): Double? {
|
||||||
|
val capRgdData = rgdDataList.find { it.name == "squad_capture_strategic_point_ext" }?.value as List<RgdData>?
|
||||||
|
val canCapture = capRgdData?.find { it.name == "able_to_capture" }?.value == "[1]"
|
||||||
|
val captureRate = capRgdData?.find { it.name == "capture_rate" }
|
||||||
|
return if(canCapture){
|
||||||
|
captureRate?.value as Double?
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findVisionCaptureRate(rgdDataList: List<RgdData>): Double? {
|
||||||
|
val visionRgdData = rgdDataList.find { it.name == "sight_ext" }?.value as List<RgdData>?
|
||||||
|
return visionRgdData?.find { it.name == "sight_radius" }?.value as Double?
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.decodeHex(): ByteArray {
|
||||||
|
check(length % 2 == 0) { "Must have an even length" }
|
||||||
|
return chunked(2)
|
||||||
|
.map { it.toInt(16).toByte() }
|
||||||
|
.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ByteArray.getFloat(): Float {
|
||||||
|
val buffer = ByteBuffer.wrap(this)
|
||||||
|
return buffer.float
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
6536
src/test/resources/RGD_DIC.TXT
Normal file
6536
src/test/resources/RGD_DIC.TXT
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/test/resources/chaos_bolter.rgd
Normal file
BIN
src/test/resources/chaos_bolter.rgd
Normal file
Binary file not shown.
BIN
src/test/resources/chaos_marine_squad.rgd
Normal file
BIN
src/test/resources/chaos_marine_squad.rgd
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user