Added v1.0

This commit is contained in:
ceikry 2021-07-16 14:35:49 -05:00
commit af4b79a14b
18 changed files with 1751 additions and 0 deletions

37
build.gradle Normal file
View file

@ -0,0 +1,37 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4.32'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
repositories {
mavenCentral()
}
}
dependencies {
implementation "org.jetbrains:markdown-jvm:0.2.0"
implementation "com.github.ajalt.mordant:mordant:2.0.0-alpha2"
implementation "com.github.weisj:darklaf-core:2.5.5"
implementation "com.googlecode.json-simple:json-simple:1.1.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib"
}
task buildJar(type: Jar) {
manifest {
attributes(
'Main-Class': 'MainScreenKt'
)
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
{
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
with jar
}

1
gradle.properties Normal file
View file

@ -0,0 +1 @@
kotlin.code.style=official

2
settings.gradle Normal file
View file

@ -0,0 +1,2 @@
rootProject.name = 'rs09-thanos-tool'

View file

@ -0,0 +1,363 @@
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.event.*
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.*
import javax.swing.*
import javax.swing.event.ChangeEvent
import javax.swing.table.DefaultTableModel
class DropTableEditor(val table: NPCDropTable) : JFrame("Editing ${if(table.ids.isNotBlank()) TableData.npcNames[table.ids.split(",")[0].toInt()] ?: "Unknown Table" else "None"}") {
val IDsField = JTextField()
val descriptionField = JTextField()
val mainModel = object : DefaultTableModel(){}
val defaultModel = object : DefaultTableModel(){}
val charmModel = object : DefaultTableModel(){}
val mainTable = object : JTable(mainModel){
override fun editCellAt(row: Int, column: Int): Boolean {
if(column == 0 || column == 1){
ItemMenu.caller = {id,name ->
model.setValueAt(id,row,0)
model.setValueAt(name,row,1)
table[row].id = id
}
ItemMenu.open()
return false
}
return super.editCellAt(row, column)
}
override fun editCellAt(row: Int, column: Int, e: EventObject?): Boolean {
if(column == 0 || column == 1){
ItemMenu.caller = {id,name ->
model.setValueAt(id,row,0)
model.setValueAt(name,row,1)
table[row].id = id
}
ItemMenu.open()
return false
}
return super.editCellAt(row, column, e)
}
override fun editingStopped(e: ChangeEvent?) {
val editor = cellEditor
val newValue = editor.cellEditorValue.toString()
when(editingColumn){
2 -> table[editingRow].minAmt = newValue
3 -> table[editingRow].maxAmt = newValue
4 -> {
table.totalWeight += newValue.toDouble() - table[editingRow].weight
table[editingRow].weight = newValue.toDouble()
updateMainPercentages()
}
}
super.editingStopped(e)
}
}
val mainScrollpane = JScrollPane(mainTable)
val mainSubtable = Subtable("Main",mainScrollpane,table,mainModel)
val defaultTable = object : JTable(defaultModel){
override fun editCellAt(row: Int, column: Int): Boolean {
if(column == 0 || column == 1){
ItemMenu.caller = {id,name ->
model.setValueAt(id,row,0)
model.setValueAt(name,row,1)
table.alwaysTable[row].id = id
}
ItemMenu.open()
return false
}
return super.editCellAt(row, column)
}
override fun editCellAt(row: Int, column: Int, e: EventObject?): Boolean {
if(column == 0 || column == 1){
ItemMenu.caller = {id,name ->
model.setValueAt(id,row,0)
model.setValueAt(name,row,1)
table.alwaysTable[row].id = id
}
ItemMenu.open()
return false
}
return super.editCellAt(row, column, e)
}
override fun editingStopped(e: ChangeEvent?) {
val editor = cellEditor
val newValue = editor.cellEditorValue.toString()
when(editingColumn){
2 -> table.alwaysTable[editingRow].minAmt = newValue
3 -> table.alwaysTable[editingRow].maxAmt = newValue
}
super.editingStopped(e)
}
}
val defaultScrollPane = JScrollPane(defaultTable)
val defaultSubtable = Subtable("Always",defaultScrollPane,table.alwaysTable,defaultModel)
val charmTable = object : JTable(charmModel){
override fun editCellAt(row: Int, column: Int): Boolean {
if(column == 0 || column == 1){
ItemMenu.caller = {id,name ->
model.setValueAt(id,row,0)
model.setValueAt(name,row,1)
table.charmTable[row].id = id
}
ItemMenu.open()
return false
}
return super.editCellAt(row, column)
}
override fun editCellAt(row: Int, column: Int, e: EventObject?): Boolean {
if(column == 0 || column == 1){
ItemMenu.caller = {id,name ->
model.setValueAt(id,row,0)
model.setValueAt(name,row,1)
table.charmTable[row].id = id
}
ItemMenu.open()
return false
}
return super.editCellAt(row, column, e)
}
override fun editingStopped(e: ChangeEvent?) {
val editor = cellEditor
val newValue = editor.cellEditorValue.toString()
when(editingColumn){
2 -> table.charmTable[editingRow].minAmt = newValue
3 -> table.charmTable[editingRow].maxAmt = newValue
4 -> {
table.charmTable.totalWeight += newValue.toDouble() - table.charmTable[editingRow].weight
table.charmTable[editingRow].weight = newValue.toDouble()
updateCharmPercentages()
}
}
super.editingStopped(e)
}
}
val charmScrollPane = JScrollPane(charmTable)
val charmSubtable = Subtable("Charm",charmScrollPane,table.charmTable,charmModel)
init {
layout = BorderLayout()
val topPanel = JPanel()
val IDsLabel = JLabel("IDs: ")
IDsField.preferredSize = Dimension(200,20)
IDsField.addKeyListener(object : KeyListener {
override fun keyTyped(e: KeyEvent?) {}
override fun keyPressed(e: KeyEvent?) {}
override fun keyReleased(e: KeyEvent?) {
table.ids = IDsField.text
}
})
IDsField.text = table.ids
descriptionField.preferredSize = Dimension(400,20)
descriptionField.text = table.description
descriptionField.addKeyListener(object : KeyListener{
override fun keyTyped(e: KeyEvent?) {}
override fun keyPressed(e: KeyEvent?) {}
override fun keyReleased(e: KeyEvent?) {
table.description = descriptionField.text
}
})
topPanel.add(IDsLabel)
topPanel.add(IDsField)
val mainButton = JButton("Edit Main")
mainButton.addActionListener {
mainSubtable.open()
}
topPanel.add(mainButton)
val charmsButton = JButton("Edit Charms")
charmsButton.addActionListener {
charmSubtable.open()
}
topPanel.add(charmsButton)
val defaultButton = JButton("Edit Default")
defaultButton.addActionListener {
defaultSubtable.open()
}
topPanel.add(defaultButton)
add(topPanel,BorderLayout.NORTH)
val descriptionPanel = JPanel()
val descriptionLabel = JLabel("Description: ")
descriptionPanel.add(descriptionLabel)
descriptionPanel.add(descriptionField)
add(descriptionPanel,BorderLayout.SOUTH)
mainModel.addColumn("ID")
mainModel.addColumn("Name")
mainModel.addColumn("Min Amt")
mainModel.addColumn("Max Amt")
mainModel.addColumn("Weight")
mainModel.addColumn("(%)")
defaultModel.addColumn("ID")
defaultModel.addColumn("Name")
defaultModel.addColumn("Min Amt")
defaultModel.addColumn("Max Amt")
charmModel.addColumn("ID")
charmModel.addColumn("Name")
charmModel.addColumn("Min Amt")
charmModel.addColumn("Max Amt")
charmModel.addColumn("Weight")
charmModel.addColumn("(%)")
mainTable.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete")
mainTable.actionMap.put("delete",object : AbstractAction() {
override fun actionPerformed(e: ActionEvent?) {
try {
table.totalWeight -= table[mainTable.selectedRow].weight
table.removeAt(mainTable.selectedRow)
mainModel.removeRow(mainTable.selectedRow)
updateMainPercentages()
} catch(e: Exception){
Logger.logErr("Tried to remove nonexistent row ${mainTable.selectedRow}")
}
}
})
defaultTable.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete")
defaultTable.actionMap.put("delete",object : AbstractAction() {
override fun actionPerformed(e: ActionEvent?) {
try {
table.alwaysTable.removeAt(defaultTable.selectedRow)
defaultModel.removeRow(defaultTable.selectedRow)
} catch(e: Exception){
Logger.logErr("Tried to remove nonexistent row ${defaultTable.selectedRow}")
}
}
})
charmTable.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete")
charmTable.actionMap.put("delete",object : AbstractAction() {
override fun actionPerformed(e: ActionEvent?) {
try {
table.totalWeight -= table.charmTable[charmTable.selectedRow].weight
table.charmTable.removeAt(charmTable.selectedRow)
charmModel.removeRow(charmTable.selectedRow)
updateCharmPercentages()
} catch(e: Exception){
Logger.logErr("Tried to remove nonexistent row ${charmTable.selectedRow}")
}
}
})
addWindowListener(object : WindowAdapter(){
override fun windowClosing(e: WindowEvent?) {
FileLoader.dropTableList?.populated = false
FileLoader.dropTableList?.dispatchEvent(WindowEvent(FileLoader.dropTableList!!, WindowEvent.WINDOW_CLOSING))
FileLoader.dropTableList?.open()
super.windowClosing(e)
}
})
pack()
isVisible = false
}
class Subtable(val label: String, val pane: JScrollPane, val table: WeightBasedTable, val model: DefaultTableModel) : JFrame("Editing $label Drops"){
init {
layout = BorderLayout()
val panel = JPanel()
panel.layout = BorderLayout()
val topPanel = JPanel()
val mainLabel = JLabel("$label Drops")
topPanel.add(mainLabel)
val addButton = JButton("Add Drop")
addButton.addActionListener {
ItemMenu.caller = {id,name ->
when(label){
"Always" -> {
model.addRow(arrayOf(id,name,1,1))
table.add(WeightedItem(id,"1","1",1.0,true))
}
else -> {
model.addRow(arrayOf(id,name,1,1,1.0,""))
table.add(WeightedItem(id,"1","1",1.0,false))
updatePercentages(model)
}
}
}
ItemMenu.open()
}
topPanel.add(addButton)
panel.add(topPanel,BorderLayout.NORTH)
panel.add(pane,BorderLayout.SOUTH)
add(panel)
pack()
minimumSize = Dimension(width + 40,height + 50)
isVisible = false
}
fun open(){
isVisible = true
}
fun updatePercentages(model: DefaultTableModel){
for(i in 0 until model.rowCount){
model.setValueAt(getPercentage(table[i].weight,table),i,5)
}
}
fun getPercentage(weight: Double, table: WeightBasedTable): String{
return "${round((weight / table.totalWeight) * 100.0,2)}%"
}
fun round(value: Double, places: Int): Double {
require(places >= 0)
var bd: BigDecimal = BigDecimal.valueOf(value)
bd = bd.setScale(places, RoundingMode.HALF_UP)
return bd.toDouble()
}
}
fun open(){
for(item in table.alwaysTable){
defaultModel.addRow(arrayOf(item.id,TableData.getItemName(item.id),item.minAmt,item.maxAmt))
}
for(item in table.charmTable){
charmModel.addRow(arrayOf(item.id,TableData.getItemName(item.id),item.minAmt,item.maxAmt,item.weight,getPercentage(item.weight,table.charmTable)))
}
for(item in table){
mainModel.addRow(arrayOf(item.id,TableData.getItemName(item.id),item.minAmt,item.maxAmt,item.weight,getPercentage(item.weight,table)))
}
isVisible = true
}
fun getPercentage(weight: Double, table: WeightBasedTable): String{
return "${round((weight / table.totalWeight) * 100.0,2)}%"
}
fun updateCharmPercentages(){
for(i in 0 until charmModel.rowCount){
charmModel.setValueAt(getPercentage(table.charmTable[i].weight,table.charmTable),i,5)
}
}
fun updateMainPercentages(){
for(i in 0 until mainModel.rowCount){
mainModel.setValueAt(getPercentage(table[i].weight,table),i,5)
}
}
fun round(value: Double, places: Int): Double {
require(places >= 0)
var bd: BigDecimal = BigDecimal.valueOf(value)
bd = bd.setScale(places, RoundingMode.HALF_UP)
return bd.toDouble()
}
}

View file

@ -0,0 +1,21 @@
import com.github.weisj.darklaf.LafManager
import com.github.weisj.darklaf.theme.OneDarkTheme
object EditorConstants {
var BUILD_NUMBER = "0.1.0"
var DARK_MODE = true
var CONFIG_PATH = ""
var VALID_FILES = arrayOf("drop_tables.json","npc_configs.json","item_configs.json", "shops.json", "object_configs.json")
var CREDITS = arrayOf(
"weisJ - darklaf look and feel themes http://github.com/weisj",
"ceikry - design and programming http://gitlab.com/ceikry"
)
fun updateTheme(){
if(EditorConstants.DARK_MODE) LafManager.install(OneDarkTheme()) else LafManager.install()
}
}

View file

@ -0,0 +1,19 @@
import org.json.simple.JSONArray
import org.json.simple.parser.JSONParser
import java.io.File
import java.io.FileReader
open class EditorData(val fileName: String) {
var reader: FileReader? = null// = FileReader(EditorConstants.CONFIG_PATH + File.separator + fileName)
val parser = JSONParser()
var data = JSONArray()
fun configureParser(){
reader = FileReader(EditorConstants.CONFIG_PATH + File.separator + fileName)
data = parser.parse(reader) as JSONArray
}
open fun parse() {}
open fun save() {}
open fun show() {}
}

269
src/main/kotlin/Editors.kt Normal file
View file

@ -0,0 +1,269 @@
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.IOException
import java.lang.Exception
import java.lang.StringBuilder
import javax.script.ScriptEngineManager
enum class Editors(val data: EditorData) {
DROP_TABLES(object : EditorData("drop_tables.json"){
var counter = 0
override fun parse() {
Logger.logInfo("Parsing Drop Tables")
configureParser()
data.forEach { dropTableRaw ->
val dropTable = dropTableRaw as org.json.simple.JSONObject
val table = NPCDropTable()
table.ids = dropTable["ids"].toString()
parseTable(dropTable["main"] as JSONArray,table,false)
parseTable(dropTable["default"] as JSONArray,table,true)
parseTable(dropTable["charm"] as JSONArray,table,false)
table.description = (dropTable["description"] ?: "").toString()
TableData.tables.add(table)
counter++
}
Logger.logInfo("$counter Drop Tables Parsed.")
}
override fun save() {
val CHARMS = intArrayOf(12160,12163,12159,12158)
val dtables = JSONArray()
for(table in TableData.tables){
val t = JSONObject()
t.put("ids",table.ids)
val main = saveTable(table)
val charms = saveTable(table.charmTable)
val always = saveTable(table.alwaysTable)
t.put("main",main)
t.put("charm",charms)
t.put("default",always)
t.put("description",table.description)
dtables.add(t)
}
val manager = ScriptEngineManager()
val scriptEngine = manager.getEngineByName("JavaScript")
scriptEngine.put("jsonString", dtables.toJSONString())
scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
val prettyPrintedJson = scriptEngine["result"] as String
try {
FileWriter(EditorConstants.CONFIG_PATH + File.separator + fileName).use { file ->
file.write(prettyPrintedJson)
file.flush()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
override fun show() {
super.show()
}
}),
NPC_CONFIGS(object : EditorData("npc_configs.json"){
override fun parse() {
Logger.logInfo("Parsing NPC configs")
configureParser()
data.forEach { npcDataRaw ->
val npcData = npcDataRaw as JSONObject
val id = npcData["id"].toString().toInt()
val name = npcData["name"].toString()
TableData.npcNames[id] = name
TableData.npcConfigKeys.addAll(npcData.keys.toHashSet() as HashSet<String>)
TableData.npcConfigs.add(npcData)
}
}
override fun save() {
val array = JSONArray()
TableData.npcConfigs.forEach {
array.add(it)
}
val manager = ScriptEngineManager()
val scriptEngine = manager.getEngineByName("JavaScript")
scriptEngine.put("jsonString", array.toJSONString())
scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
val prettyPrintedJson = scriptEngine["result"] as String
try {
FileWriter(EditorConstants.CONFIG_PATH + File.separator + fileName).use { file ->
file.write(prettyPrintedJson)
file.flush()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
}),
ITEM_CONFIGS(object : EditorData("item_configs.json"){
override fun parse() {
configureParser()
data.forEach { itemDataRaw ->
val itemData = itemDataRaw as JSONObject
val id = itemData["id"].toString().toInt()
val name = itemData["name"].toString()
TableData.itemConfigKeys.addAll(itemData.keys.toHashSet() as HashSet<String>)
TableData.itemNames[id] = name
TableData.itemConfigs.add(itemData)
}
}
override fun save() {
val array = JSONArray()
TableData.itemConfigs.forEach {
array.add(it)
}
val manager = ScriptEngineManager()
val scriptEngine = manager.getEngineByName("JavaScript")
scriptEngine.put("jsonString", array.toJSONString())
scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
val prettyPrintedJson = scriptEngine["result"] as String
try {
FileWriter(EditorConstants.CONFIG_PATH + File.separator + fileName).use { file ->
file.write(prettyPrintedJson)
file.flush()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
}),
OBJECT_CONFIGS(object : EditorData("object_configs.json"){
override fun parse() {
Logger.logInfo("Parsing Object configs")
configureParser()
data.forEach { ObjDataRaw ->
val objData = ObjDataRaw as JSONObject
TableData.objConfigKeys.addAll(objData.keys.toHashSet() as HashSet<String>)
TableData.objConfigs.add(objData)
}
}
override fun save() {
val array = JSONArray()
TableData.objConfigs.forEach {
array.add(it)
}
val manager = ScriptEngineManager()
val scriptEngine = manager.getEngineByName("JavaScript")
scriptEngine.put("jsonString", array.toJSONString())
scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
val prettyPrintedJson = scriptEngine["result"] as String
try {
FileWriter(EditorConstants.CONFIG_PATH + File.separator + fileName).use { file ->
file.write(prettyPrintedJson)
file.flush()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
}),
SHOPS(object : EditorData("shops.json"){
override fun parse() {
Logger.logInfo("Parsing shop data...")
configureParser()
var counter = 0
data.forEach { shopDataRaw ->
val shopData = shopDataRaw as JSONObject
val id = shopData["id"].toString().toInt()
val title = shopData["title"].toString()
val general = shopData["general_store"].toString().toBoolean()
val stock = parseStock(shopData["stock"].toString())
val npcs = if(shopData["npcs"].toString().isNotBlank()) shopData["npcs"].toString() else ""
val currency = shopData["currency"].toString().toInt()
val highAlch = shopData["high_alch"].toString() == "1"
TableData.shops[id] = TableData.Shop(id,title,stock,npcs,currency,general,highAlch)
counter++
}
Logger.logInfo("Loaded $counter shops.")
}
override fun save() {
val shs = JSONArray()
fun <T> ArrayList<T>.isLast(thing: T): Boolean{
return indexOf(thing) == size - 1
}
for((_,shop) in TableData.shops){
val sh = JSONObject()
sh.put("id",shop.id.toString())
sh.put("title",shop.title)
val stockBuilder = StringBuilder()
for(item in shop.stock){
stockBuilder.append("{${item.id},${if(item.infinite) "inf" else item.amount}}${if(shop.stock.isLast(item)) "" else "-"}")
}
sh.put("stock",stockBuilder.toString())
sh.put("npcs",shop.npcs)
sh.put("currency",shop.currency.toString())
sh.put("high_alch",if(shop.high_alch) "1" else "0")
sh.put("general_store",shop.general_store.toString())
shs.add(sh)
}
val manager = ScriptEngineManager()
val scriptEngine = manager.getEngineByName("JavaScript")
scriptEngine.put("jsonString", shs.toJSONString())
scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
val prettyPrintedJson = scriptEngine["result"] as String
try {
FileWriter(EditorConstants.CONFIG_PATH + File.separator + fileName).use { file ->
file.write(prettyPrintedJson)
file.flush()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
})
}
fun parseStock(stock: String): ArrayList<Item>{
val items = ArrayList<Item>()
if(stock.isEmpty()){
return items
}
stock.split('-').map {
val tokens = it.replace("{", "").replace("}", "").split(",".toRegex()).toTypedArray()
var amount = tokens[1].trim()
items.add(Item(tokens[0].trim().toInt(),amount,amount == "inf"))
}
return items
}
private fun parseTable(data: JSONArray, destTable: WeightBasedTable, isAlways: Boolean) {
for(it in data){
val item = it as JSONObject
val id = item["id"].toString().toInt()
val minAmount = item["minAmount"].toString().toInt()
val maxAmount = item["maxAmount"].toString().toInt()
val weight = item["weight"].toString().toDouble()
val newItem = WeightedItem(id,minAmount.toString(),maxAmount.toString(),weight.toDouble(),isAlways)
destTable.add(newItem)
}
}
private fun saveTable(table: WeightBasedTable): JSONArray{
val arr = JSONArray()
for(item in table){
val it = JSONObject()
it.put("id",item.id.toString())
it.put("weight",item.weight.toString())
it.put("minAmount",item.minAmt.toString())
it.put("maxAmount",item.maxAmt.toString())
arr.add(it)
}
return arr
}

View file

@ -0,0 +1,106 @@
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser
import java.io.FileReader
import java.lang.Exception
import javax.swing.*
object FileLoader{
val loadStatus = JLabel("File not loaded.")
val loadButton = JButton("Select drop_tables.json")
val chooser = JFileChooser()
var dropTableList: TableList? = null
private fun parseTable(data: JSONArray, destTable: WeightBasedTable, isAlways: Boolean) {
for(it in data){
val item = it as JSONObject
val id = item["id"].toString().toInt()
val minAmount = item["minAmount"].toString().toInt()
val maxAmount = item["maxAmount"].toString().toInt()
val weight = item["weight"].toString().toDouble()
val newItem = WeightedItem(id,minAmount.toString(),maxAmount.toString(),weight.toDouble(),isAlways)
destTable.add(newItem)
}
}
fun load(){
Logger.logInfo("Parsing Drop Tables...")
var parser = JSONParser()
var reader = FileReader(EditorConstants.CONFIG_PATH + "/drop_tables.json")
var data = parser.parse(reader) as JSONArray
var counter = 0
data.forEach { dropTableRaw ->
val dropTable = dropTableRaw as JSONObject
val table = NPCDropTable()
table.ids = dropTable["ids"].toString()
parseTable(dropTable["main"] as JSONArray,table,false)
parseTable(dropTable["default"] as JSONArray,table,true)
parseTable(dropTable["charm"] as JSONArray,table,false)
table.description = (dropTable["description"] ?: "").toString()
TableData.tables.add(table)
counter++
}
Logger.logInfo("Parsed Drop Tables.")
Logger.logInfo("Loaded $counter drop tables.")
Logger.logInfo("Parsing Item Configs...")
try {
data = parser.parse(FileReader(EditorConstants.CONFIG_PATH + "/item_configs.json")) as JSONArray
data.forEach { itemDataRaw ->
val itemData = itemDataRaw as JSONObject
val id = itemData["id"].toString().toInt()
val name = itemData["name"].toString()
TableData.itemNames[id] = name
}
Logger.logInfo("Parsed item configs.")
} catch (e: Exception){
Logger.logErr("Failed to load item_configs.json. Item names will be unavailable when viewing and editing shops.")
}
Logger.logInfo("Parsing NPC configs...")
try {
data = parser.parse(FileReader(EditorConstants.CONFIG_PATH + "/npc_configs.json")) as JSONArray
data.forEach { npcDataRaw ->
val npcData = npcDataRaw as JSONObject
val id = npcData["id"].toString().toInt()
val name = npcData["name"].toString()
TableData.npcNames[id] = name
}
Logger.logInfo("Parsed NPC configs")
} catch (e: Exception){
Logger.logErr("Failed to load npc_configs.json. Drop tables will lack proper naming. Good luck figuring out what you want to edit.")
}
}
}
class Item(var id: Int,var amount: String,var infinite: Boolean)
class WeightedItem(var id: Int, var minAmt: String, var maxAmt: String, var weight: Double, var isAlways: Boolean)
private val CHARMS = intArrayOf(12160,12163,12159,12158)
open class WeightBasedTable() : ArrayList<WeightedItem>(){
var totalWeight: Double = 0.0
override fun add(element: WeightedItem): Boolean {
totalWeight += element.weight
return super.add(element)
}
}
class NPCDropTable() : WeightBasedTable(){
val charmTable = WeightBasedTable()
val alwaysTable = WeightBasedTable()
var ids = ""
var description = ""
override fun add(element: WeightedItem): Boolean {
if(element.isAlways){
alwaysTable.add(element)
return true
}
if(CHARMS.contains(element.id)){
charmTable.add(element)
return true
}
return super.add(element)
}
}

View file

@ -0,0 +1,57 @@
import java.awt.BorderLayout
import java.awt.Dimension
import javax.swing.*
val confirmation = object : JFrame("Notice"){
init {
layout = BorderLayout()
minimumSize = Dimension(250,100)
defaultCloseOperation = HIDE_ON_CLOSE
val okButton = JButton("OK")
okButton.addActionListener {
isVisible = false
}
add(JLabel("Added a new row to the table."), BorderLayout.NORTH)
add(okButton, BorderLayout.SOUTH)
setLocationRelativeTo(null)
isVisible = false
pack()
}
}
val saveConfirmation = object : JFrame("Notice"){
init {
minimumSize = Dimension(250,100)
defaultCloseOperation = HIDE_ON_CLOSE
layout = BorderLayout()
val okButton = JButton("OK")
okButton.addActionListener {
isVisible = false
}
add(JLabel("Data saved."), BorderLayout.NORTH)
add(okButton, BorderLayout.SOUTH)
isVisible = false
setLocationRelativeTo(null)
pack()
}
}
val credits = object : JFrame("Credits"){
init {
defaultCloseOperation = HIDE_ON_CLOSE
val creditPanel = JPanel()
creditPanel.layout = BoxLayout(creditPanel, BoxLayout.PAGE_AXIS)
layout = BorderLayout()
for(credit in EditorConstants.CREDITS){
creditPanel.add(JLabel(credit))
}
add(creditPanel)
isVisible = false
setLocationRelativeTo(null)
pack()
}
}

100
src/main/kotlin/ItemMenu.kt Normal file
View file

@ -0,0 +1,100 @@
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.event.KeyEvent
import java.awt.event.KeyListener
import java.awt.event.MouseEvent
import java.awt.event.MouseListener
import java.util.*
import java.util.regex.PatternSyntaxException
import javax.swing.*
import javax.swing.table.DefaultTableCellRenderer
import javax.swing.table.DefaultTableModel
import javax.swing.table.TableRowSorter
object ItemMenu : JFrame("Item Selection Menu") {
var model = DefaultTableModel()
var searchField = JTextField()
var amountField = JTextField()
var sorter = TableRowSorter(model)
var cellRenderer = DefaultTableCellRenderer()
var populated = false
var caller: ((Int,String) -> Unit)? = null
private fun filter() {
var rf: RowFilter<DefaultTableModel?, Any?>? = null
//If current expression doesn't parse, don't update.
rf = try {
RowFilter.regexFilter(searchField.text, 1)
} catch (e: PatternSyntaxException) {
return
}
sorter.rowFilter = rf
}
fun open() {
if (!populated) {
SwingUtilities.invokeLater {
for ((id,_) in TableData.itemNames) {
model.addRow(arrayOf(id,TableData.getItemName(id)))
}
isVisible = true
populated = true
}
} else {
isVisible = true
}
}
init {
layout = BorderLayout()
cellRenderer.toolTipText = "Double-Click to select."
val searchPanel = JPanel()
val searchLabel = JLabel("Search for Item:")
val amountLabel = JLabel("Amount: ")
amountField.preferredSize = Dimension(45, 20)
searchField.preferredSize = Dimension(100, 20)
searchPanel.add(searchLabel)
searchPanel.add(searchField)
add(searchPanel, BorderLayout.NORTH)
setLocationRelativeTo(null)
val itemTable: JTable = object : JTable(model) {
override fun editCellAt(i: Int, i1: Int, eventObject: EventObject): Boolean {
return false
}
}
model.addColumn("ID")
model.addColumn("Name")
itemTable.rowSorter = sorter
itemTable.columnModel.getColumn(0).maxWidth = 55
itemTable.columnModel.getColumn(0).cellRenderer = cellRenderer
itemTable.columnModel.getColumn(1).cellRenderer = cellRenderer
itemTable.addMouseListener(object : MouseListener {
override fun mouseClicked(mouseEvent: MouseEvent) {
if (mouseEvent.clickCount == 2) {
val table = mouseEvent.source as JTable
val row = table.selectedRow
val selectedID = table.getValueAt(row,0).toString().toInt()
val selectedName = table.getValueAt(row,1).toString()
caller?.invoke(selectedID,selectedName)
isVisible = false
}
}
override fun mousePressed(mouseEvent: MouseEvent) {}
override fun mouseReleased(mouseEvent: MouseEvent) {}
override fun mouseEntered(mouseEvent: MouseEvent) {}
override fun mouseExited(mouseEvent: MouseEvent) {}
})
searchField.addKeyListener(object : KeyListener {
override fun keyTyped(keyEvent: KeyEvent) {
filter()
}
override fun keyPressed(keyEvent: KeyEvent) {}
override fun keyReleased(keyEvent: KeyEvent) {}
})
val scrollPane = JScrollPane(itemTable)
add(scrollPane, BorderLayout.SOUTH)
pack()
isVisible = false
}
}

21
src/main/kotlin/Logger.kt Normal file
View file

@ -0,0 +1,21 @@
import com.github.ajalt.mordant.rendering.TextColors
import com.github.ajalt.mordant.terminal.Terminal
import java.text.SimpleDateFormat
import java.util.*
object Logger {
val t = Terminal()
val formatter = SimpleDateFormat("HH:mm:ss")
fun logInfo(message: String){
t.println("${getTime()} ${TextColors.white("[INFO] $message")}")
}
fun logErr(message: String){
t.println("${getTime()} ${TextColors.red("[ ERR] $message")}")
}
fun getTime(): String{
return "[" + formatter.format(Date(System.currentTimeMillis())) +"]"
}
}

View file

@ -0,0 +1,143 @@
import com.github.weisj.darklaf.LafManager
import com.github.weisj.darklaf.theme.DarculaTheme
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.FlowLayout
import java.io.File
import java.util.*
import javax.swing.*
import javax.swing.table.DefaultTableModel
object MainScreen : JFrame("RS09 Thanos Tool") {
val dirChooser = object : JFileChooser(){
override fun updateUI() {
EditorConstants.updateTheme()
super.updateUI()
}
}
val selectDir = JButton("Select Config Folder")
val openDrops = JButton("Edit Drop Tables")
val openNPC = JButton("Edit NPC Configs")
val openItem = JButton("Edit Item Configs")
val openShops = JButton("Edit Shops")
val openObjects = JButton("Edit Object Configs")
var loadedModel = object : DefaultTableModel(){}
val loadedTable = object : JTable(loadedModel){
override fun editCellAt(p0: Int, p1: Int): Boolean {
return false
}
override fun editCellAt(p0: Int, p1: Int, p2: EventObject?): Boolean {
return false
}
}
val pane = JScrollPane(loadedTable)
init {
isResizable = false
layout = BorderLayout()
dirChooser.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
defaultCloseOperation = EXIT_ON_CLOSE
preferredSize = Dimension(550,300)
loadedModel.addColumn("Filename")
iconImage = ImageIcon(javaClass.getResource("/thanos.png")).image
selectDir.addActionListener {
val response = dirChooser.showOpenDialog(null)
EditorConstants.updateTheme()
if(response == JFileChooser.APPROVE_OPTION){
Logger.logInfo("Selected directory: ${dirChooser.selectedFile.absolutePath}")
EditorConstants.CONFIG_PATH = dirChooser.selectedFile.absolutePath
showLoaded()
}
}
openDrops.addActionListener {
FileLoader.dropTableList = TableList()
FileLoader.dropTableList!!.open()
}
openItem.addActionListener {
TableEditor(Editors.ITEM_CONFIGS).open()
}
openNPC.addActionListener {
TableEditor(Editors.NPC_CONFIGS).open()
}
openObjects.addActionListener {
TableEditor(Editors.OBJECT_CONFIGS).open()
}
openShops.addActionListener {
ShopList().open()
}
val showCredits = JButton("Show Credits")
showCredits.addActionListener {
credits.isVisible = true
}
val editorPanel = JPanel()
val selectionPanel = JPanel()
editorPanel.layout = BoxLayout(editorPanel, BoxLayout.Y_AXIS)//BoxLayout(editorPanel, BoxLayout.PAGE_AXIS)
for(i in arrayOf(openDrops, openNPC, openItem, openObjects, openShops, showCredits)){
i.preferredSize = Dimension(175,40)
i.maximumSize = Dimension(175,40)
i.isEnabled = i == showCredits
editorPanel.add(i)
}
add(editorPanel, BorderLayout.WEST)
val buttonPane = JPanel()
buttonPane.layout = BorderLayout()
selectDir.preferredSize = Dimension(300,40)
selectDir.maximumSize = Dimension(300,40)
buttonPane.add(selectDir, BorderLayout.CENTER)
selectionPanel.layout = BoxLayout(selectionPanel, BoxLayout.Y_AXIS)
selectionPanel.add(buttonPane)
pane.preferredSize = Dimension(225,200)
selectionPanel.add(pane)
add(JSeparator(SwingConstants.VERTICAL), BorderLayout.CENTER)
setLocationRelativeTo(null)
add(selectionPanel, BorderLayout.EAST)
EditorConstants.updateTheme()
pack()
}
}
fun showLoaded(){
MainScreen.loadedModel = object : DefaultTableModel(){}
MainScreen.loadedModel.addColumn("Filename")
MainScreen.loadedTable.model = MainScreen.loadedModel
val files = File(EditorConstants.CONFIG_PATH).listFiles() ?: return
for(file in files){
if(file.name in EditorConstants.VALID_FILES){
MainScreen.loadedModel.addRow(arrayOf(file.name))
when(file.nameWithoutExtension){
"drop_tables" -> MainScreen.openDrops.isEnabled = true.also { Editors.DROP_TABLES.data.parse() }
"npc_configs" -> MainScreen.openNPC.isEnabled = true.also { Editors.NPC_CONFIGS.data.parse() }
"item_configs" -> MainScreen.openItem.isEnabled = true.also { Editors.ITEM_CONFIGS.data.parse() }
"shops" -> MainScreen.openShops.isEnabled = true.also { Editors.SHOPS.data.parse() }
"object_configs" -> MainScreen.openObjects.isEnabled = true.also { Editors.OBJECT_CONFIGS.data.parse()}
}
}
}
MainScreen.pane.revalidate()
MainScreen.pane.repaint()
}
fun main() {
MainScreen.isVisible = true
}

170
src/main/kotlin/ShopEdit.kt Normal file
View file

@ -0,0 +1,170 @@
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.event.*
import java.util.*
import javax.swing.*
import javax.swing.event.ChangeEvent
import javax.swing.table.DefaultTableModel
import kotlin.collections.ArrayList
class ShopEdit(val shopID: Int) : JFrame("Edit Shop") {
val shop = TableData.shops[shopID]
val idField = JTextField()
val titleField = JTextField()
val currencyField = JTextField()
val generalCheckbox = JCheckBox("General Store")
val highAlchCheckbox = JCheckBox("High Alch Prices")
var model = DefaultTableModel()
init {
layout = BorderLayout()
val topPanel = JPanel()
val idLabel = JLabel("ID: ")
idField.preferredSize = Dimension(40, 20)
idField.text = shopID.toString()
idField.addKeyListener(object : KeyListener{
override fun keyTyped(e: KeyEvent?) {}
override fun keyPressed(e: KeyEvent?) {}
override fun keyReleased(e: KeyEvent?) {
shop?.id = idField.text.toInt()
}
})
topPanel.add(idLabel)
topPanel.add(idField)
val currencyLabel = JLabel("Currency: ")
currencyField.preferredSize = Dimension(50,20)
currencyField.text = shop?.currency.toString()
currencyField.addKeyListener(object : KeyListener{
override fun keyTyped(e: KeyEvent?) {}
override fun keyPressed(e: KeyEvent?) {}
override fun keyReleased(e: KeyEvent?) {
shop?.currency = currencyField.text.toInt()
}
})
topPanel.add(currencyLabel)
topPanel.add(currencyField)
val titleLabel = JLabel("Title: ")
titleField.preferredSize = Dimension(300,20)
titleField.text = shop?.title
titleField.addKeyListener(object : KeyListener{
override fun keyTyped(e: KeyEvent?) {}
override fun keyPressed(e: KeyEvent?) {}
override fun keyReleased(e: KeyEvent?) {
shop?.title = titleField.text
}
})
topPanel.add(titleLabel)
topPanel.add(titleField)
add(topPanel,BorderLayout.NORTH)
val checkPanel = JPanel()
val npcLabel = JLabel("NPCs: ")
val npcField = JTextField()
npcField.addKeyListener(object : KeyListener{
override fun keyTyped(e: KeyEvent?) {
shop?.npcs = npcField.text + e?.keyChar
}
override fun keyPressed(e: KeyEvent?) {}
override fun keyReleased(e: KeyEvent?) {}
})
npcField.preferredSize = Dimension(50,20)
npcField.text = shop?.npcs
checkPanel.add(npcLabel)
checkPanel.add(npcField)
generalCheckbox.isSelected = shop?.general_store ?: false
generalCheckbox.addChangeListener {
shop!!.general_store = !shop.general_store
}
checkPanel.add(generalCheckbox)
highAlchCheckbox.isBorderPaintedFlat = shop?.high_alch ?: false
highAlchCheckbox.isSelected = shop?.high_alch ?: false
highAlchCheckbox.addChangeListener {
shop!!.high_alch = !shop.high_alch
}
checkPanel.add(highAlchCheckbox)
val addButton = JButton("Add Item")
addButton.addActionListener {
ItemMenu.caller = {id,name ->
model.addRow(arrayOf(name,id,0,false))
shop!!.stock.add(Item(id,"0",false))
}
ItemMenu.open()
}
checkPanel.add(addButton)
add(checkPanel,BorderLayout.CENTER)
val itemTable: JTable = object : JTable(model) {
override fun editCellAt(row: Int, column: Int): Boolean {
if(column == 0 || column == 1){
ItemMenu.caller = {id,name ->
model.setValueAt(name,row,0)
model.setValueAt(id,row,1)
shop!!.stock[row].id = id
}
ItemMenu.open()
return false
}
return super.editCellAt(row, column)
}
override fun editCellAt(row: Int, column: Int, e: EventObject?): Boolean {
if(column == 0 || column == 1){
ItemMenu.caller = {id,name ->
model.setValueAt(name,row,0)
model.setValueAt(id,row,1)
shop!!.stock[row].id = id
}
ItemMenu.open()
return false
}
return super.editCellAt(row, column, e)
}
override fun editingStopped(e: ChangeEvent?) {
val editor = cellEditor
val newValue = editor.cellEditorValue.toString()
when(editingColumn){
1 -> TableData.shops[shopID]!!.stock[editingRow].id = newValue.toInt()
2 -> TableData.shops[shopID]!!.stock[editingRow].amount = newValue
3 -> TableData.shops[shopID]!!.stock[editingRow].infinite = newValue.toBoolean()
}
super.editingStopped(e)
}
}
itemTable.putClientProperty("terminateEditOnFocusLost", true);
itemTable.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete")
itemTable.actionMap.put("delete",object : AbstractAction() {
override fun actionPerformed(e: ActionEvent?) {
try {
shop!!.stock.removeAt(itemTable.selectedRow)
model.removeRow(itemTable.selectedRow)
} catch(e: Exception){
Logger.logErr("Tried to remove nonexistent row ${itemTable.selectedRow}")
}
}
})
model.addColumn("Name")
model.addColumn("ID")
model.addColumn("Amount")
model.addColumn("Infinite?")
val scrollPane = JScrollPane(itemTable)
add(scrollPane,BorderLayout.SOUTH)
pack()
isVisible = false
}
fun open(){
for(item in shop?.stock ?: ArrayList()){
model.addRow(arrayOf(TableData.itemNames[item.id] ?: "unknown",item.id,item.amount,item.infinite))
}
isVisible = true
}
}

131
src/main/kotlin/ShopList.kt Normal file
View file

@ -0,0 +1,131 @@
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.event.*
import java.util.*
import java.util.regex.PatternSyntaxException
import javax.swing.*
import javax.swing.table.DefaultTableCellRenderer
import javax.swing.table.DefaultTableModel
import javax.swing.table.TableRowSorter
import kotlin.collections.ArrayList
class ShopList : JFrame("RS09 Shop Editor") {
val searchField = JTextField()
var model = DefaultTableModel()
var sorter = TableRowSorter(model)
var cellRenderer = DefaultTableCellRenderer()
var populated = false
init {
isVisible = false
layout = BorderLayout()
val searchPanel = JPanel()
val searchLabel = JLabel("Search for shop: ")
val addButton = JButton("Add Shop")
val saveButton = JButton("Save Shops")
addButton.addActionListener {
val highestID = TableData.shops.keys.toIntArray()[TableData.shops.size - 1]
TableData.shops.put(highestID + 1, TableData.Shop(highestID + 1,"", ArrayList(),"",995,false,false))
ShopEdit(highestID + 1).open()
}
saveButton.addActionListener {
Editors.SHOPS.data.save()
saveConfirmation.isVisible = true
}
searchField.preferredSize = Dimension(100, 20)
searchPanel.add(searchLabel)
searchPanel.add(searchField)
searchPanel.add(addButton)
searchPanel.add(saveButton)
add(searchPanel, BorderLayout.NORTH)
setLocationRelativeTo(null)
val itemTable: JTable = object : JTable(model) {
override fun editCellAt(i: Int, i1: Int, eventObject: EventObject): Boolean {
return false
}
}
itemTable.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete")
itemTable.actionMap.put("delete",object : AbstractAction() {
override fun actionPerformed(e: ActionEvent?) {
try {
val table = e?.source as JTable
TableData.shops.remove(table.getValueAt(table.selectedRow,0))
model.removeRow(table.convertRowIndexToModel(table.selectedRow))
} catch(e: Exception){
Logger.logErr("Tried to remove nonexistent row ${itemTable.selectedRow}")
}
}
})
model.addColumn("ID")
model.addColumn("Name")
itemTable.rowSorter = sorter
itemTable.columnModel.getColumn(0).maxWidth = 55
itemTable.columnModel.getColumn(0).cellRenderer = cellRenderer
itemTable.columnModel.getColumn(1).cellRenderer = cellRenderer
itemTable.addMouseListener(object : MouseListener {
override fun mouseClicked(mouseEvent: MouseEvent) {
if (mouseEvent.clickCount == 2) {
val table = mouseEvent.source as JTable
val row = table.selectedRow
ShopEdit(table.getValueAt(row,0) as Int).open()
}
}
override fun mousePressed(mouseEvent: MouseEvent) {}
override fun mouseReleased(mouseEvent: MouseEvent) {}
override fun mouseEntered(mouseEvent: MouseEvent) {}
override fun mouseExited(mouseEvent: MouseEvent) {}
})
searchField.addKeyListener(object : KeyListener {
override fun keyTyped(keyEvent: KeyEvent) {
filter()
}
override fun keyPressed(keyEvent: KeyEvent) {}
override fun keyReleased(keyEvent: KeyEvent) {}
})
val scrollPane = JScrollPane(itemTable)
add(scrollPane, BorderLayout.SOUTH)
pack()
isVisible = false
}
private fun filter() {
var rf: RowFilter<DefaultTableModel?, Any?>? = null
//If current expression doesn't parse, don't update.
rf = try {
RowFilter.regexFilter(searchField.text, 1)
} catch (e: PatternSyntaxException) {
return
}
sorter.rowFilter = rf
}
fun open() {
if (!populated) {
Logger.logInfo("Loading shops data...")
for(i in 0 until model.rowCount){
model.removeRow(0)
}
SwingUtilities.invokeLater {
for ((id,shop) in TableData.shops) {
model.addRow(arrayOf<Any>(id, shop.title))
}
isVisible = true
populated = true
}
} else {
isVisible = true
}
}
}

View file

@ -0,0 +1,33 @@
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.lang.StringBuilder
import javax.script.ScriptEngineManager
object TableData {
class Shop(var id: Int,var title: String,var stock: ArrayList<Item>,var npcs: String,var currency: Int,var general_store: Boolean,var high_alch: Boolean)
val tables = ArrayList<NPCDropTable>()
val itemNames = hashMapOf<Int,String>()
val npcNames = hashMapOf<Int,String>()
val npcConfigKeys = HashSet<String>()
val npcConfigs = ArrayList<JSONObject>()
val objConfigKeys = HashSet<String>()
val objConfigs = ArrayList<JSONObject>()
var itemConfigKeys = HashSet<String>()
var itemConfigs = ArrayList<JSONObject>()
val shops = hashMapOf<Int,Shop>()
fun getItemName(id: Int): String{
return when(id){
31 -> "RDT Slot"
1 -> "Clue Scroll (easy)"
5733 -> "Clue Scroll (med)"
12070 -> "Clue Scroll (hard)"
0 -> "Nothing"
else -> itemNames[id] ?: "Unknown"
}
}
}

View file

@ -0,0 +1,152 @@
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.event.ActionEvent
import java.awt.event.KeyEvent
import java.awt.event.KeyListener
import java.util.regex.PatternSyntaxException
import javax.swing.*
import javax.swing.event.TableModelEvent
import javax.swing.event.TableModelListener
import javax.swing.table.DefaultTableModel
import javax.swing.table.TableRowSorter
/**
* Represents a generic JTable-based editor. Pretty much just a cleaner visualisation of JSON objects with editing functionality.
*/
class TableEditor(val editor: Editors) : JFrame("Editing ${editor.name.replace("_"," ").toLowerCase()}") {
val searchField = JTextField()
val model = DefaultTableModel()
var table = JTable(model)
val sorter = TableRowSorter(model)
init {
layout = BorderLayout()
isVisible = false
table.autoResizeMode = JTable.AUTO_RESIZE_OFF
searchField.preferredSize = Dimension(200,20)
searchField.maximumSize = Dimension(200,20)
minimumSize = Dimension(1000, 650)
searchField.addKeyListener(object : KeyListener{
override fun keyTyped(p0: KeyEvent?) { filter() }
override fun keyPressed(p0: KeyEvent?) {}
override fun keyReleased(p0: KeyEvent?) {}
})
table.rowSorter = sorter
val addButton = JButton("Add Row")
val saveButton = JButton("Save Data")
addButton.addActionListener {
getData().add(JSONObject())
model.addRow(arrayOf())
confirmation.isVisible = true
}
saveButton.addActionListener {
editor.data.save()
saveConfirmation.isVisible = true
}
val searchPanel = JPanel()
searchPanel.layout = FlowLayout()
searchPanel.add(JLabel("Search by Name "))
searchPanel.add(searchField)
searchPanel.add(addButton)
searchPanel.add(saveButton)
val pane = JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS)
add(pane, BorderLayout.CENTER)
add(searchPanel, BorderLayout.NORTH)
val keys = when (editor) {
Editors.NPC_CONFIGS -> TableData.npcConfigKeys
Editors.ITEM_CONFIGS -> TableData.itemConfigKeys
Editors.OBJECT_CONFIGS -> TableData.objConfigKeys
else -> ArrayList<JSONObject>()
}
if(editor == Editors.OBJECT_CONFIGS){
model.addColumn("ids")
model.addColumn("examine")
keys.remove("ids")
keys.remove("examine")
table.columnModel.getColumn(0).preferredWidth = 400
table.columnModel.getColumn(1).preferredWidth = 600
} else {
model.addColumn("id")
model.addColumn("name")
keys.remove("id")
keys.remove("name")
}
keys.sortedBy { it.toString() }.forEach { model.addColumn(it) }
if(editor != Editors.OBJECT_CONFIGS) {
table.columnModel.getColumn(1).preferredWidth = 150
}
model.addTableModelListener(object : TableModelListener{
override fun tableChanged(event: TableModelEvent?) {
event ?: return
if(event.type != TableModelEvent.UPDATE) return
val label = model.getColumnName(event.column)
getData()[event.firstRow][label] = model.getValueAt(event.firstRow, event.column)
Logger.logInfo("Changed ${getData()[event.firstRow]["name"]}'s $label to ${model.getValueAt(event.firstRow,event.column)}")
}
})
table.actionMap.put("delete",object : AbstractAction() {
override fun actionPerformed(e: ActionEvent?) {
try {
val table = e?.source as JTable
getData().removeAt(table.convertRowIndexToModel(table.selectedRow))
model.removeRow(table.convertRowIndexToModel(table.selectedRow))
} catch(e: Exception){
Logger.logErr("Tried to remove nonexistent row ${table.selectedRow}")
}
}
})
EditorConstants.updateTheme()
pack()
}
fun open(){
val data = getData()
for(datum in data){
val vals = JSONArray()
for(i in 0 until model.columnCount){
val label = model.getColumnName(i)
vals.add(datum[label] ?: "")
}
model.addRow(vals.toArray())
}
isVisible = true
}
private fun filter() {
var rf: RowFilter<DefaultTableModel?, Any?>? = null
//If current expression doesn't parse, don't update.
rf = try {
RowFilter.regexFilter(searchField.text)
} catch (e: PatternSyntaxException) {
return
}
sorter.rowFilter = rf
}
fun getData(): ArrayList<JSONObject> {
return when(editor){
Editors.ITEM_CONFIGS -> TableData.itemConfigs
Editors.NPC_CONFIGS -> TableData.npcConfigs
Editors.OBJECT_CONFIGS -> TableData.objConfigs
else -> ArrayList()
}
}
}

View file

@ -0,0 +1,126 @@
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.event.*
import java.util.*
import java.util.regex.PatternSyntaxException
import javax.swing.*
import javax.swing.table.DefaultTableCellRenderer
import javax.swing.table.DefaultTableModel
import javax.swing.table.TableRowSorter
class TableList : JFrame("RS09 Drop Table Editor") {
val searchField = JTextField()
var model = DefaultTableModel()
var sorter = TableRowSorter(model)
var cellRenderer = DefaultTableCellRenderer()
var populated = false
init {
isVisible = false
layout = BorderLayout()
val searchPanel = JPanel()
val searchLabel = JLabel("Search for table: ")
val addButton = JButton("Add Table")
val saveButton = JButton("Save Tables")
addButton.addActionListener {
TableData.tables.add(NPCDropTable())
DropTableEditor(TableData.tables.last()).open()
}
saveButton.addActionListener {
Editors.DROP_TABLES.data.save()
saveConfirmation.isVisible = true
}
searchField.preferredSize = Dimension(100, 20)
searchPanel.add(searchLabel)
searchPanel.add(searchField)
searchPanel.add(addButton)
searchPanel.add(saveButton)
add(searchPanel, BorderLayout.NORTH)
setLocationRelativeTo(null)
val itemTable: JTable = object : JTable(model) {
override fun editCellAt(i: Int, i1: Int, eventObject: EventObject): Boolean {
return false
}
}
itemTable.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete")
itemTable.actionMap.put("delete",object : AbstractAction() {
override fun actionPerformed(e: ActionEvent?) {
try {
val table = e?.source as JTable
TableData.tables.removeAt(table.convertRowIndexToModel(table.selectedRow))
model.removeRow(table.convertRowIndexToModel(table.selectedRow))
} catch(e: Exception){
Logger.logErr("Tried to remove nonexistent row ${itemTable.selectedRow}")
}
}
})
model.addColumn("Name")
itemTable.rowSorter = sorter
itemTable.columnModel.getColumn(0).cellRenderer = cellRenderer
itemTable.addMouseListener(object : MouseListener {
override fun mouseClicked(mouseEvent: MouseEvent) {
if (mouseEvent.clickCount == 2) {
val table = mouseEvent.source as JTable
val row = table.selectedRow
DropTableEditor(TableData.tables[table.convertRowIndexToModel(row)]).open()
}
}
override fun mousePressed(mouseEvent: MouseEvent) {}
override fun mouseReleased(mouseEvent: MouseEvent) {}
override fun mouseEntered(mouseEvent: MouseEvent) {}
override fun mouseExited(mouseEvent: MouseEvent) {}
})
searchField.addKeyListener(object : KeyListener {
override fun keyTyped(keyEvent: KeyEvent) {
filter()
}
override fun keyPressed(keyEvent: KeyEvent) {}
override fun keyReleased(keyEvent: KeyEvent) {}
})
val scrollPane = JScrollPane(itemTable)
add(scrollPane, BorderLayout.SOUTH)
pack()
isVisible = false
}
private fun filter() {
var rf: RowFilter<DefaultTableModel?, Any?>? = null
//If current expression doesn't parse, don't update.
rf = try {
RowFilter.regexFilter(searchField.text, 0)
} catch (e: PatternSyntaxException) {
return
}
sorter.rowFilter = rf
}
fun open() {
if (!populated) {
Logger.logInfo("Loading table data...")
for(i in 0 until model.rowCount){
model.removeRow(0)
}
SwingUtilities.invokeLater {
for (table in TableData.tables) {
model.addRow(arrayOf<Any>(if(table.ids.isNotBlank()) TableData.npcNames[table.ids.split(",")[0].toInt()] ?: "Unknown Table" else "None"))
}
isVisible = true
populated = true
}
} else {
isVisible = true
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB