Scenery visualization, info tab improvements, path refactoring

This commit is contained in:
ceikry 2021-10-30 11:17:45 -05:00
parent c108a129e9
commit ec83fcd86a
8 changed files with 206 additions and 26 deletions

View file

@ -36,6 +36,8 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
implementation files("libs/json-simple-1.1.1.jar")
}
test {

BIN
libs/json-simple-1.1.1.jar Normal file

Binary file not shown.

View file

@ -8,6 +8,7 @@ import com.displee.cache.CacheLibrary
import com.formdev.flatlaf.FlatDarculaLaf
import const.Image.RED_DOT
import const.Image.YELLOW_DOT
import const.cachePath
import misc.CustomEventQueue
import misc.ItemSpawnPanel
import tools.ItemSearchTool
@ -22,10 +23,11 @@ import kotlin.system.exitProcess
object Rs2MapEditor {
val library = CacheLibrary.create("/home/ceikry/IdeaProjects/rs09-remake/Server/data/cache/")
val library = CacheLibrary.create(cachePath)
var region = 12850
var npcs = NPCSpawnParser.parseNPCSpawns(region)
var items = GroundItemSpawnParser.parseItemSpawns(region)
var sceneries = TileSceneryParser.parseRegion(region)
val npcRows = ArrayList<NPCSpawnPanel.NPCRow>()
val itemRows = ArrayList<ItemSpawnPanel.ItemRow>()
var npcsUpdated = false
@ -134,14 +136,7 @@ object Rs2MapEditor {
for (column in 0 until mapWidth) {
gbc.gridx = column
gbc.gridy = row
val border: Border =
MatteBorder(
1,
1,
if (row == mapHeight) 1 else 0,
if (column == mapWidth) 1 else 0,
Color.GRAY
)
val border: Border = MatteBorder(1, 1, if (row == mapHeight) 1 else 0, if (column == mapWidth) 1 else 0, Color.GRAY)
val flippedY = (64 - row) - 1
val point = Point(column, flippedY)
colorPointMap[point] = MapTileParser.definition.getTile(column, flippedY, plane).underlayId
@ -157,6 +152,12 @@ object Rs2MapEditor {
}
mapCell.border = border
val sceneryHere = sceneries.filter { it.x == column && it.y == flippedY && it.plane == plane }.toList()
if(sceneryHere.isNotEmpty()){
mapCell.flagScenery(sceneryHere.first())
}
val npcsHere = npcs.filter { it.x == column && it.y == flippedY && it.plane == plane }.toList()
if (npcsHere.isNotEmpty()) {
mapCell.layout = BorderLayout()
@ -179,6 +180,7 @@ object Rs2MapEditor {
class MapCell : JPanel() {
var defaultBackground: Color = background
var scenery: Scenery = Scenery(-1,-1,-1,-1,-1,-1)
override fun getPreferredSize(): Dimension {
return Dimension(cellX, cellY)
}
@ -268,6 +270,7 @@ object Rs2MapEditor {
val point = Point(selectedPointX, selectedPointY)
val def = MapTileParser.definition.getTile(point.x, point.y, 0)
val floDef = overlayMap[def.overlayId]!!
val cell = componentPointMap[point]!!
val string = StringBuilder()
.append("<h2><b>Tile [${point.x}, ${point.y}] Information</b></h2><br>")
.append("<hr width=\"250px\"><br>")
@ -304,29 +307,82 @@ object Rs2MapEditor {
.append("</tbody>")
.append("</table>")
.append("<br/>")
.append("<table style=\"border:1px solid gray;margin-left:auto;margin-right:auto;>")
.append("<thead>")
.append("<tr style=\"text-align:center\">")
.append("<th>NPC ID</th>")
.append("<th>Name</th>")
.append("</tr>")
.append("</thead>")
.append("<tbody>")
val npcsHere = npcs.filter { println(it.definition.name + " ${it.x}:${it.y}:${it.plane}"); it.x == selectedPointX && it.y == selectedPointY && it.plane == 0 }.toList()
for(npc in npcsHere){
string.append("<tr style=\"text-align:center\">")
string.append("<td>${npc.id}</td>")
string.append("<td>${npc.definition.name}(${npc.definition.combat})</td>")
string.append("</tr>")
val itemsHere = items.filter { it.x == selectedPointX && it.y == selectedPointY && it.plane == plane }.toList()
val npcsHere = npcs.filter { it.x == selectedPointX && it.y == selectedPointY && it.plane == plane }.toList()
if(npcsHere.isNotEmpty()) {
string.append("<table style=\"border:1px solid gray;margin-left:auto;margin-right:auto;>")
.append("<thead>")
.append("<tr style=\"text-align:center\">")
.append("<th>NPC ID</th>")
.append("<th>Name</th>")
.append("</tr>")
.append("</thead>")
.append("<tbody>")
for (npc in npcsHere) {
string.append("<tr style=\"text-align:center\">")
string.append("<td>${npc.id}</td>")
string.append("<td>${npc.definition.name}(${npc.definition.combat})</td>")
string.append("</tr>")
}
string.append("</tbody></table><br/>")
}
println("${selectedPointX} : ${selectedPointY}")
string.append("</tbody></table><br/>")
if(itemsHere.isNotEmpty()) {
string.append("<table style=\"border:1px solid gray;margin-left:auto;margin-right:auto;>")
.append("<thead>")
.append("<tr style=\"text-align:center\">")
.append("<th>Item ID</th>")
.append("<th>Info</th>")
.append("</tr>")
.append("</thead>")
.append("<tbody>")
for (item in itemsHere) {
string.append("<tr style=\"text-align:center\">")
string.append("<td>${item.id}</td>")
string.append("<td>${item.definition.name}[${item.amount}] \uD83D\uDD64${item.respawnTime}</td>")
string.append("</tr>")
}
string.append("</tbody></table><br/>")
}
if(cell.scenery.id != -1){
string.append("<h3>Scenery Info</h3>")
string.append("${cell.scenery.definition.name} [ID: ${cell.scenery.id}]<br/>")
string.append("Type: ${cell.scenery.type}<br/>")
string.append("Rotation: ${cell.scenery.rotation}<br/>")
string.append("")
}
underlayInfo.text = String.format("<html><body style=\"text-align: center;\">%s</body></html>", string.toString())
infoPane.selectedIndex = 0
}
}
})
}
fun flagScenery(scenery: Scenery){
this.scenery = scenery
}
override fun paintComponent(g: Graphics) {
super.paintComponent(g)
when(scenery.type){
10 -> g.drawOval(5,5,this.width - 10, this.height - 10)
11 -> {
val color = g.color
g.color = Color(255,255,255,75)
g.drawString("",(0.25 * width).toInt(),(0.75 * height).toInt())
g.color = color
}
22 -> {
val color = g.color
g.color = Color(255,255,255,55)
g.drawString("",(0.15 * width).toInt(),(0.65 * height).toInt())
g.color = color
}
}
}
}
class ScrollPane : JPanel() {

View file

@ -3,11 +3,21 @@ package cacheops.cache.definition.decoder
import cacheops.cache.Cache
import cacheops.cache.DefinitionDecoder
import cacheops.cache.buffer.read.Reader
import cacheops.cache.definition.data.NPCDefinition
import cacheops.cache.definition.data.ObjectDefinition
import const.Indices
open class ObjectDecoder(cache: Cache, val member: Boolean, val configReplace: Boolean) : DefinitionDecoder<ObjectDefinition>(cache, Indices.CONFIGURATION_OBJECT) {
val DEFINITIONS = HashMap<Int,ObjectDefinition>()
fun forId(id: Int): ObjectDefinition{
if(DEFINITIONS[id] != null) return DEFINITIONS[id]!!
val def = readData(id)
DEFINITIONS[id] = def ?: ObjectDefinition(id,null,null,"null")
return DEFINITIONS[id]!!
}
override fun create() = ObjectDefinition()
override fun getFile(id: Int) = id and 0xff

View file

@ -0,0 +1,59 @@
package cacheops.cache.definition.decoder
import Rs2MapEditor
import cacheops.cache.definition.data.ObjectDefinition
import const.cache
import ext.getBigSmart
import ext.getSmart
import java.nio.ByteBuffer
val objectDecoder = ObjectDecoder(cache, true, false)
object TileSceneryParser {
fun parseRegion(id: Int): ArrayList<Scenery>{
if(XteaKeys.XTEAS.isEmpty()){
XteaKeys.load()
}
val regionX = (id shr 8) and 0xFF;
val regionY = id and 0xFF;
val data = Rs2MapEditor.library.data(5, "l${regionX}_${regionY}",XteaKeys.get(id))
return if(data != null) decode(ByteBuffer.wrap(data))
else ArrayList()
}
private fun decode(data: ByteBuffer): ArrayList<Scenery>{
val list = ArrayList<Scenery>()
var objectId = -1
while (true) {
var offset: Int = data.getBigSmart()
if (offset == 0) {
break
}
objectId += offset
var location = 0
while (true) {
offset = data.getSmart()
if (offset == 0) {
break
}
location += offset - 1
val y = location and 0x3f
val x = (location shr 6) and 0x3f
val configuration: Int = data.get().toInt() and 0xFF
val rotation = configuration and 0x3
val type = configuration shr 2
val z = location shr 12
list.add(Scenery(objectId,x,y,z,rotation,type))
}
}
return list
}
}
class Scenery(val id: Int, val x: Int, val y: Int, val plane: Int, val rotation: Int, val type: Int){
val definition = objectDecoder.forId(id)
}

View file

@ -0,0 +1,33 @@
package cacheops.cache.definition.decoder
import const.xteaJson
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser
import java.io.FileReader
object XteaKeys {
val XTEAS = HashMap<Int,IntArray>()
val DEFAULT_REGION_KEYS = intArrayOf(0,0,0,0)
fun get(regionId: Int): IntArray{
return XTEAS[regionId] ?: DEFAULT_REGION_KEYS
}
val parser = JSONParser()
var reader: FileReader? = null
fun load() {
var count = 0
reader = FileReader(xteaJson)
val obj = parser.parse(reader) as JSONObject
val configlist = obj["xteas"] as JSONArray
for(config in configlist){
val e = config as JSONObject
val id = e["regionId"].toString().toInt()
val keys = e["keys"].toString().split(",").map(String::toInt)
XTEAS[id] = intArrayOf(keys[0],keys[1],keys[2],keys[3])
count++
}
}
}

View file

@ -3,4 +3,6 @@ package const
import cacheops.cache.Cache
import cacheops.cache.CacheDelegate
val cache: Cache = CacheDelegate("/home/ceikry/IdeaProjects/rs09-remake/Server/data/cache/")
val cachePath = "/home/ceikry/IdeaProjects/rs09-remake/Server/data/cache"
val xteaJson = "/home/ceikry/IdeaProjects/rs09-remake/Server/data/configs/xteas.json"
val cache: Cache = CacheDelegate(cachePath)

View file

@ -40,4 +40,22 @@ fun ByteBuffer.unsignedByte(): Int {
fun ByteBuffer.unsignedShort(): Int {
return short.toInt() and 0xFFFF
}
fun ByteBuffer.getBigSmart(): Int {
var value = 0
var current: Int = getSmart()
while (current == 32767) {
current = getSmart()
value += 32767
}
value += current
return value
}
fun ByteBuffer.getSmart(): Int {
val peek: Int = get().toInt() and 0xFF
return if (peek <= Byte.MAX_VALUE) {
peek
} else (peek shl 8 or (get().toInt() and 0xFF)) - 32768
}