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
|
||||
|
||||
import java.awt.Color
|
||||
import java.awt.Dimension
|
||||
import javax.swing.JPanel
|
||||
import rt4.GameShell
|
||||
import java.awt.*
|
||||
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 {
|
||||
|
||||
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 {
|
||||
return when (skillId) {
|
||||
0 -> 197
|
||||
|
|
@ -78,13 +233,4 @@ object Helpers {
|
|||
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
|
||||
|
||||
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.showToast
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
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 plugin.api.API
|
||||
import rt4.Sprites
|
||||
|
|
@ -16,6 +23,7 @@ import java.awt.event.MouseEvent
|
|||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.SocketTimeoutException
|
||||
import java.net.URL
|
||||
import javax.swing.*
|
||||
import javax.swing.border.MatteBorder
|
||||
|
|
@ -28,35 +36,38 @@ object Constants {
|
|||
const val LVL_BAR_SPRITE = 898
|
||||
|
||||
// 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_MEDIUM = Dimension(18, 20)
|
||||
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
||||
val HISCORE_PANEL_DIMENSION = Dimension(270, 400)
|
||||
val FILTER_PANEL_DIMENSION = Dimension(270, 30)
|
||||
val SKILLS_PANEL_DIMENSION = Dimension(300, 300)
|
||||
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(270, 30)
|
||||
val SKILL_PANEL_DIMENSION = Dimension(90, 35)
|
||||
val HISCORE_PANEL_DIMENSION = Dimension(230, 500)
|
||||
val FILTER_PANEL_DIMENSION = Dimension(230, 30)
|
||||
val SKILLS_PANEL_DIMENSION = Dimension(230, 290)
|
||||
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(230, 30)
|
||||
val SKILL_PANEL_DIMENSION = Dimension(76, 35)
|
||||
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
|
||||
val COLOR_BACKGROUND_DARK = Color(27, 27, 27)
|
||||
val COLOR_BACKGROUND_MEDIUM = Color(37, 37, 37)
|
||||
val COLOR_BACKGROUND_LIGHT = Color(43, 43, 43)
|
||||
val COLOR_BACKGROUND_MEDIUM = VIEW_BACKGROUND_COLOR
|
||||
val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200)
|
||||
val COLOR_RED = Color.RED
|
||||
val COLOR_SKILL_PANEL = Color(60, 60, 60)
|
||||
|
||||
// Fonts
|
||||
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 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 = ""
|
||||
|
||||
object HiscoresView {
|
||||
|
||||
var hiScoreView: JPanel? = null
|
||||
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
|
||||
|
||||
private var cursorVisible: Boolean = true
|
||||
|
|
@ -69,6 +80,7 @@ object HiscoresView {
|
|||
size = preferredSize
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
fillColor = COLOR_BACKGROUND_DARK
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +137,7 @@ object HiscoresView {
|
|||
}
|
||||
})
|
||||
|
||||
Timer(500) {
|
||||
Timer(1000) {
|
||||
cursorVisible = !cursorVisible
|
||||
if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW")
|
||||
repaint()
|
||||
|
|
@ -140,7 +152,7 @@ object HiscoresView {
|
|||
val fm = g.fontMetrics
|
||||
val cursorX = fm.stringWidth(text) + 30
|
||||
|
||||
imageCanvas?.let { canvas ->
|
||||
imageCanvas.let { canvas ->
|
||||
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
||||
canvas.paint(imgG)
|
||||
imgG.dispose()
|
||||
|
|
@ -159,8 +171,10 @@ object HiscoresView {
|
|||
}
|
||||
|
||||
fun searchPlayer(username: String) {
|
||||
text = username
|
||||
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${username.toLowerCase()}"
|
||||
text = username.replace(" ", "_")
|
||||
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${text.toLowerCase()}"
|
||||
|
||||
updateHiscoresView(null, "Searching...")
|
||||
|
||||
Thread {
|
||||
try {
|
||||
|
|
@ -168,6 +182,10 @@ object HiscoresView {
|
|||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
|
||||
// If a request take longer than 5 seconds timeout.
|
||||
connection.connectTimeout = 5000
|
||||
connection.readTimeout = 5000
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
||||
|
|
@ -179,27 +197,45 @@ object HiscoresView {
|
|||
}
|
||||
} else {
|
||||
SwingUtilities.invokeLater {
|
||||
showError("Player not found!")
|
||||
showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
} catch (e: SocketTimeoutException) {
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
private fun updatePlayerData(jsonResponse: String, username: String) {
|
||||
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
||||
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 ironMode = data.info.iron_mode
|
||||
|
||||
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") {
|
||||
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
||||
|
|
@ -213,11 +249,14 @@ object HiscoresView {
|
|||
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
|
||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||
}
|
||||
|
||||
|
||||
playerNameLabel?.add(nameLabel)
|
||||
|
||||
playerNameLabel?.revalidate()
|
||||
|
|
@ -296,7 +335,7 @@ object HiscoresView {
|
|||
}
|
||||
}
|
||||
|
||||
fun createHiscoreSearchView(): JPanel {
|
||||
fun createHiscoreSearchView() {
|
||||
val hiscorePanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
name = "HISCORE_SEARCH_VIEW"
|
||||
|
|
@ -324,14 +363,13 @@ object HiscoresView {
|
|||
add(searchFieldWrapper)
|
||||
}
|
||||
|
||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
||||
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||
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 {
|
||||
layout = FlowLayout(FlowLayout.CENTER)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
layout = GridBagLayout() // This will center the JLabel both vertically and horizontally
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
|
|
@ -339,7 +377,7 @@ object HiscoresView {
|
|||
}
|
||||
|
||||
hiscorePanel.add(playerNamePanel)
|
||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
||||
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||
|
||||
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||
|
|
@ -348,10 +386,10 @@ object HiscoresView {
|
|||
minimumSize = preferredSize
|
||||
}
|
||||
|
||||
for (i in 0 until 24) {
|
||||
for (i in SKILL_DISPLAY_ORDER) {
|
||||
val skillPanel = JPanel().apply {
|
||||
layout = BorderLayout()
|
||||
background = Constants.COLOR_SKILL_PANEL
|
||||
background = COLOR_BACKGROUND_DARK
|
||||
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
|
|
@ -362,8 +400,9 @@ object HiscoresView {
|
|||
|
||||
val imageCanvas = bufferedImageSprite.let {
|
||||
ImageCanvas(it).apply {
|
||||
preferredSize = Constants.IMAGE_CANVAS_DIMENSION
|
||||
size = Constants.IMAGE_CANVAS_DIMENSION
|
||||
preferredSize = SKILL_SPRITE_DIMENSION
|
||||
size = SKILL_SPRITE_DIMENSION
|
||||
fillColor = COLOR_BACKGROUND_DARK
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +415,7 @@ object HiscoresView {
|
|||
}
|
||||
|
||||
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_DARK
|
||||
background = COLOR_BACKGROUND_DARK
|
||||
add(imageCanvas)
|
||||
add(numberLabel)
|
||||
}
|
||||
|
|
@ -431,7 +470,7 @@ object HiscoresView {
|
|||
}
|
||||
|
||||
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_DARK
|
||||
background = COLOR_BACKGROUND_DARK
|
||||
add(combatLevelIcon)
|
||||
add(combatLevelLabel)
|
||||
}
|
||||
|
|
@ -439,8 +478,9 @@ object HiscoresView {
|
|||
totalCombatPanel.add(totalLevelPanel)
|
||||
totalCombatPanel.add(combatLevelPanel)
|
||||
hiscorePanel.add(totalCombatPanel)
|
||||
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||
|
||||
return hiscorePanel
|
||||
hiScoreView = hiscorePanel;
|
||||
}
|
||||
|
||||
data class HiscoresResponse(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import java.awt.Canvas
|
||||
import java.awt.Color
|
||||
import java.awt.Dimension
|
||||
|
|
@ -8,31 +9,25 @@ import java.awt.image.BufferedImage
|
|||
|
||||
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
||||
|
||||
var fillColor: Color = WIDGET_COLOR
|
||||
|
||||
init {
|
||||
// Manually set the alpha value to 255 (fully opaque) only for pixels that are not fully transparent
|
||||
val width = image.width
|
||||
val height = image.height
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
// Retrieve the current pixel color
|
||||
val color = image.getRGB(x, y)
|
||||
|
||||
// Check if the pixel is not fully transparent (i.e., color is not 0)
|
||||
if (color != 0) {
|
||||
// Ensure the alpha is set to 255 (fully opaque)
|
||||
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
||||
|
||||
// Set the pixel with the updated color
|
||||
image.setRGB(x, y, newColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun paint(g: Graphics) {
|
||||
super.paint(g)
|
||||
g.color = Color(27, 27, 27)
|
||||
g.color = fillColor
|
||||
g.fillRect(0, 0, width, height)
|
||||
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
|
||||
|
||||
import KondoKit.Helpers.addMouseListenerToAll
|
||||
import KondoKit.Helpers.formatHtmlLabelText
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.XPTrackerView.wrappedWidget
|
||||
|
|
@ -9,11 +10,11 @@ import KondoKit.plugin.Companion.WIDGET_COLOR
|
|||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import plugin.api.API
|
||||
import rt4.NpcTypeList
|
||||
import rt4.ObjStackNode
|
||||
import rt4.Player
|
||||
import rt4.SceneGraph
|
||||
import rt4.*
|
||||
import java.awt.*
|
||||
import java.awt.Font
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
|
|
@ -22,6 +23,7 @@ import java.net.URL
|
|||
import java.nio.charset.StandardCharsets
|
||||
import java.text.DecimalFormat
|
||||
import javax.swing.*
|
||||
import kotlin.math.ceil
|
||||
|
||||
object LootTrackerView {
|
||||
private const val SNAPSHOT_LIFESPAN = 10
|
||||
|
|
@ -31,10 +33,12 @@ object LootTrackerView {
|
|||
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
||||
private val npcKillCounts = mutableMapOf<String, Int>()
|
||||
private var totalTrackerWidget: XPWidget? = null
|
||||
var lastConfirmedKillNpcId = -1;
|
||||
var lastConfirmedKillNpcId = -1
|
||||
var customToolTipWindow: JWindow? = null
|
||||
var lootTrackerView: JPanel? = null
|
||||
|
||||
fun loadGEPrices(): Map<String, String> {
|
||||
return if (plugin.kondoExposed_useLiveGEPrices) {
|
||||
return if (plugin.useLiveGEPrices) {
|
||||
try {
|
||||
println("LootTracker: Loading Remote GE Prices")
|
||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||
|
|
@ -69,7 +73,7 @@ object LootTrackerView {
|
|||
} else {
|
||||
try {
|
||||
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 ->
|
||||
val json = lines.joinToString("\n")
|
||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
|
|
@ -94,17 +98,34 @@ object LootTrackerView {
|
|||
|
||||
|
||||
|
||||
fun createLootTrackerView(): JPanel {
|
||||
return JPanel().apply {
|
||||
layout = FlowLayout(FlowLayout.CENTER, 0, 5)
|
||||
fun createLootTrackerView() {
|
||||
lootTrackerView = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS) // Use BoxLayout on Y axis to stack widgets vertically
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
preferredSize = Dimension(270, 700)
|
||||
maximumSize = Dimension(270, 700)
|
||||
minimumSize = Dimension(270, 700)
|
||||
add(Box.createVerticalStrut(5))
|
||||
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()
|
||||
repaint()
|
||||
}
|
||||
|
|
@ -122,7 +143,7 @@ object LootTrackerView {
|
|||
totalTrackerWidget?.let {
|
||||
it.previousXp += newVal
|
||||
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))
|
||||
return XPWidget(
|
||||
skillId = -1,
|
||||
panel = createWidgetPanel(bufferedImageSprite,l2,l1),
|
||||
container = createWidgetPanel(bufferedImageSprite,l2,l1),
|
||||
xpGainedLabel = l2,
|
||||
xpLeftLabel = JLabel(),
|
||||
actionsRemainingLabel = JLabel(),
|
||||
|
|
@ -173,7 +194,7 @@ object LootTrackerView {
|
|||
|
||||
private fun createLabel(text: String): JLabel {
|
||||
return JLabel(text).apply {
|
||||
font = Font("Arial", Font.PLAIN, 11)
|
||||
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
horizontalAlignment = JLabel.LEFT
|
||||
}
|
||||
}
|
||||
|
|
@ -189,9 +210,15 @@ object LootTrackerView {
|
|||
|
||||
// Recalculate lootPanel size based on the number of unique items.
|
||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1)
|
||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
||||
val rowsNeeded = ceil(totalItems / 6.0).toInt()
|
||||
val lootPanelHeight = rowsNeeded * (40)
|
||||
|
||||
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.repaint()
|
||||
|
|
@ -205,21 +232,86 @@ object LootTrackerView {
|
|||
|
||||
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
||||
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)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
add(ImageCanvas(bufferedImageSprite).apply {
|
||||
|
||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||
preferredSize = Dimension(36, 32)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = 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)
|
||||
|
||||
// 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) {
|
||||
panel.removeAll()
|
||||
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) {
|
||||
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
||||
} ?: run {
|
||||
// Panel doesn't exist, so create and add it
|
||||
lootTrackerView.add(createLootFrame(npcName))
|
||||
lootTrackerView.add(Helpers.Spacer(height = 15))
|
||||
lootTrackerView.add(Box.createVerticalStrut(10))
|
||||
lootTrackerView.revalidate()
|
||||
lootTrackerView.repaint()
|
||||
}
|
||||
|
|
@ -330,14 +421,15 @@ object LootTrackerView {
|
|||
val childFramePanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = Dimension(270, 0)
|
||||
maximumSize = Dimension(270, 700)
|
||||
minimumSize = Dimension(230, 0)
|
||||
maximumSize = Dimension(230, 700)
|
||||
name = "HELLO_WORLD"
|
||||
}
|
||||
|
||||
val labelPanel = JPanel(BorderLayout()).apply {
|
||||
background = Color(21, 21, 21)
|
||||
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
||||
maximumSize = Dimension(270, 24)
|
||||
maximumSize = Dimension(230, 24)
|
||||
minimumSize = maximumSize
|
||||
preferredSize = maximumSize
|
||||
}
|
||||
|
|
@ -345,14 +437,14 @@ object LootTrackerView {
|
|||
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
|
||||
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
|
||||
foreground = Color(200, 200, 200)
|
||||
font = Font("Arial", Font.PLAIN, 12)
|
||||
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
horizontalAlignment = JLabel.LEFT
|
||||
name = "killCountLabel_$npcName"
|
||||
}
|
||||
|
||||
val valueLabel = JLabel("0 gp").apply {
|
||||
foreground = Color(200, 200, 200)
|
||||
font = Font("Arial", Font.PLAIN, 12)
|
||||
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
horizontalAlignment = JLabel.RIGHT
|
||||
name = "valueLabel_$npcName"
|
||||
}
|
||||
|
|
@ -370,19 +462,115 @@ object LootTrackerView {
|
|||
|
||||
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(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
|
||||
}
|
||||
|
||||
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() {
|
||||
override fun getPreferredSize(): Dimension {
|
||||
|
|
|
|||
|
|
@ -2,18 +2,19 @@ package KondoKit
|
|||
|
||||
import java.awt.Canvas
|
||||
import java.awt.Color
|
||||
import java.awt.Dimension
|
||||
import java.awt.Font
|
||||
import java.awt.Graphics
|
||||
|
||||
class ProgressBar(
|
||||
private var progress: Double,
|
||||
private val barColor: Color,
|
||||
private var currentLevel: Int = 0,
|
||||
private var nextLevel: Int = 1
|
||||
private var progress: Double,
|
||||
private val barColor: Color,
|
||||
private var currentLevel: Int = 0,
|
||||
private var nextLevel: Int = 1
|
||||
) : Canvas() {
|
||||
|
||||
init {
|
||||
font = Font("Arial", Font.PLAIN, 12)
|
||||
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
}
|
||||
|
||||
override fun paint(g: Graphics) {
|
||||
|
|
@ -25,22 +26,32 @@ class ProgressBar(
|
|||
g.fillRect(0, 0, width, this.height)
|
||||
|
||||
// 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)
|
||||
|
||||
// Variables for text position
|
||||
val textY = this.height / 2 + 6
|
||||
|
||||
// Draw the current level on the far left
|
||||
g.color = Color(255, 255, 255)
|
||||
g.drawString("Lvl. $currentLevel", 5, this.height / 2 + 4)
|
||||
drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, Color(255, 255, 255))
|
||||
|
||||
// Draw the percentage in the middle
|
||||
val percentageText = String.format("%.2f%%", progress)
|
||||
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
|
||||
val nextLevelText = "Lvl. $nextLevel"
|
||||
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) {
|
||||
|
|
@ -50,4 +61,15 @@ class ProgressBar(
|
|||
if(isVisible)
|
||||
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
|
||||
|
||||
import KondoKit.KondoKitUtils.convertValue
|
||||
import KondoKit.Helpers.convertValue
|
||||
import KondoKit.Helpers.showToast
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import plugin.Plugin
|
||||
import plugin.PluginInfo
|
||||
import plugin.PluginRepository
|
||||
import java.awt.*
|
||||
import java.lang.reflect.Field
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.util.*
|
||||
import java.util.Timer
|
||||
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 {
|
||||
fun createReflectiveEditorView(): JPanel {
|
||||
var reflectiveEditorView: JPanel? = null
|
||||
fun createReflectiveEditorView() {
|
||||
val reflectiveEditorPanel = JPanel(BorderLayout())
|
||||
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
||||
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
||||
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
||||
return reflectiveEditorPanel
|
||||
reflectiveEditorView = reflectiveEditorPanel
|
||||
addPlugins(reflectiveEditorView!!)
|
||||
}
|
||||
|
||||
fun addPlugins(reflectiveEditorView: JPanel) {
|
||||
reflectiveEditorView.removeAll() // clear previous
|
||||
try {
|
||||
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
||||
loadedPluginsField.isAccessible = true
|
||||
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
||||
|
||||
for ((_, plugin) in loadedPlugins) {
|
||||
addPluginToEditor(reflectiveEditorView, plugin as Plugin)
|
||||
for ((pluginInfo, plugin) in loadedPlugins) {
|
||||
addPluginToEditor(reflectiveEditorView, pluginInfo as PluginInfo, plugin as Plugin)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
|
|
@ -38,112 +53,214 @@ object ReflectiveEditorView {
|
|||
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)
|
||||
|
||||
val fieldNotifier = KondoKitUtils.FieldNotifier(plugin)
|
||||
val exposedFields = KondoKitUtils.getKondoExposedFields(plugin)
|
||||
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
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)
|
||||
val fieldNotifier = Helpers.FieldNotifier(plugin)
|
||||
val exposedFields = plugin.javaClass.declaredFields.filter { field ->
|
||||
field.annotations.any { annotation ->
|
||||
annotation.annotationClass.simpleName == "Exposed"
|
||||
}
|
||||
}
|
||||
|
||||
for (field in exposedFields) {
|
||||
field.isAccessible = true
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
|
||||
val fieldPanel = JPanel()
|
||||
fieldPanel.layout = GridBagLayout()
|
||||
fieldPanel.background = WIDGET_COLOR // Match the background for minimal borders
|
||||
fieldPanel.foreground = secondaryColor
|
||||
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0) // No visible border, just spacing
|
||||
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
||||
val packageName = plugin.javaClass.`package`.name
|
||||
val version = pluginInfo.version
|
||||
val labelPanel = JPanel(BorderLayout())
|
||||
labelPanel.maximumSize = Dimension(Int.MAX_VALUE, 30)
|
||||
labelPanel.background = VIEW_BACKGROUND_COLOR
|
||||
labelPanel.border = BorderFactory.createEmptyBorder(5, 0, 0, 0)
|
||||
|
||||
val gbc = GridBagConstraints()
|
||||
gbc.insets = Insets(0, 5, 0, 5) // Less padding, more minimal spacing
|
||||
val label = JLabel("$packageName v$version", SwingConstants.CENTER)
|
||||
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())
|
||||
label.foreground = secondaryColor
|
||||
gbc.gridx = 0
|
||||
gbc.gridy = 0
|
||||
gbc.weightx = 0.0
|
||||
gbc.anchor = GridBagConstraints.WEST
|
||||
fieldPanel.add(label, gbc)
|
||||
for (field in exposedFields) {
|
||||
field.isAccessible = true
|
||||
|
||||
val textField = JTextField(field.get(plugin)?.toString() ?: "")
|
||||
textField.background = VIEW_BACKGROUND_COLOR
|
||||
textField.foreground = secondaryColor
|
||||
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
|
||||
)
|
||||
// Get the "Exposed" annotation specifically and retrieve its description, if available
|
||||
val exposedAnnotation = field.annotations.firstOrNull { annotation ->
|
||||
annotation.annotationClass.simpleName == "Exposed"
|
||||
}
|
||||
}
|
||||
|
||||
fieldPanel.add(applyButton, gbc)
|
||||
reflectiveEditorView.add(fieldPanel)
|
||||
val description = exposedAnnotation?.let { annotation ->
|
||||
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 timer = Timer()
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
val currentValue = field.get(plugin)?.toString()
|
||||
if (currentValue != previousValue) {
|
||||
previousValue = currentValue
|
||||
SwingUtilities.invokeLater {
|
||||
fieldNotifier.notifyFieldChange(field, currentValue)
|
||||
val fieldPanel = JPanel()
|
||||
fieldPanel.layout = GridBagLayout()
|
||||
fieldPanel.background = WIDGET_COLOR
|
||||
fieldPanel.foreground = secondaryColor
|
||||
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
||||
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
||||
|
||||
val gbc = GridBagConstraints()
|
||||
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)
|
||||
}, 0, 1000) // Poll every 1000 milliseconds (1 second)
|
||||
}
|
||||
|
||||
fieldNotifier.addObserver(object : KondoKitUtils.FieldObserver {
|
||||
override fun onFieldChange(field: Field, newValue: Any?) {
|
||||
if (field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).equals(label.text, ignoreCase = true)) {
|
||||
textField.text = newValue?.toString() ?: ""
|
||||
textField.revalidate()
|
||||
textField.repaint()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
reflectiveEditorView.add(Box.createVerticalStrut(10))
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
reflectiveEditorView.add(Box.createVerticalStrut(5))
|
||||
}
|
||||
}
|
||||
reflectiveEditorView.revalidate()
|
||||
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()
|
||||
}
|
||||
} else {
|
||||
// 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.getProgressBarColor
|
||||
import KondoKit.Helpers.getSpriteId
|
||||
import KondoKit.Helpers.addMouseListenerToAll
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.plugin.Companion.IMAGE_SIZE
|
||||
import KondoKit.plugin.Companion.LVL_ICON
|
||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
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.secondaryColor
|
||||
import KondoKit.plugin.StateManager.totalXPWidget
|
||||
import plugin.api.API
|
||||
import java.awt.*
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.swing.Box
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.*
|
||||
|
||||
object XPTrackerView {
|
||||
|
||||
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 {
|
||||
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 ->
|
||||
val json = lines.joinToString("\n")
|
||||
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
||||
|
|
@ -74,8 +78,8 @@ object XPTrackerView {
|
|||
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
||||
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
||||
val xpPerKill = when (xpWidget.skillId) {
|
||||
3 -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
||||
else -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
||||
3 -> playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
||||
else -> playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
||||
}
|
||||
val remainingKills = xpLeft / xpPerKill
|
||||
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
||||
|
|
@ -96,20 +100,57 @@ object XPTrackerView {
|
|||
|
||||
xpWidget.previousXp = xp
|
||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
xpWidget.panel.repaint()
|
||||
xpWidget.container.repaint()
|
||||
}
|
||||
|
||||
|
||||
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
||||
val totalXPWidget = plugin.StateManager.totalXPWidget ?: return
|
||||
val totalXPWidget = totalXPWidget ?: return
|
||||
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
||||
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
||||
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||
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 {
|
||||
val widgetPanel = Panel().apply {
|
||||
|
|
@ -120,11 +161,9 @@ object XPTrackerView {
|
|||
minimumSize = TOTAL_XP_WIDGET_SIZE
|
||||
}
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(898))
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(LVL_ICON))
|
||||
|
||||
val imageContainer = Panel(FlowLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = IMAGE_SIZE
|
||||
maximumSize = IMAGE_SIZE
|
||||
minimumSize = IMAGE_SIZE
|
||||
|
|
@ -133,15 +172,14 @@ object XPTrackerView {
|
|||
|
||||
bufferedImageSprite.let { image ->
|
||||
val imageCanvas = ImageCanvas(image).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = Dimension(image.width, image.height)
|
||||
maximumSize = Dimension(image.width, image.height)
|
||||
minimumSize = Dimension(image.width, image.height)
|
||||
size = Dimension(image.width, image.height)
|
||||
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
size = preferredSize
|
||||
}
|
||||
|
||||
imageContainer.add(imageCanvas)
|
||||
imageContainer.size = Dimension(image.width, image.height)
|
||||
imageContainer.size = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||
|
||||
imageContainer.revalidate()
|
||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
|
|
@ -149,14 +187,13 @@ object XPTrackerView {
|
|||
}
|
||||
|
||||
val textPanel = Panel().apply {
|
||||
layout = GridLayout(2, 1, 5, 5)
|
||||
background = WIDGET_COLOR
|
||||
layout = GridLayout(2, 1, 5, 0)
|
||||
}
|
||||
|
||||
val font = Font("Arial", Font.PLAIN, 11)
|
||||
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
|
||||
val xpGainedLabel = JLabel(
|
||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||
formatHtmlLabelText("Gained: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
|
|
@ -177,7 +214,7 @@ object XPTrackerView {
|
|||
|
||||
return XPWidget(
|
||||
skillId = -1,
|
||||
panel = widgetPanel,
|
||||
container = widgetPanel,
|
||||
xpGainedLabel = xpGainedLabel,
|
||||
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
|
|
@ -193,19 +230,83 @@ object XPTrackerView {
|
|||
}
|
||||
|
||||
|
||||
fun createXPTrackerView(): JPanel? {
|
||||
val widgetViewPanel = JPanel()
|
||||
widgetViewPanel.layout = BoxLayout(widgetViewPanel, BoxLayout.Y_AXIS)
|
||||
widgetViewPanel.background = VIEW_BACKGROUND_COLOR
|
||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||
fun createXPTrackerView(){
|
||||
val widgetViewPanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
}
|
||||
|
||||
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()
|
||||
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))
|
||||
|
||||
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 {
|
||||
val widgetPanel = Panel().apply {
|
||||
layout = BorderLayout(5, 5)
|
||||
|
|
@ -242,14 +343,14 @@ object XPTrackerView {
|
|||
}
|
||||
|
||||
val textPanel = Panel().apply {
|
||||
layout = GridLayout(2, 2, 5, 5)
|
||||
layout = GridLayout(2, 2, 5, 0)
|
||||
background = WIDGET_COLOR
|
||||
}
|
||||
|
||||
val font = Font("Arial", Font.PLAIN, 11)
|
||||
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
|
||||
val xpGainedLabel = JLabel(
|
||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
|
|
@ -302,7 +403,7 @@ object XPTrackerView {
|
|||
|
||||
return XPWidget(
|
||||
skillId = skillId,
|
||||
panel = widgetPanel,
|
||||
container = widgetPanel,
|
||||
xpGainedLabel = xpGainedLabel,
|
||||
xpLeftLabel = xpLeftLabel,
|
||||
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(
|
||||
component.preferredSize.width + 2 * padding,
|
||||
component.preferredSize.height + 2 * padding
|
||||
component.preferredSize.width + 2 * padding,
|
||||
component.preferredSize.height + 2 * padding
|
||||
)
|
||||
val outerPanel = Panel(GridBagLayout()).apply {
|
||||
val outerPanel = JPanel(GridBagLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = outerPanelSize
|
||||
maximumSize = outerPanelSize
|
||||
minimumSize = outerPanelSize
|
||||
}
|
||||
val innerPanel = Panel(BorderLayout()).apply {
|
||||
val innerPanel = JPanel(BorderLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = component.preferredSize
|
||||
maximumSize = component.preferredSize
|
||||
|
|
@ -343,14 +444,14 @@ object XPTrackerView {
|
|||
|
||||
|
||||
data class XPWidget(
|
||||
val panel: Panel,
|
||||
val skillId: Int,
|
||||
val xpGainedLabel: JLabel,
|
||||
val xpLeftLabel: JLabel,
|
||||
val xpPerHourLabel: JLabel,
|
||||
val actionsRemainingLabel: JLabel,
|
||||
val progressBar: ProgressBar,
|
||||
var totalXpGained: Int = 0,
|
||||
var startTime: Long = System.currentTimeMillis(),
|
||||
var previousXp: Int = 0
|
||||
val container: Container,
|
||||
val skillId: Int,
|
||||
val xpGainedLabel: JLabel,
|
||||
val xpLeftLabel: JLabel,
|
||||
val xpPerHourLabel: JLabel,
|
||||
val actionsRemainingLabel: JLabel,
|
||||
val progressBar: ProgressBar,
|
||||
var totalXpGained: Int = 0,
|
||||
var startTime: Long = System.currentTimeMillis(),
|
||||
var previousXp: Int = 0
|
||||
)
|
||||
|
|
@ -5,22 +5,27 @@ import KondoKit.Helpers.formatHtmlLabelText
|
|||
import KondoKit.Helpers.formatNumber
|
||||
import KondoKit.Helpers.getSpriteId
|
||||
import KondoKit.HiscoresView.createHiscoreSearchView
|
||||
import KondoKit.HiscoresView.hiScoreView
|
||||
import KondoKit.LootTrackerView.BAG_ICON
|
||||
import KondoKit.LootTrackerView.createLootTrackerView
|
||||
import KondoKit.LootTrackerView.lootTrackerView
|
||||
import KondoKit.LootTrackerView.npcDeathSnapshots
|
||||
import KondoKit.LootTrackerView.onPostClientTick
|
||||
import KondoKit.LootTrackerView.takeGroundSnapshot
|
||||
import KondoKit.ReflectiveEditorView.addPlugins
|
||||
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
||||
import KondoKit.ReflectiveEditorView.reflectiveEditorView
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.XPTrackerView.createTotalXPWidget
|
||||
import KondoKit.XPTrackerView.createXPTrackerView
|
||||
import KondoKit.XPTrackerView.createXPWidget
|
||||
import KondoKit.XPTrackerView.initialXP
|
||||
import KondoKit.XPTrackerView.resetXPTracker
|
||||
import KondoKit.XPTrackerView.totalXPWidget
|
||||
import KondoKit.XPTrackerView.updateWidget
|
||||
import KondoKit.XPTrackerView.wrappedWidget
|
||||
import KondoKit.plugin.StateManager.initialXP
|
||||
import KondoKit.plugin.StateManager.totalXPWidget
|
||||
import KondoKit.plugin.StateManager.xpWidgets
|
||||
import KondoKit.XPTrackerView.xpTrackerView
|
||||
import KondoKit.XPTrackerView.xpWidgets
|
||||
import KondoKit.plugin.StateManager.focusedView
|
||||
import plugin.Plugin
|
||||
import plugin.api.*
|
||||
import plugin.api.API.*
|
||||
|
|
@ -38,37 +43,47 @@ import java.awt.event.MouseAdapter
|
|||
import java.awt.event.MouseEvent
|
||||
import javax.swing.*
|
||||
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Exposed(val description: String = "")
|
||||
|
||||
class plugin : Plugin() {
|
||||
companion object {
|
||||
val WIDGET_SIZE = Dimension(270, 55)
|
||||
val TOTAL_XP_WIDGET_SIZE = Dimension(270, 30)
|
||||
val WIDGET_SIZE = Dimension(220, 50)
|
||||
val TOTAL_XP_WIDGET_SIZE = Dimension(220, 30)
|
||||
val IMAGE_SIZE = Dimension(20, 20)
|
||||
val WIDGET_COLOR = Color(27, 27, 27)
|
||||
val VIEW_BACKGROUND_COLOR = Color(37, 37, 37)
|
||||
val primaryColor = Color(129, 129, 129) // Color for "XP Gained:"
|
||||
val secondaryColor = Color(226, 226, 226) // Color for "0"
|
||||
var kondoExposed_useLiveGEPrices = true
|
||||
var kondoExposed_playerXPMultiplier = 5
|
||||
const val FIXED_WIDTH = 782
|
||||
const val SCROLLPANE_WIDTH = 340
|
||||
val WIDGET_COLOR = Color(30, 30, 30)
|
||||
val VIEW_BACKGROUND_COLOR = Color(40, 40, 40)
|
||||
val primaryColor = Color(165, 165, 165) // Color for "XP Gained:"
|
||||
val secondaryColor = Color(255, 255, 255) // Color for "0"
|
||||
|
||||
@Exposed(description = "Default: true, Use Local JSON or the prices from the Live/Stable server API")
|
||||
var useLiveGEPrices = true
|
||||
|
||||
@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 LVL_ICON = 898
|
||||
private const val LOOT_ICON = 777
|
||||
private const val MAG_SPRITE = 1423
|
||||
const val LVL_ICON = 898
|
||||
private lateinit var cardLayout: CardLayout
|
||||
private lateinit var mainContentPanel: Panel
|
||||
private var scrollPane: JScrollPane? = null
|
||||
private var hiScoreView: JPanel? = null
|
||||
private var reflectiveEditorView: JPanel? = null
|
||||
private var lootTrackerView: JPanel? = null
|
||||
private var xpTrackerView: JPanel? = null
|
||||
private lateinit var mainContentPanel: JPanel
|
||||
private var rightPanelWrapper: JScrollPane? = null
|
||||
private var accumulatedTime = 0L
|
||||
private var reloadInterfaces = false
|
||||
private const val tickInterval = 600L
|
||||
private var pluginsReloaded = false
|
||||
private var loginScreen = 160;
|
||||
private var loginScreen = 160
|
||||
private var lastLogin = ""
|
||||
private var initialized = false;
|
||||
private var lastClickTime = 0L
|
||||
}
|
||||
|
||||
fun allSpritesLoaded() : Boolean {
|
||||
|
|
@ -95,43 +110,36 @@ class plugin : Plugin() {
|
|||
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
|
||||
// if we logged in with a new character
|
||||
// we need to reset the trackers
|
||||
xpTrackerView?.removeAll()
|
||||
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()
|
||||
xpTrackerView?.let { resetXPTracker(it) }
|
||||
}
|
||||
lastLogin = Player.usernameInput.toString()
|
||||
}
|
||||
|
||||
private fun UpdateDisplaySettings() {
|
||||
val mode = GetWindowMode()
|
||||
val currentScrollPaneWidth = if (mainContentPanel.isVisible) NAVBAR_WIDTH + MAIN_CONTENT_WIDTH else NAVBAR_WIDTH
|
||||
when (mode) {
|
||||
WindowMode.FIXED -> {
|
||||
if (frame.width < FIXED_WIDTH + SCROLLPANE_WIDTH) {
|
||||
frame.setSize(FIXED_WIDTH + SCROLLPANE_WIDTH, frame.height)
|
||||
if (frame.width < FIXED_WIDTH + currentScrollPaneWidth) {
|
||||
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
|
||||
}
|
||||
WindowMode.RESIZABLE -> {
|
||||
GameShell.canvasWidth -= SCROLLPANE_WIDTH
|
||||
GameShell.canvasWidth = frame.width - (currentScrollPaneWidth + 16)
|
||||
}
|
||||
}
|
||||
scrollPane?.revalidate()
|
||||
scrollPane?.repaint()
|
||||
rightPanelWrapper?.preferredSize = Dimension(currentScrollPaneWidth, frame.height)
|
||||
rightPanelWrapper?.revalidate()
|
||||
rightPanelWrapper?.repaint()
|
||||
}
|
||||
|
||||
fun OnKondoValueUpdated(){
|
||||
StoreData("kondoUseRemoteGE", kondoExposed_useLiveGEPrices)
|
||||
StoreData("kondoPlayerXPMultiplier", kondoExposed_playerXPMultiplier)
|
||||
StoreData("kondoUseRemoteGE", useLiveGEPrices)
|
||||
StoreData("kondoPlayerXPMultiplier", playerXPMultiplier)
|
||||
LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices()
|
||||
StoreData("kondoLaunchMinimized", launchMinimized)
|
||||
}
|
||||
|
||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||
|
|
@ -139,12 +147,16 @@ class plugin : Plugin() {
|
|||
for ((index, entry) in currentEntries.withIndex()) {
|
||||
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
|
||||
val input = entry.subject
|
||||
val username = input
|
||||
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "")
|
||||
.replace(Regex("<img=\\d+>"), "")
|
||||
.split(" ") // Split by spaces
|
||||
.first() // Take the first part, which is the username
|
||||
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(username.replace(" ","_")))
|
||||
// Trim spaces, clean up tags, and remove the level info
|
||||
val cleanedInput = input
|
||||
.trim() // Remove any leading/trailing spaces
|
||||
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "") // Remove color tags
|
||||
.replace(Regex("<img=\\d+>"), "") // Remove image tags
|
||||
.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 {
|
||||
return Runnable {
|
||||
cardLayout.show(mainContentPanel, "HISCORE_SEARCH_VIEW")
|
||||
StateManager.focusedView = "HISCORE_SEARCH_VIEW"
|
||||
setActiveView("HISCORE_SEARCH_VIEW")
|
||||
val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) }
|
||||
|
||||
customSearchField?.searchPlayer(username) ?: run {
|
||||
println("searchView is null or CustomSearchField creation failed.")
|
||||
}
|
||||
hiScoreView?.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun OnPluginsReloaded(): Boolean {
|
||||
if (!initialized) return true
|
||||
|
||||
UpdateDisplaySettings()
|
||||
|
||||
frame.remove(scrollPane)
|
||||
frame.remove(rightPanelWrapper)
|
||||
frame.layout = BorderLayout()
|
||||
frame.add(scrollPane, BorderLayout.EAST)
|
||||
|
||||
// Clear or regenerate the reflectiveEditorView
|
||||
reflectiveEditorView?.removeAll()
|
||||
reflectiveEditorView?.revalidate()
|
||||
if(StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW")
|
||||
reflectiveEditorView?.repaint()
|
||||
frame.add(rightPanelWrapper, BorderLayout.EAST)
|
||||
|
||||
frame.revalidate()
|
||||
frame.repaint()
|
||||
pluginsReloaded = true
|
||||
reloadInterfaces = true
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
||||
if (!initialXP.containsKey(skillId)) {
|
||||
initialXP[skillId] = xp
|
||||
|
|
@ -203,11 +206,11 @@ class plugin : Plugin() {
|
|||
xpWidget = createXPWidget(skillId, previousXp)
|
||||
xpWidgets[skillId] = xpWidget
|
||||
|
||||
xpTrackerView?.add(wrappedWidget(xpWidget.panel))
|
||||
xpTrackerView?.add(wrappedWidget(xpWidget.container))
|
||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||
|
||||
xpTrackerView?.revalidate()
|
||||
if(StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
if(focusedView == "XP_TRACKER_VIEW")
|
||||
xpTrackerView?.repaint()
|
||||
|
||||
updateWidget(xpWidget, xp)
|
||||
|
|
@ -221,23 +224,25 @@ class plugin : Plugin() {
|
|||
}
|
||||
|
||||
if (pluginsReloaded) {
|
||||
InterfaceList.method3712(true) // Gets the resize working correctly
|
||||
reflectiveEditorView?.let { addPlugins(it) }
|
||||
pluginsReloaded = false
|
||||
}
|
||||
|
||||
if (reloadInterfaces){
|
||||
InterfaceList.method3712(true) // Gets the resize working correctly
|
||||
reloadInterfaces = false
|
||||
}
|
||||
|
||||
accumulatedTime += timeDelta
|
||||
if (accumulatedTime >= tickInterval) {
|
||||
lootTrackerView?.let { onPostClientTick(it) }
|
||||
accumulatedTime = 0L
|
||||
}
|
||||
|
||||
|
||||
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
||||
if(!initialized && mainLoadState >= loginScreen) {
|
||||
initKondoUI()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun initKondoUI(){
|
||||
|
|
@ -245,25 +250,66 @@ class plugin : Plugin() {
|
|||
if(!allSpritesLoaded()) return;
|
||||
val frame: Frame? = GameShell.frame
|
||||
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()
|
||||
hiScoreView = createHiscoreSearchView()
|
||||
lootTrackerView = createLootTrackerView()
|
||||
reflectiveEditorView = createReflectiveEditorView()
|
||||
mainContentPanel.add(xpTrackerView, "XP_TRACKER_VIEW")
|
||||
mainContentPanel.add(hiScoreView, "HISCORE_SEARCH_VIEW")
|
||||
mainContentPanel.add(lootTrackerView, "LOOT_TRACKER_VIEW")
|
||||
mainContentPanel.add(reflectiveEditorView, "REFLECTIVE_EDITOR_VIEW")
|
||||
// Disable Font AA
|
||||
System.setProperty("awt.useSystemAAFontSettings", "off")
|
||||
System.setProperty("swing.aatext", "false")
|
||||
|
||||
loadFont()
|
||||
|
||||
try {
|
||||
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 {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = Dimension(42, frame.height)
|
||||
preferredSize = Dimension(NAVBAR_WIDTH, frame.height)
|
||||
}
|
||||
|
||||
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(WRENCH_ICON, "REFLECTIVE_EDITOR_VIEW"))
|
||||
|
||||
val rightPanel = Panel(BorderLayout()).apply {
|
||||
var rightPanel = Panel(BorderLayout()).apply {
|
||||
add(mainContentPanel, BorderLayout.CENTER)
|
||||
add(navPanel, BorderLayout.EAST)
|
||||
}
|
||||
|
||||
scrollPane = JScrollPane(rightPanel).apply {
|
||||
preferredSize = Dimension(SCROLLPANE_WIDTH, frame.height)
|
||||
rightPanelWrapper = JScrollPane(rightPanel).apply {
|
||||
preferredSize = Dimension(NAVBAR_WIDTH + MAIN_CONTENT_WIDTH, frame.height)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
border = BorderFactory.createEmptyBorder() // Removes the border completely
|
||||
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||
|
|
@ -285,91 +331,173 @@ class plugin : Plugin() {
|
|||
}
|
||||
|
||||
frame.layout = BorderLayout()
|
||||
scrollPane?.let { frame.add(it, BorderLayout.EAST) }
|
||||
rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) }
|
||||
|
||||
frame.revalidate()
|
||||
frame.repaint()
|
||||
|
||||
StateManager.focusedView = "XP_TRACKER_VIEW"
|
||||
if(!launchMinimized){
|
||||
setActiveView("XP_TRACKER_VIEW")
|
||||
} else {
|
||||
setActiveView("HIDDEN")
|
||||
}
|
||||
initialized = true
|
||||
pluginsReloaded = true
|
||||
UpdateDisplaySettings()
|
||||
}
|
||||
}
|
||||
|
||||
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 xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||
val formattedXpPerHour = formatNumber(xpPerHour)
|
||||
xpWidget.xpPerHourLabel.text =
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
||||
xpWidget.panel.repaint()
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
||||
xpWidget.container.repaint()
|
||||
}
|
||||
|
||||
totalXPWidget?.let { totalXPWidget ->
|
||||
totalXP?.let { totalXPWidget ->
|
||||
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
||||
totalXPWidget.xpPerHourLabel.text =
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
||||
totalXPWidget.panel.repaint()
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
||||
totalXPWidget.container.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
||||
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
||||
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
||||
}
|
||||
|
||||
|
||||
private fun createNavButton(spriteId: Int, viewName: String): JButton {
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId))
|
||||
val buttonSize = Dimension(42, 42)
|
||||
val imageSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||
|
||||
val actionListener = ActionListener {
|
||||
private fun setActiveView(viewName: String) {
|
||||
// Handle the visibility of the main content panel
|
||||
if (viewName == "HIDDEN") {
|
||||
mainContentPanel.isVisible = false
|
||||
} else {
|
||||
if (!mainContentPanel.isVisible) {
|
||||
mainContentPanel.isVisible = true
|
||||
}
|
||||
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 {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = imageSize
|
||||
maximumSize = 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()
|
||||
preferredSize = buttonSize
|
||||
maximumSize = buttonSize
|
||||
minimumSize = buttonSize
|
||||
background = WIDGET_COLOR
|
||||
isFocusPainted = false
|
||||
isBorderPainted = false
|
||||
isOpaque = true // Ensure background is painted
|
||||
|
||||
val gbc = GridBagConstraints().apply {
|
||||
anchor = GridBagConstraints.CENTER
|
||||
fill = GridBagConstraints.NONE // Prevents stretching
|
||||
}
|
||||
|
||||
add(imageCanvas, gbc)
|
||||
addActionListener(actionListener)
|
||||
add(imageCanvasWrapper, gbc)
|
||||
|
||||
// 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 {
|
||||
val initialXP: MutableMap<Int, Int> = HashMap()
|
||||
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
||||
var totalXPWidget: XPWidget? = null
|
||||
var focusedView: String = ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
AUTHOR='downthecrop'
|
||||
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