Item Spawning, NPC + Item Search

This commit is contained in:
ceikry 2021-10-29 19:47:40 -05:00
parent 655ca258f2
commit 72a0a43706
9 changed files with 417 additions and 13 deletions

View file

@ -1,4 +1,5 @@
import cacheops.cache.definition.decoder.NPC
import const.Image
import misc.ImgButton
import java.awt.Color
import java.awt.Dimension
@ -41,7 +42,7 @@ class NPCSpawnPanel : JPanel() {
class NPCSpawnerPanel : JPanel() {
init {
val addButton = ImgButton("/add_hi.png", "/add_dark.png")
val addButton = ImgButton(Image.ADD_HI, Image.ADD_LO)
addButton.onClick {
Rs2MapEditor.state = EditorState.ADD_NPC
}
@ -63,7 +64,7 @@ class NPCSpawnPanel : JPanel() {
init {
layout = FlowLayout()
add(JLabel("${npc.definition.name} (LVL ${npc.definition.combat}) [${npc.x} | ${npc.y} | ${npc.plane}]"))
val deleteButton = ImgButton("/trash_hi.png", "/trash_dark.png")
val deleteButton = ImgButton(Image.DELETE_HI, Image.DELTE_LO)
add(deleteButton)
minimumSize = Dimension(300, 40)
preferredSize = Dimension(300, 40)

View file

@ -9,7 +9,9 @@ import com.formdev.flatlaf.FlatDarculaLaf
import const.Image.RED_DOT
import const.Image.YELLOW_DOT
import misc.CustomEventQueue
import misc.ImgButton
import misc.ItemSpawnPanel
import tools.ItemSearchTool
import tools.NPCSearchTool
import java.awt.*
import java.awt.event.KeyEvent
import java.awt.event.MouseAdapter
@ -25,10 +27,13 @@ object Rs2MapEditor {
var npcs = NPCSpawnParser.parseNPCSpawns(region)
var items = GroundItemSpawnParser.parseItemSpawns(region)
val npcRows = ArrayList<NPCSpawnPanel.NPCRow>()
val itemRows = ArrayList<ItemSpawnPanel.ItemRow>()
var npcsUpdated = false
var itemsUpdated = false
var tileDataUpdated = false
lateinit var mapPanel: MapPane
lateinit var npcPanel: NPCSpawnPanel
lateinit var itemPanel: ItemSpawnPanel
lateinit var infoPane: InfoPane
@JvmStatic
@ -67,6 +72,9 @@ object Rs2MapEditor {
var underlayInfo = JLabel("Right click a tile to get more information")
var underlayLabel = JLabel()
lateinit var npcIdInput: JTextField
lateinit var itemIdInput: JTextField
lateinit var itemRespawnInput: JTextField
lateinit var itemAmountInput: JTextField
var state = EditorState.NONE
class MainFrame : JFrame("Praesecho Map Editor v0.3") {
@ -86,7 +94,11 @@ object Rs2MapEditor {
jMenuBar = MenuBar()
mapPanel = MapPane()
npcIdInput = JTextField()
itemIdInput = JTextField()
itemRespawnInput = JTextField()
itemAmountInput = JTextField()
npcPanel = NPCSpawnPanel()
itemPanel = ItemSpawnPanel()
infoPane = InfoPane()
add(ScrollPane(), BorderLayout.CENTER)
add(ScrollPane2(), BorderLayout.EAST)
@ -203,8 +215,10 @@ object Rs2MapEditor {
defaultBackground = background
if(state == EditorState.SET_UNDERLAY || state == EditorState.NONE) {
background = Color.BLUE
} else if (state == EditorState.ADD_NPC || state == EditorState.ADD_GROUNDITEM){
} else if (state == EditorState.ADD_NPC){
background = Color.YELLOW
} else if (state == EditorState.ADD_GROUNDITEM){
background = Color.RED
}
}
@ -238,12 +252,34 @@ object Rs2MapEditor {
npcs.add(npc)
componentPointMap.filter { it.key.x == selectedPointX && it.key.y == selectedPointY }.forEach {(_, cell) ->
var hasLabel = false
cell.components.forEach { if(it is JLabel) hasLabel = true }
cell.layout = BorderLayout()
cell.components.forEach { if(it is JLabel && it.icon == YELLOW_DOT) hasLabel = true }
cell.layout = FlowLayout()
if(!hasLabel) cell.add(JLabel(YELLOW_DOT))
cell.repaint()
}
npcPanel.redrawRows()
npcsUpdated = true
}
else if (itemIdInput.text.isNotEmpty() && state == EditorState.ADD_GROUNDITEM){
val id = itemIdInput.text.toIntOrNull()
val amount = itemAmountInput.text.toIntOrNull() ?: 1
val respawnTime = itemRespawnInput.text.toIntOrNull() ?: 1
if(id == null){
state = EditorState.NONE
return
}
val item = Item(id, selectedPointX, selectedPointY, 0, respawnTime, amount)
items.add(item)
componentPointMap.filter { it.key.x == selectedPointX && it.key.y == selectedPointY }.forEach {(_, cell) ->
var hasLabel = false
cell.components.forEach { if(it is JLabel && it.icon == RED_DOT) hasLabel = true }
cell.layout = FlowLayout()
if(!hasLabel) cell.add(JLabel(RED_DOT))
cell.repaint()
}
itemPanel.redrawRows()
itemsUpdated = true
}
} else if (SwingUtilities.isRightMouseButton(e)) {
val point = Point(selectedPointX, selectedPointY)
@ -348,6 +384,7 @@ object Rs2MapEditor {
addTab("Overlays", panel3)
setMnemonicAt(2, KeyEvent.VK_3)
add("NPC Spawn", npcPanel)
add("Item Spawn", itemPanel)
}
}
@ -478,6 +515,38 @@ object Rs2MapEditor {
add(FileMenuOption())
add(RegionMenuOption())
add(PrefMenuOption())
add(ToolsMenuOption())
}
}
class ToolsMenuOption : JMenu("Tools") {
init {
add(NPCSearchItem())
add(ItemSearchItem())
}
}
class NPCSearchItem : JMenuItem("NPC Search") {
init {
addActionListener {
NPCSearchTool.open()
NPCSearchTool.caller = { id, _ ->
npcIdInput.text = id.toString()
infoPane.selectedIndex = 3
}
}
}
}
class ItemSearchItem : JMenuItem("Item Search"){
init {
addActionListener {
ItemSearchTool.open()
ItemSearchTool.caller = {id, _ ->
itemIdInput.text = id.toString()
infoPane.selectedIndex = 4
}
}
}
}

View file

@ -25,8 +25,8 @@ class ItemDecoder(cache: Cache) : DefinitionDecoder<ItemDefinition>(cache, Indic
fun forId(id : Int): ItemDefinition {
if(DEFINITIONS[id] != null) return DEFINITIONS[id]!!
val def = readData(id)
DEFINITIONS[id] = def!!
return def
DEFINITIONS[id] = def ?: ItemDefinition(id, 0, "null")
return DEFINITIONS[id]!!
}
override fun ItemDefinition.read(opcode: Int, buffer: Reader) {

View file

@ -25,8 +25,8 @@ class NPCDecoder(cache: Cache, val member: Boolean) : DefinitionDecoder<NPCDefin
fun forId(id : Int): NPCDefinition {
if(DEFINITIONS[id] != null) return DEFINITIONS[id]!!
val def = readData(id)
DEFINITIONS[id] = def!!
return def
DEFINITIONS[id] = def ?: NPCDefinition(id,null,"null")
return DEFINITIONS[id]!!
}
override fun NPCDefinition.read(opcode: Int, buffer: Reader) {

View file

@ -6,4 +6,8 @@ import javax.swing.ImageIcon
object Image {
val RED_DOT = ImageIcon(ImageIO.read(javaClass.getResource("/dot_red.png")))
val YELLOW_DOT = ImageIcon(ImageIO.read(javaClass.getResource("/dot_yellow.png")))
val DELETE_HI = ImageIcon(ImageIO.read(javaClass.getResource("/trash_hi.png")))
val DELTE_LO = ImageIcon(ImageIO.read(javaClass.getResource("/trash_dark.png")))
val ADD_HI = ImageIcon(ImageIO.read(javaClass.getResource("/add_hi.png")))
val ADD_LO = ImageIcon(ImageIO.read(javaClass.getResource("/add_dark.png")))
}

View file

@ -6,7 +6,7 @@ import java.awt.event.MouseListener
import javax.swing.ImageIcon
import javax.swing.JLabel
open class ImgButton(enabledURL: String, disabledURL: String = enabledURL, val autoHandleMouse: Boolean = true) : JLabel() {
open class ImgButton(enabledImg: ImageIcon, disabledImage: ImageIcon = enabledImg, val autoHandleMouse: Boolean = true) : JLabel() {
private var hoverMethod: (MouseEvent) -> Unit = {}
private var mouseLeaveMethod: (MouseEvent) -> Unit = {}
@ -14,8 +14,8 @@ open class ImgButton(enabledURL: String, disabledURL: String = enabledURL, val a
init {
isEnabled = false
icon = ImageIcon(javaClass.getResource(enabledURL))
disabledIcon = ImageIcon(javaClass.getResource(disabledURL))
icon = enabledImg
disabledIcon = disabledImage
addMouseListener(object : MouseListener {
override fun mouseClicked(p0: MouseEvent) {
onClickMethod.invoke(p0)

View file

@ -0,0 +1,129 @@
package misc
import cacheops.cache.definition.decoder.Item
import const.Image
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.BoxLayout
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.JSeparator
import javax.swing.border.Border
import javax.swing.border.MatteBorder
class ItemSpawnPanel : JPanel() {
val rowBorder = MatteBorder(1, 1, 1, 1, Color.WHITE)
init {
layout = BoxLayout(this, BoxLayout.PAGE_AXIS)
add(ItemSpawnerPanel())
Rs2MapEditor.items.forEach {
val row = ItemRow(it, this)
row.border = rowBorder
Rs2MapEditor.itemRows.add(row)
add(row)
}
}
fun redrawRows() {
Rs2MapEditor.itemRows.forEach {
remove(it)
}
Rs2MapEditor.itemRows.clear()
Rs2MapEditor.items.forEach {
val row = ItemRow(it, this)
row.border = rowBorder
Rs2MapEditor.itemRows.add(row)
add(row)
}
repaint()
}
class ItemSpawnerPanel : JPanel() {
init {
val topPanel = JPanel(FlowLayout())
val bottomPanel = JPanel(FlowLayout())
val addButton = ImgButton(Image.ADD_HI, Image.ADD_LO)
addButton.onClick {
Rs2MapEditor.state = EditorState.ADD_GROUNDITEM
}
Rs2MapEditor.itemIdInput.minimumSize = Dimension(200, 25)
Rs2MapEditor.itemIdInput.maximumSize = Dimension(200, 25)
Rs2MapEditor.itemIdInput.preferredSize = Dimension(200, 25)
Rs2MapEditor.itemRespawnInput.minimumSize = Dimension(50, 25)
Rs2MapEditor.itemRespawnInput.maximumSize = Dimension(50, 25)
Rs2MapEditor.itemRespawnInput.preferredSize = Dimension(50, 25)
Rs2MapEditor.itemRespawnInput.text = "1"
Rs2MapEditor.itemAmountInput.minimumSize = Dimension(75, 25)
Rs2MapEditor.itemAmountInput.maximumSize = Dimension(75, 25)
Rs2MapEditor.itemAmountInput.preferredSize = Dimension(75, 25)
Rs2MapEditor.itemAmountInput.text = "1"
val timeLabel = JLabel("\uD83D\uDD64")
timeLabel.toolTipText = "Respawn Time (ticks)"
minimumSize = Dimension(300, 68)
preferredSize = Dimension(300, 68)
maximumSize = Dimension(300, 68)
layout = BorderLayout()
topPanel.add(Rs2MapEditor.itemIdInput)
topPanel.add(addButton)
bottomPanel.add(timeLabel)
bottomPanel.add(Rs2MapEditor.itemRespawnInput)
bottomPanel.add(JLabel("AMT:"))
bottomPanel.add(Rs2MapEditor.itemAmountInput)
add(topPanel,BorderLayout.NORTH)
add(bottomPanel,BorderLayout.SOUTH)
border = MatteBorder(1, 1, 1, 1, Color.GREEN)
}
}
class ItemRow(val item: Item, val parent: JPanel) : JPanel(){
init {
layout = FlowLayout()
add(JLabel("${item.definition.name} [${item.amount}] -- \uD83D\uDD64 ${item.respawnTime}"))
val deleteButton = ImgButton(Image.DELETE_HI, Image.DELTE_LO)
add(deleteButton)
minimumSize = Dimension(300, 40)
preferredSize = Dimension(300, 40)
maximumSize = Dimension(300, 40)
deleteButton.onClick {
Rs2MapEditor.items.remove(item)
parent.remove(this)
parent.repaint()
Rs2MapEditor.itemRows.remove(this)
if(Rs2MapEditor.items.filter { it.x == item.x && it.y == item.y }.isEmpty()){
Rs2MapEditor.componentPointMap.filter { it.key.x == item.x && it.key.y == item.y }.forEach { (_,cell) ->
cell.components.forEach { c ->
if(c is JLabel && c.icon == Image.RED_DOT) cell.remove(c)
}
cell.repaint()
Rs2MapEditor.itemsUpdated = true
}
}
}
addMouseListener(object : MouseAdapter(){
val border = MatteBorder(1, 1, 1, 1, Color.YELLOW)
val defaultBorder = MatteBorder(1, 1, 1, 1, Color.GRAY)
override fun mouseEntered(e: MouseEvent?) {
super.mouseEntered(e)
Rs2MapEditor.componentPointMap.filter { it.key.x == item.x && it.key.y == item.y }.forEach { it.value.border = border }
}
override fun mouseExited(e: MouseEvent?) {
super.mouseExited(e)
Rs2MapEditor.componentPointMap.filter { it.key.x == item.x && it.key.y == item.y }.forEach{ it.value.border = defaultBorder }
}
})
}
}
}

View file

@ -0,0 +1,101 @@
package tools
import cacheops.cache.definition.decoder.decoder
import cacheops.cache.definition.decoder.itemDecoder
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 ItemSearchTool : JFrame("Item Search") {
var model = DefaultTableModel()
var searchField = 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.capitalize(), 1)
} catch (e: PatternSyntaxException) {
return
}
sorter.rowFilter = rf
}
fun open() {
if (!populated) {
SwingUtilities.invokeLater {
for (i in 0..15431) {
model.addRow(arrayOf(i, itemDecoder.forId(i).name))
}
isVisible = true
populated = true
}
} else {
isVisible = true
}
}
init {
layout = BorderLayout()
cellRenderer.toolTipText = "Double-Click to select."
val searchPanel = JPanel()
val searchLabel = JLabel("Search for NPC:")
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
}
}

View file

@ -0,0 +1,100 @@
package tools
import cacheops.cache.definition.decoder.decoder
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 NPCSearchTool : JFrame("NPC Search") {
var model = DefaultTableModel()
var searchField = 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.capitalize(), 1)
} catch (e: PatternSyntaxException) {
return
}
sorter.rowFilter = rf
}
fun open() {
if (!populated) {
SwingUtilities.invokeLater {
for (i in 0..9422) {
model.addRow(arrayOf(i, decoder.forId(i).name))
}
isVisible = true
populated = true
}
} else {
isVisible = true
}
}
init {
layout = BorderLayout()
cellRenderer.toolTipText = "Double-Click to select."
val searchPanel = JPanel()
val searchLabel = JLabel("Search for NPC:")
searchField.preferredSize = Dimension(100, 20)
searchPanel.add(searchLabel)
searchPanel.add(searchField)
add(searchPanel, BorderLayout.NORTH)
setLocationRelativeTo(null)
val npcTable: JTable = object : JTable(model) {
override fun editCellAt(i: Int, i1: Int, eventObject: EventObject): Boolean {
return false
}
}
model.addColumn("ID")
model.addColumn("Name")
npcTable.rowSorter = sorter
npcTable.columnModel.getColumn(0).maxWidth = 55
npcTable.columnModel.getColumn(0).cellRenderer = cellRenderer
npcTable.columnModel.getColumn(1).cellRenderer = cellRenderer
npcTable.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(npcTable)
add(scrollPane, BorderLayout.SOUTH)
pack()
isVisible = false
}
}