2025-03-15 21:47:51 +03:00

380 lines
16 KiB
Kotlin

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)
}