mirror of
https://gitlab.com/2009scape/rt4-client.git
synced 2025-12-09 16:45:46 -07:00
Merge branch 'KondoKit-2.0' into 'master'
Draft: KondoKit 2.0 See merge request 2009scape/rt4-client!28
This commit is contained in:
commit
083d0c558d
29 changed files with 2080 additions and 759 deletions
|
|
@ -13,10 +13,10 @@ import java.util.Properties;
|
||||||
* A data class for storing information about plugins.
|
* A data class for storing information about plugins.
|
||||||
* @author ceikry
|
* @author ceikry
|
||||||
*/
|
*/
|
||||||
class PluginInfo {
|
public class PluginInfo {
|
||||||
double version;
|
public double version;
|
||||||
String author;
|
public String author;
|
||||||
String description;
|
public String description;
|
||||||
|
|
||||||
public PluginInfo(String author, String description, double version) {
|
public PluginInfo(String author, String description, double version) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
|
|
||||||
|
|
@ -110,4 +110,10 @@ task buildPlugins(type: Copy, dependsOn: classes) {
|
||||||
from "build/classes/kotlin/main"
|
from "build/classes/kotlin/main"
|
||||||
into pluginsPath
|
into pluginsPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find and copy any 'res' directories from 'src/main/kotlin/**/res/'
|
||||||
|
copy {
|
||||||
|
from fileTree(dir: "src/main/kotlin", include: "**/res/**")
|
||||||
|
into pluginsPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package GroundItems
|
package GroundItems
|
||||||
|
|
||||||
|
import KondoKit.Exposed
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.annotations.PluginMeta
|
|
||||||
import plugin.api.API.*
|
import plugin.api.API.*
|
||||||
import plugin.api.FontColor.fromColor
|
import plugin.api.FontColor.fromColor
|
||||||
import plugin.api.FontType
|
import plugin.api.FontType
|
||||||
|
|
@ -18,27 +18,26 @@ import java.nio.charset.StandardCharsets
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@PluginMeta(
|
class plugin : Plugin() {
|
||||||
author = "downthecrop",
|
|
||||||
description =
|
@Exposed(description = "Default: true, Use Local JSON or the prices from the Live/Stable server API")
|
||||||
"""
|
private var useLiveGEPrices = true
|
||||||
Ground Items Overlay. Just like Runelite!
|
@Exposed( "Default: 5,000 (blue)")
|
||||||
cmds ::set(low,med,high,insane,hide), ::(tag,ignore)item ID, ::(reset)groundconfig
|
private var lowValue = 5000
|
||||||
Special thanks to Chisato for the original skeleton.
|
@Exposed( "Default: 20,000 (green)")
|
||||||
""",
|
private var mediumValue = 20000
|
||||||
version = 1.2
|
@Exposed( "Default: 50,000 (orange)")
|
||||||
)
|
private var highValue = 50000
|
||||||
open class plugin : Plugin() {
|
@Exposed( "Default: 100,000 (pink)")
|
||||||
|
private var insaneValue = 100000
|
||||||
|
@Exposed("Default: 0, No labels will be drawn for items below this value (unless tagged)")
|
||||||
|
private var hideBelowValue = 0
|
||||||
|
@Exposed("Tag Items (purple) add/remove with Ctrl+RightClick Tag/Ignore")
|
||||||
|
private lateinit var taggedItems: List<Int>
|
||||||
|
@Exposed("Ignore items add/remove with Ctrl+RightClick Tag/Ignore")
|
||||||
|
private lateinit var ignoredItems: List<Int>
|
||||||
|
|
||||||
private var kondoExposed_lowValue = 5000
|
|
||||||
private var kondoExposed_mediumValue = 20000
|
|
||||||
private var kondoExposed_highValue = 50000
|
|
||||||
private var kondoExposed_insaneValue = 100000
|
|
||||||
private var kondoExposed_hideBelowValue = 0
|
|
||||||
private var kondoExposed_useLiveGEPrices = true
|
|
||||||
private val coindId = 995
|
private val coindId = 995
|
||||||
private lateinit var kondoExposed_taggedItems: List<Int>
|
|
||||||
private lateinit var kondoExposed_ignoredItems: List<Int>
|
|
||||||
|
|
||||||
private var gePriceMap = loadGEPrices()
|
private var gePriceMap = loadGEPrices()
|
||||||
|
|
||||||
|
|
@ -60,23 +59,23 @@ open class plugin : Plugin() {
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun Init() {
|
override fun Init() {
|
||||||
kondoExposed_lowValue = GetData("low-value") as? Int ?: 5000
|
lowValue = GetData("low-value") as? Int ?: 5000
|
||||||
kondoExposed_mediumValue = GetData("medium-value") as? Int ?: 20000
|
mediumValue = GetData("medium-value") as? Int ?: 20000
|
||||||
kondoExposed_highValue = GetData("high-value") as? Int ?: 50000
|
highValue = GetData("high-value") as? Int ?: 50000
|
||||||
kondoExposed_insaneValue = GetData("insane-value") as? Int ?: 100000
|
insaneValue = GetData("insane-value") as? Int ?: 100000
|
||||||
kondoExposed_hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
||||||
kondoExposed_useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true
|
useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true
|
||||||
kondoExposed_taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||||
kondoExposed_ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||||
if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $kondoExposed_useLiveGEPrices")
|
if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $useLiveGEPrices")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isTagged(itemId: Int): Boolean {
|
private fun isTagged(itemId: Int): Boolean {
|
||||||
return kondoExposed_taggedItems.contains(itemId)
|
return taggedItems.contains(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isHidden(itemId: Int): Boolean {
|
private fun isHidden(itemId: Int): Boolean {
|
||||||
return kondoExposed_ignoredItems.contains(itemId)
|
return ignoredItems.contains(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) = renderGroundItemNames()
|
override fun Draw(timeDelta: Long) = renderGroundItemNames()
|
||||||
|
|
@ -141,10 +140,10 @@ open class plugin : Plugin() {
|
||||||
val screenY = screenPos[1]
|
val screenY = screenPos[1]
|
||||||
val color = when {
|
val color = when {
|
||||||
isTagged(itemDef.id) -> colorMap["tagged"]
|
isTagged(itemDef.id) -> colorMap["tagged"]
|
||||||
highestValue < kondoExposed_lowValue -> "#FFFFFF"
|
highestValue < lowValue -> "#FFFFFF"
|
||||||
highestValue < kondoExposed_mediumValue -> colorMap["lowValue"]
|
highestValue < mediumValue -> colorMap["lowValue"]
|
||||||
highestValue < kondoExposed_highValue -> colorMap["mediumValue"]
|
highestValue < highValue -> colorMap["mediumValue"]
|
||||||
highestValue < kondoExposed_insaneValue -> colorMap["highValue"]
|
highestValue < insaneValue -> colorMap["highValue"]
|
||||||
else -> colorMap["insaneValue"]
|
else -> colorMap["insaneValue"]
|
||||||
} ?: "#FFFFFF"
|
} ?: "#FFFFFF"
|
||||||
val colorInt = color.drop(1).toInt(16)
|
val colorInt = color.drop(1).toInt(16)
|
||||||
|
|
@ -183,7 +182,7 @@ open class plugin : Plugin() {
|
||||||
val haValue = if (itemDef.id == coindId) item.value.amount else (itemDef.cost * 0.6 * item.value.amount).roundToInt()
|
val haValue = if (itemDef.id == coindId) item.value.amount else (itemDef.cost * 0.6 * item.value.amount).roundToInt()
|
||||||
val geValue = (gePriceMap[itemDef.id.toString()]?.toInt() ?: 0) * item.value.amount
|
val geValue = (gePriceMap[itemDef.id.toString()]?.toInt() ?: 0) * item.value.amount
|
||||||
val highestValue = maxOf(haValue, geValue)
|
val highestValue = maxOf(haValue, geValue)
|
||||||
return !((highestValue < kondoExposed_hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
return !((highestValue < hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||||
|
|
@ -200,7 +199,7 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
private fun ignoreItem(itemId: Int): Runnable {
|
private fun ignoreItem(itemId: Int): Runnable {
|
||||||
return Runnable {
|
return Runnable {
|
||||||
val existingIgnores = kondoExposed_ignoredItems.toMutableList()
|
val existingIgnores = ignoredItems.toMutableList()
|
||||||
|
|
||||||
if (existingIgnores.contains(itemId)) {
|
if (existingIgnores.contains(itemId)) {
|
||||||
existingIgnores.remove(itemId)
|
existingIgnores.remove(itemId)
|
||||||
|
|
@ -215,7 +214,7 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
private fun tagItem(itemId: Int): Runnable {
|
private fun tagItem(itemId: Int): Runnable {
|
||||||
return Runnable {
|
return Runnable {
|
||||||
val existingTags = kondoExposed_taggedItems.toMutableList()
|
val existingTags = taggedItems.toMutableList()
|
||||||
|
|
||||||
if (existingTags.contains(itemId)) {
|
if (existingTags.contains(itemId)) {
|
||||||
existingTags.remove(itemId)
|
existingTags.remove(itemId)
|
||||||
|
|
@ -230,28 +229,28 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
|
|
||||||
private fun resetConfig() {
|
private fun resetConfig() {
|
||||||
kondoExposed_lowValue = 5000
|
lowValue = 5000
|
||||||
kondoExposed_mediumValue = 20000
|
mediumValue = 20000
|
||||||
kondoExposed_highValue = 50000
|
highValue = 50000
|
||||||
kondoExposed_insaneValue = 100000
|
insaneValue = 100000
|
||||||
kondoExposed_hideBelowValue = 0
|
hideBelowValue = 0
|
||||||
kondoExposed_useLiveGEPrices = true
|
useLiveGEPrices = true
|
||||||
StoreData("ground-item-tags","");
|
StoreData("ground-item-tags","");
|
||||||
StoreData("ground-item-ignore","");
|
StoreData("ground-item-ignore","");
|
||||||
StoreData("low-value", kondoExposed_lowValue)
|
StoreData("low-value", lowValue)
|
||||||
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
StoreData("ground-item-use-remote", useLiveGEPrices)
|
||||||
StoreData("medium-value", kondoExposed_mediumValue)
|
StoreData("medium-value", mediumValue)
|
||||||
StoreData("high-value", kondoExposed_highValue)
|
StoreData("high-value", highValue)
|
||||||
StoreData("insane-value", kondoExposed_insaneValue)
|
StoreData("insane-value", insaneValue)
|
||||||
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
StoreData("hide-below-value", hideBelowValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun displayRanges() {
|
private fun displayRanges() {
|
||||||
val low = kondoExposed_lowValue
|
val low = lowValue
|
||||||
val medium = kondoExposed_mediumValue
|
val medium = mediumValue
|
||||||
val high = kondoExposed_highValue
|
val high = highValue
|
||||||
val insane = kondoExposed_insaneValue
|
val insane = insaneValue
|
||||||
val hide = kondoExposed_hideBelowValue
|
val hide = hideBelowValue
|
||||||
|
|
||||||
SendMessage("== Ground Item Config ==")
|
SendMessage("== Ground Item Config ==")
|
||||||
SendMessage("Low: $low")
|
SendMessage("Low: $low")
|
||||||
|
|
@ -260,12 +259,12 @@ open class plugin : Plugin() {
|
||||||
SendMessage("Insane: $insane")
|
SendMessage("Insane: $insane")
|
||||||
SendMessage("Hide Below: $hide")
|
SendMessage("Hide Below: $hide")
|
||||||
SendMessage("-- Ignored Items --")
|
SendMessage("-- Ignored Items --")
|
||||||
for(item in kondoExposed_ignoredItems){
|
for(item in ignoredItems){
|
||||||
val itemDef = ObjTypeList.get(item)
|
val itemDef = ObjTypeList.get(item)
|
||||||
SendMessage("Ignored: ${itemDef.name} ${itemDef.id}")
|
SendMessage("Ignored: ${itemDef.name} ${itemDef.id}")
|
||||||
}
|
}
|
||||||
SendMessage("-- Tagged Items --")
|
SendMessage("-- Tagged Items --")
|
||||||
for(item in kondoExposed_taggedItems){
|
for(item in taggedItems){
|
||||||
val itemDef = ObjTypeList.get(item)
|
val itemDef = ObjTypeList.get(item)
|
||||||
SendMessage("Tagged: ${itemDef.name} ${itemDef.id}")
|
SendMessage("Tagged: ${itemDef.name} ${itemDef.id}")
|
||||||
}
|
}
|
||||||
|
|
@ -278,19 +277,19 @@ open class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun OnKondoValueUpdated() {
|
fun OnKondoValueUpdated() {
|
||||||
StoreData("ground-item-tags",kondoExposed_taggedItems);
|
StoreData("ground-item-tags",taggedItems);
|
||||||
StoreData("ground-item-ignore",kondoExposed_ignoredItems);
|
StoreData("ground-item-ignore",ignoredItems);
|
||||||
StoreData("low-value", kondoExposed_lowValue)
|
StoreData("low-value", lowValue)
|
||||||
StoreData("medium-value", kondoExposed_mediumValue)
|
StoreData("medium-value", mediumValue)
|
||||||
StoreData("high-value", kondoExposed_highValue)
|
StoreData("high-value", highValue)
|
||||||
StoreData("insane-value", kondoExposed_insaneValue)
|
StoreData("insane-value", insaneValue)
|
||||||
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
StoreData("ground-item-use-remote", useLiveGEPrices)
|
||||||
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
StoreData("hide-below-value", hideBelowValue)
|
||||||
gePriceMap = loadGEPrices();
|
gePriceMap = loadGEPrices();
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadGEPrices(): Map<String, String> {
|
fun loadGEPrices(): Map<String, String> {
|
||||||
return if (kondoExposed_useLiveGEPrices) {
|
return if (useLiveGEPrices) {
|
||||||
try {
|
try {
|
||||||
println("GroundItems: Loading Remote GE Prices")
|
println("GroundItems: Loading Remote GE Prices")
|
||||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||||
|
|
@ -325,7 +324,7 @@ open class plugin : Plugin() {
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
println("GroundItems: Loading Local GE Prices")
|
println("GroundItems: Loading Local GE Prices")
|
||||||
BufferedReader(InputStreamReader(GroundItems.plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8))
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
val json = lines.joinToString("\n")
|
val json = lines.joinToString("\n")
|
||||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ Commands:\
|
||||||
::(tag,ignore)item ID\
|
::(tag,ignore)item ID\
|
||||||
::(reset)groundconfig\
|
::(reset)groundconfig\
|
||||||
Special thanks to Chisato for the original skeleton.
|
Special thanks to Chisato for the original skeleton.
|
||||||
VERSION=1.2
|
VERSION=1.3
|
||||||
|
|
@ -1,10 +1,165 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
import java.awt.Color
|
import rt4.GameShell
|
||||||
import java.awt.Dimension
|
import java.awt.*
|
||||||
import javax.swing.JPanel
|
import java.awt.event.MouseListener
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.util.*
|
||||||
|
import java.util.Timer
|
||||||
|
import javax.swing.*
|
||||||
|
|
||||||
object Helpers {
|
object Helpers {
|
||||||
|
|
||||||
|
fun convertValue(type: Class<*>, genericType: Type?, value: String): Any {
|
||||||
|
return when {
|
||||||
|
type == Int::class.java -> value.toInt()
|
||||||
|
type == Double::class.java -> value.toDouble()
|
||||||
|
type == Boolean::class.java -> value.toBoolean()
|
||||||
|
type == Color::class.java -> convertToColor(value)
|
||||||
|
type == List::class.java && genericType is ParameterizedType -> {
|
||||||
|
val actualTypeArgument = genericType.actualTypeArguments.firstOrNull()
|
||||||
|
when {
|
||||||
|
value.isBlank() -> emptyList<Any>() // Handle empty string by returning an empty list
|
||||||
|
actualTypeArgument == Int::class.javaObjectType -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim().toInt() }
|
||||||
|
actualTypeArgument == String::class.java -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim() }
|
||||||
|
else -> throw IllegalArgumentException("Unsupported List type: $actualTypeArgument")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> value // Default to String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showToast(
|
||||||
|
parentComponent: Component?,
|
||||||
|
message: String,
|
||||||
|
messageType: Int = JOptionPane.INFORMATION_MESSAGE
|
||||||
|
) {
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
val toast = JWindow()
|
||||||
|
toast.type = Window.Type.POPUP
|
||||||
|
toast.background = Color(0, 0, 0, 0)
|
||||||
|
|
||||||
|
val panel = JPanel()
|
||||||
|
panel.isOpaque = false
|
||||||
|
panel.layout = BoxLayout(panel, BoxLayout.Y_AXIS)
|
||||||
|
|
||||||
|
val label = JLabel(message)
|
||||||
|
label.foreground = Color.WHITE
|
||||||
|
|
||||||
|
label.background = when (messageType) {
|
||||||
|
JOptionPane.ERROR_MESSAGE -> Color(220, 20, 60, 230) // Crimson for errors
|
||||||
|
JOptionPane.INFORMATION_MESSAGE -> Color(0, 128, 0, 230) // Green for success
|
||||||
|
JOptionPane.WARNING_MESSAGE -> Color(255, 165, 0, 230) // Orange for warnings
|
||||||
|
else -> Color(0, 0, 0, 170) // Default semi-transparent black
|
||||||
|
}
|
||||||
|
|
||||||
|
label.isOpaque = true
|
||||||
|
label.border = BorderFactory.createEmptyBorder(10, 20, 10, 20)
|
||||||
|
label.maximumSize = Dimension(242, 50)
|
||||||
|
label.preferredSize = Dimension(242, 50)
|
||||||
|
panel.add(label)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
toast.contentPane.add(panel)
|
||||||
|
toast.pack()
|
||||||
|
|
||||||
|
|
||||||
|
// Adjust for parent component location if it exists
|
||||||
|
if (parentComponent != null) {
|
||||||
|
val parentLocation = parentComponent.locationOnScreen
|
||||||
|
val x = parentLocation.x
|
||||||
|
val y = GameShell.canvas.locationOnScreen.y
|
||||||
|
toast.setLocation(x, y)
|
||||||
|
} else {
|
||||||
|
// Fallback to screen center if no parent is provided
|
||||||
|
val screenSize = Toolkit.getDefaultToolkit().screenSize
|
||||||
|
val x = (screenSize.width - toast.width) / 2
|
||||||
|
val y = screenSize.height - toast.height - 50
|
||||||
|
toast.setLocation(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.isVisible = true
|
||||||
|
|
||||||
|
Timer().schedule(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
toast.isVisible = false
|
||||||
|
toast.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun convertToColor(value: String): Color {
|
||||||
|
val color = Color.decode(value) // Assumes value is in format "#RRGGBB" or "0xRRGGBB"
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|
||||||
|
fun colorToHex(color: Color): String {
|
||||||
|
return "#%02x%02x%02x".format(color.red, color.green, color.blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun colorToIntArray(color: Color): IntArray {
|
||||||
|
return intArrayOf(color.red, color.green, color.blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FieldObserver {
|
||||||
|
fun onFieldChange(field: Field, newValue: Any?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addMouseListenerToAll(container: Container, listener: MouseListener) {
|
||||||
|
// Recursively go through all components within the container
|
||||||
|
for (component in container.components) {
|
||||||
|
// Add the passed MouseListener to the component
|
||||||
|
if (component is JComponent || component is Canvas) {
|
||||||
|
component.addMouseListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the component is a container, recursively call this function
|
||||||
|
if (component is Container) {
|
||||||
|
addMouseListenerToAll(component, listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class FieldNotifier(private val plugin: Any) {
|
||||||
|
private val observers = mutableListOf<FieldObserver>()
|
||||||
|
|
||||||
|
fun addObserver(observer: FieldObserver) {
|
||||||
|
observers.add(observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyFieldChange(field: Field, newValue: Any?) {
|
||||||
|
for (observer in observers) {
|
||||||
|
observer.onFieldChange(field, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFieldValue(field: Field, value: Any?) {
|
||||||
|
field.isAccessible = true
|
||||||
|
field.set(plugin, value)
|
||||||
|
notifyFieldChange(field, value)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val onUpdateMethod = plugin::class.java.getMethod("OnKondoValueUpdated")
|
||||||
|
onUpdateMethod.invoke(plugin)
|
||||||
|
} catch (e: NoSuchMethodException) {
|
||||||
|
// The method doesn't exist
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getSpriteId(skillId: Int) : Int {
|
fun getSpriteId(skillId: Int) : Int {
|
||||||
return when (skillId) {
|
return when (skillId) {
|
||||||
0 -> 197
|
0 -> 197
|
||||||
|
|
@ -35,9 +190,15 @@ object Helpers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showAlert(message: String, title: String, type: Int){
|
||||||
|
JOptionPane.showMessageDialog(null, message, title, type)
|
||||||
|
}
|
||||||
|
|
||||||
fun formatHtmlLabelText(text1: String, color1: Color, text2: String, color2: Color): String {
|
fun formatHtmlLabelText(text1: String, color1: Color, text2: String, color2: Color): String {
|
||||||
return "<html><span style='color:rgb(${color1.red},${color1.green},${color1.blue});'>$text1</span>" +
|
return "<html><div style='white-space:nowrap;'>" +
|
||||||
"<span style='color:rgb(${color2.red},${color2.green},${color2.blue});'>$text2</span></html>"
|
"<span style='color:rgb(${color1.red},${color1.green},${color1.blue});'>$text1</span>" +
|
||||||
|
"<span style='color:rgb(${color2.red},${color2.green},${color2.blue});'>$text2</span>" +
|
||||||
|
"</div></html>"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatNumber(value: Int): String {
|
fun formatNumber(value: Int): String {
|
||||||
|
|
@ -78,13 +239,4 @@ object Helpers {
|
||||||
else -> Color(128, 128, 128) // Default grey for unhandled skill IDs
|
else -> Color(128, 128, 128) // Default grey for unhandled skill IDs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Spacer(width: Int = 0, height: Int = 0) : JPanel() {
|
|
||||||
init {
|
|
||||||
preferredSize = Dimension(width, height)
|
|
||||||
maximumSize = preferredSize
|
|
||||||
minimumSize = preferredSize
|
|
||||||
isOpaque = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,21 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
import KondoKit.Constants.COLOR_BACKGROUND_DARK
|
import KondoKit.Constants.COLOR_BACKGROUND_DARK
|
||||||
|
import KondoKit.Constants.SKILL_DISPLAY_ORDER
|
||||||
|
import KondoKit.Constants.SKILL_SPRITE_DIMENSION
|
||||||
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
|
import KondoKit.Helpers.showToast
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
|
import KondoKit.plugin.Companion.POPUP_BACKGROUND
|
||||||
|
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||||
|
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
|
||||||
|
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import rt4.Sprites
|
import rt4.Sprites
|
||||||
|
|
@ -16,9 +28,11 @@ import java.awt.event.MouseEvent
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
import javax.swing.border.MatteBorder
|
import javax.swing.border.MatteBorder
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
// Sprite IDs
|
// Sprite IDs
|
||||||
|
|
@ -28,35 +42,36 @@ object Constants {
|
||||||
const val LVL_BAR_SPRITE = 898
|
const val LVL_BAR_SPRITE = 898
|
||||||
|
|
||||||
// Dimensions
|
// Dimensions
|
||||||
val SEARCH_FIELD_DIMENSION = Dimension(270, 30)
|
val SEARCH_FIELD_DIMENSION = Dimension(230, 30)
|
||||||
val ICON_DIMENSION_SMALL = Dimension(12, 12)
|
val ICON_DIMENSION_SMALL = Dimension(12, 12)
|
||||||
val ICON_DIMENSION_MEDIUM = Dimension(18, 20)
|
|
||||||
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
||||||
val HISCORE_PANEL_DIMENSION = Dimension(270, 400)
|
val HISCORE_PANEL_DIMENSION = Dimension(230, 500)
|
||||||
val FILTER_PANEL_DIMENSION = Dimension(270, 30)
|
val FILTER_PANEL_DIMENSION = Dimension(230, 30)
|
||||||
val SKILLS_PANEL_DIMENSION = Dimension(300, 300)
|
val SKILLS_PANEL_DIMENSION = Dimension(230, 290)
|
||||||
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(270, 30)
|
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(230, 30)
|
||||||
val SKILL_PANEL_DIMENSION = Dimension(90, 35)
|
val SKILL_PANEL_DIMENSION = Dimension(76, 35)
|
||||||
val IMAGE_CANVAS_DIMENSION = Dimension(20, 20)
|
val IMAGE_CANVAS_DIMENSION = Dimension(20, 20)
|
||||||
val NUMBER_LABEL_DIMENSION = Dimension(30, 20)
|
val SKILL_SPRITE_DIMENSION = Dimension(14, 14)
|
||||||
|
val NUMBER_LABEL_DIMENSION = Dimension(20, 20)
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
val COLOR_BACKGROUND_DARK = Color(27, 27, 27)
|
val COLOR_BACKGROUND_DARK = WIDGET_COLOR
|
||||||
val COLOR_BACKGROUND_MEDIUM = Color(37, 37, 37)
|
val COLOR_BACKGROUND_MEDIUM = VIEW_BACKGROUND_COLOR
|
||||||
val COLOR_BACKGROUND_LIGHT = Color(43, 43, 43)
|
val COLOR_FOREGROUND_LIGHT = POPUP_FOREGROUND
|
||||||
val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200)
|
|
||||||
val COLOR_RED = Color.RED
|
|
||||||
val COLOR_SKILL_PANEL = Color(60, 60, 60)
|
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
|
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
|
||||||
val FONT_ARIAL_PLAIN_12 = Font("Arial", Font.PLAIN, 12)
|
val FONT_ARIAL_PLAIN_12 = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12)
|
val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12)
|
||||||
|
val SKILL_DISPLAY_ORDER = arrayOf(0,3,14,2,16,13,1,15,10,4,17,7,5,12,11,6,9,8,20,18,19,22,21,23)
|
||||||
}
|
}
|
||||||
|
|
||||||
var text: String = ""
|
var text: String = ""
|
||||||
|
|
||||||
object HiscoresView {
|
object HiscoresView {
|
||||||
|
|
||||||
|
const val VIEW_NAME = "HISCORE_SEARCH_VIEW"
|
||||||
|
var hiScoreView: JPanel? = null
|
||||||
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
|
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
|
||||||
|
|
||||||
private var cursorVisible: Boolean = true
|
private var cursorVisible: Boolean = true
|
||||||
|
|
@ -69,6 +84,7 @@ object HiscoresView {
|
||||||
size = preferredSize
|
size = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +143,7 @@ object HiscoresView {
|
||||||
|
|
||||||
Timer(500) {
|
Timer(500) {
|
||||||
cursorVisible = !cursorVisible
|
cursorVisible = !cursorVisible
|
||||||
if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW")
|
if(focusedView == VIEW_NAME)
|
||||||
repaint()
|
repaint()
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +156,7 @@ object HiscoresView {
|
||||||
val fm = g.fontMetrics
|
val fm = g.fontMetrics
|
||||||
val cursorX = fm.stringWidth(text) + 30
|
val cursorX = fm.stringWidth(text) + 30
|
||||||
|
|
||||||
imageCanvas?.let { canvas ->
|
imageCanvas.let { canvas ->
|
||||||
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
||||||
canvas.paint(imgG)
|
canvas.paint(imgG)
|
||||||
imgG.dispose()
|
imgG.dispose()
|
||||||
|
|
@ -153,14 +169,16 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.isNotEmpty()) {
|
if (text.isNotEmpty()) {
|
||||||
g.color = Constants.COLOR_RED
|
g.color = Color.RED
|
||||||
g.drawString("x", width - 20, 20)
|
g.drawString("x", width - 20, 20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchPlayer(username: String) {
|
fun searchPlayer(username: String) {
|
||||||
text = username
|
text = username.replace(" ", "_")
|
||||||
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${username.toLowerCase()}"
|
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${text.toLowerCase()}"
|
||||||
|
|
||||||
|
updateHiscoresView(null, "Searching...")
|
||||||
|
|
||||||
Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
|
|
@ -168,6 +186,10 @@ object HiscoresView {
|
||||||
val connection = url.openConnection() as HttpURLConnection
|
val connection = url.openConnection() as HttpURLConnection
|
||||||
connection.requestMethod = "GET"
|
connection.requestMethod = "GET"
|
||||||
|
|
||||||
|
// If a request take longer than 5 seconds timeout.
|
||||||
|
connection.connectTimeout = 5000
|
||||||
|
connection.readTimeout = 5000
|
||||||
|
|
||||||
val responseCode = connection.responseCode
|
val responseCode = connection.responseCode
|
||||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
||||||
|
|
@ -179,27 +201,45 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SwingUtilities.invokeLater {
|
SwingUtilities.invokeLater {
|
||||||
showError("Player not found!")
|
showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: SocketTimeoutException) {
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
showToast(hiscoresPanel, "Request timed out", JOptionPane.ERROR_MESSAGE)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
// Handle other errors
|
||||||
SwingUtilities.invokeLater {
|
SwingUtilities.invokeLater {
|
||||||
showError("Error fetching data!")
|
showToast(hiscoresPanel, "Error fetching data!", JOptionPane.ERROR_MESSAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updatePlayerData(jsonResponse: String, username: String) {
|
private fun updatePlayerData(jsonResponse: String, username: String) {
|
||||||
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
||||||
updateHiscoresView(hiscoresResponse, username)
|
updateHiscoresView(hiscoresResponse, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateHiscoresView(data: HiscoresResponse, username: String) {
|
private fun updateHiscoresView(data: HiscoresResponse?, username: String) {
|
||||||
val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel
|
val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel
|
||||||
val ironMode = data.info.iron_mode
|
|
||||||
|
|
||||||
playerNameLabel?.removeAll() // Clear previous components
|
playerNameLabel?.removeAll() // Clear previous components
|
||||||
|
var nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, "", primaryColor), JLabel.CENTER).apply {
|
||||||
|
font = Constants.FONT_ARIAL_BOLD_12
|
||||||
|
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||||
|
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||||
|
}
|
||||||
|
playerNameLabel?.add(nameLabel)
|
||||||
|
playerNameLabel?.revalidate()
|
||||||
|
playerNameLabel?.repaint()
|
||||||
|
|
||||||
|
if(data == null) return;
|
||||||
|
|
||||||
|
playerNameLabel?.removeAll()
|
||||||
|
|
||||||
|
val ironMode = data.info.iron_mode
|
||||||
|
|
||||||
if (ironMode != "0") {
|
if (ironMode != "0") {
|
||||||
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
||||||
|
|
@ -213,11 +253,14 @@ object HiscoresView {
|
||||||
playerNameLabel?.add(imageCanvas)
|
playerNameLabel?.add(imageCanvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nameLabel = JLabel(username, JLabel.CENTER).apply {
|
val exp_multiplier = data.info.exp_multiplier
|
||||||
|
nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, " (${exp_multiplier}x)", primaryColor), JLabel.CENTER).apply {
|
||||||
font = Constants.FONT_ARIAL_BOLD_12
|
font = Constants.FONT_ARIAL_BOLD_12
|
||||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||||
|
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
playerNameLabel?.add(nameLabel)
|
playerNameLabel?.add(nameLabel)
|
||||||
|
|
||||||
playerNameLabel?.revalidate()
|
playerNameLabel?.revalidate()
|
||||||
|
|
@ -266,13 +309,13 @@ object HiscoresView {
|
||||||
summoning: Int,
|
summoning: Int,
|
||||||
isMemberWorld: Boolean
|
isMemberWorld: Boolean
|
||||||
): Double {
|
): Double {
|
||||||
val base = (defence + hitpoints + Math.floor(prayer.toDouble() / 2)) * 0.25
|
val base = (defence + hitpoints + floor(prayer.toDouble() / 2)) * 0.25
|
||||||
val melee = (attack + strength) * 0.325
|
val melee = (attack + strength) * 0.325
|
||||||
val range = Math.floor(ranged * 1.5) * 0.325
|
val range = floor(ranged * 1.5) * 0.325
|
||||||
val mage = Math.floor(magic * 1.5) * 0.325
|
val mage = floor(magic * 1.5) * 0.325
|
||||||
val maxCombatType = maxOf(melee, range, mage)
|
val maxCombatType = maxOf(melee, range, mage)
|
||||||
|
|
||||||
val summoningFactor = if (isMemberWorld) Math.floor(summoning.toDouble() / 8) else 0.0
|
val summoningFactor = if (isMemberWorld) floor(summoning.toDouble() / 8) else 0.0
|
||||||
return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0
|
return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,10 +339,10 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createHiscoreSearchView(): JPanel {
|
fun createHiscoreSearchView() {
|
||||||
val hiscorePanel = JPanel().apply {
|
val hiscorePanel = JPanel().apply {
|
||||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
name = "HISCORE_SEARCH_VIEW"
|
name = VIEW_NAME
|
||||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||||
preferredSize = Constants.HISCORE_PANEL_DIMENSION
|
preferredSize = Constants.HISCORE_PANEL_DIMENSION
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
|
|
@ -324,14 +367,13 @@ object HiscoresView {
|
||||||
add(searchFieldWrapper)
|
add(searchFieldWrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
hiscorePanel.add(searchPanel)
|
hiscorePanel.add(searchPanel)
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
// Adding the player name panel in place of the filterPanel
|
|
||||||
val playerNamePanel = JPanel().apply {
|
val playerNamePanel = JPanel().apply {
|
||||||
layout = FlowLayout(FlowLayout.CENTER)
|
layout = GridBagLayout() // This will center the JLabel both vertically and horizontally
|
||||||
background = VIEW_BACKGROUND_COLOR
|
background = TOOLTIP_BACKGROUND.darker()
|
||||||
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
|
|
@ -339,7 +381,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
hiscorePanel.add(playerNamePanel)
|
hiscorePanel.add(playerNamePanel)
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||||
|
|
@ -348,10 +390,10 @@ object HiscoresView {
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until 24) {
|
for (i in SKILL_DISPLAY_ORDER) {
|
||||||
val skillPanel = JPanel().apply {
|
val skillPanel = JPanel().apply {
|
||||||
layout = BorderLayout()
|
layout = BorderLayout()
|
||||||
background = Constants.COLOR_SKILL_PANEL
|
background = COLOR_BACKGROUND_DARK
|
||||||
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
|
|
@ -362,8 +404,9 @@ object HiscoresView {
|
||||||
|
|
||||||
val imageCanvas = bufferedImageSprite.let {
|
val imageCanvas = bufferedImageSprite.let {
|
||||||
ImageCanvas(it).apply {
|
ImageCanvas(it).apply {
|
||||||
preferredSize = Constants.IMAGE_CANVAS_DIMENSION
|
preferredSize = SKILL_SPRITE_DIMENSION
|
||||||
size = Constants.IMAGE_CANVAS_DIMENSION
|
size = SKILL_SPRITE_DIMENSION
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,7 +419,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_DARK
|
background = COLOR_BACKGROUND_DARK
|
||||||
add(imageCanvas)
|
add(imageCanvas)
|
||||||
add(numberLabel)
|
add(numberLabel)
|
||||||
}
|
}
|
||||||
|
|
@ -397,6 +440,7 @@ object HiscoresView {
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE));
|
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE));
|
||||||
|
|
||||||
val totalLevelIcon = ImageCanvas(bufferedImageSprite).apply {
|
val totalLevelIcon = ImageCanvas(bufferedImageSprite).apply {
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
preferredSize = Constants.ICON_DIMENSION_LARGE
|
preferredSize = Constants.ICON_DIMENSION_LARGE
|
||||||
size = Constants.ICON_DIMENSION_LARGE
|
size = Constants.ICON_DIMENSION_LARGE
|
||||||
}
|
}
|
||||||
|
|
@ -418,6 +462,7 @@ object HiscoresView {
|
||||||
val bufferedImageSprite2 = getBufferedImageFromSprite(API.GetSprite(Constants.COMBAT_LVL_SPRITE))
|
val bufferedImageSprite2 = getBufferedImageFromSprite(API.GetSprite(Constants.COMBAT_LVL_SPRITE))
|
||||||
|
|
||||||
val combatLevelIcon = ImageCanvas(bufferedImageSprite2).apply {
|
val combatLevelIcon = ImageCanvas(bufferedImageSprite2).apply {
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
preferredSize = Constants.ICON_DIMENSION_LARGE
|
preferredSize = Constants.ICON_DIMENSION_LARGE
|
||||||
size = Constants.ICON_DIMENSION_LARGE
|
size = Constants.ICON_DIMENSION_LARGE
|
||||||
}
|
}
|
||||||
|
|
@ -431,7 +476,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_DARK
|
background = COLOR_BACKGROUND_DARK
|
||||||
add(combatLevelIcon)
|
add(combatLevelIcon)
|
||||||
add(combatLevelLabel)
|
add(combatLevelLabel)
|
||||||
}
|
}
|
||||||
|
|
@ -439,8 +484,9 @@ object HiscoresView {
|
||||||
totalCombatPanel.add(totalLevelPanel)
|
totalCombatPanel.add(totalLevelPanel)
|
||||||
totalCombatPanel.add(combatLevelPanel)
|
totalCombatPanel.add(combatLevelPanel)
|
||||||
hiscorePanel.add(totalCombatPanel)
|
hiscorePanel.add(totalCombatPanel)
|
||||||
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
return hiscorePanel
|
hiScoreView = hiscorePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HiscoresResponse(
|
data class HiscoresResponse(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import java.awt.Canvas
|
import java.awt.Canvas
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.Dimension
|
import java.awt.Dimension
|
||||||
|
|
@ -8,31 +9,25 @@ import java.awt.image.BufferedImage
|
||||||
|
|
||||||
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
||||||
|
|
||||||
|
var fillColor: Color = WIDGET_COLOR
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Manually set the alpha value to 255 (fully opaque) only for pixels that are not fully transparent
|
|
||||||
val width = image.width
|
val width = image.width
|
||||||
val height = image.height
|
val height = image.height
|
||||||
for (y in 0 until height) {
|
for (y in 0 until height) {
|
||||||
for (x in 0 until width) {
|
for (x in 0 until width) {
|
||||||
// Retrieve the current pixel color
|
|
||||||
val color = image.getRGB(x, y)
|
val color = image.getRGB(x, y)
|
||||||
|
|
||||||
// Check if the pixel is not fully transparent (i.e., color is not 0)
|
|
||||||
if (color != 0) {
|
if (color != 0) {
|
||||||
// Ensure the alpha is set to 255 (fully opaque)
|
|
||||||
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
||||||
|
|
||||||
// Set the pixel with the updated color
|
|
||||||
image.setRGB(x, y, newColor)
|
image.setRGB(x, y, newColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun paint(g: Graphics) {
|
override fun paint(g: Graphics) {
|
||||||
super.paint(g)
|
super.paint(g)
|
||||||
g.color = Color(27, 27, 27)
|
g.color = fillColor
|
||||||
g.fillRect(0, 0, width, height)
|
g.fillRect(0, 0, width, height)
|
||||||
g.drawImage(image, 0, 0, width, height, this)
|
g.drawImage(image, 0, 0, width, height, this)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
package KondoKit
|
|
||||||
|
|
||||||
import java.awt.Color
|
|
||||||
import java.lang.reflect.Field
|
|
||||||
import java.lang.reflect.ParameterizedType
|
|
||||||
import java.lang.reflect.Type
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is used for the runtime editing of plugin variables.
|
|
||||||
To expose fields name them starting with `kondoExposed_`
|
|
||||||
When they are applied this will trigger an invoke of OnKondoValueUpdated()
|
|
||||||
if it is implemented. Check GroundItems plugin for an example.
|
|
||||||
*/
|
|
||||||
|
|
||||||
object KondoKitUtils {
|
|
||||||
const val KONDO_PREFIX = "kondoExposed_"
|
|
||||||
|
|
||||||
fun isKondoExposed(field: Field): Boolean {
|
|
||||||
return field.name.startsWith(KONDO_PREFIX)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getKondoExposedFields(instance: Any): List<Field> {
|
|
||||||
val exposedFields: MutableList<Field> = ArrayList()
|
|
||||||
for (field in instance.javaClass.declaredFields) {
|
|
||||||
if (isKondoExposed(field)) {
|
|
||||||
exposedFields.add(field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exposedFields
|
|
||||||
}
|
|
||||||
|
|
||||||
fun convertValue(type: Class<*>, genericType: Type?, value: String): Any {
|
|
||||||
return when {
|
|
||||||
type == Int::class.java -> value.toInt()
|
|
||||||
type == Double::class.java -> value.toDouble()
|
|
||||||
type == Boolean::class.java -> value.toBoolean()
|
|
||||||
type == Color::class.java -> convertToColor(value)
|
|
||||||
type == List::class.java && genericType is ParameterizedType -> {
|
|
||||||
val actualTypeArgument = genericType.actualTypeArguments.firstOrNull()
|
|
||||||
when {
|
|
||||||
value.isBlank() -> emptyList<Any>() // Handle empty string by returning an empty list
|
|
||||||
actualTypeArgument == Int::class.javaObjectType -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim().toInt() }
|
|
||||||
actualTypeArgument == String::class.java -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim() }
|
|
||||||
else -> throw IllegalArgumentException("Unsupported List type: $actualTypeArgument")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> value // Default to String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun convertToColor(value: String): Color {
|
|
||||||
val color = Color.decode(value) // Assumes value is in format "#RRGGBB" or "0xRRGGBB"
|
|
||||||
return color
|
|
||||||
}
|
|
||||||
|
|
||||||
fun colorToHex(color: Color): String {
|
|
||||||
return "#%02x%02x%02x".format(color.red, color.green, color.blue)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun colorToIntArray(color: Color): IntArray {
|
|
||||||
return intArrayOf(color.red, color.green, color.blue)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FieldObserver {
|
|
||||||
fun onFieldChange(field: Field, newValue: Any?)
|
|
||||||
}
|
|
||||||
|
|
||||||
class FieldNotifier(private val plugin: Any) {
|
|
||||||
private val observers = mutableListOf<FieldObserver>()
|
|
||||||
|
|
||||||
fun addObserver(observer: FieldObserver) {
|
|
||||||
observers.add(observer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun notifyFieldChange(field: Field, newValue: Any?) {
|
|
||||||
for (observer in observers) {
|
|
||||||
observer.onFieldChange(field, newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setFieldValue(field: Field, value: Any?) {
|
|
||||||
field.isAccessible = true
|
|
||||||
field.set(plugin, value)
|
|
||||||
notifyFieldChange(field, value)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val onUpdateMethod = plugin::class.java.getMethod("OnKondoValueUpdated")
|
|
||||||
onUpdateMethod.invoke(plugin)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +1,26 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.Helpers.addMouseListenerToAll
|
||||||
import KondoKit.Helpers.formatHtmlLabelText
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.XPTrackerView.wrappedWidget
|
import KondoKit.XPTrackerView.wrappedWidget
|
||||||
|
import KondoKit.plugin.Companion.IMAGE_SIZE
|
||||||
|
import KondoKit.plugin.Companion.POPUP_BACKGROUND
|
||||||
|
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||||
|
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
|
||||||
|
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import rt4.NpcTypeList
|
import rt4.*
|
||||||
import rt4.ObjStackNode
|
|
||||||
import rt4.Player
|
|
||||||
import rt4.SceneGraph
|
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
|
import java.awt.Font
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
|
@ -22,19 +29,23 @@ import java.net.URL
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
object LootTrackerView {
|
object LootTrackerView {
|
||||||
private const val SNAPSHOT_LIFESPAN = 10
|
private const val SNAPSHOT_LIFESPAN = 10
|
||||||
const val BAG_ICON = 900;
|
const val BAG_ICON = 900;
|
||||||
val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>()
|
val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>()
|
||||||
var gePriceMap = loadGEPrices()
|
var gePriceMap = loadGEPrices()
|
||||||
|
const val VIEW_NAME = "LOOT_TRACKER_VIEW";
|
||||||
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
||||||
private val npcKillCounts = mutableMapOf<String, Int>()
|
private val npcKillCounts = mutableMapOf<String, Int>()
|
||||||
private var totalTrackerWidget: XPWidget? = null
|
private var totalTrackerWidget: XPWidget? = null
|
||||||
var lastConfirmedKillNpcId = -1;
|
var lastConfirmedKillNpcId = -1
|
||||||
|
var customToolTipWindow: JWindow? = null
|
||||||
|
var lootTrackerView: JPanel? = null
|
||||||
|
|
||||||
fun loadGEPrices(): Map<String, String> {
|
fun loadGEPrices(): Map<String, String> {
|
||||||
return if (plugin.kondoExposed_useLiveGEPrices) {
|
return if (plugin.useLiveGEPrices) {
|
||||||
try {
|
try {
|
||||||
println("LootTracker: Loading Remote GE Prices")
|
println("LootTracker: Loading Remote GE Prices")
|
||||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||||
|
|
@ -69,7 +80,7 @@ object LootTrackerView {
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
println("LootTracker: Loading Local GE Prices")
|
println("LootTracker: Loading Local GE Prices")
|
||||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8))
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
val json = lines.joinToString("\n")
|
val json = lines.joinToString("\n")
|
||||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||||
|
|
@ -94,18 +105,36 @@ object LootTrackerView {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun createLootTrackerView(): JPanel {
|
fun createLootTrackerView() {
|
||||||
return JPanel().apply {
|
lootTrackerView = JPanel().apply {
|
||||||
layout = FlowLayout(FlowLayout.CENTER, 0, 5)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS) // Use BoxLayout on Y axis to stack widgets vertically
|
||||||
background = VIEW_BACKGROUND_COLOR
|
background = VIEW_BACKGROUND_COLOR
|
||||||
preferredSize = Dimension(270, 700)
|
|
||||||
maximumSize = Dimension(270, 700)
|
|
||||||
minimumSize = Dimension(270, 700)
|
|
||||||
add(Box.createVerticalStrut(5))
|
add(Box.createVerticalStrut(5))
|
||||||
totalTrackerWidget = createTotalLootWidget()
|
totalTrackerWidget = createTotalLootWidget()
|
||||||
add(wrappedWidget(totalTrackerWidget!!.panel))
|
|
||||||
add(Helpers.Spacer(height = 15))
|
val wrapped = wrappedWidget(totalTrackerWidget!!.container)
|
||||||
|
val popupMenu = resetLootTrackerMenu()
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addMouseListenerToAll(wrapped,rightClickListener)
|
||||||
|
wrapped.addMouseListener(rightClickListener)
|
||||||
|
add(wrapped)
|
||||||
|
add(Box.createVerticalStrut(8))
|
||||||
revalidate()
|
revalidate()
|
||||||
|
if(focusedView == VIEW_NAME)
|
||||||
repaint()
|
repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +151,7 @@ object LootTrackerView {
|
||||||
totalTrackerWidget?.let {
|
totalTrackerWidget?.let {
|
||||||
it.previousXp += newVal
|
it.previousXp += newVal
|
||||||
it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor)
|
it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor)
|
||||||
it.panel.repaint()
|
it.container.repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,12 +161,12 @@ object LootTrackerView {
|
||||||
val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor))
|
val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor))
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = -1,
|
skillId = -1,
|
||||||
panel = createWidgetPanel(bufferedImageSprite,l2,l1),
|
container = createWidgetPanel(bufferedImageSprite,l2,l1),
|
||||||
xpGainedLabel = l2,
|
xpGainedLabel = l2,
|
||||||
xpLeftLabel = JLabel(),
|
xpLeftLabel = JLabel(),
|
||||||
actionsRemainingLabel = JLabel(),
|
actionsRemainingLabel = JLabel(),
|
||||||
xpPerHourLabel = l1,
|
xpPerHourLabel = l1,
|
||||||
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
|
progressBar = ProgressBar(0.0, Color(0,0,0)), // unused.
|
||||||
totalXpGained = 0,
|
totalXpGained = 0,
|
||||||
startTime = System.currentTimeMillis(),
|
startTime = System.currentTimeMillis(),
|
||||||
previousXp = 0
|
previousXp = 0
|
||||||
|
|
@ -146,16 +175,19 @@ object LootTrackerView {
|
||||||
|
|
||||||
private fun createWidgetPanel(bufferedImageSprite: BufferedImage, l1 : JLabel, l2 : JLabel): Panel {
|
private fun createWidgetPanel(bufferedImageSprite: BufferedImage, l1 : JLabel, l2 : JLabel): Panel {
|
||||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||||
size = Dimension(width, height)
|
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
|
minimumSize = preferredSize
|
||||||
|
maximumSize = preferredSize
|
||||||
|
size = preferredSize
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
val imageContainer = Panel(FlowLayout()).apply {
|
val imageContainer = Panel(BorderLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
add(imageCanvas)
|
add(imageCanvas, BorderLayout.NORTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Panel(BorderLayout(5, 5)).apply {
|
return Panel(BorderLayout(5, 0)).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = TOTAL_XP_WIDGET_SIZE
|
preferredSize = TOTAL_XP_WIDGET_SIZE
|
||||||
add(imageContainer, BorderLayout.WEST)
|
add(imageContainer, BorderLayout.WEST)
|
||||||
|
|
@ -164,7 +196,7 @@ object LootTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTextPanel(l1 : JLabel, l2: JLabel): Panel {
|
private fun createTextPanel(l1 : JLabel, l2: JLabel): Panel {
|
||||||
return Panel(GridLayout(2, 1, 5, 5)).apply {
|
return Panel(GridLayout(2, 1, 5, 0)).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
add(l1)
|
add(l1)
|
||||||
add(l2)
|
add(l2)
|
||||||
|
|
@ -173,7 +205,7 @@ object LootTrackerView {
|
||||||
|
|
||||||
private fun createLabel(text: String): JLabel {
|
private fun createLabel(text: String): JLabel {
|
||||||
return JLabel(text).apply {
|
return JLabel(text).apply {
|
||||||
font = Font("Arial", Font.PLAIN, 11)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.LEFT
|
horizontalAlignment = JLabel.LEFT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,11 +221,18 @@ object LootTrackerView {
|
||||||
|
|
||||||
// Recalculate lootPanel size based on the number of unique items.
|
// Recalculate lootPanel size based on the number of unique items.
|
||||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
||||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
val rowsNeeded = ceil(totalItems / 6.0).toInt()
|
||||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1)
|
val lootPanelHeight = rowsNeeded * (40)
|
||||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
|
||||||
|
|
||||||
|
val size = Dimension(lootPanel.width,lootPanelHeight+32)
|
||||||
|
lootPanel.parent.preferredSize = size
|
||||||
|
lootPanel.parent.minimumSize = size
|
||||||
|
lootPanel.parent.maximumSize = size
|
||||||
|
lootPanel.parent.revalidate()
|
||||||
|
lootPanel.parent.repaint()
|
||||||
lootPanel.revalidate()
|
lootPanel.revalidate()
|
||||||
|
|
||||||
|
if(focusedView == VIEW_NAME)
|
||||||
lootPanel.repaint()
|
lootPanel.repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,26 +243,92 @@ object LootTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 0, 0))
|
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 1, 3153952))
|
||||||
return FixedSizePanel(Dimension(36, 32)).apply {
|
|
||||||
|
// Create the panel for the item
|
||||||
|
val itemPanel = FixedSizePanel(Dimension(36, 32)).apply {
|
||||||
preferredSize = Dimension(36, 32)
|
preferredSize = Dimension(36, 32)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
add(ImageCanvas(bufferedImageSprite).apply {
|
|
||||||
|
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||||
preferredSize = Dimension(36, 32)
|
preferredSize = Dimension(36, 32)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
}, BorderLayout.CENTER)
|
}
|
||||||
|
|
||||||
|
// Add the imageCanvas to the panel
|
||||||
|
add(imageCanvas, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
// Put the itemId as a property for reference
|
||||||
putClientProperty("itemId", itemId)
|
putClientProperty("itemId", itemId)
|
||||||
|
|
||||||
|
// Add mouse listener for custom hover text
|
||||||
|
imageCanvas.addMouseListener(object : MouseAdapter() {
|
||||||
|
override fun mouseEntered(e: MouseEvent) {
|
||||||
|
// Show custom tooltip when the mouse enters the component
|
||||||
|
showCustomToolTip(e.point, itemId,quantity,imageCanvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseExited(e: MouseEvent) {
|
||||||
|
// Hide tooltip when mouse exits
|
||||||
|
hideCustomToolTip()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemPanel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to show the custom tooltip
|
||||||
|
fun showCustomToolTip(location: Point, itemId: Int, quantity: Int, parentComponent: ImageCanvas) {
|
||||||
|
var itemDef = ObjTypeList.get(itemId)
|
||||||
|
val gePricePerItem = gePriceMap[itemDef.id.toString()]?.toInt() ?: 0
|
||||||
|
val totalGePrice = gePricePerItem * quantity
|
||||||
|
val totalHaPrice = itemDef.cost * quantity
|
||||||
|
val geText = if (quantity > 1) " (${formatValue(gePricePerItem)} ea)" else ""
|
||||||
|
val haText = if (quantity > 1) " (${formatValue(itemDef.cost)} ea)" else ""
|
||||||
|
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
|
||||||
|
val textColor = Helpers.colorToHex(secondaryColor)
|
||||||
|
val text = "<html><div style='color: "+textColor+"; background-color: "+bgColor+"; padding: 3px;'>" +
|
||||||
|
"${itemDef.name} x $quantity<br>" +
|
||||||
|
"GE: ${formatValue(totalGePrice)} ${geText}<br>" +
|
||||||
|
"HA: ${formatValue(totalHaPrice)} ${haText}</div></html>"
|
||||||
|
|
||||||
|
val _font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
if (customToolTipWindow == null) {
|
||||||
|
customToolTipWindow = JWindow().apply {
|
||||||
|
contentPane = JLabel(text).apply {
|
||||||
|
border = BorderFactory.createLineBorder(Color.BLACK)
|
||||||
|
isOpaque = true
|
||||||
|
background = TOOLTIP_BACKGROUND
|
||||||
|
foreground = Color.WHITE
|
||||||
|
font = _font
|
||||||
|
}
|
||||||
|
pack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the tooltip location relative to the parent component
|
||||||
|
val screenLocation = parentComponent.locationOnScreen
|
||||||
|
customToolTipWindow!!.setLocation(screenLocation.x + location.x, screenLocation.y + location.y + 20)
|
||||||
|
customToolTipWindow!!.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to hide the custom tooltip
|
||||||
|
fun hideCustomToolTip() {
|
||||||
|
customToolTipWindow?.isVisible = false
|
||||||
|
customToolTipWindow = null // Nullify the global instance
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateItemPanelIcon(panel: JPanel, itemId: Int, quantity: Int) {
|
private fun updateItemPanelIcon(panel: JPanel, itemId: Int, quantity: Int) {
|
||||||
panel.removeAll()
|
panel.removeAll()
|
||||||
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
|
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
|
||||||
panel.revalidate()
|
panel.revalidate()
|
||||||
|
if(focusedView == VIEW_NAME)
|
||||||
panel.repaint()
|
panel.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,6 +353,7 @@ object LootTrackerView {
|
||||||
text = "${formatValue(newValue)} gp"
|
text = "${formatValue(newValue)} gp"
|
||||||
putClientProperty("val", newValue)
|
putClientProperty("val", newValue)
|
||||||
revalidate()
|
revalidate()
|
||||||
|
if(focusedView == VIEW_NAME)
|
||||||
repaint()
|
repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -308,10 +414,10 @@ object LootTrackerView {
|
||||||
private fun handleNewDrops(npcName: String, newDrops: Set<Item>, lootTrackerView: JPanel) {
|
private fun handleNewDrops(npcName: String, newDrops: Set<Item>, lootTrackerView: JPanel) {
|
||||||
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
||||||
} ?: run {
|
} ?: run {
|
||||||
// Panel doesn't exist, so create and add it
|
|
||||||
lootTrackerView.add(createLootFrame(npcName))
|
lootTrackerView.add(createLootFrame(npcName))
|
||||||
lootTrackerView.add(Helpers.Spacer(height = 15))
|
lootTrackerView.add(Box.createVerticalStrut(8))
|
||||||
lootTrackerView.revalidate()
|
lootTrackerView.revalidate()
|
||||||
|
if(focusedView == VIEW_NAME)
|
||||||
lootTrackerView.repaint()
|
lootTrackerView.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,7 +427,7 @@ object LootTrackerView {
|
||||||
newDrops.forEach { drop ->
|
newDrops.forEach { drop ->
|
||||||
val geValue = (gePriceMap[drop.id.toString()]?.toInt() ?: 0) * drop.quantity
|
val geValue = (gePriceMap[drop.id.toString()]?.toInt() ?: 0) * drop.quantity
|
||||||
updateValueLabel(lootTrackerView, geValue.toString(), npcName)
|
updateValueLabel(lootTrackerView, geValue.toString(), npcName)
|
||||||
addItemToLootPanel(lootTrackerView, drop, npcName)
|
plugin.registerDrawAction { addItemToLootPanel(lootTrackerView, drop, npcName) }
|
||||||
updateTotalValue(geValue)
|
updateTotalValue(geValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -330,29 +436,30 @@ object LootTrackerView {
|
||||||
val childFramePanel = JPanel().apply {
|
val childFramePanel = JPanel().apply {
|
||||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = Dimension(270, 0)
|
minimumSize = Dimension(230, 0)
|
||||||
maximumSize = Dimension(270, 700)
|
maximumSize = Dimension(230, 700)
|
||||||
|
name = "HELLO_WORLD"
|
||||||
}
|
}
|
||||||
|
|
||||||
val labelPanel = JPanel(BorderLayout()).apply {
|
val labelPanel = JPanel(BorderLayout()).apply {
|
||||||
background = Color(21, 21, 21)
|
background = TITLE_BAR_COLOR
|
||||||
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
||||||
maximumSize = Dimension(270, 24)
|
maximumSize = Dimension(230, 24)
|
||||||
minimumSize = maximumSize
|
minimumSize = maximumSize
|
||||||
preferredSize = maximumSize
|
preferredSize = maximumSize
|
||||||
}
|
}
|
||||||
|
|
||||||
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
|
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
|
||||||
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
|
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
|
||||||
foreground = Color(200, 200, 200)
|
foreground = secondaryColor
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.LEFT
|
horizontalAlignment = JLabel.LEFT
|
||||||
name = "killCountLabel_$npcName"
|
name = "killCountLabel_$npcName"
|
||||||
}
|
}
|
||||||
|
|
||||||
val valueLabel = JLabel("0 gp").apply {
|
val valueLabel = JLabel("0 gp").apply {
|
||||||
foreground = Color(200, 200, 200)
|
foreground = secondaryColor
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.RIGHT
|
horizontalAlignment = JLabel.RIGHT
|
||||||
name = "valueLabel_$npcName"
|
name = "valueLabel_$npcName"
|
||||||
}
|
}
|
||||||
|
|
@ -370,19 +477,120 @@ object LootTrackerView {
|
||||||
|
|
||||||
lootItemPanels[npcName] = mutableMapOf()
|
lootItemPanels[npcName] = mutableMapOf()
|
||||||
|
|
||||||
// Determine number of items and adjust size of lootPanel
|
|
||||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
|
||||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
|
||||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1) // Height per row = 36 + spacing
|
|
||||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
|
||||||
|
|
||||||
childFramePanel.add(labelPanel)
|
childFramePanel.add(labelPanel)
|
||||||
childFramePanel.add(lootPanel)
|
childFramePanel.add(lootPanel)
|
||||||
childFramePanel.add(lootPanel)
|
|
||||||
|
|
||||||
|
val popupMenu = removeLootFrameMenu(childFramePanel, npcName)
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labelPanel.addMouseListener(rightClickListener)
|
||||||
return childFramePanel
|
return childFramePanel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeLootFrameMenu(toRemove: JPanel, npcName: String): JPopupMenu {
|
||||||
|
// Create a popup menu
|
||||||
|
val popupMenu = JPopupMenu()
|
||||||
|
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
|
popupMenu.background = POPUP_BACKGROUND
|
||||||
|
|
||||||
|
// Create menu items with custom font and colors
|
||||||
|
val menuItem1 = JMenuItem("Remove").apply {
|
||||||
|
font = rFont // Set custom font
|
||||||
|
background = POPUP_BACKGROUND // Dark background for item
|
||||||
|
foreground = POPUP_FOREGROUND // 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(8) 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 = POPUP_BACKGROUND
|
||||||
|
|
||||||
|
// Create menu items with custom font and colors
|
||||||
|
val menuItem1 = JMenuItem("Reset Loot Tracker").apply {
|
||||||
|
font = rFont // Set custom font
|
||||||
|
background = POPUP_BACKGROUND // Dark background for item
|
||||||
|
foreground = POPUP_FOREGROUND // Light text color for item
|
||||||
|
}
|
||||||
|
popupMenu.add(menuItem1)
|
||||||
|
menuItem1.addActionListener {
|
||||||
|
plugin.registerDrawAction {
|
||||||
|
resetLootTracker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return popupMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetLootTracker(){
|
||||||
|
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(8))
|
||||||
|
lootTrackerView?.revalidate()
|
||||||
|
lootTrackerView?.repaint()
|
||||||
|
}
|
||||||
|
|
||||||
class FixedSizePanel(private val fixedSize: Dimension) : JPanel() {
|
class FixedSizePanel(private val fixedSize: Dimension) : JPanel() {
|
||||||
override fun getPreferredSize(): Dimension {
|
override fun getPreferredSize(): Dimension {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.plugin.Companion.PROGRESS_BAR_FILL
|
||||||
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
import java.awt.Canvas
|
import java.awt.Canvas
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
|
import java.awt.Dimension
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
import java.awt.Graphics
|
import java.awt.Graphics
|
||||||
|
|
||||||
|
|
@ -13,7 +16,7 @@ class ProgressBar(
|
||||||
) : Canvas() {
|
) : Canvas() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun paint(g: Graphics) {
|
override fun paint(g: Graphics) {
|
||||||
|
|
@ -25,22 +28,32 @@ class ProgressBar(
|
||||||
g.fillRect(0, 0, width, this.height)
|
g.fillRect(0, 0, width, this.height)
|
||||||
|
|
||||||
// Draw the unfilled part of the progress bar
|
// Draw the unfilled part of the progress bar
|
||||||
g.color = Color(100, 100, 100)
|
g.color = PROGRESS_BAR_FILL
|
||||||
g.fillRect(width, 0, this.width - width, this.height)
|
g.fillRect(width, 0, this.width - width, this.height)
|
||||||
|
|
||||||
|
// Variables for text position
|
||||||
|
val textY = this.height / 2 + 6
|
||||||
|
|
||||||
// Draw the current level on the far left
|
// Draw the current level on the far left
|
||||||
g.color = Color(255, 255, 255)
|
drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, secondaryColor)
|
||||||
g.drawString("Lvl. $currentLevel", 5, this.height / 2 + 4)
|
|
||||||
|
|
||||||
// Draw the percentage in the middle
|
// Draw the percentage in the middle
|
||||||
val percentageText = String.format("%.2f%%", progress)
|
val percentageText = String.format("%.2f%%", progress)
|
||||||
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
|
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
|
||||||
g.drawString(percentageText, (this.width - percentageWidth) / 2, this.height / 2 + 4)
|
drawTextWithShadow(g, percentageText, (this.width - percentageWidth) / 2, textY, secondaryColor)
|
||||||
|
|
||||||
// Draw the next level on the far right
|
// Draw the next level on the far right
|
||||||
val nextLevelText = "Lvl. $nextLevel"
|
val nextLevelText = "Lvl. $nextLevel"
|
||||||
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
|
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
|
||||||
g.drawString(nextLevelText, this.width - nextLevelWidth - 5, this.height / 2 + 4)
|
drawTextWithShadow(g, nextLevelText, this.width - nextLevelWidth - 5, textY, secondaryColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPreferredSize(): Dimension {
|
||||||
|
return Dimension(220, 16) // Force the height to 16px, width can be anything appropriate
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMinimumSize(): Dimension {
|
||||||
|
return Dimension(220, 16) // Force the minimum height to 16px, width can be smaller
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
|
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
|
||||||
|
|
@ -50,4 +63,15 @@ class ProgressBar(
|
||||||
if(isVisible)
|
if(isVisible)
|
||||||
repaint()
|
repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to draw text with a shadow effect
|
||||||
|
private fun drawTextWithShadow(g: Graphics, text: String, x: Int, y: Int, textColor: Color) {
|
||||||
|
// Draw shadow (black text with -1 x and -1 y offset)
|
||||||
|
g.color = Color(0, 0, 0)
|
||||||
|
g.drawString(text, x + 1, y + 1)
|
||||||
|
|
||||||
|
// Draw actual text on top
|
||||||
|
g.color = textColor
|
||||||
|
g.drawString(text, x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,115 +1,236 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
import KondoKit.KondoKitUtils.convertValue
|
import KondoKit.Helpers.convertValue
|
||||||
|
import KondoKit.Helpers.showToast
|
||||||
|
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
|
||||||
|
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
|
import plugin.PluginInfo
|
||||||
import plugin.PluginRepository
|
import plugin.PluginRepository
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
import java.lang.reflect.Field
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is used for the runtime editing of plugin variables.
|
||||||
|
To expose fields add the @Exposed annotation.
|
||||||
|
When they are applied this will trigger an invoke of OnKondoValueUpdated()
|
||||||
|
if it is implemented. Check GroundItems plugin for an example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
object ReflectiveEditorView {
|
object ReflectiveEditorView {
|
||||||
fun createReflectiveEditorView(): JPanel {
|
var reflectiveEditorView: JPanel? = null
|
||||||
|
val loadedPlugins: MutableList<String> = mutableListOf()
|
||||||
|
const val VIEW_NAME = "REFLECTIVE_EDITOR_VIEW"
|
||||||
|
fun createReflectiveEditorView() {
|
||||||
val reflectiveEditorPanel = JPanel(BorderLayout())
|
val reflectiveEditorPanel = JPanel(BorderLayout())
|
||||||
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
||||||
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
||||||
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
||||||
return reflectiveEditorPanel
|
reflectiveEditorView = reflectiveEditorPanel
|
||||||
|
addPlugins(reflectiveEditorView!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addPlugins(reflectiveEditorView: JPanel) {
|
fun addPlugins(reflectiveEditorView: JPanel) {
|
||||||
|
reflectiveEditorView.removeAll() // clear previous
|
||||||
|
loadedPlugins.clear()
|
||||||
try {
|
try {
|
||||||
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
||||||
loadedPluginsField.isAccessible = true
|
loadedPluginsField.isAccessible = true
|
||||||
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
||||||
|
|
||||||
for ((_, plugin) in loadedPlugins) {
|
for ((pluginInfo, plugin) in loadedPlugins) {
|
||||||
addPluginToEditor(reflectiveEditorView, plugin as Plugin)
|
addPluginToEditor(reflectiveEditorView, pluginInfo as PluginInfo, plugin as Plugin)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a centered box for plugins that have no exposed fields
|
||||||
|
if (loadedPlugins.isNotEmpty()) {
|
||||||
|
val noExposedPanel = JPanel(BorderLayout())
|
||||||
|
noExposedPanel.background = VIEW_BACKGROUND_COLOR
|
||||||
|
noExposedPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
||||||
|
|
||||||
|
val label = JLabel("Loaded Plugins without Exposed Fields", SwingConstants.CENTER)
|
||||||
|
label.font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||||
|
label.foreground = primaryColor
|
||||||
|
noExposedPanel.add(label, BorderLayout.NORTH)
|
||||||
|
|
||||||
|
val pluginsList = JList(loadedPlugins.toTypedArray())
|
||||||
|
pluginsList.background = WIDGET_COLOR
|
||||||
|
pluginsList.foreground = secondaryColor
|
||||||
|
pluginsList.font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||||
|
|
||||||
|
// Wrap the JList in a JScrollPane with a fixed height
|
||||||
|
val maxScrollPaneHeight = 200
|
||||||
|
val scrollPane = JScrollPane(pluginsList).apply {
|
||||||
|
verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
|
||||||
|
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a wrapper panel with BoxLayout to constrain the scroll pane
|
||||||
|
val scrollPaneWrapper = JPanel().apply {
|
||||||
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
|
add(scrollPane)
|
||||||
|
}
|
||||||
|
|
||||||
|
noExposedPanel.add(scrollPaneWrapper, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
// Center the panel within the reflectiveEditorView
|
||||||
|
val centeredPanel = JPanel().apply {
|
||||||
|
preferredSize = Dimension(240, maxScrollPaneHeight)
|
||||||
|
maximumSize = preferredSize
|
||||||
|
minimumSize = preferredSize
|
||||||
|
}
|
||||||
|
centeredPanel.layout = BoxLayout(centeredPanel, BoxLayout.Y_AXIS)
|
||||||
|
centeredPanel.add(Box.createVerticalGlue())
|
||||||
|
centeredPanel.add(noExposedPanel)
|
||||||
|
centeredPanel.add(Box.createVerticalGlue())
|
||||||
|
|
||||||
|
reflectiveEditorView.add(Box.createVerticalStrut(10))
|
||||||
|
reflectiveEditorView.add(centeredPanel)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
reflectiveEditorView.revalidate()
|
reflectiveEditorView.revalidate()
|
||||||
|
if(focusedView == VIEW_NAME)
|
||||||
reflectiveEditorView.repaint()
|
reflectiveEditorView.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addPluginToEditor(reflectiveEditorView: JPanel, plugin: Any) {
|
private fun addPluginToEditor(reflectiveEditorView: JPanel, pluginInfo : PluginInfo, plugin: Plugin) {
|
||||||
reflectiveEditorView.layout = BoxLayout(reflectiveEditorView, BoxLayout.Y_AXIS)
|
reflectiveEditorView.layout = BoxLayout(reflectiveEditorView, BoxLayout.Y_AXIS)
|
||||||
|
|
||||||
val fieldNotifier = KondoKitUtils.FieldNotifier(plugin)
|
val fieldNotifier = Helpers.FieldNotifier(plugin)
|
||||||
val exposedFields = KondoKitUtils.getKondoExposedFields(plugin)
|
val exposedFields = plugin.javaClass.declaredFields.filter { field ->
|
||||||
|
field.annotations.any { annotation ->
|
||||||
|
annotation.annotationClass.simpleName == "Exposed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (exposedFields.isNotEmpty()) {
|
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)
|
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 label = JLabel("$packageName v$version", SwingConstants.CENTER)
|
||||||
label.foreground = primaryColor
|
label.foreground = primaryColor
|
||||||
label.font = Font("Arial", Font.BOLD, 14)
|
label.font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
labelPanel.add(label, BorderLayout.CENTER)
|
labelPanel.add(label, BorderLayout.CENTER)
|
||||||
|
label.isOpaque = true
|
||||||
|
label.background = TITLE_BAR_COLOR
|
||||||
reflectiveEditorView.add(labelPanel)
|
reflectiveEditorView.add(labelPanel)
|
||||||
}
|
|
||||||
|
|
||||||
for (field in exposedFields) {
|
for (field in exposedFields) {
|
||||||
field.isAccessible = true
|
field.isAccessible = true
|
||||||
|
|
||||||
|
// Get the "Exposed" annotation specifically and retrieve its description, if available
|
||||||
|
val exposedAnnotation = field.annotations.firstOrNull { annotation ->
|
||||||
|
annotation.annotationClass.simpleName == "Exposed"
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
} ?: ""
|
||||||
|
|
||||||
val fieldPanel = JPanel()
|
val fieldPanel = JPanel()
|
||||||
fieldPanel.layout = GridBagLayout()
|
fieldPanel.layout = GridBagLayout()
|
||||||
fieldPanel.background = WIDGET_COLOR // Match the background for minimal borders
|
fieldPanel.background = WIDGET_COLOR
|
||||||
fieldPanel.foreground = secondaryColor
|
fieldPanel.foreground = secondaryColor
|
||||||
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0) // No visible border, just spacing
|
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
||||||
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
||||||
|
|
||||||
val gbc = GridBagConstraints()
|
val gbc = GridBagConstraints()
|
||||||
gbc.insets = Insets(0, 5, 0, 5) // Less padding, more minimal spacing
|
gbc.insets = Insets(0, 5, 0, 5)
|
||||||
|
|
||||||
val label = JLabel(field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).capitalize())
|
val label = JLabel(field.name.capitalize())
|
||||||
label.foreground = secondaryColor
|
label.foreground = secondaryColor
|
||||||
gbc.gridx = 0
|
gbc.gridx = 0
|
||||||
gbc.gridy = 0
|
gbc.gridy = 0
|
||||||
gbc.weightx = 0.0
|
gbc.weightx = 0.0
|
||||||
|
label.font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
gbc.anchor = GridBagConstraints.WEST
|
gbc.anchor = GridBagConstraints.WEST
|
||||||
fieldPanel.add(label, gbc)
|
fieldPanel.add(label, gbc)
|
||||||
|
|
||||||
val textField = JTextField(field.get(plugin)?.toString() ?: "")
|
// Create appropriate input component based on field type
|
||||||
textField.background = VIEW_BACKGROUND_COLOR
|
val inputComponent: JComponent = when {
|
||||||
textField.foreground = secondaryColor
|
field.type == Boolean::class.javaPrimitiveType || field.type == java.lang.Boolean::class.java -> JCheckBox().apply {
|
||||||
textField.border = BorderFactory.createLineBorder(WIDGET_COLOR, 1) // Subtle border
|
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.gridx = 1
|
||||||
gbc.gridy = 0
|
gbc.gridy = 0
|
||||||
gbc.weightx = 1.0
|
gbc.weightx = 1.0
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL
|
gbc.fill = GridBagConstraints.HORIZONTAL
|
||||||
fieldPanel.add(textField, gbc)
|
fieldPanel.add(inputComponent, gbc)
|
||||||
|
|
||||||
val applyButton = JButton("Apply")
|
val applyButton = JButton("\u2714").apply {
|
||||||
applyButton.background = primaryColor
|
maximumSize = Dimension(Int.MAX_VALUE, 8)
|
||||||
applyButton.foreground = VIEW_BACKGROUND_COLOR
|
}
|
||||||
applyButton.border = BorderFactory.createLineBorder(primaryColor, 1)
|
|
||||||
gbc.gridx = 2
|
gbc.gridx = 2
|
||||||
gbc.gridy = 0
|
gbc.gridy = 0
|
||||||
gbc.weightx = 0.0
|
gbc.weightx = 0.0
|
||||||
gbc.fill = GridBagConstraints.NONE
|
gbc.fill = GridBagConstraints.NONE
|
||||||
applyButton.addActionListener {
|
applyButton.addActionListener {
|
||||||
try {
|
try {
|
||||||
val newValue = convertValue(field.type, field.genericType, textField.text)
|
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)
|
fieldNotifier.setFieldValue(field, newValue)
|
||||||
JOptionPane.showMessageDialog(
|
showToast(
|
||||||
null,
|
reflectiveEditorView,
|
||||||
"${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)} updated successfully!"
|
"${field.name} updated successfully!"
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
JOptionPane.showMessageDialog(
|
showToast(
|
||||||
null,
|
reflectiveEditorView,
|
||||||
"Failed to update ${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)}: ${e.message}",
|
"Failed to update ${field.name}: ${e.message}",
|
||||||
"Error",
|
|
||||||
JOptionPane.ERROR_MESSAGE
|
JOptionPane.ERROR_MESSAGE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -118,6 +239,7 @@ object ReflectiveEditorView {
|
||||||
fieldPanel.add(applyButton, gbc)
|
fieldPanel.add(applyButton, gbc)
|
||||||
reflectiveEditorView.add(fieldPanel)
|
reflectiveEditorView.add(fieldPanel)
|
||||||
|
|
||||||
|
// Track field changes in real-time and update UI
|
||||||
var previousValue = field.get(plugin)?.toString()
|
var previousValue = field.get(plugin)?.toString()
|
||||||
val timer = Timer()
|
val timer = Timer()
|
||||||
timer.schedule(object : TimerTask() {
|
timer.schedule(object : TimerTask() {
|
||||||
|
|
@ -126,24 +248,77 @@ object ReflectiveEditorView {
|
||||||
if (currentValue != previousValue) {
|
if (currentValue != previousValue) {
|
||||||
previousValue = currentValue
|
previousValue = currentValue
|
||||||
SwingUtilities.invokeLater {
|
SwingUtilities.invokeLater {
|
||||||
fieldNotifier.notifyFieldChange(field, currentValue)
|
// 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()) {
|
if (exposedFields.isNotEmpty()) {
|
||||||
reflectiveEditorView.add(Box.createVerticalStrut(10))
|
reflectiveEditorView.add(Box.createVerticalStrut(5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
loadedPlugins.add(plugin.javaClass.`package`.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var customToolTipWindow: JWindow? = null
|
||||||
|
|
||||||
|
fun showCustomToolTip(text: String, component: JComponent) {
|
||||||
|
val _font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||||
|
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 {
|
||||||
|
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
|
||||||
|
val textColor = Helpers.colorToHex(secondaryColor)
|
||||||
|
contentPane = JLabel("<html><div style='color: $textColor; background-color: $bgColor; padding: 3px; word-break: break-all;'>$text</div></html>").apply {
|
||||||
|
border = BorderFactory.createLineBorder(Color.BLACK)
|
||||||
|
isOpaque = true
|
||||||
|
background = TOOLTIP_BACKGROUND
|
||||||
|
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
|
||||||
|
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
|
||||||
|
val textColor = Helpers.colorToHex(secondaryColor)
|
||||||
|
label.text = "<html><div style='color: $textColor; background-color: $bgColor; 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
158
plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt
Normal file
158
plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.plugin.Companion.SCROLL_BAR_COLOR
|
||||||
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
|
import rt4.GameShell.frame
|
||||||
|
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
|
||||||
|
private var lastSize = 0
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
if(lastSize != content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer))
|
||||||
|
handleResize()
|
||||||
|
}
|
||||||
|
}, 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
|
||||||
|
lastSize = content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer)
|
||||||
|
content.bounds = Rectangle(0, 0, 242, lastSize)
|
||||||
|
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 = SCROLL_BAR_COLOR
|
||||||
|
g2.fillRect(scrollbarX, scrollbarY, 2, scrollbarHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,109 +4,196 @@ import rt4.GlIndexedSprite
|
||||||
import rt4.GlSprite
|
import rt4.GlSprite
|
||||||
import rt4.SoftwareIndexedSprite
|
import rt4.SoftwareIndexedSprite
|
||||||
import rt4.SoftwareSprite
|
import rt4.SoftwareSprite
|
||||||
|
import java.awt.Color
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
|
||||||
|
// Define interfaces for common sprite types
|
||||||
|
interface BaseSprite {
|
||||||
|
val width: Int
|
||||||
|
val height: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IndexedSprite : BaseSprite {
|
||||||
|
val pixels: ByteArray
|
||||||
|
val palette: IntArray
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NonIndexedSprite : BaseSprite {
|
||||||
|
val pixels: IntArray?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapter functions for existing sprite types
|
||||||
|
fun adaptSoftwareSprite(sprite: SoftwareSprite): NonIndexedSprite {
|
||||||
|
return object : NonIndexedSprite {
|
||||||
|
override val width: Int = sprite.width
|
||||||
|
override val height: Int = sprite.height
|
||||||
|
override val pixels: IntArray? = sprite.pixels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun adaptSoftwareIndexedSprite(sprite: SoftwareIndexedSprite): IndexedSprite {
|
||||||
|
return object : IndexedSprite {
|
||||||
|
override val width: Int = sprite.width
|
||||||
|
override val height: Int = sprite.height
|
||||||
|
override val pixels: ByteArray = sprite.pixels
|
||||||
|
override val palette: IntArray = sprite.pallet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun adaptGlSprite(sprite: GlSprite): NonIndexedSprite {
|
||||||
|
return object : NonIndexedSprite {
|
||||||
|
override val width: Int = sprite.width
|
||||||
|
override val height: Int = sprite.height
|
||||||
|
override val pixels: IntArray? = sprite.pixels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun adaptGlIndexedSprite(sprite: GlIndexedSprite): IndexedSprite {
|
||||||
|
return object : IndexedSprite {
|
||||||
|
override val width: Int = sprite.width
|
||||||
|
override val height: Int = sprite.height
|
||||||
|
override val pixels: ByteArray = sprite.pixels
|
||||||
|
override val palette: IntArray = sprite.pallet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object SpriteToBufferedImage {
|
object SpriteToBufferedImage {
|
||||||
/**
|
/**
|
||||||
* Converts a SoftwareSprite back into a BufferedImage.
|
* Converts a BaseSprite into a BufferedImage.
|
||||||
|
*
|
||||||
|
* Handles both indexed and non-indexed sprites, with optional tinting and grayscale.
|
||||||
*
|
*
|
||||||
* @param sprite The sprite to be converted.
|
* @param sprite The sprite to be converted.
|
||||||
|
* @param tint An optional Color to tint the image.
|
||||||
|
* @param grayscale If true, converts the image to grayscale.
|
||||||
|
* @param brightnessBoost A multiplier to boost the brightness of the image.
|
||||||
* @return The BufferedImage created from the sprite.
|
* @return The BufferedImage created from the sprite.
|
||||||
*/
|
*/
|
||||||
fun convertToBufferedImage(sprite: SoftwareSprite): BufferedImage {
|
fun convertToBufferedImage(
|
||||||
|
sprite: BaseSprite,
|
||||||
|
tint: Color? = null,
|
||||||
|
grayscale: Boolean = false,
|
||||||
|
brightnessBoost: Float = 1.0f
|
||||||
|
): BufferedImage {
|
||||||
val width = sprite.width
|
val width = sprite.width
|
||||||
val height = sprite.height
|
val height = sprite.height
|
||||||
|
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
||||||
|
|
||||||
|
when (sprite) {
|
||||||
|
is IndexedSprite -> {
|
||||||
val pixels = sprite.pixels
|
val pixels = sprite.pixels
|
||||||
|
val palette = sprite.palette
|
||||||
// Create a BufferedImage with ARGB color model
|
|
||||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
|
||||||
|
|
||||||
// Manually set pixels and print the pixel data
|
|
||||||
for (y in 0 until height) {
|
|
||||||
for (x in 0 until width) {
|
|
||||||
val pixel = pixels[y * width + x]
|
|
||||||
image.setRGB(x, y, pixel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return image
|
|
||||||
}
|
|
||||||
|
|
||||||
fun convertToBufferedImage(sprite: SoftwareIndexedSprite): BufferedImage {
|
|
||||||
val width = sprite.width
|
|
||||||
val height = sprite.height
|
|
||||||
val pixels = sprite.pixels // byte[]
|
|
||||||
val palette = sprite.pallet
|
|
||||||
|
|
||||||
// Create a BufferedImage with ARGB color model
|
|
||||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
|
||||||
|
|
||||||
// Manually set pixels using the palette
|
// Manually set pixels using the palette
|
||||||
for (y in 0 until height) {
|
for (y in 0 until height) {
|
||||||
for (x in 0 until width) {
|
for (x in 0 until width) {
|
||||||
// Get the index from the sprite's pixel array
|
|
||||||
val index = pixels[y * width + x].toInt() and 0xFF
|
val index = pixels[y * width + x].toInt() and 0xFF
|
||||||
// Map the index to a color in the palette
|
var color = palette[index]
|
||||||
val color = palette[index]
|
|
||||||
// Set the ARGB color in the BufferedImage
|
// Apply grayscale or tint if provided
|
||||||
image.setRGB(x, y, color)
|
val finalColor = if (grayscale) {
|
||||||
}
|
applyGrayscale(Color(color, true), brightnessBoost)
|
||||||
}
|
} else if (tint != null) {
|
||||||
return image
|
applyTint(Color(color, true), tint, brightnessBoost)
|
||||||
|
} else {
|
||||||
|
applyBrightness(Color(color, true), brightnessBoost)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertToBufferedImage(sprite: GlIndexedSprite): BufferedImage {
|
image.setRGB(x, y, finalColor.rgb)
|
||||||
val width = sprite.width
|
}
|
||||||
val height = sprite.height
|
}
|
||||||
val pixels = sprite.pixels // byte[]
|
}
|
||||||
val palette = sprite.pallet
|
is NonIndexedSprite -> {
|
||||||
|
val pixels = sprite.pixels ?: return image // Handle null case for GlSprite
|
||||||
|
|
||||||
// Create a BufferedImage with ARGB color model
|
// Manually set pixels directly
|
||||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
|
||||||
|
|
||||||
// Manually set pixels using the palette
|
|
||||||
for (y in 0 until height) {
|
for (y in 0 until height) {
|
||||||
for (x in 0 until width) {
|
for (x in 0 until width) {
|
||||||
// Get the index from the sprite's pixel array
|
var color = pixels[y * width + x]
|
||||||
val index = pixels[y * width + x].toInt() and 0xFF
|
|
||||||
// Map the index to a color in the palette
|
// Apply grayscale or tint if provided
|
||||||
val color = palette[index]
|
val finalColor = if (grayscale) {
|
||||||
// Set the ARGB color in the BufferedImage
|
applyGrayscale(Color(color, true), brightnessBoost)
|
||||||
image.setRGB(x, y, color)
|
} else if (tint != null) {
|
||||||
|
applyTint(Color(color, true), tint, brightnessBoost)
|
||||||
|
} else {
|
||||||
|
applyBrightness(Color(color, true), brightnessBoost)
|
||||||
|
}
|
||||||
|
|
||||||
|
image.setRGB(x, y, finalColor.rgb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
fun convertToBufferedImage(sprite: GlSprite): BufferedImage {
|
* Applies a tint to a given color using the tint's alpha value to control the intensity.
|
||||||
val width = sprite.width
|
*
|
||||||
val height = sprite.height
|
* @param original The original color.
|
||||||
val pixels = sprite.pixels
|
* @param tint The tint color to be applied.
|
||||||
|
* @param brightnessBoost A multiplier to boost the brightness of the image.
|
||||||
// Create a BufferedImage with ARGB color model
|
* @return The tinted color.
|
||||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
*/
|
||||||
|
fun applyTint(original: Color, tint: Color, brightnessBoost: Float): Color {
|
||||||
if(pixels == null) {
|
val boostedColor = applyBrightness(original, brightnessBoost)
|
||||||
return image
|
val r = (boostedColor.red * tint.red / 255).coerceIn(0, 255)
|
||||||
|
val g = (boostedColor.green * tint.green / 255).coerceIn(0, 255)
|
||||||
|
val b = (boostedColor.blue * tint.blue / 255).coerceIn(0, 255)
|
||||||
|
return Color(r, g, b, boostedColor.alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manually set pixels and print the pixel data
|
/**
|
||||||
for (y in 0 until height) {
|
* Boosts the brightness of a given color.
|
||||||
for (x in 0 until width) {
|
*
|
||||||
val pixel = pixels[y * width + x]
|
* @param original The original color.
|
||||||
image.setRGB(x, y, pixel)
|
* @param factor The multiplier to boost the brightness.
|
||||||
}
|
* @return The color with boosted brightness.
|
||||||
}
|
*/
|
||||||
return image
|
fun applyBrightness(original: Color, factor: Float): Color {
|
||||||
|
val r = (original.red * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||||
|
val g = (original.green * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||||
|
val b = (original.blue * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||||
|
return Color(r, g, b, original.alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBufferedImageFromSprite(sprite: Any?) : BufferedImage {
|
/**
|
||||||
return when(sprite){
|
* Converts a color to grayscale and applies a brightness boost.
|
||||||
is GlSprite -> convertToBufferedImage(sprite)
|
*
|
||||||
is SoftwareSprite -> convertToBufferedImage(sprite)
|
* @param original The original color.
|
||||||
is SoftwareIndexedSprite -> convertToBufferedImage(sprite)
|
* @param brightnessBoost A multiplier to boost the brightness.
|
||||||
is GlIndexedSprite -> convertToBufferedImage(sprite)
|
* @return The grayscale version of the color with boosted brightness.
|
||||||
else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
|
*/
|
||||||
|
fun applyGrayscale(original: Color, brightnessBoost: Float): Color {
|
||||||
|
// Calculate the grayscale value using the luminosity method
|
||||||
|
val grayValue = (0.3 * original.red + 0.59 * original.green + 0.11 * original.blue).toInt()
|
||||||
|
val boostedGray = (grayValue * brightnessBoost).coerceIn(0.0f, 255.0f).toInt()
|
||||||
|
return Color(boostedGray, boostedGray, boostedGray, original.alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an unknown sprite object into a BufferedImage if it matches a known sprite type.
|
||||||
|
*
|
||||||
|
* @param sprite The sprite object to be converted.
|
||||||
|
* @param tint An optional Color to tint the image.
|
||||||
|
* @param grayscale If true, converts the image to grayscale.
|
||||||
|
* @param brightnessBoost A multiplier to boost the brightness of the image.
|
||||||
|
* @return The BufferedImage created from the sprite or a default image if unsupported.
|
||||||
|
*/
|
||||||
|
fun getBufferedImageFromSprite(
|
||||||
|
sprite: Any?,
|
||||||
|
tint: Color? = null,
|
||||||
|
grayscale: Boolean = false,
|
||||||
|
brightnessBoost: Float = 1.0f
|
||||||
|
): BufferedImage {
|
||||||
|
return when (sprite) {
|
||||||
|
is SoftwareSprite -> convertToBufferedImage(adaptSoftwareSprite(sprite), tint, grayscale, brightnessBoost)
|
||||||
|
is SoftwareIndexedSprite -> convertToBufferedImage(adaptSoftwareIndexedSprite(sprite), tint, grayscale, brightnessBoost)
|
||||||
|
is GlSprite -> convertToBufferedImage(adaptGlSprite(sprite), tint, grayscale, brightnessBoost)
|
||||||
|
is GlIndexedSprite -> convertToBufferedImage(adaptGlIndexedSprite(sprite), tint, grayscale, brightnessBoost)
|
||||||
|
else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) // Default empty image for unsupported types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
94
plugin-playground/src/main/kotlin/KondoKit/Themes.kt
Normal file
94
plugin-playground/src/main/kotlin/KondoKit/Themes.kt
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
package KondoKit
|
||||||
|
|
||||||
|
import java.awt.Color
|
||||||
|
|
||||||
|
object Themes {
|
||||||
|
enum class ThemeType {
|
||||||
|
RUNELITE,
|
||||||
|
DARKER_RUNELITE,
|
||||||
|
SARADOMIN,
|
||||||
|
ORION,
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTheme(themeType: ThemeType): Theme {
|
||||||
|
return when (themeType) {
|
||||||
|
ThemeType.RUNELITE -> Theme(
|
||||||
|
widgetColor = Color(30, 30, 30),
|
||||||
|
titleBarColor = Color(21, 21, 21),
|
||||||
|
viewBackgroundColor = Color(40, 40, 40),
|
||||||
|
primaryColor = Color(165, 165, 165),
|
||||||
|
secondaryColor = Color(255, 255, 255),
|
||||||
|
popupBackground = Color(45, 45, 45),
|
||||||
|
popupForeground = Color(220, 220, 220),
|
||||||
|
tooltipBackground = Color(50, 50, 50),
|
||||||
|
scrollBarColor = Color(64, 64, 64),
|
||||||
|
progressBarFill = Color(61, 56, 49),
|
||||||
|
navTint = null,
|
||||||
|
navGreyScale = false,
|
||||||
|
boost = 1f
|
||||||
|
)
|
||||||
|
ThemeType.DARKER_RUNELITE -> Theme(
|
||||||
|
widgetColor = Color(15, 15, 15),
|
||||||
|
titleBarColor = Color(10, 10, 10),
|
||||||
|
viewBackgroundColor = Color(20, 20, 20),
|
||||||
|
primaryColor = Color(140, 140, 140),
|
||||||
|
secondaryColor = Color(210, 210, 210),
|
||||||
|
popupBackground = Color(25, 25, 25),
|
||||||
|
popupForeground = Color(200, 200, 200),
|
||||||
|
tooltipBackground = Color(30, 30, 30),
|
||||||
|
scrollBarColor = Color(40, 40, 40),
|
||||||
|
progressBarFill = Color(45, 40, 35),
|
||||||
|
navTint = null,
|
||||||
|
navGreyScale = false,
|
||||||
|
boost = 0.8f
|
||||||
|
)
|
||||||
|
ThemeType.ORION -> Theme(
|
||||||
|
widgetColor = Color(50, 50, 50),
|
||||||
|
titleBarColor = Color(35, 35, 35),
|
||||||
|
viewBackgroundColor = Color(60, 60, 60),
|
||||||
|
primaryColor = Color(180, 180, 180),
|
||||||
|
secondaryColor = Color(210, 210, 210),
|
||||||
|
popupBackground = Color(45, 45, 45),
|
||||||
|
popupForeground = Color(230, 230, 230),
|
||||||
|
tooltipBackground = Color(55, 55, 55),
|
||||||
|
scrollBarColor = Color(75, 75, 75),
|
||||||
|
progressBarFill = Color(100, 100, 100),
|
||||||
|
navTint = null,
|
||||||
|
navGreyScale = true,
|
||||||
|
boost = 1.3f
|
||||||
|
)
|
||||||
|
ThemeType.SARADOMIN -> Theme(
|
||||||
|
widgetColor = Color(62, 53, 41),
|
||||||
|
titleBarColor = Color(111, 93, 69).darker(),
|
||||||
|
viewBackgroundColor = Color(101, 85, 63),
|
||||||
|
primaryColor = Color(180, 150, 120),
|
||||||
|
secondaryColor = Color(230, 210, 190),
|
||||||
|
popupBackground = Color(70, 56, 42),
|
||||||
|
popupForeground = Color(220, 200, 180),
|
||||||
|
tooltipBackground = Color(80, 65, 50),
|
||||||
|
scrollBarColor = Color(100, 85, 70),
|
||||||
|
progressBarFill = Color(130, 110, 90),
|
||||||
|
navTint = null,
|
||||||
|
navGreyScale = false,
|
||||||
|
boost = 1f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class Theme(
|
||||||
|
val widgetColor: Color,
|
||||||
|
val titleBarColor: Color,
|
||||||
|
val viewBackgroundColor: Color,
|
||||||
|
val primaryColor: Color,
|
||||||
|
val secondaryColor: Color,
|
||||||
|
val popupBackground: Color,
|
||||||
|
val popupForeground: Color,
|
||||||
|
val tooltipBackground: Color,
|
||||||
|
val scrollBarColor: Color,
|
||||||
|
val progressBarFill: Color,
|
||||||
|
val navTint: Color?,
|
||||||
|
val navGreyScale: Boolean,
|
||||||
|
val boost: Float
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,35 +1,43 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.Helpers.addMouseListenerToAll
|
||||||
import KondoKit.Helpers.formatHtmlLabelText
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.formatNumber
|
import KondoKit.Helpers.formatNumber
|
||||||
import KondoKit.Helpers.getProgressBarColor
|
import KondoKit.Helpers.getProgressBarColor
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.plugin.Companion.IMAGE_SIZE
|
import KondoKit.plugin.Companion.IMAGE_SIZE
|
||||||
|
import KondoKit.plugin.Companion.LVL_ICON
|
||||||
|
import KondoKit.plugin.Companion.POPUP_BACKGROUND
|
||||||
|
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_SIZE
|
import KondoKit.plugin.Companion.WIDGET_SIZE
|
||||||
import KondoKit.plugin.Companion.kondoExposed_playerXPMultiplier
|
import KondoKit.plugin.Companion.playerXPMultiplier
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
import KondoKit.plugin.StateManager.totalXPWidget
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import javax.swing.Box
|
import javax.swing.*
|
||||||
import javax.swing.BoxLayout
|
|
||||||
import javax.swing.JLabel
|
|
||||||
import javax.swing.JPanel
|
|
||||||
|
|
||||||
object XPTrackerView {
|
object XPTrackerView {
|
||||||
|
|
||||||
private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4)
|
private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4)
|
||||||
|
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
||||||
|
var totalXPWidget: XPWidget? = null
|
||||||
|
val initialXP: MutableMap<Int, Int> = HashMap()
|
||||||
|
var xpTrackerView: JPanel? = null
|
||||||
|
const val VIEW_NAME = "XP_TRACKER_VIEW"
|
||||||
|
|
||||||
|
|
||||||
val npcHitpointsMap: Map<Int, Int> = try {
|
val npcHitpointsMap: Map<Int, Int> = try {
|
||||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
val json = lines.joinToString("\n")
|
val json = lines.joinToString("\n")
|
||||||
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
||||||
|
|
@ -74,8 +82,8 @@ object XPTrackerView {
|
||||||
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
||||||
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
||||||
val xpPerKill = when (xpWidget.skillId) {
|
val xpPerKill = when (xpWidget.skillId) {
|
||||||
3 -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
3 -> playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
||||||
else -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
else -> playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
||||||
}
|
}
|
||||||
val remainingKills = xpLeft / xpPerKill
|
val remainingKills = xpLeft / xpPerKill
|
||||||
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
||||||
|
|
@ -92,24 +100,61 @@ object XPTrackerView {
|
||||||
xpWidget.xpGainedLabel.text = formatHtmlLabelText("XP Gained: ", primaryColor, formattedXp, secondaryColor)
|
xpWidget.xpGainedLabel.text = formatHtmlLabelText("XP Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||||
|
|
||||||
// Update the progress bar with current level, progress, and next level
|
// Update the progress bar with current level, progress, and next level
|
||||||
xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, focusedView == VIEW_NAME)
|
||||||
|
|
||||||
xpWidget.previousXp = xp
|
xpWidget.previousXp = xp
|
||||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if (focusedView == VIEW_NAME)
|
||||||
xpWidget.panel.repaint()
|
xpWidget.container.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
||||||
val totalXPWidget = plugin.StateManager.totalXPWidget ?: return
|
val totalXPWidget = totalXPWidget ?: return
|
||||||
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
||||||
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
||||||
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if (focusedView == VIEW_NAME)
|
||||||
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 (focusedView == VIEW_NAME)
|
||||||
|
xpTrackerView.repaint()
|
||||||
|
}
|
||||||
|
|
||||||
fun createTotalXPWidget(): XPWidget {
|
fun createTotalXPWidget(): XPWidget {
|
||||||
val widgetPanel = Panel().apply {
|
val widgetPanel = Panel().apply {
|
||||||
|
|
@ -120,43 +165,39 @@ object XPTrackerView {
|
||||||
minimumSize = TOTAL_XP_WIDGET_SIZE
|
minimumSize = TOTAL_XP_WIDGET_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(898))
|
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(LVL_ICON))
|
||||||
|
|
||||||
|
|
||||||
val imageContainer = Panel(FlowLayout()).apply {
|
val imageContainer = Panel(FlowLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
preferredSize = IMAGE_SIZE
|
maximumSize = preferredSize
|
||||||
maximumSize = IMAGE_SIZE
|
minimumSize = preferredSize
|
||||||
minimumSize = IMAGE_SIZE
|
size = preferredSize
|
||||||
size = IMAGE_SIZE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferedImageSprite.let { image ->
|
bufferedImageSprite.let { image ->
|
||||||
val imageCanvas = ImageCanvas(image).apply {
|
val imageCanvas = ImageCanvas(image).apply {
|
||||||
background = WIDGET_COLOR
|
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
preferredSize = Dimension(image.width, image.height)
|
maximumSize = preferredSize
|
||||||
maximumSize = Dimension(image.width, image.height)
|
minimumSize = preferredSize
|
||||||
minimumSize = Dimension(image.width, image.height)
|
size = preferredSize
|
||||||
size = Dimension(image.width, image.height)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imageContainer.add(imageCanvas)
|
imageContainer.add(imageCanvas)
|
||||||
imageContainer.size = Dimension(image.width, image.height)
|
imageContainer.size = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
|
|
||||||
imageContainer.revalidate()
|
imageContainer.revalidate()
|
||||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(focusedView == VIEW_NAME)
|
||||||
imageContainer.repaint()
|
imageContainer.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
val textPanel = Panel().apply {
|
val textPanel = Panel().apply {
|
||||||
layout = GridLayout(2, 1, 5, 5)
|
layout = GridLayout(2, 1, 5, 0)
|
||||||
background = WIDGET_COLOR
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val font = Font("Arial", Font.PLAIN, 11)
|
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
val xpGainedLabel = JLabel(
|
val xpGainedLabel = JLabel(
|
||||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
formatHtmlLabelText("Gained: ", primaryColor, "0", secondaryColor)
|
||||||
).apply {
|
).apply {
|
||||||
this.horizontalAlignment = JLabel.LEFT
|
this.horizontalAlignment = JLabel.LEFT
|
||||||
this.font = font
|
this.font = font
|
||||||
|
|
@ -177,14 +218,14 @@ object XPTrackerView {
|
||||||
|
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = -1,
|
skillId = -1,
|
||||||
panel = widgetPanel,
|
container = widgetPanel,
|
||||||
xpGainedLabel = xpGainedLabel,
|
xpGainedLabel = xpGainedLabel,
|
||||||
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
||||||
this.horizontalAlignment = JLabel.LEFT
|
this.horizontalAlignment = JLabel.LEFT
|
||||||
this.font = font
|
this.font = font
|
||||||
},
|
},
|
||||||
xpPerHourLabel = xpPerHourLabel,
|
xpPerHourLabel = xpPerHourLabel,
|
||||||
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
|
progressBar = ProgressBar(0.0, Color.BLACK), // Unused
|
||||||
totalXpGained = 0,
|
totalXpGained = 0,
|
||||||
startTime = System.currentTimeMillis(),
|
startTime = System.currentTimeMillis(),
|
||||||
previousXp = 0,
|
previousXp = 0,
|
||||||
|
|
@ -193,19 +234,66 @@ object XPTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createXPTrackerView(): JPanel? {
|
fun createXPTrackerView(){
|
||||||
val widgetViewPanel = JPanel()
|
val widgetViewPanel = JPanel().apply {
|
||||||
widgetViewPanel.layout = BoxLayout(widgetViewPanel, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
widgetViewPanel.background = VIEW_BACKGROUND_COLOR
|
background = VIEW_BACKGROUND_COLOR
|
||||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
|
||||||
|
|
||||||
totalXPWidget = createTotalXPWidget()
|
|
||||||
widgetViewPanel.add(wrappedWidget(totalXPWidget!!.panel))
|
|
||||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
|
||||||
|
|
||||||
return widgetViewPanel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||||
|
widgetViewPanel.add(wrapped)
|
||||||
|
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||||
|
|
||||||
|
xpTrackerView = widgetViewPanel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun createResetMenu(): JPopupMenu {
|
||||||
|
// Create a popup menu
|
||||||
|
val popupMenu = JPopupMenu()
|
||||||
|
|
||||||
|
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
|
popupMenu.background = POPUP_BACKGROUND
|
||||||
|
|
||||||
|
// Create menu items with custom font and colors
|
||||||
|
val menuItem1 = JMenuItem("Reset Tracker").apply {
|
||||||
|
font = rFont // Set custom font
|
||||||
|
background = POPUP_BACKGROUND // Dark background for item
|
||||||
|
foreground = POPUP_FOREGROUND // Light text color for item
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add menu items to the popup menu
|
||||||
|
popupMenu.add(menuItem1)
|
||||||
|
|
||||||
|
// Add action listeners to each menu item (optional)
|
||||||
|
menuItem1.addActionListener { plugin.registerDrawAction { resetXPTracker(xpTrackerView!!) } }
|
||||||
|
return popupMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createXPWidget(skillId: Int, previousXp: Int): XPWidget {
|
fun createXPWidget(skillId: Int, previousXp: Int): XPWidget {
|
||||||
val widgetPanel = Panel().apply {
|
val widgetPanel = Panel().apply {
|
||||||
layout = BorderLayout(5, 5)
|
layout = BorderLayout(5, 5)
|
||||||
|
|
@ -237,16 +325,16 @@ object XPTrackerView {
|
||||||
imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size
|
imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size
|
||||||
|
|
||||||
imageContainer.revalidate()
|
imageContainer.revalidate()
|
||||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(focusedView == VIEW_NAME)
|
||||||
imageContainer.repaint()
|
imageContainer.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
val textPanel = Panel().apply {
|
val textPanel = Panel().apply {
|
||||||
layout = GridLayout(2, 2, 5, 5)
|
layout = GridLayout(2, 2, 5, 0)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
val font = Font("Arial", Font.PLAIN, 11)
|
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
val xpGainedLabel = JLabel(
|
val xpGainedLabel = JLabel(
|
||||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||||
|
|
@ -278,7 +366,7 @@ object XPTrackerView {
|
||||||
|
|
||||||
val levelPanel = Panel().apply {
|
val levelPanel = Panel().apply {
|
||||||
layout = BorderLayout(5, 0)
|
layout = BorderLayout(5, 0)
|
||||||
background = Color(43, 43, 43)
|
background = WIDGET_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply {
|
val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply {
|
||||||
|
|
@ -297,12 +385,12 @@ object XPTrackerView {
|
||||||
widgetPanel.add(levelPanel, BorderLayout.SOUTH)
|
widgetPanel.add(levelPanel, BorderLayout.SOUTH)
|
||||||
|
|
||||||
widgetPanel.revalidate()
|
widgetPanel.revalidate()
|
||||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(focusedView == VIEW_NAME)
|
||||||
widgetPanel.repaint()
|
widgetPanel.repaint()
|
||||||
|
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = skillId,
|
skillId = skillId,
|
||||||
panel = widgetPanel,
|
container = widgetPanel,
|
||||||
xpGainedLabel = xpGainedLabel,
|
xpGainedLabel = xpGainedLabel,
|
||||||
xpLeftLabel = xpLeftLabel,
|
xpLeftLabel = xpLeftLabel,
|
||||||
xpPerHourLabel = xpPerHourLabel,
|
xpPerHourLabel = xpPerHourLabel,
|
||||||
|
|
@ -314,18 +402,18 @@ object XPTrackerView {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wrappedWidget(component: Component, padding: Int = 7): Panel {
|
fun wrappedWidget(component: Component, padding: Int = 7): Container {
|
||||||
val outerPanelSize = Dimension(
|
val outerPanelSize = Dimension(
|
||||||
component.preferredSize.width + 2 * padding,
|
component.preferredSize.width + 2 * padding,
|
||||||
component.preferredSize.height + 2 * padding
|
component.preferredSize.height + 2 * padding
|
||||||
)
|
)
|
||||||
val outerPanel = Panel(GridBagLayout()).apply {
|
val outerPanel = JPanel(GridBagLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = outerPanelSize
|
preferredSize = outerPanelSize
|
||||||
maximumSize = outerPanelSize
|
maximumSize = outerPanelSize
|
||||||
minimumSize = outerPanelSize
|
minimumSize = outerPanelSize
|
||||||
}
|
}
|
||||||
val innerPanel = Panel(BorderLayout()).apply {
|
val innerPanel = JPanel(BorderLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = component.preferredSize
|
preferredSize = component.preferredSize
|
||||||
maximumSize = component.preferredSize
|
maximumSize = component.preferredSize
|
||||||
|
|
@ -343,7 +431,7 @@ object XPTrackerView {
|
||||||
|
|
||||||
|
|
||||||
data class XPWidget(
|
data class XPWidget(
|
||||||
val panel: Panel,
|
val container: Container,
|
||||||
val skillId: Int,
|
val skillId: Int,
|
||||||
val xpGainedLabel: JLabel,
|
val xpGainedLabel: JLabel,
|
||||||
val xpLeftLabel: JLabel,
|
val xpLeftLabel: JLabel,
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,32 @@ import KondoKit.Constants.COMBAT_LVL_SPRITE
|
||||||
import KondoKit.Helpers.formatHtmlLabelText
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.formatNumber
|
import KondoKit.Helpers.formatNumber
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
|
import KondoKit.Helpers.showAlert
|
||||||
import KondoKit.HiscoresView.createHiscoreSearchView
|
import KondoKit.HiscoresView.createHiscoreSearchView
|
||||||
|
import KondoKit.HiscoresView.hiScoreView
|
||||||
import KondoKit.LootTrackerView.BAG_ICON
|
import KondoKit.LootTrackerView.BAG_ICON
|
||||||
import KondoKit.LootTrackerView.createLootTrackerView
|
import KondoKit.LootTrackerView.createLootTrackerView
|
||||||
|
import KondoKit.LootTrackerView.lootTrackerView
|
||||||
import KondoKit.LootTrackerView.npcDeathSnapshots
|
import KondoKit.LootTrackerView.npcDeathSnapshots
|
||||||
import KondoKit.LootTrackerView.onPostClientTick
|
import KondoKit.LootTrackerView.onPostClientTick
|
||||||
import KondoKit.LootTrackerView.takeGroundSnapshot
|
import KondoKit.LootTrackerView.takeGroundSnapshot
|
||||||
import KondoKit.ReflectiveEditorView.addPlugins
|
import KondoKit.ReflectiveEditorView.addPlugins
|
||||||
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
||||||
|
import KondoKit.ReflectiveEditorView.reflectiveEditorView
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.XPTrackerView.createTotalXPWidget
|
import KondoKit.Themes.Theme
|
||||||
|
import KondoKit.Themes.ThemeType
|
||||||
|
import KondoKit.Themes.getTheme
|
||||||
import KondoKit.XPTrackerView.createXPTrackerView
|
import KondoKit.XPTrackerView.createXPTrackerView
|
||||||
import KondoKit.XPTrackerView.createXPWidget
|
import KondoKit.XPTrackerView.createXPWidget
|
||||||
|
import KondoKit.XPTrackerView.initialXP
|
||||||
|
import KondoKit.XPTrackerView.resetXPTracker
|
||||||
|
import KondoKit.XPTrackerView.totalXPWidget
|
||||||
import KondoKit.XPTrackerView.updateWidget
|
import KondoKit.XPTrackerView.updateWidget
|
||||||
import KondoKit.XPTrackerView.wrappedWidget
|
import KondoKit.XPTrackerView.wrappedWidget
|
||||||
import KondoKit.plugin.StateManager.initialXP
|
import KondoKit.XPTrackerView.xpTrackerView
|
||||||
import KondoKit.plugin.StateManager.totalXPWidget
|
import KondoKit.XPTrackerView.xpWidgets
|
||||||
import KondoKit.plugin.StateManager.xpWidgets
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.api.*
|
import plugin.api.*
|
||||||
import plugin.api.API.*
|
import plugin.api.API.*
|
||||||
|
|
@ -39,36 +48,76 @@ import java.awt.event.MouseEvent
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FIELD)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class Exposed(val description: String = "")
|
||||||
|
|
||||||
class plugin : Plugin() {
|
class plugin : Plugin() {
|
||||||
companion object {
|
companion object {
|
||||||
val WIDGET_SIZE = Dimension(270, 55)
|
val WIDGET_SIZE = Dimension(220, 50)
|
||||||
val TOTAL_XP_WIDGET_SIZE = Dimension(270, 30)
|
val TOTAL_XP_WIDGET_SIZE = Dimension(220, 30)
|
||||||
val IMAGE_SIZE = Dimension(20, 20)
|
val IMAGE_SIZE = Dimension(25, 23)
|
||||||
val WIDGET_COLOR = Color(27, 27, 27)
|
|
||||||
val VIEW_BACKGROUND_COLOR = Color(37, 37, 37)
|
// Default Theme Colors
|
||||||
val primaryColor = Color(129, 129, 129) // Color for "XP Gained:"
|
var WIDGET_COLOR = Color(30, 30, 30)
|
||||||
val secondaryColor = Color(226, 226, 226) // Color for "0"
|
var TITLE_BAR_COLOR = Color(21, 21, 21)
|
||||||
var kondoExposed_useLiveGEPrices = true
|
var VIEW_BACKGROUND_COLOR = Color(40, 40, 40)
|
||||||
var kondoExposed_playerXPMultiplier = 5
|
var primaryColor = Color(165, 165, 165) // Color for "XP Gained:"
|
||||||
const val FIXED_WIDTH = 782
|
var secondaryColor = Color(255, 255, 255) // Color for "0"
|
||||||
const val SCROLLPANE_WIDTH = 340
|
var POPUP_BACKGROUND = Color(45, 45, 45)
|
||||||
|
var POPUP_FOREGROUND = Color(220, 220, 220)
|
||||||
|
var TOOLTIP_BACKGROUND = Color(50,50,50)
|
||||||
|
var SCROLL_BAR_COLOR = Color(64, 64, 64)
|
||||||
|
var PROGRESS_BAR_FILL = Color(61, 56, 49)
|
||||||
|
var NAV_TINT: Color? = null
|
||||||
|
var NAV_GREYSCALE = false
|
||||||
|
var BOOST = 1f
|
||||||
|
|
||||||
|
var appliedTheme = ThemeType.RUNELITE
|
||||||
|
|
||||||
|
@Exposed("Theme colors for KondoKit, requires a relaunch to apply.")
|
||||||
|
var theme = ThemeType.RUNELITE
|
||||||
|
|
||||||
|
@Exposed("Default: true, Use Local JSON or the prices from the Live/Stable server API")
|
||||||
|
var useLiveGEPrices = true
|
||||||
|
|
||||||
|
@Exposed("Used to calculate Combat Actions until next level.")
|
||||||
|
var playerXPMultiplier = 5
|
||||||
|
|
||||||
|
@Exposed("Start minimized/collapsed by default")
|
||||||
|
var launchMinimized = false
|
||||||
|
|
||||||
|
@Exposed("Default 16 on Windows, 0 Linux/macOS. If Kondo is not " +
|
||||||
|
"perfectly snapped to the edge of the game due to window chrome you can update this to fix it")
|
||||||
|
var uiOffset = 0
|
||||||
|
|
||||||
|
private const val FIXED_WIDTH = 765
|
||||||
|
private const val NAVBAR_WIDTH = 30
|
||||||
|
private const val MAIN_CONTENT_WIDTH = 242
|
||||||
private const val WRENCH_ICON = 907
|
private const val WRENCH_ICON = 907
|
||||||
private const val LVL_ICON = 898
|
|
||||||
private const val LOOT_ICON = 777
|
private const val LOOT_ICON = 777
|
||||||
private const val MAG_SPRITE = 1423
|
private const val MAG_SPRITE = 1423
|
||||||
|
const val LVL_ICON = 898
|
||||||
private lateinit var cardLayout: CardLayout
|
private lateinit var cardLayout: CardLayout
|
||||||
private lateinit var mainContentPanel: Panel
|
private lateinit var mainContentPanel: JPanel
|
||||||
private var scrollPane: JScrollPane? = null
|
private var rightPanelWrapper: JScrollPane? = null
|
||||||
private var hiScoreView: JPanel? = null
|
|
||||||
private var reflectiveEditorView: JPanel? = null
|
|
||||||
private var lootTrackerView: JPanel? = null
|
|
||||||
private var xpTrackerView: JPanel? = null
|
|
||||||
private var accumulatedTime = 0L
|
private var accumulatedTime = 0L
|
||||||
|
private var reloadInterfaces = false
|
||||||
private const val tickInterval = 600L
|
private const val tickInterval = 600L
|
||||||
private var pluginsReloaded = false
|
private var pluginsReloaded = false
|
||||||
private var loginScreen = 160;
|
private var loginScreen = 160
|
||||||
private var lastLogin = ""
|
private var lastLogin = ""
|
||||||
private var initialized = false;
|
private var initialized = false
|
||||||
|
private var lastClickTime = 0L
|
||||||
|
private var lastUIOffset = 0
|
||||||
|
private const val HIDDEN_VIEW = "HIDDEN"
|
||||||
|
private val drawActions = mutableListOf<() -> Unit>()
|
||||||
|
|
||||||
|
fun registerDrawAction(action: () -> Unit) {
|
||||||
|
synchronized(drawActions) {
|
||||||
|
drawActions.add(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allSpritesLoaded() : Boolean {
|
fun allSpritesLoaded() : Boolean {
|
||||||
|
|
@ -76,62 +125,77 @@ class plugin : Plugin() {
|
||||||
try{
|
try{
|
||||||
for (i in 0 until 24) {
|
for (i in 0 until 24) {
|
||||||
if(!js5Archive8.isFileReady(getSpriteId(i))){
|
if(!js5Archive8.isFileReady(getSpriteId(i))){
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val otherIcons = arrayOf(LVL_ICON, MAG_SPRITE, LOOT_ICON, WRENCH_ICON, COMBAT_LVL_SPRITE, BAG_ICON);
|
val otherIcons = arrayOf(LVL_ICON, MAG_SPRITE, LOOT_ICON, WRENCH_ICON, COMBAT_LVL_SPRITE, BAG_ICON)
|
||||||
for (icon in otherIcons) {
|
for (icon in otherIcons) {
|
||||||
if(!js5Archive8.isFileReady(icon)){
|
if(!js5Archive8.isFileReady(icon)){
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e : Exception){
|
} catch (e : Exception){
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun OnLogin() {
|
override fun OnLogin() {
|
||||||
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
|
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
|
||||||
// if we logged in with a new character
|
// if we logged in with a new character
|
||||||
// we need to reset the trackers
|
// we need to reset the trackers
|
||||||
xpTrackerView?.removeAll()
|
xpTrackerView?.let { resetXPTracker(it) }
|
||||||
totalXPWidget = createTotalXPWidget()
|
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
|
||||||
xpTrackerView?.add(wrappedWidget(totalXPWidget!!.panel))
|
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
|
||||||
initialXP.clear()
|
|
||||||
xpWidgets.clear()
|
|
||||||
|
|
||||||
xpTrackerView?.revalidate()
|
|
||||||
if (StateManager.focusedView == "XP_TRACKER_VIEW")
|
|
||||||
xpTrackerView?.repaint()
|
|
||||||
}
|
}
|
||||||
lastLogin = Player.usernameInput.toString()
|
lastLogin = Player.usernameInput.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun Init() {
|
||||||
|
// Disable Font AA
|
||||||
|
System.setProperty("sun.java2d.opengl", "false")
|
||||||
|
System.setProperty("awt.useSystemAAFontSettings", "off")
|
||||||
|
System.setProperty("swing.aatext", "false")
|
||||||
|
}
|
||||||
|
|
||||||
private fun UpdateDisplaySettings() {
|
private fun UpdateDisplaySettings() {
|
||||||
val mode = GetWindowMode()
|
val mode = GetWindowMode()
|
||||||
|
val currentScrollPaneWidth = if (mainContentPanel.isVisible) NAVBAR_WIDTH + MAIN_CONTENT_WIDTH else NAVBAR_WIDTH
|
||||||
|
lastUIOffset = uiOffset
|
||||||
when (mode) {
|
when (mode) {
|
||||||
WindowMode.FIXED -> {
|
WindowMode.FIXED -> {
|
||||||
if (frame.width < FIXED_WIDTH + SCROLLPANE_WIDTH) {
|
if (frame.width < FIXED_WIDTH + currentScrollPaneWidth + uiOffset) {
|
||||||
frame.setSize(FIXED_WIDTH + SCROLLPANE_WIDTH, frame.height)
|
frame.setSize(FIXED_WIDTH + currentScrollPaneWidth + uiOffset, frame.height)
|
||||||
}
|
}
|
||||||
val difference = frame.width - (FIXED_WIDTH + SCROLLPANE_WIDTH)
|
|
||||||
|
val difference = frame.width - (FIXED_WIDTH + uiOffset + currentScrollPaneWidth)
|
||||||
GameShell.leftMargin = difference / 2
|
GameShell.leftMargin = difference / 2
|
||||||
}
|
}
|
||||||
WindowMode.RESIZABLE -> {
|
WindowMode.RESIZABLE -> {
|
||||||
GameShell.canvasWidth -= SCROLLPANE_WIDTH
|
GameShell.canvasWidth = frame.width - (currentScrollPaneWidth + uiOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scrollPane?.revalidate()
|
rightPanelWrapper?.preferredSize = Dimension(currentScrollPaneWidth, frame.height)
|
||||||
scrollPane?.repaint()
|
rightPanelWrapper?.revalidate()
|
||||||
|
rightPanelWrapper?.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun OnKondoValueUpdated(){
|
fun OnKondoValueUpdated(){
|
||||||
StoreData("kondoUseRemoteGE", kondoExposed_useLiveGEPrices)
|
StoreData("kondoUseRemoteGE", useLiveGEPrices)
|
||||||
StoreData("kondoPlayerXPMultiplier", kondoExposed_playerXPMultiplier)
|
StoreData("kondoTheme", theme.toString())
|
||||||
|
if(appliedTheme != theme) {
|
||||||
|
showAlert(
|
||||||
|
"KondoKit Theme changes require a relaunch.",
|
||||||
|
"KondoKit",
|
||||||
|
JOptionPane.INFORMATION_MESSAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
StoreData("kondoPlayerXPMultiplier", playerXPMultiplier)
|
||||||
LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices()
|
LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices()
|
||||||
|
StoreData("kondoLaunchMinimized", launchMinimized)
|
||||||
|
StoreData("kondoUIOffset", uiOffset)
|
||||||
|
if(lastUIOffset != uiOffset){
|
||||||
|
UpdateDisplaySettings()
|
||||||
|
reloadInterfaces = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||||
|
|
@ -139,12 +203,16 @@ class plugin : Plugin() {
|
||||||
for ((index, entry) in currentEntries.withIndex()) {
|
for ((index, entry) in currentEntries.withIndex()) {
|
||||||
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
|
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
|
||||||
val input = entry.subject
|
val input = entry.subject
|
||||||
val username = input
|
// Trim spaces, clean up tags, and remove the level info
|
||||||
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "")
|
val cleanedInput = input
|
||||||
.replace(Regex("<img=\\d+>"), "")
|
.trim() // Remove any leading/trailing spaces
|
||||||
.split(" ") // Split by spaces
|
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "") // Remove color tags
|
||||||
.first() // Take the first part, which is the username
|
.replace(Regex("<img=\\d+>"), "") // Remove image tags
|
||||||
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(username.replace(" ","_")))
|
.replace(Regex("\\(level: \\d+\\)"), "") // Remove level text e.g. (level: 44)
|
||||||
|
.trim() // Trim again to remove extra spaces after removing level text
|
||||||
|
|
||||||
|
// Proceed with the full cleaned username
|
||||||
|
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(cleanedInput))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,40 +220,31 @@ class plugin : Plugin() {
|
||||||
|
|
||||||
private fun searchHiscore(username: String): Runnable {
|
private fun searchHiscore(username: String): Runnable {
|
||||||
return Runnable {
|
return Runnable {
|
||||||
cardLayout.show(mainContentPanel, "HISCORE_SEARCH_VIEW")
|
setActiveView(HiscoresView.VIEW_NAME)
|
||||||
StateManager.focusedView = "HISCORE_SEARCH_VIEW"
|
|
||||||
val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) }
|
val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) }
|
||||||
|
|
||||||
customSearchField?.searchPlayer(username) ?: run {
|
customSearchField?.searchPlayer(username) ?: run {
|
||||||
println("searchView is null or CustomSearchField creation failed.")
|
println("searchView is null or CustomSearchField creation failed.")
|
||||||
}
|
}
|
||||||
hiScoreView?.repaint()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun OnPluginsReloaded(): Boolean {
|
override fun OnPluginsReloaded(): Boolean {
|
||||||
if (!initialized) return true
|
if (!initialized) return true
|
||||||
|
|
||||||
UpdateDisplaySettings()
|
UpdateDisplaySettings()
|
||||||
|
|
||||||
frame.remove(scrollPane)
|
frame.remove(rightPanelWrapper)
|
||||||
frame.layout = BorderLayout()
|
frame.layout = BorderLayout()
|
||||||
frame.add(scrollPane, BorderLayout.EAST)
|
frame.add(rightPanelWrapper, BorderLayout.EAST)
|
||||||
|
|
||||||
// Clear or regenerate the reflectiveEditorView
|
|
||||||
reflectiveEditorView?.removeAll()
|
|
||||||
reflectiveEditorView?.revalidate()
|
|
||||||
if(StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW")
|
|
||||||
reflectiveEditorView?.repaint()
|
|
||||||
|
|
||||||
frame.revalidate()
|
frame.revalidate()
|
||||||
frame.repaint()
|
frame.repaint()
|
||||||
pluginsReloaded = true
|
pluginsReloaded = true
|
||||||
|
reloadInterfaces = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
||||||
if (!initialXP.containsKey(skillId)) {
|
if (!initialXP.containsKey(skillId)) {
|
||||||
initialXP[skillId] = xp
|
initialXP[skillId] = xp
|
||||||
|
|
@ -203,11 +262,11 @@ class plugin : Plugin() {
|
||||||
xpWidget = createXPWidget(skillId, previousXp)
|
xpWidget = createXPWidget(skillId, previousXp)
|
||||||
xpWidgets[skillId] = xpWidget
|
xpWidgets[skillId] = xpWidget
|
||||||
|
|
||||||
xpTrackerView?.add(wrappedWidget(xpWidget.panel))
|
xpTrackerView?.add(wrappedWidget(xpWidget.container))
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||||
|
|
||||||
xpTrackerView?.revalidate()
|
xpTrackerView?.revalidate()
|
||||||
if(StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(focusedView == XPTrackerView.VIEW_NAME)
|
||||||
xpTrackerView?.repaint()
|
xpTrackerView?.repaint()
|
||||||
|
|
||||||
updateWidget(xpWidget, xp)
|
updateWidget(xpWidget, xp)
|
||||||
|
|
@ -221,23 +280,36 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginsReloaded) {
|
if (pluginsReloaded) {
|
||||||
InterfaceList.method3712(true) // Gets the resize working correctly
|
|
||||||
reflectiveEditorView?.let { addPlugins(it) }
|
reflectiveEditorView?.let { addPlugins(it) }
|
||||||
pluginsReloaded = false
|
pluginsReloaded = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reloadInterfaces){
|
||||||
|
InterfaceList.method3712(true) // Gets the resize working correctly
|
||||||
|
reloadInterfaces = false
|
||||||
|
}
|
||||||
|
|
||||||
accumulatedTime += timeDelta
|
accumulatedTime += timeDelta
|
||||||
if (accumulatedTime >= tickInterval) {
|
if (accumulatedTime >= tickInterval) {
|
||||||
lootTrackerView?.let { onPostClientTick(it) }
|
lootTrackerView?.let { onPostClientTick(it) }
|
||||||
accumulatedTime = 0L
|
accumulatedTime = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw synced actions (that require to be done between glBegin and glEnd)
|
||||||
|
if (drawActions.isNotEmpty()) {
|
||||||
|
synchronized(drawActions) {
|
||||||
|
val actionsCopy = drawActions.toList()
|
||||||
|
drawActions.clear()
|
||||||
|
for (action in actionsCopy) {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
||||||
if(!initialized && mainLoadState >= loginScreen) {
|
if(!initialized && mainLoadState >= loginScreen) {
|
||||||
initKondoUI()
|
initKondoUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initKondoUI(){
|
private fun initKondoUI(){
|
||||||
|
|
@ -245,39 +317,115 @@ class plugin : Plugin() {
|
||||||
if(!allSpritesLoaded()) return;
|
if(!allSpritesLoaded()) return;
|
||||||
val frame: Frame? = GameShell.frame
|
val frame: Frame? = GameShell.frame
|
||||||
if (frame != null) {
|
if (frame != null) {
|
||||||
kondoExposed_useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true
|
|
||||||
kondoExposed_playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5
|
|
||||||
cardLayout = CardLayout()
|
|
||||||
mainContentPanel = Panel(cardLayout)
|
|
||||||
mainContentPanel.background = VIEW_BACKGROUND_COLOR
|
|
||||||
|
|
||||||
xpTrackerView = createXPTrackerView()
|
loadFont()
|
||||||
hiScoreView = createHiscoreSearchView()
|
val themeIndex = (GetData("kondoTheme") as? String) ?: "RUNELITE"
|
||||||
lootTrackerView = createLootTrackerView()
|
theme = ThemeType.valueOf(themeIndex)
|
||||||
reflectiveEditorView = createReflectiveEditorView()
|
applyTheme(getTheme(theme))
|
||||||
mainContentPanel.add(xpTrackerView, "XP_TRACKER_VIEW")
|
appliedTheme = theme
|
||||||
mainContentPanel.add(hiScoreView, "HISCORE_SEARCH_VIEW")
|
|
||||||
mainContentPanel.add(lootTrackerView, "LOOT_TRACKER_VIEW")
|
try {
|
||||||
mainContentPanel.add(reflectiveEditorView, "REFLECTIVE_EDITOR_VIEW")
|
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel")
|
||||||
|
|
||||||
|
// Modify the UI properties to match theme
|
||||||
|
UIManager.put("control", VIEW_BACKGROUND_COLOR)
|
||||||
|
UIManager.put("info", VIEW_BACKGROUND_COLOR)
|
||||||
|
UIManager.put("nimbusBase", WIDGET_COLOR)
|
||||||
|
UIManager.put("nimbusBlueGrey", TITLE_BAR_COLOR)
|
||||||
|
|
||||||
|
UIManager.put("nimbusDisabledText", primaryColor)
|
||||||
|
UIManager.put("nimbusSelectedText", secondaryColor)
|
||||||
|
UIManager.put("text", secondaryColor)
|
||||||
|
|
||||||
|
UIManager.put("nimbusFocus", TITLE_BAR_COLOR)
|
||||||
|
UIManager.put("nimbusInfoBlue", POPUP_BACKGROUND)
|
||||||
|
UIManager.put("nimbusLightBackground", WIDGET_COLOR)
|
||||||
|
UIManager.put("nimbusSelectionBackground", PROGRESS_BAR_FILL)
|
||||||
|
|
||||||
|
UIManager.put("Button.background", WIDGET_COLOR)
|
||||||
|
UIManager.put("Button.foreground", secondaryColor)
|
||||||
|
|
||||||
|
UIManager.put("CheckBox.background", VIEW_BACKGROUND_COLOR)
|
||||||
|
UIManager.put("CheckBox.foreground", secondaryColor)
|
||||||
|
UIManager.put("CheckBox.icon", UIManager.getIcon("CheckBox.icon"))
|
||||||
|
|
||||||
|
UIManager.put("ComboBox.background", WIDGET_COLOR)
|
||||||
|
UIManager.put("ComboBox.foreground", secondaryColor)
|
||||||
|
UIManager.put("ComboBox.selectionBackground", PROGRESS_BAR_FILL)
|
||||||
|
UIManager.put("ComboBox.selectionForeground", primaryColor)
|
||||||
|
UIManager.put("ComboBox.buttonBackground", WIDGET_COLOR)
|
||||||
|
|
||||||
|
UIManager.put("Spinner.background", WIDGET_COLOR)
|
||||||
|
UIManager.put("Spinner.foreground", secondaryColor)
|
||||||
|
UIManager.put("Spinner.border", BorderFactory.createLineBorder(TITLE_BAR_COLOR))
|
||||||
|
|
||||||
|
UIManager.put("TextField.background", WIDGET_COLOR)
|
||||||
|
UIManager.put("TextField.foreground", secondaryColor)
|
||||||
|
UIManager.put("TextField.caretForeground", secondaryColor)
|
||||||
|
UIManager.put("TextField.border", BorderFactory.createLineBorder(TITLE_BAR_COLOR))
|
||||||
|
|
||||||
|
UIManager.put("ScrollBar.thumb", WIDGET_COLOR)
|
||||||
|
UIManager.put("ScrollBar.track", VIEW_BACKGROUND_COLOR)
|
||||||
|
UIManager.put("ScrollBar.thumbHighlight", TITLE_BAR_COLOR)
|
||||||
|
|
||||||
|
UIManager.put("ProgressBar.foreground", PROGRESS_BAR_FILL)
|
||||||
|
UIManager.put("ProgressBar.background", WIDGET_COLOR)
|
||||||
|
UIManager.put("ProgressBar.border", BorderFactory.createLineBorder(TITLE_BAR_COLOR))
|
||||||
|
|
||||||
|
UIManager.put("ToolTip.background", VIEW_BACKGROUND_COLOR)
|
||||||
|
UIManager.put("ToolTip.foreground", secondaryColor)
|
||||||
|
UIManager.put("ToolTip.border", BorderFactory.createLineBorder(TITLE_BAR_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
|
||||||
|
val osName = System.getProperty("os.name").toLowerCase()
|
||||||
|
uiOffset = (GetData("kondoUIOffset") as? Int) ?: if (osName.contains("win")) 16 else 0
|
||||||
|
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!!), XPTrackerView.VIEW_NAME)
|
||||||
|
mainContentPanel.add(ScrollablePanel(hiScoreView!!), HiscoresView.VIEW_NAME)
|
||||||
|
mainContentPanel.add(ScrollablePanel(lootTrackerView!!), LootTrackerView.VIEW_NAME)
|
||||||
|
mainContentPanel.add(ScrollablePanel(reflectiveEditorView!!), ReflectiveEditorView.VIEW_NAME)
|
||||||
|
|
||||||
val navPanel = Panel().apply {
|
val navPanel = Panel().apply {
|
||||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = Dimension(42, frame.height)
|
preferredSize = Dimension(NAVBAR_WIDTH, frame.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
navPanel.add(createNavButton(LVL_ICON, "XP_TRACKER_VIEW"))
|
navPanel.add(createNavButton(LVL_ICON, XPTrackerView.VIEW_NAME))
|
||||||
navPanel.add(createNavButton(MAG_SPRITE, "HISCORE_SEARCH_VIEW"))
|
navPanel.add(createNavButton(MAG_SPRITE, HiscoresView.VIEW_NAME))
|
||||||
navPanel.add(createNavButton(LOOT_ICON, "LOOT_TRACKER_VIEW"))
|
navPanel.add(createNavButton(LOOT_ICON, LootTrackerView.VIEW_NAME))
|
||||||
navPanel.add(createNavButton(WRENCH_ICON, "REFLECTIVE_EDITOR_VIEW"))
|
navPanel.add(createNavButton(WRENCH_ICON, ReflectiveEditorView.VIEW_NAME))
|
||||||
|
|
||||||
val rightPanel = Panel(BorderLayout()).apply {
|
var rightPanel = Panel(BorderLayout()).apply {
|
||||||
add(mainContentPanel, BorderLayout.CENTER)
|
add(mainContentPanel, BorderLayout.CENTER)
|
||||||
add(navPanel, BorderLayout.EAST)
|
add(navPanel, BorderLayout.EAST)
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollPane = JScrollPane(rightPanel).apply {
|
rightPanelWrapper = JScrollPane(rightPanel).apply {
|
||||||
preferredSize = Dimension(SCROLLPANE_WIDTH, frame.height)
|
preferredSize = Dimension(NAVBAR_WIDTH + MAIN_CONTENT_WIDTH, frame.height)
|
||||||
background = VIEW_BACKGROUND_COLOR
|
background = VIEW_BACKGROUND_COLOR
|
||||||
border = BorderFactory.createEmptyBorder() // Removes the border completely
|
border = BorderFactory.createEmptyBorder() // Removes the border completely
|
||||||
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||||
|
|
@ -285,91 +433,189 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.layout = BorderLayout()
|
frame.layout = BorderLayout()
|
||||||
scrollPane?.let { frame.add(it, BorderLayout.EAST) }
|
rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) }
|
||||||
|
|
||||||
frame.revalidate()
|
if(!launchMinimized){
|
||||||
frame.repaint()
|
setActiveView(XPTrackerView.VIEW_NAME)
|
||||||
|
} else {
|
||||||
StateManager.focusedView = "XP_TRACKER_VIEW"
|
setActiveView(HIDDEN_VIEW)
|
||||||
|
}
|
||||||
initialized = true
|
initialized = true
|
||||||
pluginsReloaded = true
|
pluginsReloaded = true
|
||||||
UpdateDisplaySettings()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Update() {
|
override fun Update() {
|
||||||
xpWidgets.values.forEach { xpWidget ->
|
|
||||||
|
val widgets = xpWidgets.values
|
||||||
|
val totalXP = totalXPWidget
|
||||||
|
|
||||||
|
widgets.forEach { xpWidget ->
|
||||||
val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0
|
val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||||
val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||||
val formattedXpPerHour = formatNumber(xpPerHour)
|
val formattedXpPerHour = formatNumber(xpPerHour)
|
||||||
xpWidget.xpPerHourLabel.text =
|
xpWidget.xpPerHourLabel.text =
|
||||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
||||||
xpWidget.panel.repaint()
|
xpWidget.container.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
totalXPWidget?.let { totalXPWidget ->
|
totalXP?.let { totalXPWidget ->
|
||||||
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||||
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||||
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
||||||
totalXPWidget.xpPerHourLabel.text =
|
totalXPWidget.xpPerHourLabel.text =
|
||||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
||||||
totalXPWidget.panel.repaint()
|
totalXPWidget.container.repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
||||||
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
||||||
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setActiveView(viewName: String) {
|
||||||
private fun createNavButton(spriteId: Int, viewName: String): JButton {
|
// Handle the visibility of the main content panel
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId))
|
if (viewName == HIDDEN_VIEW) {
|
||||||
val buttonSize = Dimension(42, 42)
|
mainContentPanel.isVisible = false
|
||||||
val imageSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
} else {
|
||||||
|
if (!mainContentPanel.isVisible) {
|
||||||
val actionListener = ActionListener {
|
mainContentPanel.isVisible = true
|
||||||
|
}
|
||||||
cardLayout.show(mainContentPanel, viewName)
|
cardLayout.show(mainContentPanel, viewName)
|
||||||
StateManager.focusedView = viewName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadInterfaces = true
|
||||||
|
UpdateDisplaySettings()
|
||||||
|
|
||||||
|
// Revalidate and repaint necessary panels
|
||||||
|
mainContentPanel.revalidate()
|
||||||
|
mainContentPanel.repaint()
|
||||||
|
rightPanelWrapper?.revalidate()
|
||||||
|
rightPanelWrapper?.repaint()
|
||||||
|
frame?.revalidate()
|
||||||
|
frame?.repaint()
|
||||||
|
|
||||||
|
focusedView = viewName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNavButton(spriteId: Int, viewName: String): JPanel {
|
||||||
|
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId), NAV_TINT, NAV_GREYSCALE, BOOST)
|
||||||
|
val buttonSize = Dimension(NAVBAR_WIDTH, 32)
|
||||||
|
val imageSize = Dimension((bufferedImageSprite.width / 1.2f).toInt(), (bufferedImageSprite.height / 1.2f).toInt())
|
||||||
|
val cooldownDuration = 100L
|
||||||
|
|
||||||
|
val actionListener = ActionListener {
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
if (currentTime - lastClickTime < cooldownDuration) {
|
||||||
|
return@ActionListener
|
||||||
|
}
|
||||||
|
lastClickTime = currentTime
|
||||||
|
|
||||||
|
if (focusedView == viewName) {
|
||||||
|
setActiveView("HIDDEN")
|
||||||
|
} else {
|
||||||
|
setActiveView(viewName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageCanvas with forced size
|
||||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = imageSize
|
preferredSize = imageSize
|
||||||
maximumSize = imageSize
|
maximumSize = imageSize
|
||||||
minimumSize = imageSize
|
minimumSize = imageSize
|
||||||
addMouseListener(object : MouseAdapter() {
|
|
||||||
override fun mouseClicked(e: MouseEvent?) {
|
|
||||||
actionListener.actionPerformed(null)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val button = JButton().apply {
|
// Wrapping the ImageCanvas in another JPanel to prevent stretching
|
||||||
|
val imageCanvasWrapper = JPanel().apply {
|
||||||
|
layout = GridBagLayout() // Keeps the layout of the wrapped panel minimal
|
||||||
|
preferredSize = imageSize
|
||||||
|
maximumSize = imageSize
|
||||||
|
minimumSize = imageSize
|
||||||
|
isOpaque = false // No background for the wrapper
|
||||||
|
add(imageCanvas) // Adding ImageCanvas directly, layout won't stretch it
|
||||||
|
}
|
||||||
|
|
||||||
|
val panelButton = JPanel().apply {
|
||||||
layout = GridBagLayout()
|
layout = GridBagLayout()
|
||||||
preferredSize = buttonSize
|
preferredSize = buttonSize
|
||||||
maximumSize = buttonSize
|
maximumSize = buttonSize
|
||||||
minimumSize = buttonSize
|
minimumSize = buttonSize
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
isFocusPainted = false
|
isOpaque = true // Ensure background is painted
|
||||||
isBorderPainted = false
|
|
||||||
|
|
||||||
val gbc = GridBagConstraints().apply {
|
val gbc = GridBagConstraints().apply {
|
||||||
anchor = GridBagConstraints.CENTER
|
anchor = GridBagConstraints.CENTER
|
||||||
|
fill = GridBagConstraints.NONE // Prevents stretching
|
||||||
}
|
}
|
||||||
|
|
||||||
add(imageCanvas, gbc)
|
add(imageCanvasWrapper, gbc)
|
||||||
addActionListener(actionListener)
|
|
||||||
|
// Hover and click behavior
|
||||||
|
val hoverListener = object : MouseAdapter() {
|
||||||
|
override fun mouseEntered(e: MouseEvent?) {
|
||||||
|
background = WIDGET_COLOR.darker()
|
||||||
|
imageCanvas.fillColor = WIDGET_COLOR.darker()
|
||||||
|
imageCanvas.repaint()
|
||||||
|
repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
return button
|
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 panelButton
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun loadFont(): Font? {
|
||||||
|
val fontStream = plugin::class.java.getResourceAsStream("res/runescape_small.ttf")
|
||||||
|
return if (fontStream != null) {
|
||||||
|
try {
|
||||||
|
val font = Font.createFont(Font.TRUETYPE_FONT, fontStream)
|
||||||
|
val ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||||
|
ge.registerFont(font) // Register the font in the graphics environment
|
||||||
|
font
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println("Font not found!")
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object StateManager {
|
object StateManager {
|
||||||
val initialXP: MutableMap<Int, Int> = HashMap()
|
|
||||||
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
|
||||||
var totalXPWidget: XPWidget? = null
|
|
||||||
var focusedView: String = ""
|
var focusedView: String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun applyTheme(theme: Theme) {
|
||||||
|
WIDGET_COLOR = theme.widgetColor
|
||||||
|
TITLE_BAR_COLOR = theme.titleBarColor
|
||||||
|
VIEW_BACKGROUND_COLOR = theme.viewBackgroundColor
|
||||||
|
primaryColor = theme.primaryColor
|
||||||
|
secondaryColor = theme.secondaryColor
|
||||||
|
POPUP_BACKGROUND = theme.popupBackground
|
||||||
|
POPUP_FOREGROUND = theme.popupForeground
|
||||||
|
TOOLTIP_BACKGROUND = theme.tooltipBackground
|
||||||
|
SCROLL_BAR_COLOR = theme.scrollBarColor
|
||||||
|
PROGRESS_BAR_FILL = theme.progressBarFill
|
||||||
|
NAV_TINT = theme.navTint
|
||||||
|
NAV_GREYSCALE = theme.navGreyScale
|
||||||
|
BOOST = theme.boost
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='downthecrop'
|
AUTHOR='downthecrop'
|
||||||
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.'
|
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.'
|
||||||
VERSION=1.1
|
VERSION=2.0
|
||||||
Binary file not shown.
|
|
@ -25,6 +25,7 @@ class plugin : Plugin() {
|
||||||
|
|
||||||
private var timeMode = TIME_MODE_INITIALIZATION
|
private var timeMode = TIME_MODE_INITIALIZATION
|
||||||
private var initTime: Long = 0
|
private var initTime: Long = 0
|
||||||
|
private var logoutFlag = true
|
||||||
private var displayMessageCounter = 0
|
private var displayMessageCounter = 0
|
||||||
|
|
||||||
private var component: Component? = null
|
private var component: Component? = null
|
||||||
|
|
@ -35,6 +36,20 @@ class plugin : Plugin() {
|
||||||
displayMessageCounter = 0
|
displayMessageCounter = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun OnPluginsReloaded(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun OnLogin() {
|
||||||
|
if(logoutFlag)
|
||||||
|
initTime = System.currentTimeMillis()
|
||||||
|
logoutFlag = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun OnLogout() {
|
||||||
|
logoutFlag = true
|
||||||
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) {
|
override fun Draw(timeDelta: Long) {
|
||||||
if (component == null)
|
if (component == null)
|
||||||
return
|
return
|
||||||
|
|
@ -134,6 +149,9 @@ class plugin : Plugin() {
|
||||||
API.InsertMiniMenuEntry("Disable Timer", "") {
|
API.InsertMiniMenuEntry("Disable Timer", "") {
|
||||||
timeMode = DEFAULT_TIME_MODE
|
timeMode = DEFAULT_TIME_MODE
|
||||||
}
|
}
|
||||||
|
API.InsertMiniMenuEntry("Reset Play Time", "") {
|
||||||
|
initTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='Woahscam, Ceikry'
|
AUTHOR='Woahscam, Ceikry'
|
||||||
DESCRIPTION='Displays the session time played, system time, or no time over the "Report Abuse" button.'
|
DESCRIPTION='Displays the session time played, system time, or no time over the "Report Abuse" button.'
|
||||||
VERSION=1.2
|
VERSION=1.1
|
||||||
|
|
@ -1,52 +1,56 @@
|
||||||
package ToggleResizableSD
|
package ToggleResizableSD
|
||||||
|
|
||||||
|
import KondoKit.Exposed
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.annotations.PluginMeta
|
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import rt4.*
|
import plugin.api.API.StoreData
|
||||||
|
import rt4.DisplayMode
|
||||||
|
import rt4.GameShell
|
||||||
|
import rt4.InterfaceList
|
||||||
|
import rt4.client
|
||||||
import java.awt.event.KeyAdapter
|
import java.awt.event.KeyAdapter
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
|
|
||||||
class plugin : Plugin() {
|
class plugin : Plugin() {
|
||||||
var toggleResize = false
|
|
||||||
var wantHd = false //Setting wantHd to true hides the black screen on logout (when resize SD is enabled), by enabling HD on logout
|
@Exposed("Use Resizable SD")
|
||||||
|
var useResizable = false
|
||||||
|
|
||||||
|
@Exposed("Setting wantHd to true hides the black screen on logout (when resize SD is enabled), by enabling HD on logout ")
|
||||||
|
var wantHd = false
|
||||||
|
|
||||||
override fun Init() {
|
override fun Init() {
|
||||||
API.AddKeyboardListener(object : KeyAdapter() {
|
API.AddKeyboardListener(object : KeyAdapter() {
|
||||||
override fun keyPressed(e: KeyEvent) {
|
override fun keyPressed(e: KeyEvent) {
|
||||||
if (e.keyCode == KeyEvent.VK_F12) {
|
if (e.keyCode == KeyEvent.VK_F12) {
|
||||||
toggleResize = true
|
toggleResizableSd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!DisplayMode.resizableSD && API.GetData("use-resizable-sd") == true) {
|
|
||||||
toggleResize = true
|
|
||||||
}
|
|
||||||
var osNameLowerCase: String = ""
|
|
||||||
var osName: String
|
|
||||||
|
|
||||||
try {
|
useResizable = DisplayMode.resizableSD
|
||||||
osName = System.getProperty("os.name")
|
if (API.GetData("use-resizable-sd") == true) {
|
||||||
} catch (e: Exception) {
|
useResizable = true
|
||||||
osName = "Unknown"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
osNameLowerCase = osName.toLowerCase()
|
var osNameLowerCase: String = System.getProperty("os.name").toLowerCase()
|
||||||
if (!osNameLowerCase.startsWith("mac")) {
|
if (!osNameLowerCase.startsWith("mac")) {
|
||||||
wantHd = true
|
wantHd = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (API.GetData("want-hd") == false) {
|
if (API.GetData("want-hd") == false) {
|
||||||
wantHd = false
|
wantHd = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ProcessCommand(commandStr: String, args: Array<out String>?) {
|
override fun ProcessCommand(commandStr: String, args: Array<out String>?) {
|
||||||
when(commandStr.toLowerCase()) {
|
when (commandStr.toLowerCase()) {
|
||||||
"::toggleresizablesd", "::resizablesd", "::togglersd", "::rsd" -> {
|
"::toggleresizablesd", "::resizablesd", "::togglersd", "::rsd" -> {
|
||||||
toggleResize = true //We could call toggleResizableSd() directly here, but it's not necessary.
|
toggleResizableSd()
|
||||||
}
|
}
|
||||||
"::toggleresizablesdhd", "::resizablesdhd", "::togglersdhd", "::rsdhd", -> {
|
"::toggleresizablesdhd", "::resizablesdhd", "::togglersdhd", "::rsdhd" -> {
|
||||||
wantHd = !wantHd
|
wantHd = !wantHd
|
||||||
API.StoreData("want-hd", wantHd)
|
StoreData("want-hd", wantHd)
|
||||||
API.SendMessage("You have turned login screen HD " + (if (wantHd) "on" else "off"))
|
API.SendMessage("You have turned login screen HD " + (if (wantHd) "on" else "off"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,25 +61,29 @@ class plugin : Plugin() {
|
||||||
if (InterfaceList.aClass13_26 == null || client.gameState != 30) {
|
if (InterfaceList.aClass13_26 == null || client.gameState != 30) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
toggleResize = false
|
|
||||||
DisplayMode.resizableSD = !DisplayMode.resizableSD;
|
DisplayMode.resizableSD = !DisplayMode.resizableSD
|
||||||
if(!DisplayMode.resizableSD){
|
useResizable = DisplayMode.resizableSD
|
||||||
//Revert to fixed
|
StoreData("use-resizable-sd", useResizable)
|
||||||
API.StoreData("use-resizable-sd", false) //Note: It is important to call StoreData before setWindowMode because setWindowMode causes all plugins to reload.
|
|
||||||
|
if (!DisplayMode.resizableSD) {
|
||||||
DisplayMode.setWindowMode(true, 0, -1, -1)
|
DisplayMode.setWindowMode(true, 0, -1, -1)
|
||||||
} else {
|
} else {
|
||||||
//Use resizable
|
|
||||||
API.StoreData("use-resizable-sd", true) //Note: It is important to call StoreData before setWindowMode because setWindowMode causes all plugins to reload.
|
|
||||||
DisplayMode.setWindowMode(true, 0, GameShell.frameWidth, GameShell.frameHeight)
|
DisplayMode.setWindowMode(true, 0, GameShell.frameWidth, GameShell.frameHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) {
|
override fun Draw(timeDelta: Long) {
|
||||||
if (toggleResize) {
|
if (useResizable != DisplayMode.resizableSD) {
|
||||||
toggleResizableSd()
|
toggleResizableSd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun OnKondoValueUpdated() {
|
||||||
|
StoreData("want-hd", wantHd)
|
||||||
|
StoreData("use-resizable-sd", useResizable)
|
||||||
|
}
|
||||||
|
|
||||||
override fun OnLogout() {
|
override fun OnLogout() {
|
||||||
if (DisplayMode.resizableSD && wantHd) {
|
if (DisplayMode.resizableSD && wantHd) {
|
||||||
//Because resizable SD always uses the "HD" size canvas/window mode (check the in-game Graphics Options with resizeable SD enabled if you don't believe me!), useHD becomes true when logging out, so logging out with resizeSD enabled means "HD" will always be enabled on the login screen after logging out, so we might as well fix the HD flyover by setting resizableSD to false first, and then calling setWindowMode to replace the canvas and set newMode to 2.
|
//Because resizable SD always uses the "HD" size canvas/window mode (check the in-game Graphics Options with resizeable SD enabled if you don't believe me!), useHD becomes true when logging out, so logging out with resizeSD enabled means "HD" will always be enabled on the login screen after logging out, so we might as well fix the HD flyover by setting resizableSD to false first, and then calling setWindowMode to replace the canvas and set newMode to 2.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='ipkpjersi'
|
AUTHOR='ipkpjersi'
|
||||||
DESCRIPTION='Allows you to use F12 to toggle resizable SD.'
|
DESCRIPTION='Allows you to use F12 to toggle resizable SD.'
|
||||||
VERSION=1.0
|
VERSION=1.1
|
||||||
|
|
@ -1,12 +1,34 @@
|
||||||
package XPDropPlugin;
|
package XPDropPlugin
|
||||||
|
|
||||||
|
import KondoKit.Exposed
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.annotations.PluginMeta
|
import plugin.api.API
|
||||||
import plugin.api.*
|
import plugin.api.API.*
|
||||||
|
import plugin.api.FontColor.fromColor
|
||||||
|
import plugin.api.FontType
|
||||||
|
import plugin.api.TextModifier
|
||||||
|
import plugin.api.WindowMode
|
||||||
|
import rt4.Sprite
|
||||||
|
import rt4.client
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.InputStream
|
||||||
|
import javax.imageio.ImageIO
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
|
||||||
class plugin : Plugin() {
|
class plugin : Plugin() {
|
||||||
|
|
||||||
|
enum class Theme {
|
||||||
|
DEFAULT, RUNELITE
|
||||||
|
}
|
||||||
|
|
||||||
|
@Exposed
|
||||||
|
private var theme = Theme.DEFAULT
|
||||||
|
|
||||||
|
@Exposed
|
||||||
|
private var alwaysShow = false
|
||||||
|
|
||||||
private val displayTimeout = 10000L // 10 seconds
|
private val displayTimeout = 10000L // 10 seconds
|
||||||
private val drawStart = 175
|
private val drawStart = 175
|
||||||
private val drawPadding = 25
|
private val drawPadding = 25
|
||||||
|
|
@ -15,41 +37,50 @@ class plugin : Plugin() {
|
||||||
private var totalXp = 0
|
private var totalXp = 0
|
||||||
private val activeGains = ArrayList<XPGain>()
|
private val activeGains = ArrayList<XPGain>()
|
||||||
private var lastGain = 0L
|
private var lastGain = 0L
|
||||||
|
private val IN_GAME = 30
|
||||||
|
|
||||||
|
private val spriteCache = HashMap<Int, Sprite?>()
|
||||||
|
|
||||||
|
override fun Init() {
|
||||||
|
val themeIndex = (GetData("xp-drop-theme") as? String) ?: "DEFAULT"
|
||||||
|
theme = Theme.valueOf(themeIndex)
|
||||||
|
alwaysShow = (GetData("xp-drop-alwaysShow") as? Boolean) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
override fun Draw(deltaTime: Long) {
|
override fun Draw(deltaTime: Long) {
|
||||||
if (System.currentTimeMillis() - lastGain >= displayTimeout && activeGains.isEmpty())
|
if (shouldSkipDrawing()) return
|
||||||
return
|
|
||||||
|
|
||||||
drawTotalXPBox()
|
drawTotalXPBox()
|
||||||
|
|
||||||
val removeList = ArrayList<XPGain>()
|
val removeList = ArrayList<XPGain>()
|
||||||
var posX = API.GetWindowDimensions().width / 2
|
|
||||||
|
|
||||||
if (API.GetWindowMode() == WindowMode.FIXED)
|
val movementSpeedFactor = deltaTime / 16.666 // 60 FPS
|
||||||
posX += 60
|
|
||||||
|
|
||||||
for(gain in activeGains) {
|
for (gain in activeGains) {
|
||||||
gain.currentPos -= ceil(deltaTime / 20.0).toInt()
|
gain.currentPos -= ceil(movementSpeedFactor).toInt() // Adjust movement based on deltaTime
|
||||||
if (gain.currentPos <= drawClear) {
|
if (gain.currentPos <= drawClear) {
|
||||||
removeList.add(gain)
|
removeList.add(gain)
|
||||||
totalXp += gain.xp
|
totalXp += gain.xp
|
||||||
} else if (gain.currentPos <= drawStart){
|
} else if (gain.currentPos <= drawStart) {
|
||||||
val sprite = XPSprites.getSpriteForSkill(skillId = gain.skill)
|
drawXPDrops(gain)
|
||||||
sprite?.render(posX - 25, gain.currentPos - 20)
|
|
||||||
API.DrawText(
|
|
||||||
FontType.SMALL,
|
|
||||||
FontColor.fromColor(Color.WHITE),
|
|
||||||
TextModifier.LEFT,
|
|
||||||
addCommas(gain.xp.toString()),
|
|
||||||
posX,
|
|
||||||
gain.currentPos
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activeGains.removeAll(removeList.toSet())
|
activeGains.removeAll(removeList.toSet())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldSkipDrawing(): Boolean {
|
||||||
|
return client.gameState < IN_GAME || (!alwaysShow && isDisplayTimeoutExpired() && activeGains.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OnKondoValueUpdated() {
|
||||||
|
StoreData("xp-drop-theme",theme.toString())
|
||||||
|
StoreData("xp-drop-alwaysShow",alwaysShow)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isDisplayTimeoutExpired(): Boolean {
|
||||||
|
return System.currentTimeMillis() - lastGain >= displayTimeout
|
||||||
|
}
|
||||||
|
|
||||||
override fun OnXPUpdate(skill: Int, xp: Int) {
|
override fun OnXPUpdate(skill: Int, xp: Int) {
|
||||||
if (xp == lastXp[skill]) return
|
if (xp == lastXp[skill]) return
|
||||||
|
|
||||||
|
|
@ -81,6 +112,54 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawTotalXPBox() {
|
private fun drawTotalXPBox() {
|
||||||
|
when (theme) {
|
||||||
|
Theme.DEFAULT -> drawDefaultXPBox()
|
||||||
|
Theme.RUNELITE -> drawRuneliteXPBox()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawXPDrops(gain : XPGain) {
|
||||||
|
when (theme) {
|
||||||
|
Theme.DEFAULT -> drawDefaultXPDrop(gain)
|
||||||
|
Theme.RUNELITE -> drawRuneliteXPDrops(gain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawDefaultXPDrop(gain: XPGain) {
|
||||||
|
var posX = API.GetWindowDimensions().width / 2
|
||||||
|
if (API.GetWindowMode() == WindowMode.FIXED)
|
||||||
|
posX += 60
|
||||||
|
val sprite = spriteCache.getOrPut(gain.skill) { XPSprites.getSpriteForSkill(skillId = gain.skill) }
|
||||||
|
sprite?.render(posX - 25, gain.currentPos - 20)
|
||||||
|
DrawText(
|
||||||
|
FontType.SMALL,
|
||||||
|
fromColor(Color.WHITE),
|
||||||
|
TextModifier.LEFT,
|
||||||
|
addCommas(gain.xp.toString()),
|
||||||
|
posX,
|
||||||
|
gain.currentPos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawRuneliteXPDrops(gain: XPGain) {
|
||||||
|
val w = API.GetWindowDimensions().width
|
||||||
|
val offset = if(API.GetWindowMode() == WindowMode.FIXED) 251 else 225
|
||||||
|
val extra = 2;
|
||||||
|
val posX = w - (offset + extra)
|
||||||
|
|
||||||
|
val str = addCommas(gain.xp.toString())
|
||||||
|
val fontCharWidth = 4
|
||||||
|
val displace = str.length*fontCharWidth + 30
|
||||||
|
|
||||||
|
// should be scaled https://github.com/runelite/runelite/blob/0500906f8de9cd20875c168a7a59e5e066ed5058/runelite-client/src/main/java/net/runelite/client/game/SkillIconManager.java#L50
|
||||||
|
// but for now this is good enough
|
||||||
|
|
||||||
|
val sprite = spriteCache.getOrPut(gain.skill) { XPSprites.getSpriteForSkill(skillId = gain.skill) }
|
||||||
|
sprite?.render(posX - displace, gain.currentPos - 20)
|
||||||
|
drawTextWithDropShadow(posX, gain.currentPos, Color.WHITE, addCommas(gain.xp.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawDefaultXPBox() {
|
||||||
var posX = API.GetWindowDimensions().width / 2
|
var posX = API.GetWindowDimensions().width / 2
|
||||||
val posY = API.GetWindowDimensions().height / 4
|
val posY = API.GetWindowDimensions().height / 4
|
||||||
|
|
||||||
|
|
@ -89,13 +168,13 @@ class plugin : Plugin() {
|
||||||
|
|
||||||
API.ClipRect(0, 0, posX * 2, posY * 4)
|
API.ClipRect(0, 0, posX * 2, posY * 4)
|
||||||
|
|
||||||
val horizontal = API.GetSprite(822)
|
val horizontal = spriteCache.getOrPut(822) { API.GetSprite(822) }
|
||||||
val horizontalTop = API.GetSprite(820)
|
val horizontalTop = spriteCache.getOrPut(820) { API.GetSprite(820) }
|
||||||
val tlCorner = API.GetSprite(824)
|
val tlCorner = spriteCache.getOrPut(824) { API.GetSprite(824) }
|
||||||
val blCorner = API.GetSprite(826)
|
val blCorner = spriteCache.getOrPut(826) { API.GetSprite(826) }
|
||||||
val trCorner = API.GetSprite(825)
|
val trCorner = spriteCache.getOrPut(825) { API.GetSprite(825) }
|
||||||
val brCorner = API.GetSprite(827)
|
val brCorner = spriteCache.getOrPut(827) { API.GetSprite(827) }
|
||||||
val bg = API.GetSprite(657)
|
val bg = spriteCache.getOrPut(657) { API.GetSprite(657) }
|
||||||
|
|
||||||
bg?.render(posX - 77, 10)
|
bg?.render(posX - 77, 10)
|
||||||
API.FillRect(posX - 75, 5, 140, 30, 0, 64)
|
API.FillRect(posX - 75, 5, 140, 30, 0, 64)
|
||||||
|
|
@ -112,9 +191,9 @@ class plugin : Plugin() {
|
||||||
horizontalTop?.render(posX + 9, -8)
|
horizontalTop?.render(posX + 9, -8)
|
||||||
horizontal?.render(posX + 9, 22)
|
horizontal?.render(posX + 9, 22)
|
||||||
|
|
||||||
API.DrawText(
|
DrawText(
|
||||||
FontType.SMALL,
|
FontType.SMALL,
|
||||||
FontColor.fromColor(Color.WHITE),
|
fromColor(Color.WHITE),
|
||||||
TextModifier.LEFT,
|
TextModifier.LEFT,
|
||||||
"Total Xp: ${addCommas(totalXp.toString())}",
|
"Total Xp: ${addCommas(totalXp.toString())}",
|
||||||
posX - 65,
|
posX - 65,
|
||||||
|
|
@ -122,16 +201,48 @@ class plugin : Plugin() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun drawRuneliteXPBox() {
|
||||||
|
val boxHeight = 29
|
||||||
|
val boxWidth = 119
|
||||||
|
val posX = API.GetWindowDimensions().width
|
||||||
|
|
||||||
|
val innerBorderColor = Color(90, 82, 69).rgb
|
||||||
|
val outerBorderColor = Color(56,48,35).rgb
|
||||||
|
|
||||||
|
val offset = if(API.GetWindowMode() == WindowMode.FIXED) 251 else 225
|
||||||
|
val boxStart = posX - (offset + boxWidth)
|
||||||
|
val yOffset = if(API.GetWindowMode() == WindowMode.FIXED) 4 else 0
|
||||||
|
|
||||||
|
val lvlIcon = 898;
|
||||||
|
val sprite = spriteCache.getOrPut(lvlIcon){
|
||||||
|
val imageStream: InputStream = plugin::class.java.getResourceAsStream("res/rl-lvls.png")
|
||||||
|
imageStream.use { imageStream ->
|
||||||
|
val image: BufferedImage = ImageIO.read(imageStream)
|
||||||
|
API.GetSpriteFromPNG(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a simple rectangle instead of the default box design
|
||||||
|
API.FillRect(boxStart, yOffset, boxWidth, boxHeight, innerBorderColor, 150)
|
||||||
|
drawTextWithDropShadow(boxStart+boxWidth-4, 18+yOffset, Color.WHITE, addCommas(totalXp.toString()))
|
||||||
|
|
||||||
|
// Inner Border
|
||||||
|
API.DrawRect(boxStart+1, 1+yOffset, boxWidth-2, boxHeight-2, innerBorderColor)
|
||||||
|
// redraw around the border
|
||||||
|
API.DrawRect(boxStart, yOffset, boxWidth, boxHeight, outerBorderColor)
|
||||||
|
sprite?.render(boxStart + 3, 3+yOffset)
|
||||||
|
}
|
||||||
|
|
||||||
data class XPGain(val skill: Int, val xp: Int, var currentPos: Int)
|
data class XPGain(val skill: Int, val xp: Int, var currentPos: Int)
|
||||||
|
|
||||||
fun addCommas(num: String): String{
|
fun addCommas(num: String): String {
|
||||||
var newString = ""
|
var newString = ""
|
||||||
if(num.length > 9){
|
if (num.length > 9) {
|
||||||
return "Lots!"
|
return "Lots!"
|
||||||
}
|
}
|
||||||
var counter = 1
|
var counter = 1
|
||||||
num.reversed().forEach {
|
num.reversed().forEach {
|
||||||
if(counter % 3 == 0 && counter != num.length){
|
if (counter % 3 == 0 && counter != num.length) {
|
||||||
newString += "$it,"
|
newString += "$it,"
|
||||||
} else {
|
} else {
|
||||||
newString += it
|
newString += it
|
||||||
|
|
@ -140,4 +251,10 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
return newString.reversed()
|
return newString.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun drawTextWithDropShadow(x: Int, y: Int, color: Color, text: String, mod : TextModifier = TextModifier.RIGHT) {
|
||||||
|
DrawText(FontType.SMALL, fromColor(Color(0)), mod, text, x + 1, y + 1)
|
||||||
|
DrawText(FontType.SMALL, fromColor(color), mod, text, x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='Ceikry'
|
AUTHOR='Ceikry'
|
||||||
DESCRIPTION='Draws nice and clean experience drops onto the screen.'
|
DESCRIPTION='Draws nice and clean experience drops onto the screen.'
|
||||||
VERSION=1.2
|
VERSION=1.3
|
||||||
|
|
|
||||||
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png
Normal file
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 348 B |
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png
Normal file
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 329 B |
Loading…
Add table
Add a link
Reference in a new issue