mirror of
https://gitlab.com/2009scape/rt4-client.git
synced 2025-12-09 16:45:46 -07:00
Kondo 2.0
This commit is contained in:
parent
637e5bf68a
commit
b8c7a1150f
14 changed files with 1265 additions and 474 deletions
|
|
@ -1,10 +1,165 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
import java.awt.Color
|
import rt4.GameShell
|
||||||
import java.awt.Dimension
|
import java.awt.*
|
||||||
import javax.swing.JPanel
|
import java.awt.event.MouseListener
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.util.*
|
||||||
|
import java.util.Timer
|
||||||
|
import javax.swing.*
|
||||||
|
|
||||||
object Helpers {
|
object Helpers {
|
||||||
|
|
||||||
|
fun convertValue(type: Class<*>, genericType: Type?, value: String): Any {
|
||||||
|
return when {
|
||||||
|
type == Int::class.java -> value.toInt()
|
||||||
|
type == Double::class.java -> value.toDouble()
|
||||||
|
type == Boolean::class.java -> value.toBoolean()
|
||||||
|
type == Color::class.java -> convertToColor(value)
|
||||||
|
type == List::class.java && genericType is ParameterizedType -> {
|
||||||
|
val actualTypeArgument = genericType.actualTypeArguments.firstOrNull()
|
||||||
|
when {
|
||||||
|
value.isBlank() -> emptyList<Any>() // Handle empty string by returning an empty list
|
||||||
|
actualTypeArgument == Int::class.javaObjectType -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim().toInt() }
|
||||||
|
actualTypeArgument == String::class.java -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim() }
|
||||||
|
else -> throw IllegalArgumentException("Unsupported List type: $actualTypeArgument")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> value // Default to String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showToast(
|
||||||
|
parentComponent: Component?,
|
||||||
|
message: String,
|
||||||
|
messageType: Int = JOptionPane.INFORMATION_MESSAGE
|
||||||
|
) {
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
val toast = JWindow()
|
||||||
|
toast.type = Window.Type.POPUP
|
||||||
|
toast.background = Color(0, 0, 0, 0)
|
||||||
|
|
||||||
|
val panel = JPanel()
|
||||||
|
panel.isOpaque = false
|
||||||
|
panel.layout = BoxLayout(panel, BoxLayout.Y_AXIS)
|
||||||
|
|
||||||
|
val label = JLabel(message)
|
||||||
|
label.foreground = Color.WHITE
|
||||||
|
|
||||||
|
label.background = when (messageType) {
|
||||||
|
JOptionPane.ERROR_MESSAGE -> Color(220, 20, 60, 230) // Crimson for errors
|
||||||
|
JOptionPane.INFORMATION_MESSAGE -> Color(0, 128, 0, 230) // Green for success
|
||||||
|
JOptionPane.WARNING_MESSAGE -> Color(255, 165, 0, 230) // Orange for warnings
|
||||||
|
else -> Color(0, 0, 0, 170) // Default semi-transparent black
|
||||||
|
}
|
||||||
|
|
||||||
|
label.isOpaque = true
|
||||||
|
label.border = BorderFactory.createEmptyBorder(10, 20, 10, 20)
|
||||||
|
label.maximumSize = Dimension(242, 50)
|
||||||
|
label.preferredSize = Dimension(242, 50)
|
||||||
|
panel.add(label)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
toast.contentPane.add(panel)
|
||||||
|
toast.pack()
|
||||||
|
|
||||||
|
|
||||||
|
// Adjust for parent component location if it exists
|
||||||
|
if (parentComponent != null) {
|
||||||
|
val parentLocation = parentComponent.locationOnScreen
|
||||||
|
val x = parentLocation.x
|
||||||
|
val y = GameShell.canvas.locationOnScreen.y
|
||||||
|
toast.setLocation(x, y)
|
||||||
|
} else {
|
||||||
|
// Fallback to screen center if no parent is provided
|
||||||
|
val screenSize = Toolkit.getDefaultToolkit().screenSize
|
||||||
|
val x = (screenSize.width - toast.width) / 2
|
||||||
|
val y = screenSize.height - toast.height - 50
|
||||||
|
toast.setLocation(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.isVisible = true
|
||||||
|
|
||||||
|
Timer().schedule(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
toast.isVisible = false
|
||||||
|
toast.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun convertToColor(value: String): Color {
|
||||||
|
val color = Color.decode(value) // Assumes value is in format "#RRGGBB" or "0xRRGGBB"
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|
||||||
|
fun colorToHex(color: Color): String {
|
||||||
|
return "#%02x%02x%02x".format(color.red, color.green, color.blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun colorToIntArray(color: Color): IntArray {
|
||||||
|
return intArrayOf(color.red, color.green, color.blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FieldObserver {
|
||||||
|
fun onFieldChange(field: Field, newValue: Any?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addMouseListenerToAll(container: Container, listener: MouseListener) {
|
||||||
|
// Recursively go through all components within the container
|
||||||
|
for (component in container.components) {
|
||||||
|
// Add the passed MouseListener to the component
|
||||||
|
if (component is JComponent || component is Canvas) {
|
||||||
|
component.addMouseListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the component is a container, recursively call this function
|
||||||
|
if (component is Container) {
|
||||||
|
addMouseListenerToAll(component, listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class FieldNotifier(private val plugin: Any) {
|
||||||
|
private val observers = mutableListOf<FieldObserver>()
|
||||||
|
|
||||||
|
fun addObserver(observer: FieldObserver) {
|
||||||
|
observers.add(observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyFieldChange(field: Field, newValue: Any?) {
|
||||||
|
for (observer in observers) {
|
||||||
|
observer.onFieldChange(field, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFieldValue(field: Field, value: Any?) {
|
||||||
|
field.isAccessible = true
|
||||||
|
field.set(plugin, value)
|
||||||
|
notifyFieldChange(field, value)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val onUpdateMethod = plugin::class.java.getMethod("OnKondoValueUpdated")
|
||||||
|
onUpdateMethod.invoke(plugin)
|
||||||
|
} catch (e: NoSuchMethodException) {
|
||||||
|
// The method doesn't exist
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getSpriteId(skillId: Int) : Int {
|
fun getSpriteId(skillId: Int) : Int {
|
||||||
return when (skillId) {
|
return when (skillId) {
|
||||||
0 -> 197
|
0 -> 197
|
||||||
|
|
@ -78,13 +233,4 @@ object Helpers {
|
||||||
else -> Color(128, 128, 128) // Default grey for unhandled skill IDs
|
else -> Color(128, 128, 128) // Default grey for unhandled skill IDs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Spacer(width: Int = 0, height: Int = 0) : JPanel() {
|
|
||||||
init {
|
|
||||||
preferredSize = Dimension(width, height)
|
|
||||||
maximumSize = preferredSize
|
|
||||||
minimumSize = preferredSize
|
|
||||||
isOpaque = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
import KondoKit.Constants.COLOR_BACKGROUND_DARK
|
import KondoKit.Constants.COLOR_BACKGROUND_DARK
|
||||||
|
import KondoKit.Constants.SKILL_DISPLAY_ORDER
|
||||||
|
import KondoKit.Constants.SKILL_SPRITE_DIMENSION
|
||||||
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
|
import KondoKit.Helpers.showToast
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import rt4.Sprites
|
import rt4.Sprites
|
||||||
|
|
@ -16,6 +23,7 @@ import java.awt.event.MouseEvent
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
import javax.swing.border.MatteBorder
|
import javax.swing.border.MatteBorder
|
||||||
|
|
@ -28,35 +36,38 @@ object Constants {
|
||||||
const val LVL_BAR_SPRITE = 898
|
const val LVL_BAR_SPRITE = 898
|
||||||
|
|
||||||
// Dimensions
|
// Dimensions
|
||||||
val SEARCH_FIELD_DIMENSION = Dimension(270, 30)
|
val SEARCH_FIELD_DIMENSION = Dimension(230, 30)
|
||||||
val ICON_DIMENSION_SMALL = Dimension(12, 12)
|
val ICON_DIMENSION_SMALL = Dimension(12, 12)
|
||||||
val ICON_DIMENSION_MEDIUM = Dimension(18, 20)
|
val ICON_DIMENSION_MEDIUM = Dimension(18, 20)
|
||||||
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
||||||
val HISCORE_PANEL_DIMENSION = Dimension(270, 400)
|
val HISCORE_PANEL_DIMENSION = Dimension(230, 500)
|
||||||
val FILTER_PANEL_DIMENSION = Dimension(270, 30)
|
val FILTER_PANEL_DIMENSION = Dimension(230, 30)
|
||||||
val SKILLS_PANEL_DIMENSION = Dimension(300, 300)
|
val SKILLS_PANEL_DIMENSION = Dimension(230, 290)
|
||||||
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(270, 30)
|
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(230, 30)
|
||||||
val SKILL_PANEL_DIMENSION = Dimension(90, 35)
|
val SKILL_PANEL_DIMENSION = Dimension(76, 35)
|
||||||
val IMAGE_CANVAS_DIMENSION = Dimension(20, 20)
|
val IMAGE_CANVAS_DIMENSION = Dimension(20, 20)
|
||||||
val NUMBER_LABEL_DIMENSION = Dimension(30, 20)
|
val SKILL_SPRITE_DIMENSION = Dimension(14, 14)
|
||||||
|
val NUMBER_LABEL_DIMENSION = Dimension(20, 20)
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
val COLOR_BACKGROUND_DARK = Color(27, 27, 27)
|
val COLOR_BACKGROUND_DARK = Color(27, 27, 27)
|
||||||
val COLOR_BACKGROUND_MEDIUM = Color(37, 37, 37)
|
val COLOR_BACKGROUND_MEDIUM = VIEW_BACKGROUND_COLOR
|
||||||
val COLOR_BACKGROUND_LIGHT = Color(43, 43, 43)
|
|
||||||
val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200)
|
val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200)
|
||||||
val COLOR_RED = Color.RED
|
val COLOR_RED = Color.RED
|
||||||
val COLOR_SKILL_PANEL = Color(60, 60, 60)
|
val COLOR_SKILL_PANEL = Color(60, 60, 60)
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
|
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
|
||||||
val FONT_ARIAL_PLAIN_12 = Font("Arial", Font.PLAIN, 12)
|
val FONT_ARIAL_PLAIN_12 = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12)
|
val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12)
|
||||||
|
val SKILL_DISPLAY_ORDER = arrayOf(0,3,14,2,16,13,1,15,10,4,17,7,5,12,11,6,9,8,20,18,19,22,21,23)
|
||||||
}
|
}
|
||||||
|
|
||||||
var text: String = ""
|
var text: String = ""
|
||||||
|
|
||||||
object HiscoresView {
|
object HiscoresView {
|
||||||
|
|
||||||
|
var hiScoreView: JPanel? = null
|
||||||
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
|
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
|
||||||
|
|
||||||
private var cursorVisible: Boolean = true
|
private var cursorVisible: Boolean = true
|
||||||
|
|
@ -69,6 +80,7 @@ object HiscoresView {
|
||||||
size = preferredSize
|
size = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,7 +137,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Timer(500) {
|
Timer(1000) {
|
||||||
cursorVisible = !cursorVisible
|
cursorVisible = !cursorVisible
|
||||||
if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW")
|
if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW")
|
||||||
repaint()
|
repaint()
|
||||||
|
|
@ -140,7 +152,7 @@ object HiscoresView {
|
||||||
val fm = g.fontMetrics
|
val fm = g.fontMetrics
|
||||||
val cursorX = fm.stringWidth(text) + 30
|
val cursorX = fm.stringWidth(text) + 30
|
||||||
|
|
||||||
imageCanvas?.let { canvas ->
|
imageCanvas.let { canvas ->
|
||||||
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
||||||
canvas.paint(imgG)
|
canvas.paint(imgG)
|
||||||
imgG.dispose()
|
imgG.dispose()
|
||||||
|
|
@ -159,8 +171,10 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchPlayer(username: String) {
|
fun searchPlayer(username: String) {
|
||||||
text = username
|
text = username.replace(" ", "_")
|
||||||
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${username.toLowerCase()}"
|
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${text.toLowerCase()}"
|
||||||
|
|
||||||
|
updateHiscoresView(null, "Searching...")
|
||||||
|
|
||||||
Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
|
|
@ -168,6 +182,10 @@ object HiscoresView {
|
||||||
val connection = url.openConnection() as HttpURLConnection
|
val connection = url.openConnection() as HttpURLConnection
|
||||||
connection.requestMethod = "GET"
|
connection.requestMethod = "GET"
|
||||||
|
|
||||||
|
// If a request take longer than 5 seconds timeout.
|
||||||
|
connection.connectTimeout = 5000
|
||||||
|
connection.readTimeout = 5000
|
||||||
|
|
||||||
val responseCode = connection.responseCode
|
val responseCode = connection.responseCode
|
||||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
||||||
|
|
@ -179,27 +197,45 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SwingUtilities.invokeLater {
|
SwingUtilities.invokeLater {
|
||||||
showError("Player not found!")
|
showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: SocketTimeoutException) {
|
||||||
SwingUtilities.invokeLater {
|
SwingUtilities.invokeLater {
|
||||||
showError("Error fetching data!")
|
showToast(hiscoresPanel, "Request timed out", JOptionPane.ERROR_MESSAGE)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Handle other errors
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
showToast(hiscoresPanel, "Error fetching data!", JOptionPane.ERROR_MESSAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updatePlayerData(jsonResponse: String, username: String) {
|
private fun updatePlayerData(jsonResponse: String, username: String) {
|
||||||
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
||||||
updateHiscoresView(hiscoresResponse, username)
|
updateHiscoresView(hiscoresResponse, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateHiscoresView(data: HiscoresResponse, username: String) {
|
private fun updateHiscoresView(data: HiscoresResponse?, username: String) {
|
||||||
val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel
|
val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel
|
||||||
val ironMode = data.info.iron_mode
|
|
||||||
|
|
||||||
playerNameLabel?.removeAll() // Clear previous components
|
playerNameLabel?.removeAll() // Clear previous components
|
||||||
|
var nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, "", primaryColor), JLabel.CENTER).apply {
|
||||||
|
font = Constants.FONT_ARIAL_BOLD_12
|
||||||
|
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||||
|
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||||
|
}
|
||||||
|
playerNameLabel?.add(nameLabel)
|
||||||
|
playerNameLabel?.revalidate()
|
||||||
|
playerNameLabel?.repaint()
|
||||||
|
|
||||||
|
if(data == null) return;
|
||||||
|
|
||||||
|
playerNameLabel?.removeAll()
|
||||||
|
|
||||||
|
val ironMode = data.info.iron_mode
|
||||||
|
|
||||||
if (ironMode != "0") {
|
if (ironMode != "0") {
|
||||||
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
||||||
|
|
@ -213,11 +249,14 @@ object HiscoresView {
|
||||||
playerNameLabel?.add(imageCanvas)
|
playerNameLabel?.add(imageCanvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nameLabel = JLabel(username, JLabel.CENTER).apply {
|
val exp_multiplier = data.info.exp_multiplier
|
||||||
|
nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, " (${exp_multiplier}x)", primaryColor), JLabel.CENTER).apply {
|
||||||
font = Constants.FONT_ARIAL_BOLD_12
|
font = Constants.FONT_ARIAL_BOLD_12
|
||||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||||
|
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
playerNameLabel?.add(nameLabel)
|
playerNameLabel?.add(nameLabel)
|
||||||
|
|
||||||
playerNameLabel?.revalidate()
|
playerNameLabel?.revalidate()
|
||||||
|
|
@ -296,7 +335,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createHiscoreSearchView(): JPanel {
|
fun createHiscoreSearchView() {
|
||||||
val hiscorePanel = JPanel().apply {
|
val hiscorePanel = JPanel().apply {
|
||||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
name = "HISCORE_SEARCH_VIEW"
|
name = "HISCORE_SEARCH_VIEW"
|
||||||
|
|
@ -324,14 +363,13 @@ object HiscoresView {
|
||||||
add(searchFieldWrapper)
|
add(searchFieldWrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
hiscorePanel.add(searchPanel)
|
hiscorePanel.add(searchPanel)
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
// Adding the player name panel in place of the filterPanel
|
|
||||||
val playerNamePanel = JPanel().apply {
|
val playerNamePanel = JPanel().apply {
|
||||||
layout = FlowLayout(FlowLayout.CENTER)
|
layout = GridBagLayout() // This will center the JLabel both vertically and horizontally
|
||||||
background = VIEW_BACKGROUND_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
|
|
@ -339,7 +377,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
hiscorePanel.add(playerNamePanel)
|
hiscorePanel.add(playerNamePanel)
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||||
|
|
@ -348,10 +386,10 @@ object HiscoresView {
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until 24) {
|
for (i in SKILL_DISPLAY_ORDER) {
|
||||||
val skillPanel = JPanel().apply {
|
val skillPanel = JPanel().apply {
|
||||||
layout = BorderLayout()
|
layout = BorderLayout()
|
||||||
background = Constants.COLOR_SKILL_PANEL
|
background = COLOR_BACKGROUND_DARK
|
||||||
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
|
|
@ -362,8 +400,9 @@ object HiscoresView {
|
||||||
|
|
||||||
val imageCanvas = bufferedImageSprite.let {
|
val imageCanvas = bufferedImageSprite.let {
|
||||||
ImageCanvas(it).apply {
|
ImageCanvas(it).apply {
|
||||||
preferredSize = Constants.IMAGE_CANVAS_DIMENSION
|
preferredSize = SKILL_SPRITE_DIMENSION
|
||||||
size = Constants.IMAGE_CANVAS_DIMENSION
|
size = SKILL_SPRITE_DIMENSION
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,7 +415,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_DARK
|
background = COLOR_BACKGROUND_DARK
|
||||||
add(imageCanvas)
|
add(imageCanvas)
|
||||||
add(numberLabel)
|
add(numberLabel)
|
||||||
}
|
}
|
||||||
|
|
@ -431,7 +470,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_DARK
|
background = COLOR_BACKGROUND_DARK
|
||||||
add(combatLevelIcon)
|
add(combatLevelIcon)
|
||||||
add(combatLevelLabel)
|
add(combatLevelLabel)
|
||||||
}
|
}
|
||||||
|
|
@ -439,8 +478,9 @@ object HiscoresView {
|
||||||
totalCombatPanel.add(totalLevelPanel)
|
totalCombatPanel.add(totalLevelPanel)
|
||||||
totalCombatPanel.add(combatLevelPanel)
|
totalCombatPanel.add(combatLevelPanel)
|
||||||
hiscorePanel.add(totalCombatPanel)
|
hiscorePanel.add(totalCombatPanel)
|
||||||
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
return hiscorePanel
|
hiScoreView = hiscorePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HiscoresResponse(
|
data class HiscoresResponse(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import java.awt.Canvas
|
import java.awt.Canvas
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.Dimension
|
import java.awt.Dimension
|
||||||
|
|
@ -8,31 +9,25 @@ import java.awt.image.BufferedImage
|
||||||
|
|
||||||
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
||||||
|
|
||||||
|
var fillColor: Color = WIDGET_COLOR
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Manually set the alpha value to 255 (fully opaque) only for pixels that are not fully transparent
|
|
||||||
val width = image.width
|
val width = image.width
|
||||||
val height = image.height
|
val height = image.height
|
||||||
for (y in 0 until height) {
|
for (y in 0 until height) {
|
||||||
for (x in 0 until width) {
|
for (x in 0 until width) {
|
||||||
// Retrieve the current pixel color
|
|
||||||
val color = image.getRGB(x, y)
|
val color = image.getRGB(x, y)
|
||||||
|
|
||||||
// Check if the pixel is not fully transparent (i.e., color is not 0)
|
|
||||||
if (color != 0) {
|
if (color != 0) {
|
||||||
// Ensure the alpha is set to 255 (fully opaque)
|
|
||||||
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
||||||
|
|
||||||
// Set the pixel with the updated color
|
|
||||||
image.setRGB(x, y, newColor)
|
image.setRGB(x, y, newColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun paint(g: Graphics) {
|
override fun paint(g: Graphics) {
|
||||||
super.paint(g)
|
super.paint(g)
|
||||||
g.color = Color(27, 27, 27)
|
g.color = fillColor
|
||||||
g.fillRect(0, 0, width, height)
|
g.fillRect(0, 0, width, height)
|
||||||
g.drawImage(image, 0, 0, width, height, this)
|
g.drawImage(image, 0, 0, width, height, this)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
package KondoKit
|
|
||||||
|
|
||||||
import java.awt.Color
|
|
||||||
import java.lang.reflect.Field
|
|
||||||
import java.lang.reflect.ParameterizedType
|
|
||||||
import java.lang.reflect.Type
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is used for the runtime editing of plugin variables.
|
|
||||||
To expose fields name them starting with `kondoExposed_`
|
|
||||||
When they are applied this will trigger an invoke of OnKondoValueUpdated()
|
|
||||||
if it is implemented. Check GroundItems plugin for an example.
|
|
||||||
*/
|
|
||||||
|
|
||||||
object KondoKitUtils {
|
|
||||||
const val KONDO_PREFIX = "kondoExposed_"
|
|
||||||
|
|
||||||
fun isKondoExposed(field: Field): Boolean {
|
|
||||||
return field.name.startsWith(KONDO_PREFIX)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getKondoExposedFields(instance: Any): List<Field> {
|
|
||||||
val exposedFields: MutableList<Field> = ArrayList()
|
|
||||||
for (field in instance.javaClass.declaredFields) {
|
|
||||||
if (isKondoExposed(field)) {
|
|
||||||
exposedFields.add(field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exposedFields
|
|
||||||
}
|
|
||||||
|
|
||||||
fun convertValue(type: Class<*>, genericType: Type?, value: String): Any {
|
|
||||||
return when {
|
|
||||||
type == Int::class.java -> value.toInt()
|
|
||||||
type == Double::class.java -> value.toDouble()
|
|
||||||
type == Boolean::class.java -> value.toBoolean()
|
|
||||||
type == Color::class.java -> convertToColor(value)
|
|
||||||
type == List::class.java && genericType is ParameterizedType -> {
|
|
||||||
val actualTypeArgument = genericType.actualTypeArguments.firstOrNull()
|
|
||||||
when {
|
|
||||||
value.isBlank() -> emptyList<Any>() // Handle empty string by returning an empty list
|
|
||||||
actualTypeArgument == Int::class.javaObjectType -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim().toInt() }
|
|
||||||
actualTypeArgument == String::class.java -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim() }
|
|
||||||
else -> throw IllegalArgumentException("Unsupported List type: $actualTypeArgument")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> value // Default to String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun convertToColor(value: String): Color {
|
|
||||||
val color = Color.decode(value) // Assumes value is in format "#RRGGBB" or "0xRRGGBB"
|
|
||||||
return color
|
|
||||||
}
|
|
||||||
|
|
||||||
fun colorToHex(color: Color): String {
|
|
||||||
return "#%02x%02x%02x".format(color.red, color.green, color.blue)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun colorToIntArray(color: Color): IntArray {
|
|
||||||
return intArrayOf(color.red, color.green, color.blue)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FieldObserver {
|
|
||||||
fun onFieldChange(field: Field, newValue: Any?)
|
|
||||||
}
|
|
||||||
|
|
||||||
class FieldNotifier(private val plugin: Any) {
|
|
||||||
private val observers = mutableListOf<FieldObserver>()
|
|
||||||
|
|
||||||
fun addObserver(observer: FieldObserver) {
|
|
||||||
observers.add(observer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun notifyFieldChange(field: Field, newValue: Any?) {
|
|
||||||
for (observer in observers) {
|
|
||||||
observer.onFieldChange(field, newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setFieldValue(field: Field, value: Any?) {
|
|
||||||
field.isAccessible = true
|
|
||||||
field.set(plugin, value)
|
|
||||||
notifyFieldChange(field, value)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val onUpdateMethod = plugin::class.java.getMethod("OnKondoValueUpdated")
|
|
||||||
onUpdateMethod.invoke(plugin)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.Helpers.addMouseListenerToAll
|
||||||
import KondoKit.Helpers.formatHtmlLabelText
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.XPTrackerView.wrappedWidget
|
import KondoKit.XPTrackerView.wrappedWidget
|
||||||
|
|
@ -9,11 +10,11 @@ import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import rt4.NpcTypeList
|
import rt4.*
|
||||||
import rt4.ObjStackNode
|
|
||||||
import rt4.Player
|
|
||||||
import rt4.SceneGraph
|
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
|
import java.awt.Font
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
|
@ -22,6 +23,7 @@ import java.net.URL
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
object LootTrackerView {
|
object LootTrackerView {
|
||||||
private const val SNAPSHOT_LIFESPAN = 10
|
private const val SNAPSHOT_LIFESPAN = 10
|
||||||
|
|
@ -31,10 +33,12 @@ object LootTrackerView {
|
||||||
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
||||||
private val npcKillCounts = mutableMapOf<String, Int>()
|
private val npcKillCounts = mutableMapOf<String, Int>()
|
||||||
private var totalTrackerWidget: XPWidget? = null
|
private var totalTrackerWidget: XPWidget? = null
|
||||||
var lastConfirmedKillNpcId = -1;
|
var lastConfirmedKillNpcId = -1
|
||||||
|
var customToolTipWindow: JWindow? = null
|
||||||
|
var lootTrackerView: JPanel? = null
|
||||||
|
|
||||||
fun loadGEPrices(): Map<String, String> {
|
fun loadGEPrices(): Map<String, String> {
|
||||||
return if (plugin.kondoExposed_useLiveGEPrices) {
|
return if (plugin.useLiveGEPrices) {
|
||||||
try {
|
try {
|
||||||
println("LootTracker: Loading Remote GE Prices")
|
println("LootTracker: Loading Remote GE Prices")
|
||||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||||
|
|
@ -69,7 +73,7 @@ object LootTrackerView {
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
println("LootTracker: Loading Local GE Prices")
|
println("LootTracker: Loading Local GE Prices")
|
||||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8))
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
val json = lines.joinToString("\n")
|
val json = lines.joinToString("\n")
|
||||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||||
|
|
@ -94,17 +98,34 @@ object LootTrackerView {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun createLootTrackerView(): JPanel {
|
fun createLootTrackerView() {
|
||||||
return JPanel().apply {
|
lootTrackerView = JPanel().apply {
|
||||||
layout = FlowLayout(FlowLayout.CENTER, 0, 5)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS) // Use BoxLayout on Y axis to stack widgets vertically
|
||||||
background = VIEW_BACKGROUND_COLOR
|
background = VIEW_BACKGROUND_COLOR
|
||||||
preferredSize = Dimension(270, 700)
|
|
||||||
maximumSize = Dimension(270, 700)
|
|
||||||
minimumSize = Dimension(270, 700)
|
|
||||||
add(Box.createVerticalStrut(5))
|
add(Box.createVerticalStrut(5))
|
||||||
totalTrackerWidget = createTotalLootWidget()
|
totalTrackerWidget = createTotalLootWidget()
|
||||||
add(wrappedWidget(totalTrackerWidget!!.panel))
|
|
||||||
add(Helpers.Spacer(height = 15))
|
val wrapped = wrappedWidget(totalTrackerWidget!!.container)
|
||||||
|
val popupMenu = resetLootTrackerMenu()
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addMouseListenerToAll(wrapped,rightClickListener)
|
||||||
|
wrapped.addMouseListener(rightClickListener)
|
||||||
|
add(wrapped)
|
||||||
|
add(Box.createVerticalStrut(10))
|
||||||
revalidate()
|
revalidate()
|
||||||
repaint()
|
repaint()
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +143,7 @@ object LootTrackerView {
|
||||||
totalTrackerWidget?.let {
|
totalTrackerWidget?.let {
|
||||||
it.previousXp += newVal
|
it.previousXp += newVal
|
||||||
it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor)
|
it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor)
|
||||||
it.panel.repaint()
|
it.container.repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,7 +153,7 @@ object LootTrackerView {
|
||||||
val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor))
|
val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor))
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = -1,
|
skillId = -1,
|
||||||
panel = createWidgetPanel(bufferedImageSprite,l2,l1),
|
container = createWidgetPanel(bufferedImageSprite,l2,l1),
|
||||||
xpGainedLabel = l2,
|
xpGainedLabel = l2,
|
||||||
xpLeftLabel = JLabel(),
|
xpLeftLabel = JLabel(),
|
||||||
actionsRemainingLabel = JLabel(),
|
actionsRemainingLabel = JLabel(),
|
||||||
|
|
@ -173,7 +194,7 @@ object LootTrackerView {
|
||||||
|
|
||||||
private fun createLabel(text: String): JLabel {
|
private fun createLabel(text: String): JLabel {
|
||||||
return JLabel(text).apply {
|
return JLabel(text).apply {
|
||||||
font = Font("Arial", Font.PLAIN, 11)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.LEFT
|
horizontalAlignment = JLabel.LEFT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,9 +210,15 @@ object LootTrackerView {
|
||||||
|
|
||||||
// Recalculate lootPanel size based on the number of unique items.
|
// Recalculate lootPanel size based on the number of unique items.
|
||||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
||||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
val rowsNeeded = ceil(totalItems / 6.0).toInt()
|
||||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1)
|
val lootPanelHeight = rowsNeeded * (40)
|
||||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
|
||||||
|
val size = Dimension(lootPanel.width,lootPanelHeight+32)
|
||||||
|
lootPanel.parent.preferredSize = size
|
||||||
|
lootPanel.parent.minimumSize = size
|
||||||
|
lootPanel.parent.maximumSize = size
|
||||||
|
lootPanel.parent.revalidate()
|
||||||
|
lootPanel.parent.repaint()
|
||||||
|
|
||||||
lootPanel.revalidate()
|
lootPanel.revalidate()
|
||||||
lootPanel.repaint()
|
lootPanel.repaint()
|
||||||
|
|
@ -205,21 +232,86 @@ object LootTrackerView {
|
||||||
|
|
||||||
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 0, 0))
|
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 0, 0))
|
||||||
return FixedSizePanel(Dimension(36, 32)).apply {
|
|
||||||
|
// Create the panel for the item
|
||||||
|
val itemPanel = FixedSizePanel(Dimension(36, 32)).apply {
|
||||||
preferredSize = Dimension(36, 32)
|
preferredSize = Dimension(36, 32)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
add(ImageCanvas(bufferedImageSprite).apply {
|
|
||||||
|
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||||
preferredSize = Dimension(36, 32)
|
preferredSize = Dimension(36, 32)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
}, BorderLayout.CENTER)
|
}
|
||||||
|
|
||||||
|
// Add the imageCanvas to the panel
|
||||||
|
add(imageCanvas, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
// Put the itemId as a property for reference
|
||||||
putClientProperty("itemId", itemId)
|
putClientProperty("itemId", itemId)
|
||||||
|
|
||||||
|
// Add mouse listener for custom hover text
|
||||||
|
imageCanvas.addMouseListener(object : MouseAdapter() {
|
||||||
|
override fun mouseEntered(e: MouseEvent) {
|
||||||
|
// Show custom tooltip when the mouse enters the component
|
||||||
|
showCustomToolTip(e.point, itemId,quantity,imageCanvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseExited(e: MouseEvent) {
|
||||||
|
// Hide tooltip when mouse exits
|
||||||
|
hideCustomToolTip()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return itemPanel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to show the custom tooltip
|
||||||
|
fun showCustomToolTip(location: Point, itemId: Int, quantity: Int, parentComponent: ImageCanvas) {
|
||||||
|
var itemDef = ObjTypeList.get(itemId)
|
||||||
|
val gePricePerItem = gePriceMap[itemDef.id.toString()]?.toInt() ?: 0
|
||||||
|
val totalGePrice = gePricePerItem * quantity
|
||||||
|
val totalHaPrice = itemDef.cost * quantity
|
||||||
|
val geText = if (quantity > 1) " (${gePricePerItem} ea)" else ""
|
||||||
|
val haText = if (quantity > 1) " (${itemDef.cost} ea)" else ""
|
||||||
|
|
||||||
|
val text = "<html><div style='color: white; background-color: #323232; padding: 3px;'>" +
|
||||||
|
"${itemDef.name} x $quantity<br>" +
|
||||||
|
"GE: $totalGePrice ${geText}<br>" +
|
||||||
|
"HA: $totalHaPrice ${haText}</div></html>"
|
||||||
|
|
||||||
|
val _font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
val c = Color(50,50,50)
|
||||||
|
if (customToolTipWindow == null) {
|
||||||
|
customToolTipWindow = JWindow().apply {
|
||||||
|
contentPane = JLabel(text).apply {
|
||||||
|
border = BorderFactory.createLineBorder(Color.BLACK)
|
||||||
|
isOpaque = true
|
||||||
|
background = c
|
||||||
|
foreground = Color.WHITE
|
||||||
|
font = _font
|
||||||
|
}
|
||||||
|
pack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the tooltip location relative to the parent component
|
||||||
|
val screenLocation = parentComponent.locationOnScreen
|
||||||
|
customToolTipWindow!!.setLocation(screenLocation.x + location.x, screenLocation.y + location.y + 20)
|
||||||
|
customToolTipWindow!!.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to hide the custom tooltip
|
||||||
|
fun hideCustomToolTip() {
|
||||||
|
customToolTipWindow?.isVisible = false
|
||||||
|
customToolTipWindow = null // Nullify the global instance
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateItemPanelIcon(panel: JPanel, itemId: Int, quantity: Int) {
|
private fun updateItemPanelIcon(panel: JPanel, itemId: Int, quantity: Int) {
|
||||||
panel.removeAll()
|
panel.removeAll()
|
||||||
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
|
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
|
||||||
|
|
@ -308,9 +400,8 @@ object LootTrackerView {
|
||||||
private fun handleNewDrops(npcName: String, newDrops: Set<Item>, lootTrackerView: JPanel) {
|
private fun handleNewDrops(npcName: String, newDrops: Set<Item>, lootTrackerView: JPanel) {
|
||||||
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
||||||
} ?: run {
|
} ?: run {
|
||||||
// Panel doesn't exist, so create and add it
|
|
||||||
lootTrackerView.add(createLootFrame(npcName))
|
lootTrackerView.add(createLootFrame(npcName))
|
||||||
lootTrackerView.add(Helpers.Spacer(height = 15))
|
lootTrackerView.add(Box.createVerticalStrut(10))
|
||||||
lootTrackerView.revalidate()
|
lootTrackerView.revalidate()
|
||||||
lootTrackerView.repaint()
|
lootTrackerView.repaint()
|
||||||
}
|
}
|
||||||
|
|
@ -330,14 +421,15 @@ object LootTrackerView {
|
||||||
val childFramePanel = JPanel().apply {
|
val childFramePanel = JPanel().apply {
|
||||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = Dimension(270, 0)
|
minimumSize = Dimension(230, 0)
|
||||||
maximumSize = Dimension(270, 700)
|
maximumSize = Dimension(230, 700)
|
||||||
|
name = "HELLO_WORLD"
|
||||||
}
|
}
|
||||||
|
|
||||||
val labelPanel = JPanel(BorderLayout()).apply {
|
val labelPanel = JPanel(BorderLayout()).apply {
|
||||||
background = Color(21, 21, 21)
|
background = Color(21, 21, 21)
|
||||||
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
||||||
maximumSize = Dimension(270, 24)
|
maximumSize = Dimension(230, 24)
|
||||||
minimumSize = maximumSize
|
minimumSize = maximumSize
|
||||||
preferredSize = maximumSize
|
preferredSize = maximumSize
|
||||||
}
|
}
|
||||||
|
|
@ -345,14 +437,14 @@ object LootTrackerView {
|
||||||
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
|
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
|
||||||
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
|
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
|
||||||
foreground = Color(200, 200, 200)
|
foreground = Color(200, 200, 200)
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.LEFT
|
horizontalAlignment = JLabel.LEFT
|
||||||
name = "killCountLabel_$npcName"
|
name = "killCountLabel_$npcName"
|
||||||
}
|
}
|
||||||
|
|
||||||
val valueLabel = JLabel("0 gp").apply {
|
val valueLabel = JLabel("0 gp").apply {
|
||||||
foreground = Color(200, 200, 200)
|
foreground = Color(200, 200, 200)
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.RIGHT
|
horizontalAlignment = JLabel.RIGHT
|
||||||
name = "valueLabel_$npcName"
|
name = "valueLabel_$npcName"
|
||||||
}
|
}
|
||||||
|
|
@ -370,19 +462,115 @@ object LootTrackerView {
|
||||||
|
|
||||||
lootItemPanels[npcName] = mutableMapOf()
|
lootItemPanels[npcName] = mutableMapOf()
|
||||||
|
|
||||||
// Determine number of items and adjust size of lootPanel
|
|
||||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
|
||||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
|
||||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1) // Height per row = 36 + spacing
|
|
||||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
|
||||||
|
|
||||||
childFramePanel.add(labelPanel)
|
childFramePanel.add(labelPanel)
|
||||||
childFramePanel.add(lootPanel)
|
childFramePanel.add(lootPanel)
|
||||||
childFramePanel.add(lootPanel)
|
|
||||||
|
|
||||||
|
val popupMenu = removeLootFrameMenu(childFramePanel, npcName)
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labelPanel.addMouseListener(rightClickListener)
|
||||||
return childFramePanel
|
return childFramePanel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeLootFrameMenu(toRemove: JPanel, npcName: String): JPopupMenu {
|
||||||
|
// Create a popup menu
|
||||||
|
val popupMenu = JPopupMenu()
|
||||||
|
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
|
popupMenu.background = Color(45, 45, 45)
|
||||||
|
|
||||||
|
// Create menu items with custom font and colors
|
||||||
|
val menuItem1 = JMenuItem("Remove").apply {
|
||||||
|
font = rFont // Set custom font
|
||||||
|
background = Color(45, 45, 45) // Dark background for item
|
||||||
|
foreground = Color(220, 220, 220) // Light text color for item
|
||||||
|
}
|
||||||
|
popupMenu.add(menuItem1)
|
||||||
|
menuItem1.addActionListener {
|
||||||
|
lootItemPanels[npcName]?.clear()
|
||||||
|
npcKillCounts[npcName] = 0
|
||||||
|
lootTrackerView?.let { parent ->
|
||||||
|
val components = parent.components
|
||||||
|
val toRemoveIndex = components.indexOf(toRemove)
|
||||||
|
if (toRemoveIndex >= 0 && toRemoveIndex < components.size - 1) {
|
||||||
|
val nextComponent = components[toRemoveIndex + 1]
|
||||||
|
if (nextComponent is Box.Filler) {
|
||||||
|
// Nasty way to remove the Box.createVerticalStrut(10) after
|
||||||
|
// the lootpanel.
|
||||||
|
parent.remove(nextComponent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent.remove(toRemove)
|
||||||
|
parent.revalidate()
|
||||||
|
parent.repaint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return popupMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun resetLootTrackerMenu(): JPopupMenu {
|
||||||
|
// Create a popup menu
|
||||||
|
val popupMenu = JPopupMenu()
|
||||||
|
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
|
popupMenu.background = Color(45, 45, 45)
|
||||||
|
|
||||||
|
// Create menu items with custom font and colors
|
||||||
|
val menuItem1 = JMenuItem("Reset Loot Tracker").apply {
|
||||||
|
font = rFont // Set custom font
|
||||||
|
background = Color(45, 45, 45) // Dark background for item
|
||||||
|
foreground = Color(220, 220, 220) // Light text color for item
|
||||||
|
}
|
||||||
|
popupMenu.add(menuItem1)
|
||||||
|
menuItem1.addActionListener {
|
||||||
|
lootTrackerView?.removeAll()
|
||||||
|
npcKillCounts.clear()
|
||||||
|
lootItemPanels.clear()
|
||||||
|
totalTrackerWidget = createTotalLootWidget()
|
||||||
|
|
||||||
|
val wrapped = wrappedWidget(totalTrackerWidget!!.container)
|
||||||
|
val _popupMenu = resetLootTrackerMenu()
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
_popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
_popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addMouseListenerToAll(wrapped,rightClickListener)
|
||||||
|
wrapped.addMouseListener(rightClickListener)
|
||||||
|
lootTrackerView?.add(Box.createVerticalStrut(5))
|
||||||
|
lootTrackerView?.add(wrapped)
|
||||||
|
lootTrackerView?.add(Box.createVerticalStrut(10))
|
||||||
|
lootTrackerView?.revalidate()
|
||||||
|
lootTrackerView?.repaint()
|
||||||
|
}
|
||||||
|
return popupMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class FixedSizePanel(private val fixedSize: Dimension) : JPanel() {
|
class FixedSizePanel(private val fixedSize: Dimension) : JPanel() {
|
||||||
override fun getPreferredSize(): Dimension {
|
override fun getPreferredSize(): Dimension {
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,19 @@ package KondoKit
|
||||||
|
|
||||||
import java.awt.Canvas
|
import java.awt.Canvas
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
|
import java.awt.Dimension
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
import java.awt.Graphics
|
import java.awt.Graphics
|
||||||
|
|
||||||
class ProgressBar(
|
class ProgressBar(
|
||||||
private var progress: Double,
|
private var progress: Double,
|
||||||
private val barColor: Color,
|
private val barColor: Color,
|
||||||
private var currentLevel: Int = 0,
|
private var currentLevel: Int = 0,
|
||||||
private var nextLevel: Int = 1
|
private var nextLevel: Int = 1
|
||||||
) : Canvas() {
|
) : Canvas() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun paint(g: Graphics) {
|
override fun paint(g: Graphics) {
|
||||||
|
|
@ -25,22 +26,32 @@ class ProgressBar(
|
||||||
g.fillRect(0, 0, width, this.height)
|
g.fillRect(0, 0, width, this.height)
|
||||||
|
|
||||||
// Draw the unfilled part of the progress bar
|
// Draw the unfilled part of the progress bar
|
||||||
g.color = Color(100, 100, 100)
|
g.color = Color(61, 56, 49) // from Runelite
|
||||||
g.fillRect(width, 0, this.width - width, this.height)
|
g.fillRect(width, 0, this.width - width, this.height)
|
||||||
|
|
||||||
|
// Variables for text position
|
||||||
|
val textY = this.height / 2 + 6
|
||||||
|
|
||||||
// Draw the current level on the far left
|
// Draw the current level on the far left
|
||||||
g.color = Color(255, 255, 255)
|
drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, Color(255, 255, 255))
|
||||||
g.drawString("Lvl. $currentLevel", 5, this.height / 2 + 4)
|
|
||||||
|
|
||||||
// Draw the percentage in the middle
|
// Draw the percentage in the middle
|
||||||
val percentageText = String.format("%.2f%%", progress)
|
val percentageText = String.format("%.2f%%", progress)
|
||||||
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
|
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
|
||||||
g.drawString(percentageText, (this.width - percentageWidth) / 2, this.height / 2 + 4)
|
drawTextWithShadow(g, percentageText, (this.width - percentageWidth) / 2, textY, Color(255, 255, 255))
|
||||||
|
|
||||||
// Draw the next level on the far right
|
// Draw the next level on the far right
|
||||||
val nextLevelText = "Lvl. $nextLevel"
|
val nextLevelText = "Lvl. $nextLevel"
|
||||||
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
|
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
|
||||||
g.drawString(nextLevelText, this.width - nextLevelWidth - 5, this.height / 2 + 4)
|
drawTextWithShadow(g, nextLevelText, this.width - nextLevelWidth - 5, textY, Color(255, 255, 255))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPreferredSize(): Dimension {
|
||||||
|
return Dimension(220, 16) // Force the height to 16px, width can be anything appropriate
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMinimumSize(): Dimension {
|
||||||
|
return Dimension(220, 16) // Force the minimum height to 16px, width can be smaller
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
|
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
|
||||||
|
|
@ -50,4 +61,15 @@ class ProgressBar(
|
||||||
if(isVisible)
|
if(isVisible)
|
||||||
repaint()
|
repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to draw text with a shadow effect
|
||||||
|
private fun drawTextWithShadow(g: Graphics, text: String, x: Int, y: Int, textColor: Color) {
|
||||||
|
// Draw shadow (black text with -1 x and -1 y offset)
|
||||||
|
g.color = Color(0, 0, 0)
|
||||||
|
g.drawString(text, x + 1, y + 1)
|
||||||
|
|
||||||
|
// Draw actual text on top
|
||||||
|
g.color = textColor
|
||||||
|
g.drawString(text, x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,50 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
import KondoKit.KondoKitUtils.convertValue
|
import KondoKit.Helpers.convertValue
|
||||||
|
import KondoKit.Helpers.showToast
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
|
import plugin.PluginInfo
|
||||||
import plugin.PluginRepository
|
import plugin.PluginRepository
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
import java.lang.reflect.Field
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is used for the runtime editing of plugin variables.
|
||||||
|
To expose fields add the @Exposed annotation.
|
||||||
|
When they are applied this will trigger an invoke of OnKondoValueUpdated()
|
||||||
|
if it is implemented. Check GroundItems plugin for an example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
object ReflectiveEditorView {
|
object ReflectiveEditorView {
|
||||||
fun createReflectiveEditorView(): JPanel {
|
var reflectiveEditorView: JPanel? = null
|
||||||
|
fun createReflectiveEditorView() {
|
||||||
val reflectiveEditorPanel = JPanel(BorderLayout())
|
val reflectiveEditorPanel = JPanel(BorderLayout())
|
||||||
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
||||||
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
||||||
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
||||||
return reflectiveEditorPanel
|
reflectiveEditorView = reflectiveEditorPanel
|
||||||
|
addPlugins(reflectiveEditorView!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addPlugins(reflectiveEditorView: JPanel) {
|
fun addPlugins(reflectiveEditorView: JPanel) {
|
||||||
|
reflectiveEditorView.removeAll() // clear previous
|
||||||
try {
|
try {
|
||||||
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
||||||
loadedPluginsField.isAccessible = true
|
loadedPluginsField.isAccessible = true
|
||||||
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
||||||
|
|
||||||
for ((_, plugin) in loadedPlugins) {
|
for ((pluginInfo, plugin) in loadedPlugins) {
|
||||||
addPluginToEditor(reflectiveEditorView, plugin as Plugin)
|
addPluginToEditor(reflectiveEditorView, pluginInfo as PluginInfo, plugin as Plugin)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|
@ -38,112 +53,214 @@ object ReflectiveEditorView {
|
||||||
reflectiveEditorView.repaint()
|
reflectiveEditorView.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addPluginToEditor(reflectiveEditorView: JPanel, plugin: Any) {
|
private fun addPluginToEditor(reflectiveEditorView: JPanel, pluginInfo : PluginInfo, plugin: Plugin) {
|
||||||
reflectiveEditorView.layout = BoxLayout(reflectiveEditorView, BoxLayout.Y_AXIS)
|
reflectiveEditorView.layout = BoxLayout(reflectiveEditorView, BoxLayout.Y_AXIS)
|
||||||
|
|
||||||
val fieldNotifier = KondoKitUtils.FieldNotifier(plugin)
|
val fieldNotifier = Helpers.FieldNotifier(plugin)
|
||||||
val exposedFields = KondoKitUtils.getKondoExposedFields(plugin)
|
val exposedFields = plugin.javaClass.declaredFields.filter { field ->
|
||||||
|
field.annotations.any { annotation ->
|
||||||
if (exposedFields.isNotEmpty()) {
|
annotation.annotationClass.simpleName == "Exposed"
|
||||||
val packageName = plugin.javaClass.`package`.name
|
}
|
||||||
val labelPanel = JPanel(BorderLayout())
|
|
||||||
labelPanel.maximumSize = Dimension(Int.MAX_VALUE, 30) // Adjust height to be minimal
|
|
||||||
labelPanel.background = VIEW_BACKGROUND_COLOR // Ensure it matches the overall background
|
|
||||||
labelPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
|
||||||
|
|
||||||
val label = JLabel("$packageName", SwingConstants.CENTER)
|
|
||||||
label.foreground = primaryColor
|
|
||||||
label.font = Font("Arial", Font.BOLD, 14)
|
|
||||||
labelPanel.add(label, BorderLayout.CENTER)
|
|
||||||
reflectiveEditorView.add(labelPanel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (field in exposedFields) {
|
if (exposedFields.isNotEmpty()) {
|
||||||
field.isAccessible = true
|
|
||||||
|
|
||||||
val fieldPanel = JPanel()
|
val packageName = plugin.javaClass.`package`.name
|
||||||
fieldPanel.layout = GridBagLayout()
|
val version = pluginInfo.version
|
||||||
fieldPanel.background = WIDGET_COLOR // Match the background for minimal borders
|
val labelPanel = JPanel(BorderLayout())
|
||||||
fieldPanel.foreground = secondaryColor
|
labelPanel.maximumSize = Dimension(Int.MAX_VALUE, 30)
|
||||||
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0) // No visible border, just spacing
|
labelPanel.background = VIEW_BACKGROUND_COLOR
|
||||||
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
labelPanel.border = BorderFactory.createEmptyBorder(5, 0, 0, 0)
|
||||||
|
|
||||||
val gbc = GridBagConstraints()
|
val label = JLabel("$packageName v$version", SwingConstants.CENTER)
|
||||||
gbc.insets = Insets(0, 5, 0, 5) // Less padding, more minimal spacing
|
label.foreground = primaryColor
|
||||||
|
label.font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
labelPanel.add(label, BorderLayout.CENTER)
|
||||||
|
label.isOpaque = true
|
||||||
|
label.background = Color(21, 21, 21)
|
||||||
|
reflectiveEditorView.add(labelPanel)
|
||||||
|
|
||||||
val label = JLabel(field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).capitalize())
|
for (field in exposedFields) {
|
||||||
label.foreground = secondaryColor
|
field.isAccessible = true
|
||||||
gbc.gridx = 0
|
|
||||||
gbc.gridy = 0
|
|
||||||
gbc.weightx = 0.0
|
|
||||||
gbc.anchor = GridBagConstraints.WEST
|
|
||||||
fieldPanel.add(label, gbc)
|
|
||||||
|
|
||||||
val textField = JTextField(field.get(plugin)?.toString() ?: "")
|
// Get the "Exposed" annotation specifically and retrieve its description, if available
|
||||||
textField.background = VIEW_BACKGROUND_COLOR
|
val exposedAnnotation = field.annotations.firstOrNull { annotation ->
|
||||||
textField.foreground = secondaryColor
|
annotation.annotationClass.simpleName == "Exposed"
|
||||||
textField.border = BorderFactory.createLineBorder(WIDGET_COLOR, 1) // Subtle border
|
|
||||||
gbc.gridx = 1
|
|
||||||
gbc.gridy = 0
|
|
||||||
gbc.weightx = 1.0
|
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL
|
|
||||||
fieldPanel.add(textField, gbc)
|
|
||||||
|
|
||||||
val applyButton = JButton("Apply")
|
|
||||||
applyButton.background = primaryColor
|
|
||||||
applyButton.foreground = VIEW_BACKGROUND_COLOR
|
|
||||||
applyButton.border = BorderFactory.createLineBorder(primaryColor, 1)
|
|
||||||
gbc.gridx = 2
|
|
||||||
gbc.gridy = 0
|
|
||||||
gbc.weightx = 0.0
|
|
||||||
gbc.fill = GridBagConstraints.NONE
|
|
||||||
applyButton.addActionListener {
|
|
||||||
try {
|
|
||||||
val newValue = convertValue(field.type, field.genericType, textField.text)
|
|
||||||
fieldNotifier.setFieldValue(field, newValue)
|
|
||||||
JOptionPane.showMessageDialog(
|
|
||||||
null,
|
|
||||||
"${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)} updated successfully!"
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
JOptionPane.showMessageDialog(
|
|
||||||
null,
|
|
||||||
"Failed to update ${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)}: ${e.message}",
|
|
||||||
"Error",
|
|
||||||
JOptionPane.ERROR_MESSAGE
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fieldPanel.add(applyButton, gbc)
|
val description = exposedAnnotation?.let { annotation ->
|
||||||
reflectiveEditorView.add(fieldPanel)
|
try {
|
||||||
|
val descriptionField = annotation.annotationClass.java.getMethod("description")
|
||||||
|
descriptionField.invoke(annotation) as String
|
||||||
|
} catch (e: NoSuchMethodException) {
|
||||||
|
"" // No description method, return empty string
|
||||||
|
}
|
||||||
|
} ?: ""
|
||||||
|
|
||||||
var previousValue = field.get(plugin)?.toString()
|
val fieldPanel = JPanel()
|
||||||
val timer = Timer()
|
fieldPanel.layout = GridBagLayout()
|
||||||
timer.schedule(object : TimerTask() {
|
fieldPanel.background = WIDGET_COLOR
|
||||||
override fun run() {
|
fieldPanel.foreground = secondaryColor
|
||||||
val currentValue = field.get(plugin)?.toString()
|
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
||||||
if (currentValue != previousValue) {
|
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
||||||
previousValue = currentValue
|
|
||||||
SwingUtilities.invokeLater {
|
val gbc = GridBagConstraints()
|
||||||
fieldNotifier.notifyFieldChange(field, currentValue)
|
gbc.insets = Insets(0, 5, 0, 5)
|
||||||
|
|
||||||
|
val label = JLabel(field.name.capitalize())
|
||||||
|
label.foreground = secondaryColor
|
||||||
|
gbc.gridx = 0
|
||||||
|
gbc.gridy = 0
|
||||||
|
gbc.weightx = 0.0
|
||||||
|
label.font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
gbc.anchor = GridBagConstraints.WEST
|
||||||
|
fieldPanel.add(label, gbc)
|
||||||
|
|
||||||
|
// Create appropriate input component based on field type
|
||||||
|
val inputComponent: JComponent = when {
|
||||||
|
field.type == Boolean::class.javaPrimitiveType || field.type == java.lang.Boolean::class.java -> JCheckBox().apply {
|
||||||
|
isSelected = field.get(plugin) as Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
field.type.isEnum -> JComboBox((field.type.enumConstants as Array<Enum<*>>)).apply {
|
||||||
|
selectedItem = field.get(plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
field.type == Int::class.javaPrimitiveType || field.type == Integer::class.java -> JSpinner(SpinnerNumberModel(field.get(plugin) as Int, Int.MIN_VALUE, Int.MAX_VALUE, 1))
|
||||||
|
field.type == Float::class.javaPrimitiveType || field.type == Double::class.javaPrimitiveType || field.type == java.lang.Float::class.java || field.type == java.lang.Double::class.java -> JSpinner(SpinnerNumberModel((field.get(plugin) as Number).toDouble(), -Double.MAX_VALUE, Double.MAX_VALUE, 0.1))
|
||||||
|
else -> JTextField(field.get(plugin)?.toString() ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add mouse listener to the label only if a description is available
|
||||||
|
if (description.isNotBlank()) {
|
||||||
|
label.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
|
||||||
|
label.addMouseListener(object : MouseAdapter() {
|
||||||
|
override fun mouseEntered(e: MouseEvent) {
|
||||||
|
showCustomToolTip(description, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseExited(e: MouseEvent) {
|
||||||
|
customToolTipWindow?.isVisible = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
gbc.gridx = 1
|
||||||
|
gbc.gridy = 0
|
||||||
|
gbc.weightx = 1.0
|
||||||
|
gbc.fill = GridBagConstraints.HORIZONTAL
|
||||||
|
fieldPanel.add(inputComponent, gbc)
|
||||||
|
|
||||||
|
val applyButton = JButton("\u2714").apply {
|
||||||
|
maximumSize = Dimension(Int.MAX_VALUE, 10)
|
||||||
|
}
|
||||||
|
gbc.gridx = 2
|
||||||
|
gbc.gridy = 0
|
||||||
|
gbc.weightx = 0.0
|
||||||
|
gbc.fill = GridBagConstraints.NONE
|
||||||
|
applyButton.addActionListener {
|
||||||
|
try {
|
||||||
|
val newValue = when (inputComponent) {
|
||||||
|
is JCheckBox -> inputComponent.isSelected
|
||||||
|
is JComboBox<*> -> inputComponent.selectedItem
|
||||||
|
is JSpinner -> inputComponent.value
|
||||||
|
is JTextField -> convertValue(field.type, field.genericType, inputComponent.text)
|
||||||
|
else -> throw IllegalArgumentException("Unsupported input component type")
|
||||||
|
}
|
||||||
|
fieldNotifier.setFieldValue(field, newValue)
|
||||||
|
showToast(
|
||||||
|
reflectiveEditorView,
|
||||||
|
"${field.name} updated successfully!"
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showToast(
|
||||||
|
reflectiveEditorView,
|
||||||
|
"Failed to update ${field.name}: ${e.message}",
|
||||||
|
JOptionPane.ERROR_MESSAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldPanel.add(applyButton, gbc)
|
||||||
|
reflectiveEditorView.add(fieldPanel)
|
||||||
|
|
||||||
|
// Track field changes in real-time and update UI
|
||||||
|
var previousValue = field.get(plugin)?.toString()
|
||||||
|
val timer = Timer()
|
||||||
|
timer.schedule(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
val currentValue = field.get(plugin)?.toString()
|
||||||
|
if (currentValue != previousValue) {
|
||||||
|
previousValue = currentValue
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
// Update the inputComponent based on the new value
|
||||||
|
when (inputComponent) {
|
||||||
|
is JCheckBox -> inputComponent.isSelected = field.get(plugin) as Boolean
|
||||||
|
is JComboBox<*> -> inputComponent.selectedItem = field.get(plugin)
|
||||||
|
is JSpinner -> inputComponent.value = field.get(plugin)
|
||||||
|
is JTextField -> inputComponent.text = field.get(plugin)?.toString() ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, 0, 1000) // Poll every 1000 milliseconds (1 second)
|
||||||
}, 0, 1000)
|
}
|
||||||
|
|
||||||
fieldNotifier.addObserver(object : KondoKitUtils.FieldObserver {
|
if (exposedFields.isNotEmpty()) {
|
||||||
override fun onFieldChange(field: Field, newValue: Any?) {
|
reflectiveEditorView.add(Box.createVerticalStrut(5))
|
||||||
if (field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).equals(label.text, ignoreCase = true)) {
|
}
|
||||||
textField.text = newValue?.toString() ?: ""
|
}
|
||||||
textField.revalidate()
|
reflectiveEditorView.revalidate()
|
||||||
textField.repaint()
|
if(KondoKit.plugin.StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW")
|
||||||
}
|
reflectiveEditorView.repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
var customToolTipWindow: JWindow? = null
|
||||||
|
|
||||||
|
fun showCustomToolTip(text: String, component: JComponent) {
|
||||||
|
val _font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||||
|
val backgroundColor = Color(50, 50, 50)
|
||||||
|
val maxWidth = 150
|
||||||
|
val lineHeight = 16
|
||||||
|
|
||||||
|
// Create a dummy JLabel to get FontMetrics for the font used in the tooltip
|
||||||
|
val dummyLabel = JLabel()
|
||||||
|
dummyLabel.font = _font
|
||||||
|
val fontMetrics = dummyLabel.getFontMetrics(_font)
|
||||||
|
|
||||||
|
// Calculate the approximate width of the text
|
||||||
|
val textWidth = fontMetrics.stringWidth(text)
|
||||||
|
|
||||||
|
// Calculate the number of lines required based on the text width and max tooltip width
|
||||||
|
val numberOfLines = ceil(textWidth.toDouble() / maxWidth).toInt()
|
||||||
|
|
||||||
|
// Calculate the required height of the tooltip
|
||||||
|
val requiredHeight = numberOfLines * lineHeight + 6 // Adding some padding
|
||||||
|
|
||||||
|
if (customToolTipWindow == null) {
|
||||||
|
customToolTipWindow = JWindow().apply {
|
||||||
|
contentPane = JLabel("<html><div style='color: white; background-color: #323232; padding: 3px; word-break: break-all;'>$text</div></html>").apply {
|
||||||
|
border = BorderFactory.createLineBorder(Color.BLACK)
|
||||||
|
isOpaque = true
|
||||||
|
background = backgroundColor
|
||||||
|
foreground = Color.WHITE
|
||||||
|
font = _font
|
||||||
|
maximumSize = Dimension(maxWidth, Int.MAX_VALUE)
|
||||||
|
preferredSize = Dimension(maxWidth, requiredHeight)
|
||||||
}
|
}
|
||||||
})
|
pack()
|
||||||
}
|
}
|
||||||
if (exposedFields.isNotEmpty()) {
|
} else {
|
||||||
reflectiveEditorView.add(Box.createVerticalStrut(10))
|
// Update the tooltip text
|
||||||
|
val label = customToolTipWindow!!.contentPane as JLabel
|
||||||
|
label.text = "<html><div style='color: white; background-color: #323232; padding: 3px; word-break: break-all;'>$text</div></html>"
|
||||||
|
label.preferredSize = Dimension(maxWidth, requiredHeight)
|
||||||
|
customToolTipWindow!!.pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Position the tooltip near the component
|
||||||
|
val locationOnScreen = component.locationOnScreen
|
||||||
|
customToolTipWindow!!.setLocation(locationOnScreen.x, locationOnScreen.y + 15)
|
||||||
|
customToolTipWindow!!.isVisible = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
154
plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt
Normal file
154
plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
|
import rt4.GameShell.frame
|
||||||
|
import java.awt.Color
|
||||||
|
import java.awt.Graphics
|
||||||
|
import java.awt.Graphics2D
|
||||||
|
import java.awt.Rectangle
|
||||||
|
import java.awt.event.*
|
||||||
|
import java.util.*
|
||||||
|
import javax.swing.JPanel
|
||||||
|
|
||||||
|
class ScrollablePanel(private val content: JPanel) : JPanel() {
|
||||||
|
private var lastMouseY = 0
|
||||||
|
private var currentOffsetY = 0
|
||||||
|
private var scrollbarHeight = 0
|
||||||
|
private var scrollbarY = 0
|
||||||
|
private var showScrollbar = false
|
||||||
|
private var draggingScrollPill = false
|
||||||
|
|
||||||
|
// Define a buffer for the view height (extra space for smoother scrolling)
|
||||||
|
private val viewBuffer = -30
|
||||||
|
|
||||||
|
init {
|
||||||
|
layout = null
|
||||||
|
background = VIEW_BACKGROUND_COLOR // Color.red color can be set to debug
|
||||||
|
|
||||||
|
// Initial content bounds
|
||||||
|
content.bounds = Rectangle(0, 0, 242, content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer))
|
||||||
|
add(content)
|
||||||
|
|
||||||
|
// Add listeners for scrolling interactions
|
||||||
|
addMouseListener(object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
lastMouseY = e.y
|
||||||
|
if (showScrollbar && e.x in (242 - 10)..242 && e.y in scrollbarY..(scrollbarY + scrollbarHeight)) {
|
||||||
|
draggingScrollPill = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
draggingScrollPill = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
addMouseMotionListener(object : MouseMotionAdapter() {
|
||||||
|
override fun mouseDragged(e: MouseEvent) {
|
||||||
|
val deltaY = e.y - lastMouseY
|
||||||
|
if (draggingScrollPill && showScrollbar) {
|
||||||
|
val viewHeight = frame.height
|
||||||
|
val contentHeight = content.height
|
||||||
|
val scrollRatio = contentHeight.toDouble() / viewHeight
|
||||||
|
scrollContent((deltaY * scrollRatio).toInt())
|
||||||
|
} else if (showScrollbar) {
|
||||||
|
scrollContent(deltaY)
|
||||||
|
}
|
||||||
|
lastMouseY = e.y
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
addMouseWheelListener { e ->
|
||||||
|
if (showScrollbar) {
|
||||||
|
scrollContent(-e.wheelRotation * 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer to periodically check and update scrollbar status
|
||||||
|
Timer().schedule(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
updateScrollbar()
|
||||||
|
}
|
||||||
|
}, 0, 1000)
|
||||||
|
|
||||||
|
// Component listener for resizing the frame
|
||||||
|
frame.addComponentListener(object : ComponentAdapter() {
|
||||||
|
override fun componentResized(e: ComponentEvent) {
|
||||||
|
handleResize()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleResize() {
|
||||||
|
// Ensure the ScrollablePanel resizes with the frame
|
||||||
|
bounds = Rectangle(0, 0, 242, frame.height)
|
||||||
|
|
||||||
|
// Dynamically update content bounds and scrollbar on frame resize with buffer
|
||||||
|
content.bounds = Rectangle(0, 0, 242, content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer))
|
||||||
|
showScrollbar = content.height > frame.height
|
||||||
|
|
||||||
|
currentOffsetY = 0
|
||||||
|
|
||||||
|
content.setLocation(0, currentOffsetY)
|
||||||
|
updateScrollbar()
|
||||||
|
|
||||||
|
revalidate()
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scrollContent(deltaY: Int) {
|
||||||
|
if (!showScrollbar) {
|
||||||
|
currentOffsetY = 0
|
||||||
|
content.setLocation(0, currentOffsetY)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentOffsetY += deltaY
|
||||||
|
|
||||||
|
// Apply buffer to maxOffset
|
||||||
|
val maxOffset = (frame.height - content.height + viewBuffer).coerceAtMost(0)
|
||||||
|
currentOffsetY = currentOffsetY.coerceAtMost(0).coerceAtLeast(maxOffset)
|
||||||
|
|
||||||
|
content.setLocation(0, currentOffsetY)
|
||||||
|
|
||||||
|
val contentHeight = content.height
|
||||||
|
val viewHeight = frame.height + viewBuffer
|
||||||
|
val scrollableRatio = viewHeight.toDouble() / contentHeight
|
||||||
|
scrollbarY = ((-currentOffsetY / contentHeight.toDouble()) * viewHeight).toInt()
|
||||||
|
scrollbarHeight = (viewHeight * scrollableRatio).toInt().coerceAtLeast(20)
|
||||||
|
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateScrollbar() {
|
||||||
|
showScrollbar = content.height > frame.height
|
||||||
|
|
||||||
|
val contentHeight = content.height
|
||||||
|
val viewHeight = frame.height + viewBuffer
|
||||||
|
|
||||||
|
if (showScrollbar) {
|
||||||
|
val scrollableRatio = viewHeight.toDouble() / contentHeight
|
||||||
|
scrollbarY = ((-currentOffsetY / contentHeight.toDouble()) * viewHeight).toInt()
|
||||||
|
scrollbarHeight = (viewHeight * scrollableRatio).toInt().coerceAtLeast(20)
|
||||||
|
} else {
|
||||||
|
scrollbarY = 0
|
||||||
|
scrollbarHeight = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun paintComponent(g: Graphics) {
|
||||||
|
super.paintComponent(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun paintChildren(g: Graphics) {
|
||||||
|
super.paintChildren(g)
|
||||||
|
if (showScrollbar) {
|
||||||
|
val g2 = g as Graphics2D
|
||||||
|
val scrollbarX = 238
|
||||||
|
g2.color = Color(64, 64, 64)
|
||||||
|
g2.fillRect(scrollbarX, scrollbarY, 2, scrollbarHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,32 +4,36 @@ import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.formatNumber
|
import KondoKit.Helpers.formatNumber
|
||||||
import KondoKit.Helpers.getProgressBarColor
|
import KondoKit.Helpers.getProgressBarColor
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
|
import KondoKit.Helpers.addMouseListenerToAll
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.plugin.Companion.IMAGE_SIZE
|
import KondoKit.plugin.Companion.IMAGE_SIZE
|
||||||
|
import KondoKit.plugin.Companion.LVL_ICON
|
||||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_SIZE
|
import KondoKit.plugin.Companion.WIDGET_SIZE
|
||||||
import KondoKit.plugin.Companion.kondoExposed_playerXPMultiplier
|
import KondoKit.plugin.Companion.playerXPMultiplier
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
import KondoKit.plugin.StateManager.totalXPWidget
|
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import javax.swing.Box
|
import javax.swing.*
|
||||||
import javax.swing.BoxLayout
|
|
||||||
import javax.swing.JLabel
|
|
||||||
import javax.swing.JPanel
|
|
||||||
|
|
||||||
object XPTrackerView {
|
object XPTrackerView {
|
||||||
|
|
||||||
private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4)
|
private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4)
|
||||||
|
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
||||||
|
var totalXPWidget: XPWidget? = null
|
||||||
|
val initialXP: MutableMap<Int, Int> = HashMap()
|
||||||
|
var xpTrackerView: JPanel? = null
|
||||||
|
|
||||||
|
|
||||||
val npcHitpointsMap: Map<Int, Int> = try {
|
val npcHitpointsMap: Map<Int, Int> = try {
|
||||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
val json = lines.joinToString("\n")
|
val json = lines.joinToString("\n")
|
||||||
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
||||||
|
|
@ -74,8 +78,8 @@ object XPTrackerView {
|
||||||
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
||||||
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
||||||
val xpPerKill = when (xpWidget.skillId) {
|
val xpPerKill = when (xpWidget.skillId) {
|
||||||
3 -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
3 -> playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
||||||
else -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
else -> playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
||||||
}
|
}
|
||||||
val remainingKills = xpLeft / xpPerKill
|
val remainingKills = xpLeft / xpPerKill
|
||||||
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
||||||
|
|
@ -96,20 +100,57 @@ object XPTrackerView {
|
||||||
|
|
||||||
xpWidget.previousXp = xp
|
xpWidget.previousXp = xp
|
||||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||||
xpWidget.panel.repaint()
|
xpWidget.container.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
||||||
val totalXPWidget = plugin.StateManager.totalXPWidget ?: return
|
val totalXPWidget = totalXPWidget ?: return
|
||||||
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
||||||
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
||||||
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||||
totalXPWidget.panel.repaint()
|
totalXPWidget.container.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun resetXPTracker(xpTrackerView : JPanel){
|
||||||
|
|
||||||
|
// Redo logic here
|
||||||
|
xpTrackerView.removeAll()
|
||||||
|
val popupMenu = createResetMenu()
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the XP widget
|
||||||
|
totalXPWidget = createTotalXPWidget()
|
||||||
|
val wrapped = wrappedWidget(totalXPWidget!!.container)
|
||||||
|
addMouseListenerToAll(wrapped,rightClickListener)
|
||||||
|
wrapped.addMouseListener(rightClickListener)
|
||||||
|
xpTrackerView.add(Box.createVerticalStrut(5))
|
||||||
|
xpTrackerView.add(wrapped)
|
||||||
|
xpTrackerView.add(Box.createVerticalStrut(5))
|
||||||
|
|
||||||
|
initialXP.clear()
|
||||||
|
xpWidgets.clear()
|
||||||
|
|
||||||
|
xpTrackerView.revalidate()
|
||||||
|
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||||
|
xpTrackerView.repaint()
|
||||||
|
}
|
||||||
|
|
||||||
fun createTotalXPWidget(): XPWidget {
|
fun createTotalXPWidget(): XPWidget {
|
||||||
val widgetPanel = Panel().apply {
|
val widgetPanel = Panel().apply {
|
||||||
|
|
@ -120,11 +161,9 @@ object XPTrackerView {
|
||||||
minimumSize = TOTAL_XP_WIDGET_SIZE
|
minimumSize = TOTAL_XP_WIDGET_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(898))
|
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(LVL_ICON))
|
||||||
|
|
||||||
|
|
||||||
val imageContainer = Panel(FlowLayout()).apply {
|
val imageContainer = Panel(FlowLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
|
||||||
preferredSize = IMAGE_SIZE
|
preferredSize = IMAGE_SIZE
|
||||||
maximumSize = IMAGE_SIZE
|
maximumSize = IMAGE_SIZE
|
||||||
minimumSize = IMAGE_SIZE
|
minimumSize = IMAGE_SIZE
|
||||||
|
|
@ -133,15 +172,14 @@ object XPTrackerView {
|
||||||
|
|
||||||
bufferedImageSprite.let { image ->
|
bufferedImageSprite.let { image ->
|
||||||
val imageCanvas = ImageCanvas(image).apply {
|
val imageCanvas = ImageCanvas(image).apply {
|
||||||
background = WIDGET_COLOR
|
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
preferredSize = Dimension(image.width, image.height)
|
maximumSize = preferredSize
|
||||||
maximumSize = Dimension(image.width, image.height)
|
minimumSize = preferredSize
|
||||||
minimumSize = Dimension(image.width, image.height)
|
size = preferredSize
|
||||||
size = Dimension(image.width, image.height)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imageContainer.add(imageCanvas)
|
imageContainer.add(imageCanvas)
|
||||||
imageContainer.size = Dimension(image.width, image.height)
|
imageContainer.size = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
|
|
||||||
imageContainer.revalidate()
|
imageContainer.revalidate()
|
||||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||||
|
|
@ -149,14 +187,13 @@ object XPTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
val textPanel = Panel().apply {
|
val textPanel = Panel().apply {
|
||||||
layout = GridLayout(2, 1, 5, 5)
|
layout = GridLayout(2, 1, 5, 0)
|
||||||
background = WIDGET_COLOR
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val font = Font("Arial", Font.PLAIN, 11)
|
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
val xpGainedLabel = JLabel(
|
val xpGainedLabel = JLabel(
|
||||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
formatHtmlLabelText("Gained: ", primaryColor, "0", secondaryColor)
|
||||||
).apply {
|
).apply {
|
||||||
this.horizontalAlignment = JLabel.LEFT
|
this.horizontalAlignment = JLabel.LEFT
|
||||||
this.font = font
|
this.font = font
|
||||||
|
|
@ -177,7 +214,7 @@ object XPTrackerView {
|
||||||
|
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = -1,
|
skillId = -1,
|
||||||
panel = widgetPanel,
|
container = widgetPanel,
|
||||||
xpGainedLabel = xpGainedLabel,
|
xpGainedLabel = xpGainedLabel,
|
||||||
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
||||||
this.horizontalAlignment = JLabel.LEFT
|
this.horizontalAlignment = JLabel.LEFT
|
||||||
|
|
@ -193,19 +230,83 @@ object XPTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createXPTrackerView(): JPanel? {
|
fun createXPTrackerView(){
|
||||||
val widgetViewPanel = JPanel()
|
val widgetViewPanel = JPanel().apply {
|
||||||
widgetViewPanel.layout = BoxLayout(widgetViewPanel, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
widgetViewPanel.background = VIEW_BACKGROUND_COLOR
|
background = VIEW_BACKGROUND_COLOR
|
||||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
}
|
||||||
|
|
||||||
|
val popupMenu = createResetMenu()
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the XP widget
|
||||||
totalXPWidget = createTotalXPWidget()
|
totalXPWidget = createTotalXPWidget()
|
||||||
widgetViewPanel.add(wrappedWidget(totalXPWidget!!.panel))
|
val wrapped = wrappedWidget(totalXPWidget!!.container)
|
||||||
|
addMouseListenerToAll(wrapped,rightClickListener)
|
||||||
|
wrapped.addMouseListener(rightClickListener)
|
||||||
|
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||||
|
widgetViewPanel.add(wrapped)
|
||||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||||
|
|
||||||
return widgetViewPanel
|
xpTrackerView = widgetViewPanel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun createResetMenu(): JPopupMenu {
|
||||||
|
// Create a popup menu
|
||||||
|
val popupMenu = JPopupMenu()
|
||||||
|
|
||||||
|
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
|
popupMenu.background = Color(45, 45, 45)
|
||||||
|
|
||||||
|
// Create menu items with custom font and colors
|
||||||
|
val menuItem1 = JMenuItem("Reset Tracker").apply {
|
||||||
|
font = rFont // Set custom font
|
||||||
|
background = Color(45, 45, 45) // Dark background for item
|
||||||
|
foreground = Color(220, 220, 220) // Light text color for item
|
||||||
|
}
|
||||||
|
|
||||||
|
val menuItem2 = JMenuItem("Option 2").apply {
|
||||||
|
font = rFont
|
||||||
|
background = Color(45, 45, 45)
|
||||||
|
foreground = Color(220, 220, 220)
|
||||||
|
}
|
||||||
|
|
||||||
|
val menuItem3 = JMenuItem("Option 3").apply {
|
||||||
|
font = rFont
|
||||||
|
background = Color(45, 45, 45)
|
||||||
|
foreground = Color(220, 220, 220)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add menu items to the popup menu
|
||||||
|
popupMenu.add(menuItem1)
|
||||||
|
//popupMenu.add(menuItem2)
|
||||||
|
//popupMenu.add(menuItem3)
|
||||||
|
|
||||||
|
// Add action listeners to each menu item (optional)
|
||||||
|
menuItem1.addActionListener { resetXPTracker(xpTrackerView!!) }
|
||||||
|
//menuItem2.addActionListener { println("Option 2 selected") }
|
||||||
|
//menuItem3.addActionListener { println("Option 3 selected") }
|
||||||
|
|
||||||
|
return popupMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createXPWidget(skillId: Int, previousXp: Int): XPWidget {
|
fun createXPWidget(skillId: Int, previousXp: Int): XPWidget {
|
||||||
val widgetPanel = Panel().apply {
|
val widgetPanel = Panel().apply {
|
||||||
layout = BorderLayout(5, 5)
|
layout = BorderLayout(5, 5)
|
||||||
|
|
@ -242,14 +343,14 @@ object XPTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
val textPanel = Panel().apply {
|
val textPanel = Panel().apply {
|
||||||
layout = GridLayout(2, 2, 5, 5)
|
layout = GridLayout(2, 2, 5, 0)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
val font = Font("Arial", Font.PLAIN, 11)
|
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
val xpGainedLabel = JLabel(
|
val xpGainedLabel = JLabel(
|
||||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||||
).apply {
|
).apply {
|
||||||
this.horizontalAlignment = JLabel.LEFT
|
this.horizontalAlignment = JLabel.LEFT
|
||||||
this.font = font
|
this.font = font
|
||||||
|
|
@ -302,7 +403,7 @@ object XPTrackerView {
|
||||||
|
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = skillId,
|
skillId = skillId,
|
||||||
panel = widgetPanel,
|
container = widgetPanel,
|
||||||
xpGainedLabel = xpGainedLabel,
|
xpGainedLabel = xpGainedLabel,
|
||||||
xpLeftLabel = xpLeftLabel,
|
xpLeftLabel = xpLeftLabel,
|
||||||
xpPerHourLabel = xpPerHourLabel,
|
xpPerHourLabel = xpPerHourLabel,
|
||||||
|
|
@ -314,18 +415,18 @@ object XPTrackerView {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wrappedWidget(component: Component, padding: Int = 7): Panel {
|
fun wrappedWidget(component: Component, padding: Int = 7): Container {
|
||||||
val outerPanelSize = Dimension(
|
val outerPanelSize = Dimension(
|
||||||
component.preferredSize.width + 2 * padding,
|
component.preferredSize.width + 2 * padding,
|
||||||
component.preferredSize.height + 2 * padding
|
component.preferredSize.height + 2 * padding
|
||||||
)
|
)
|
||||||
val outerPanel = Panel(GridBagLayout()).apply {
|
val outerPanel = JPanel(GridBagLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = outerPanelSize
|
preferredSize = outerPanelSize
|
||||||
maximumSize = outerPanelSize
|
maximumSize = outerPanelSize
|
||||||
minimumSize = outerPanelSize
|
minimumSize = outerPanelSize
|
||||||
}
|
}
|
||||||
val innerPanel = Panel(BorderLayout()).apply {
|
val innerPanel = JPanel(BorderLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = component.preferredSize
|
preferredSize = component.preferredSize
|
||||||
maximumSize = component.preferredSize
|
maximumSize = component.preferredSize
|
||||||
|
|
@ -343,14 +444,14 @@ object XPTrackerView {
|
||||||
|
|
||||||
|
|
||||||
data class XPWidget(
|
data class XPWidget(
|
||||||
val panel: Panel,
|
val container: Container,
|
||||||
val skillId: Int,
|
val skillId: Int,
|
||||||
val xpGainedLabel: JLabel,
|
val xpGainedLabel: JLabel,
|
||||||
val xpLeftLabel: JLabel,
|
val xpLeftLabel: JLabel,
|
||||||
val xpPerHourLabel: JLabel,
|
val xpPerHourLabel: JLabel,
|
||||||
val actionsRemainingLabel: JLabel,
|
val actionsRemainingLabel: JLabel,
|
||||||
val progressBar: ProgressBar,
|
val progressBar: ProgressBar,
|
||||||
var totalXpGained: Int = 0,
|
var totalXpGained: Int = 0,
|
||||||
var startTime: Long = System.currentTimeMillis(),
|
var startTime: Long = System.currentTimeMillis(),
|
||||||
var previousXp: Int = 0
|
var previousXp: Int = 0
|
||||||
)
|
)
|
||||||
|
|
@ -5,22 +5,27 @@ import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.formatNumber
|
import KondoKit.Helpers.formatNumber
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
import KondoKit.HiscoresView.createHiscoreSearchView
|
import KondoKit.HiscoresView.createHiscoreSearchView
|
||||||
|
import KondoKit.HiscoresView.hiScoreView
|
||||||
import KondoKit.LootTrackerView.BAG_ICON
|
import KondoKit.LootTrackerView.BAG_ICON
|
||||||
import KondoKit.LootTrackerView.createLootTrackerView
|
import KondoKit.LootTrackerView.createLootTrackerView
|
||||||
|
import KondoKit.LootTrackerView.lootTrackerView
|
||||||
import KondoKit.LootTrackerView.npcDeathSnapshots
|
import KondoKit.LootTrackerView.npcDeathSnapshots
|
||||||
import KondoKit.LootTrackerView.onPostClientTick
|
import KondoKit.LootTrackerView.onPostClientTick
|
||||||
import KondoKit.LootTrackerView.takeGroundSnapshot
|
import KondoKit.LootTrackerView.takeGroundSnapshot
|
||||||
import KondoKit.ReflectiveEditorView.addPlugins
|
import KondoKit.ReflectiveEditorView.addPlugins
|
||||||
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
||||||
|
import KondoKit.ReflectiveEditorView.reflectiveEditorView
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.XPTrackerView.createTotalXPWidget
|
|
||||||
import KondoKit.XPTrackerView.createXPTrackerView
|
import KondoKit.XPTrackerView.createXPTrackerView
|
||||||
import KondoKit.XPTrackerView.createXPWidget
|
import KondoKit.XPTrackerView.createXPWidget
|
||||||
|
import KondoKit.XPTrackerView.initialXP
|
||||||
|
import KondoKit.XPTrackerView.resetXPTracker
|
||||||
|
import KondoKit.XPTrackerView.totalXPWidget
|
||||||
import KondoKit.XPTrackerView.updateWidget
|
import KondoKit.XPTrackerView.updateWidget
|
||||||
import KondoKit.XPTrackerView.wrappedWidget
|
import KondoKit.XPTrackerView.wrappedWidget
|
||||||
import KondoKit.plugin.StateManager.initialXP
|
import KondoKit.XPTrackerView.xpTrackerView
|
||||||
import KondoKit.plugin.StateManager.totalXPWidget
|
import KondoKit.XPTrackerView.xpWidgets
|
||||||
import KondoKit.plugin.StateManager.xpWidgets
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.api.*
|
import plugin.api.*
|
||||||
import plugin.api.API.*
|
import plugin.api.API.*
|
||||||
|
|
@ -38,37 +43,47 @@ import java.awt.event.MouseAdapter
|
||||||
import java.awt.event.MouseEvent
|
import java.awt.event.MouseEvent
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FIELD)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class Exposed(val description: String = "")
|
||||||
|
|
||||||
class plugin : Plugin() {
|
class plugin : Plugin() {
|
||||||
companion object {
|
companion object {
|
||||||
val WIDGET_SIZE = Dimension(270, 55)
|
val WIDGET_SIZE = Dimension(220, 50)
|
||||||
val TOTAL_XP_WIDGET_SIZE = Dimension(270, 30)
|
val TOTAL_XP_WIDGET_SIZE = Dimension(220, 30)
|
||||||
val IMAGE_SIZE = Dimension(20, 20)
|
val IMAGE_SIZE = Dimension(20, 20)
|
||||||
val WIDGET_COLOR = Color(27, 27, 27)
|
val WIDGET_COLOR = Color(30, 30, 30)
|
||||||
val VIEW_BACKGROUND_COLOR = Color(37, 37, 37)
|
val VIEW_BACKGROUND_COLOR = Color(40, 40, 40)
|
||||||
val primaryColor = Color(129, 129, 129) // Color for "XP Gained:"
|
val primaryColor = Color(165, 165, 165) // Color for "XP Gained:"
|
||||||
val secondaryColor = Color(226, 226, 226) // Color for "0"
|
val secondaryColor = Color(255, 255, 255) // Color for "0"
|
||||||
var kondoExposed_useLiveGEPrices = true
|
|
||||||
var kondoExposed_playerXPMultiplier = 5
|
@Exposed(description = "Default: true, Use Local JSON or the prices from the Live/Stable server API")
|
||||||
const val FIXED_WIDTH = 782
|
var useLiveGEPrices = true
|
||||||
const val SCROLLPANE_WIDTH = 340
|
|
||||||
|
@Exposed(description = "Used to calculate Combat Actions until next level.")
|
||||||
|
var playerXPMultiplier = 5
|
||||||
|
|
||||||
|
@Exposed(description = "Start minimized/collapsed by default")
|
||||||
|
var launchMinimized = false
|
||||||
|
|
||||||
|
private const val FIXED_WIDTH = 782
|
||||||
|
private const val NAVBAR_WIDTH = 30
|
||||||
|
private const val MAIN_CONTENT_WIDTH = 242
|
||||||
private const val WRENCH_ICON = 907
|
private const val WRENCH_ICON = 907
|
||||||
private const val LVL_ICON = 898
|
|
||||||
private const val LOOT_ICON = 777
|
private const val LOOT_ICON = 777
|
||||||
private const val MAG_SPRITE = 1423
|
private const val MAG_SPRITE = 1423
|
||||||
|
const val LVL_ICON = 898
|
||||||
private lateinit var cardLayout: CardLayout
|
private lateinit var cardLayout: CardLayout
|
||||||
private lateinit var mainContentPanel: Panel
|
private lateinit var mainContentPanel: JPanel
|
||||||
private var scrollPane: JScrollPane? = null
|
private var rightPanelWrapper: JScrollPane? = null
|
||||||
private var hiScoreView: JPanel? = null
|
|
||||||
private var reflectiveEditorView: JPanel? = null
|
|
||||||
private var lootTrackerView: JPanel? = null
|
|
||||||
private var xpTrackerView: JPanel? = null
|
|
||||||
private var accumulatedTime = 0L
|
private var accumulatedTime = 0L
|
||||||
|
private var reloadInterfaces = false
|
||||||
private const val tickInterval = 600L
|
private const val tickInterval = 600L
|
||||||
private var pluginsReloaded = false
|
private var pluginsReloaded = false
|
||||||
private var loginScreen = 160;
|
private var loginScreen = 160
|
||||||
private var lastLogin = ""
|
private var lastLogin = ""
|
||||||
private var initialized = false;
|
private var initialized = false;
|
||||||
|
private var lastClickTime = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allSpritesLoaded() : Boolean {
|
fun allSpritesLoaded() : Boolean {
|
||||||
|
|
@ -95,43 +110,36 @@ class plugin : Plugin() {
|
||||||
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
|
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
|
||||||
// if we logged in with a new character
|
// if we logged in with a new character
|
||||||
// we need to reset the trackers
|
// we need to reset the trackers
|
||||||
xpTrackerView?.removeAll()
|
xpTrackerView?.let { resetXPTracker(it) }
|
||||||
totalXPWidget = createTotalXPWidget()
|
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
|
||||||
xpTrackerView?.add(wrappedWidget(totalXPWidget!!.panel))
|
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
|
||||||
initialXP.clear()
|
|
||||||
xpWidgets.clear()
|
|
||||||
|
|
||||||
xpTrackerView?.revalidate()
|
|
||||||
if (StateManager.focusedView == "XP_TRACKER_VIEW")
|
|
||||||
xpTrackerView?.repaint()
|
|
||||||
}
|
}
|
||||||
lastLogin = Player.usernameInput.toString()
|
lastLogin = Player.usernameInput.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun UpdateDisplaySettings() {
|
private fun UpdateDisplaySettings() {
|
||||||
val mode = GetWindowMode()
|
val mode = GetWindowMode()
|
||||||
|
val currentScrollPaneWidth = if (mainContentPanel.isVisible) NAVBAR_WIDTH + MAIN_CONTENT_WIDTH else NAVBAR_WIDTH
|
||||||
when (mode) {
|
when (mode) {
|
||||||
WindowMode.FIXED -> {
|
WindowMode.FIXED -> {
|
||||||
if (frame.width < FIXED_WIDTH + SCROLLPANE_WIDTH) {
|
if (frame.width < FIXED_WIDTH + currentScrollPaneWidth) {
|
||||||
frame.setSize(FIXED_WIDTH + SCROLLPANE_WIDTH, frame.height)
|
frame.setSize(FIXED_WIDTH + currentScrollPaneWidth, frame.height)
|
||||||
}
|
}
|
||||||
val difference = frame.width - (FIXED_WIDTH + SCROLLPANE_WIDTH)
|
val difference = frame.width - (FIXED_WIDTH + currentScrollPaneWidth)
|
||||||
GameShell.leftMargin = difference / 2
|
GameShell.leftMargin = difference / 2
|
||||||
}
|
}
|
||||||
WindowMode.RESIZABLE -> {
|
WindowMode.RESIZABLE -> {
|
||||||
GameShell.canvasWidth -= SCROLLPANE_WIDTH
|
GameShell.canvasWidth = frame.width - (currentScrollPaneWidth + 16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scrollPane?.revalidate()
|
rightPanelWrapper?.preferredSize = Dimension(currentScrollPaneWidth, frame.height)
|
||||||
scrollPane?.repaint()
|
rightPanelWrapper?.revalidate()
|
||||||
|
rightPanelWrapper?.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun OnKondoValueUpdated(){
|
fun OnKondoValueUpdated(){
|
||||||
StoreData("kondoUseRemoteGE", kondoExposed_useLiveGEPrices)
|
StoreData("kondoUseRemoteGE", useLiveGEPrices)
|
||||||
StoreData("kondoPlayerXPMultiplier", kondoExposed_playerXPMultiplier)
|
StoreData("kondoPlayerXPMultiplier", playerXPMultiplier)
|
||||||
LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices()
|
LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices()
|
||||||
|
StoreData("kondoLaunchMinimized", launchMinimized)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||||
|
|
@ -139,12 +147,16 @@ class plugin : Plugin() {
|
||||||
for ((index, entry) in currentEntries.withIndex()) {
|
for ((index, entry) in currentEntries.withIndex()) {
|
||||||
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
|
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
|
||||||
val input = entry.subject
|
val input = entry.subject
|
||||||
val username = input
|
// Trim spaces, clean up tags, and remove the level info
|
||||||
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "")
|
val cleanedInput = input
|
||||||
.replace(Regex("<img=\\d+>"), "")
|
.trim() // Remove any leading/trailing spaces
|
||||||
.split(" ") // Split by spaces
|
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "") // Remove color tags
|
||||||
.first() // Take the first part, which is the username
|
.replace(Regex("<img=\\d+>"), "") // Remove image tags
|
||||||
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(username.replace(" ","_")))
|
.replace(Regex("\\(level: \\d+\\)"), "") // Remove level text e.g. (level: 44)
|
||||||
|
.trim() // Trim again to remove extra spaces after removing level text
|
||||||
|
|
||||||
|
// Proceed with the full cleaned username
|
||||||
|
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(cleanedInput))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,40 +164,31 @@ class plugin : Plugin() {
|
||||||
|
|
||||||
private fun searchHiscore(username: String): Runnable {
|
private fun searchHiscore(username: String): Runnable {
|
||||||
return Runnable {
|
return Runnable {
|
||||||
cardLayout.show(mainContentPanel, "HISCORE_SEARCH_VIEW")
|
setActiveView("HISCORE_SEARCH_VIEW")
|
||||||
StateManager.focusedView = "HISCORE_SEARCH_VIEW"
|
|
||||||
val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) }
|
val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) }
|
||||||
|
|
||||||
customSearchField?.searchPlayer(username) ?: run {
|
customSearchField?.searchPlayer(username) ?: run {
|
||||||
println("searchView is null or CustomSearchField creation failed.")
|
println("searchView is null or CustomSearchField creation failed.")
|
||||||
}
|
}
|
||||||
hiScoreView?.repaint()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun OnPluginsReloaded(): Boolean {
|
override fun OnPluginsReloaded(): Boolean {
|
||||||
if (!initialized) return true
|
if (!initialized) return true
|
||||||
|
|
||||||
UpdateDisplaySettings()
|
UpdateDisplaySettings()
|
||||||
|
|
||||||
frame.remove(scrollPane)
|
frame.remove(rightPanelWrapper)
|
||||||
frame.layout = BorderLayout()
|
frame.layout = BorderLayout()
|
||||||
frame.add(scrollPane, BorderLayout.EAST)
|
frame.add(rightPanelWrapper, BorderLayout.EAST)
|
||||||
|
|
||||||
// Clear or regenerate the reflectiveEditorView
|
|
||||||
reflectiveEditorView?.removeAll()
|
|
||||||
reflectiveEditorView?.revalidate()
|
|
||||||
if(StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW")
|
|
||||||
reflectiveEditorView?.repaint()
|
|
||||||
|
|
||||||
frame.revalidate()
|
frame.revalidate()
|
||||||
frame.repaint()
|
frame.repaint()
|
||||||
pluginsReloaded = true
|
pluginsReloaded = true
|
||||||
|
reloadInterfaces = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
||||||
if (!initialXP.containsKey(skillId)) {
|
if (!initialXP.containsKey(skillId)) {
|
||||||
initialXP[skillId] = xp
|
initialXP[skillId] = xp
|
||||||
|
|
@ -203,11 +206,11 @@ class plugin : Plugin() {
|
||||||
xpWidget = createXPWidget(skillId, previousXp)
|
xpWidget = createXPWidget(skillId, previousXp)
|
||||||
xpWidgets[skillId] = xpWidget
|
xpWidgets[skillId] = xpWidget
|
||||||
|
|
||||||
xpTrackerView?.add(wrappedWidget(xpWidget.panel))
|
xpTrackerView?.add(wrappedWidget(xpWidget.container))
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||||
|
|
||||||
xpTrackerView?.revalidate()
|
xpTrackerView?.revalidate()
|
||||||
if(StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(focusedView == "XP_TRACKER_VIEW")
|
||||||
xpTrackerView?.repaint()
|
xpTrackerView?.repaint()
|
||||||
|
|
||||||
updateWidget(xpWidget, xp)
|
updateWidget(xpWidget, xp)
|
||||||
|
|
@ -221,23 +224,25 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginsReloaded) {
|
if (pluginsReloaded) {
|
||||||
InterfaceList.method3712(true) // Gets the resize working correctly
|
|
||||||
reflectiveEditorView?.let { addPlugins(it) }
|
reflectiveEditorView?.let { addPlugins(it) }
|
||||||
pluginsReloaded = false
|
pluginsReloaded = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reloadInterfaces){
|
||||||
|
InterfaceList.method3712(true) // Gets the resize working correctly
|
||||||
|
reloadInterfaces = false
|
||||||
|
}
|
||||||
|
|
||||||
accumulatedTime += timeDelta
|
accumulatedTime += timeDelta
|
||||||
if (accumulatedTime >= tickInterval) {
|
if (accumulatedTime >= tickInterval) {
|
||||||
lootTrackerView?.let { onPostClientTick(it) }
|
lootTrackerView?.let { onPostClientTick(it) }
|
||||||
accumulatedTime = 0L
|
accumulatedTime = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
||||||
if(!initialized && mainLoadState >= loginScreen) {
|
if(!initialized && mainLoadState >= loginScreen) {
|
||||||
initKondoUI()
|
initKondoUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initKondoUI(){
|
private fun initKondoUI(){
|
||||||
|
|
@ -245,25 +250,66 @@ class plugin : Plugin() {
|
||||||
if(!allSpritesLoaded()) return;
|
if(!allSpritesLoaded()) return;
|
||||||
val frame: Frame? = GameShell.frame
|
val frame: Frame? = GameShell.frame
|
||||||
if (frame != null) {
|
if (frame != null) {
|
||||||
kondoExposed_useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true
|
|
||||||
kondoExposed_playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5
|
|
||||||
cardLayout = CardLayout()
|
|
||||||
mainContentPanel = Panel(cardLayout)
|
|
||||||
mainContentPanel.background = VIEW_BACKGROUND_COLOR
|
|
||||||
|
|
||||||
xpTrackerView = createXPTrackerView()
|
// Disable Font AA
|
||||||
hiScoreView = createHiscoreSearchView()
|
System.setProperty("awt.useSystemAAFontSettings", "off")
|
||||||
lootTrackerView = createLootTrackerView()
|
System.setProperty("swing.aatext", "false")
|
||||||
reflectiveEditorView = createReflectiveEditorView()
|
|
||||||
mainContentPanel.add(xpTrackerView, "XP_TRACKER_VIEW")
|
loadFont()
|
||||||
mainContentPanel.add(hiScoreView, "HISCORE_SEARCH_VIEW")
|
|
||||||
mainContentPanel.add(lootTrackerView, "LOOT_TRACKER_VIEW")
|
try {
|
||||||
mainContentPanel.add(reflectiveEditorView, "REFLECTIVE_EDITOR_VIEW")
|
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel")
|
||||||
|
|
||||||
|
// Modify the UI properties for a dark theme
|
||||||
|
UIManager.put("control", Color(50, 50, 50)) // Default background for most controls
|
||||||
|
UIManager.put("info", Color(50, 50, 50))
|
||||||
|
UIManager.put("nimbusBase", Color(35, 35, 35)) // Base color for Nimbus L&F
|
||||||
|
UIManager.put("nimbusAlertYellow", Color(255, 220, 35))
|
||||||
|
UIManager.put("nimbusDisabledText", Color(100, 100, 100))
|
||||||
|
UIManager.put("nimbusFocus", Color(115, 164, 209))
|
||||||
|
UIManager.put("nimbusGreen", Color(176, 179, 50))
|
||||||
|
UIManager.put("nimbusInfoBlue", Color(66, 139, 221))
|
||||||
|
UIManager.put("nimbusLightBackground", Color(35, 35, 35)) // Background of text fields, etc.
|
||||||
|
UIManager.put("nimbusOrange", Color(191, 98, 4))
|
||||||
|
UIManager.put("nimbusRed", Color(169, 46, 34))
|
||||||
|
UIManager.put("nimbusSelectedText", Color(255, 255, 255))
|
||||||
|
UIManager.put("nimbusSelectionBackground", Color(75, 110, 175)) // Selection background
|
||||||
|
UIManager.put("text", Color(230, 230, 230)) // General text color
|
||||||
|
|
||||||
|
// Update component tree UI to apply the new theme
|
||||||
|
SwingUtilities.updateComponentTreeUI(GameShell.frame)
|
||||||
|
} catch (e : Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore saved values
|
||||||
|
useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true
|
||||||
|
playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5
|
||||||
|
launchMinimized = (GetData("kondoLaunchMinimized") as? Boolean) ?: false
|
||||||
|
|
||||||
|
cardLayout = CardLayout()
|
||||||
|
mainContentPanel = JPanel(cardLayout).apply {
|
||||||
|
border = BorderFactory.createEmptyBorder(0, 0, 0, 0) // Removes any default border or padding
|
||||||
|
background = VIEW_BACKGROUND_COLOR
|
||||||
|
preferredSize = Dimension(MAIN_CONTENT_WIDTH, frame.height)
|
||||||
|
isOpaque = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register Views
|
||||||
|
createXPTrackerView()
|
||||||
|
createHiscoreSearchView()
|
||||||
|
createLootTrackerView()
|
||||||
|
createReflectiveEditorView()
|
||||||
|
|
||||||
|
mainContentPanel.add(ScrollablePanel(xpTrackerView!!), "XP_TRACKER_VIEW")
|
||||||
|
mainContentPanel.add(ScrollablePanel(hiScoreView!!), "HISCORE_SEARCH_VIEW")
|
||||||
|
mainContentPanel.add(ScrollablePanel(lootTrackerView!!), "LOOT_TRACKER_VIEW")
|
||||||
|
mainContentPanel.add(ScrollablePanel(reflectiveEditorView!!), "REFLECTIVE_EDITOR_VIEW")
|
||||||
|
|
||||||
val navPanel = Panel().apply {
|
val navPanel = Panel().apply {
|
||||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = Dimension(42, frame.height)
|
preferredSize = Dimension(NAVBAR_WIDTH, frame.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
navPanel.add(createNavButton(LVL_ICON, "XP_TRACKER_VIEW"))
|
navPanel.add(createNavButton(LVL_ICON, "XP_TRACKER_VIEW"))
|
||||||
|
|
@ -271,13 +317,13 @@ class plugin : Plugin() {
|
||||||
navPanel.add(createNavButton(LOOT_ICON, "LOOT_TRACKER_VIEW"))
|
navPanel.add(createNavButton(LOOT_ICON, "LOOT_TRACKER_VIEW"))
|
||||||
navPanel.add(createNavButton(WRENCH_ICON, "REFLECTIVE_EDITOR_VIEW"))
|
navPanel.add(createNavButton(WRENCH_ICON, "REFLECTIVE_EDITOR_VIEW"))
|
||||||
|
|
||||||
val rightPanel = Panel(BorderLayout()).apply {
|
var rightPanel = Panel(BorderLayout()).apply {
|
||||||
add(mainContentPanel, BorderLayout.CENTER)
|
add(mainContentPanel, BorderLayout.CENTER)
|
||||||
add(navPanel, BorderLayout.EAST)
|
add(navPanel, BorderLayout.EAST)
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollPane = JScrollPane(rightPanel).apply {
|
rightPanelWrapper = JScrollPane(rightPanel).apply {
|
||||||
preferredSize = Dimension(SCROLLPANE_WIDTH, frame.height)
|
preferredSize = Dimension(NAVBAR_WIDTH + MAIN_CONTENT_WIDTH, frame.height)
|
||||||
background = VIEW_BACKGROUND_COLOR
|
background = VIEW_BACKGROUND_COLOR
|
||||||
border = BorderFactory.createEmptyBorder() // Removes the border completely
|
border = BorderFactory.createEmptyBorder() // Removes the border completely
|
||||||
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||||
|
|
@ -285,91 +331,173 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.layout = BorderLayout()
|
frame.layout = BorderLayout()
|
||||||
scrollPane?.let { frame.add(it, BorderLayout.EAST) }
|
rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) }
|
||||||
|
|
||||||
frame.revalidate()
|
if(!launchMinimized){
|
||||||
frame.repaint()
|
setActiveView("XP_TRACKER_VIEW")
|
||||||
|
} else {
|
||||||
StateManager.focusedView = "XP_TRACKER_VIEW"
|
setActiveView("HIDDEN")
|
||||||
|
}
|
||||||
initialized = true
|
initialized = true
|
||||||
pluginsReloaded = true
|
pluginsReloaded = true
|
||||||
UpdateDisplaySettings()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Update() {
|
override fun Update() {
|
||||||
xpWidgets.values.forEach { xpWidget ->
|
|
||||||
|
val widgets = xpWidgets.values
|
||||||
|
val totalXP = totalXPWidget
|
||||||
|
|
||||||
|
widgets.forEach { xpWidget ->
|
||||||
val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0
|
val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||||
val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||||
val formattedXpPerHour = formatNumber(xpPerHour)
|
val formattedXpPerHour = formatNumber(xpPerHour)
|
||||||
xpWidget.xpPerHourLabel.text =
|
xpWidget.xpPerHourLabel.text =
|
||||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
||||||
xpWidget.panel.repaint()
|
xpWidget.container.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
totalXPWidget?.let { totalXPWidget ->
|
totalXP?.let { totalXPWidget ->
|
||||||
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||||
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||||
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
||||||
totalXPWidget.xpPerHourLabel.text =
|
totalXPWidget.xpPerHourLabel.text =
|
||||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
||||||
totalXPWidget.panel.repaint()
|
totalXPWidget.container.repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
||||||
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
||||||
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setActiveView(viewName: String) {
|
||||||
private fun createNavButton(spriteId: Int, viewName: String): JButton {
|
// Handle the visibility of the main content panel
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId))
|
if (viewName == "HIDDEN") {
|
||||||
val buttonSize = Dimension(42, 42)
|
mainContentPanel.isVisible = false
|
||||||
val imageSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
} else {
|
||||||
|
if (!mainContentPanel.isVisible) {
|
||||||
val actionListener = ActionListener {
|
mainContentPanel.isVisible = true
|
||||||
|
}
|
||||||
cardLayout.show(mainContentPanel, viewName)
|
cardLayout.show(mainContentPanel, viewName)
|
||||||
StateManager.focusedView = viewName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadInterfaces = true
|
||||||
|
UpdateDisplaySettings()
|
||||||
|
|
||||||
|
// Revalidate and repaint necessary panels
|
||||||
|
mainContentPanel.revalidate()
|
||||||
|
mainContentPanel.repaint()
|
||||||
|
rightPanelWrapper?.revalidate()
|
||||||
|
rightPanelWrapper?.repaint()
|
||||||
|
frame?.revalidate()
|
||||||
|
frame?.repaint()
|
||||||
|
|
||||||
|
focusedView = viewName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNavButton(spriteId: Int, viewName: String): JPanel {
|
||||||
|
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId))
|
||||||
|
val buttonSize = Dimension(NAVBAR_WIDTH, 32)
|
||||||
|
val imageSize = Dimension((bufferedImageSprite.width / 1.2f).toInt(), (bufferedImageSprite.height / 1.2f).toInt())
|
||||||
|
val cooldownDuration = 100L
|
||||||
|
|
||||||
|
val actionListener = ActionListener {
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
if (currentTime - lastClickTime < cooldownDuration) {
|
||||||
|
return@ActionListener
|
||||||
|
}
|
||||||
|
lastClickTime = currentTime
|
||||||
|
|
||||||
|
if (focusedView == viewName) {
|
||||||
|
setActiveView("HIDDEN")
|
||||||
|
} else {
|
||||||
|
setActiveView(viewName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageCanvas with forced size
|
||||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = imageSize
|
preferredSize = imageSize
|
||||||
maximumSize = imageSize
|
maximumSize = imageSize
|
||||||
minimumSize = imageSize
|
minimumSize = imageSize
|
||||||
addMouseListener(object : MouseAdapter() {
|
|
||||||
override fun mouseClicked(e: MouseEvent?) {
|
|
||||||
actionListener.actionPerformed(null)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val button = JButton().apply {
|
// Wrapping the ImageCanvas in another JPanel to prevent stretching
|
||||||
|
val imageCanvasWrapper = JPanel().apply {
|
||||||
|
layout = GridBagLayout() // Keeps the layout of the wrapped panel minimal
|
||||||
|
preferredSize = imageSize
|
||||||
|
maximumSize = imageSize
|
||||||
|
minimumSize = imageSize
|
||||||
|
isOpaque = false // No background for the wrapper
|
||||||
|
add(imageCanvas) // Adding ImageCanvas directly, layout won't stretch it
|
||||||
|
}
|
||||||
|
|
||||||
|
val panelButton = JPanel().apply {
|
||||||
layout = GridBagLayout()
|
layout = GridBagLayout()
|
||||||
preferredSize = buttonSize
|
preferredSize = buttonSize
|
||||||
maximumSize = buttonSize
|
maximumSize = buttonSize
|
||||||
minimumSize = buttonSize
|
minimumSize = buttonSize
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
isFocusPainted = false
|
isOpaque = true // Ensure background is painted
|
||||||
isBorderPainted = false
|
|
||||||
|
|
||||||
val gbc = GridBagConstraints().apply {
|
val gbc = GridBagConstraints().apply {
|
||||||
anchor = GridBagConstraints.CENTER
|
anchor = GridBagConstraints.CENTER
|
||||||
|
fill = GridBagConstraints.NONE // Prevents stretching
|
||||||
}
|
}
|
||||||
|
|
||||||
add(imageCanvas, gbc)
|
add(imageCanvasWrapper, gbc)
|
||||||
addActionListener(actionListener)
|
|
||||||
|
// Hover and click behavior
|
||||||
|
val hoverListener = object : MouseAdapter() {
|
||||||
|
override fun mouseEntered(e: MouseEvent?) {
|
||||||
|
background = WIDGET_COLOR.darker()
|
||||||
|
imageCanvas.fillColor = WIDGET_COLOR.darker()
|
||||||
|
imageCanvas.repaint()
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseExited(e: MouseEvent?) {
|
||||||
|
background = WIDGET_COLOR
|
||||||
|
imageCanvas.fillColor = WIDGET_COLOR
|
||||||
|
imageCanvas.repaint()
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseClicked(e: MouseEvent?) {
|
||||||
|
actionListener.actionPerformed(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addMouseListener(hoverListener)
|
||||||
|
imageCanvas.addMouseListener(hoverListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
return button
|
return panelButton
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun loadFont(): Font? {
|
||||||
|
val fontStream = plugin::class.java.getResourceAsStream("res/runescape_small.ttf")
|
||||||
|
return if (fontStream != null) {
|
||||||
|
try {
|
||||||
|
val font = Font.createFont(Font.TRUETYPE_FONT, fontStream)
|
||||||
|
val ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||||
|
ge.registerFont(font) // Register the font in the graphics environment
|
||||||
|
font
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println("Font not found!")
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object StateManager {
|
object StateManager {
|
||||||
val initialXP: MutableMap<Int, Int> = HashMap()
|
|
||||||
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
|
||||||
var totalXPWidget: XPWidget? = null
|
|
||||||
var focusedView: String = ""
|
var focusedView: String = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='downthecrop'
|
AUTHOR='downthecrop'
|
||||||
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.'
|
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.'
|
||||||
VERSION=1.1
|
VERSION=2.0
|
||||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue