mirror of
https://gitlab.com/2009scape/tools/2009scape-map-viewer.git
synced 2026-08-01 14:39:25 -06:00
Scenery visualization, info tab improvements, path refactoring
This commit is contained in:
parent
c108a129e9
commit
ec83fcd86a
8 changed files with 206 additions and 26 deletions
|
|
@ -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
BIN
libs/json-simple-1.1.1.jar
Normal file
Binary file not shown.
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
59
src/main/kotlin/cacheops/cache/definition/decoder/TileSceneryParser.kt
vendored
Normal file
59
src/main/kotlin/cacheops/cache/definition/decoder/TileSceneryParser.kt
vendored
Normal 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)
|
||||
}
|
||||
|
||||
33
src/main/kotlin/cacheops/cache/definition/decoder/XteaKeys.kt
vendored
Normal file
33
src/main/kotlin/cacheops/cache/definition/decoder/XteaKeys.kt
vendored
Normal 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++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue