mirror of
https://gitlab.com/2009scape/rt4-client.git
synced 2025-12-10 10:20:44 -07:00
Merge branch 'Kondo-Kit-Plugin' into 'master'
KondoKit v1.0 Plugin See merge request 2009scape/rt4-client!23
This commit is contained in:
commit
dd52e7e9fb
20 changed files with 133161 additions and 77 deletions
|
|
@ -92,11 +92,32 @@ public abstract class Plugin {
|
|||
*/
|
||||
public void OnLogin() {}
|
||||
|
||||
|
||||
/**
|
||||
* Called when an NPC is killed.
|
||||
*
|
||||
* @param npcID the unique identifier of the NPC
|
||||
* @param x the x-coordinate where the NPC died
|
||||
* @param z the z-coordinate where the NPC died
|
||||
*/
|
||||
public void OnKillingBlowNPC(int npcID, int x, int z) {}
|
||||
|
||||
/**
|
||||
* OnLogout is called when the client logs out. This should be used to clear player-relevant plugin state.
|
||||
*/
|
||||
public void OnLogout() {}
|
||||
|
||||
/**
|
||||
* Called when the client attempts to reload plugins.
|
||||
* Implement this method to control the behavior of the plugin during a reload.
|
||||
*
|
||||
* @return {@code false} to allow the plugin to be reloaded.
|
||||
* {@code true} to prevent the plugin from being reloaded and stay loaded,
|
||||
*/
|
||||
public boolean OnPluginsReloaded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* DrawMiniMenu is called when a MiniMenu entry has been created.
|
||||
* @param entry the entry
|
||||
|
|
|
|||
|
|
@ -43,7 +43,23 @@ public class PluginRepository {
|
|||
API.registeredWheelListeners.clear();
|
||||
API.registeredMouseListeners.clear();
|
||||
API.registeredKeyListeners.clear();
|
||||
|
||||
HashMap<PluginInfo, Plugin> pluginsToKeep = new HashMap<>();
|
||||
|
||||
// Check and store plugins with OnPluginsReloaded method
|
||||
loadedPlugins.forEach((info, plugin) -> {
|
||||
try {
|
||||
boolean keep = plugin.OnPluginsReloaded();
|
||||
if (keep) {
|
||||
pluginsToKeep.put(info, plugin);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
loadedPlugins.clear();
|
||||
loadedPlugins.putAll(pluginsToKeep);
|
||||
SaveStorage();
|
||||
Init();
|
||||
}
|
||||
|
|
@ -98,6 +114,11 @@ public class PluginRepository {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (loadedPlugins.containsKey(info)) {
|
||||
System.out.println("Skipping reloading of plugin " + file.getName() + " as it already exists and has OnPluginsReloaded.");
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Plugin thisPlugin = (Plugin) clazz.newInstance();
|
||||
thisPlugin._init();
|
||||
|
|
@ -179,6 +200,10 @@ public class PluginRepository {
|
|||
loadedPlugins.values().forEach((plugin) -> plugin.OnLogin());
|
||||
}
|
||||
|
||||
public static void OnKillingBlowNPC(int npcId, int x, int z) {
|
||||
loadedPlugins.values().forEach((plugin) -> plugin.OnKillingBlowNPC(npcId, x, z));
|
||||
}
|
||||
|
||||
public static void SaveStorage() {
|
||||
if (pluginStorage.containsKey("_keystoreDirty")) {
|
||||
pluginStorage.remove("_keystoreDirty");
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ public final class GlIndexedSprite extends IndexedSprite {
|
|||
@OriginalMember(owner = "client!oh", name = "q", descriptor = "I")
|
||||
private int anInt4284 = 0;
|
||||
|
||||
public byte[] pixels;
|
||||
|
||||
public int[] pallet;
|
||||
|
||||
@OriginalMember(owner = "client!oh", name = "<init>", descriptor = "(IIIIII[B[I)V")
|
||||
public GlIndexedSprite(@OriginalArg(0) int arg0, @OriginalArg(1) int arg1, @OriginalArg(2) int arg2, @OriginalArg(3) int arg3, @OriginalArg(4) int arg4, @OriginalArg(5) int arg5, @OriginalArg(6) byte[] arg6, @OriginalArg(7) int[] arg7) {
|
||||
this.innerWidth = arg0;
|
||||
|
|
@ -40,6 +44,8 @@ public final class GlIndexedSprite extends IndexedSprite {
|
|||
this.yOffset = arg3;
|
||||
this.width = arg4;
|
||||
this.height = arg5;
|
||||
this.pixels = arg6;
|
||||
this.pallet = arg7;
|
||||
this.method3337(arg6, arg7);
|
||||
this.method3339();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ public class GlSprite extends Sprite {
|
|||
@OriginalMember(owner = "client!cf", name = "ab", descriptor = "I")
|
||||
public int textureId = -1;
|
||||
|
||||
public int[] pixels;
|
||||
|
||||
@OriginalMember(owner = "client!cf", name = "Z", descriptor = "I")
|
||||
private int anInt1871 = -1;
|
||||
|
||||
|
|
@ -40,6 +42,7 @@ public class GlSprite extends Sprite {
|
|||
this.anInt1861 = arg3;
|
||||
this.width = arg4;
|
||||
this.height = arg5;
|
||||
this.pixels = arg6;
|
||||
this.method1430(arg6);
|
||||
this.method1431();
|
||||
}
|
||||
|
|
@ -52,6 +55,7 @@ public class GlSprite extends Sprite {
|
|||
this.anInt1861 = arg0.anInt1861;
|
||||
this.width = arg0.width;
|
||||
this.height = arg0.height;
|
||||
this.pixels = arg0.pixels;
|
||||
this.method1430(arg0.pixels);
|
||||
this.method1431();
|
||||
}
|
||||
|
|
@ -515,4 +519,4 @@ public class GlSprite extends Sprite {
|
|||
gl.glEnd();
|
||||
gl.glEndList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3034,6 +3034,7 @@ public class Protocol {
|
|||
|
||||
boolean isKillingBlow = (local18 & 0x80) != 0;
|
||||
if (isKillingBlow) {
|
||||
PluginRepository.OnKillingBlowNPC(npc.type.id,npc.movementQueueX[0],npc.movementQueueZ[0]);
|
||||
local43 = inboundBuffer.g2add();
|
||||
if (local43 == 65535) {
|
||||
local43 = -1;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
|||
public byte[] pixels;
|
||||
|
||||
@OriginalMember(owner = "client!ek", name = "n", descriptor = "[I")
|
||||
private final int[] anIntArray144;
|
||||
public final int[] pallet;
|
||||
|
||||
@OriginalMember(owner = "client!ek", name = "<init>", descriptor = "(IIIIII[B[I)V")
|
||||
public SoftwareIndexedSprite(@OriginalArg(0) int arg0, @OriginalArg(1) int arg1, @OriginalArg(2) int arg2, @OriginalArg(3) int arg3, @OriginalArg(4) int arg4, @OriginalArg(5) int arg5, @OriginalArg(6) byte[] arg6, @OriginalArg(7) int[] arg7) {
|
||||
|
|
@ -23,7 +23,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
|||
this.width = arg4;
|
||||
this.height = arg5;
|
||||
this.pixels = arg6;
|
||||
this.anIntArray144 = arg7;
|
||||
this.pallet = arg7;
|
||||
}
|
||||
|
||||
@OriginalMember(owner = "client!ek", name = "<init>", descriptor = "(III)V")
|
||||
|
|
@ -32,7 +32,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
|||
this.innerHeight = this.height = arg1;
|
||||
this.xOffset = this.yOffset = 0;
|
||||
this.pixels = new byte[arg0 * arg1];
|
||||
this.anIntArray144 = new int[arg2];
|
||||
this.pallet = new int[arg2];
|
||||
}
|
||||
|
||||
@OriginalMember(owner = "client!ek", name = "a", descriptor = "([I[B[IIIIIIIIII)V")
|
||||
|
|
@ -149,29 +149,29 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
|||
|
||||
@OriginalMember(owner = "client!ek", name = "b", descriptor = "(III)V")
|
||||
public final void adjustPalette(@OriginalArg(0) int arg0, @OriginalArg(1) int arg1, @OriginalArg(2) int arg2) {
|
||||
for (@Pc(1) int local1 = 0; local1 < this.anIntArray144.length; local1++) {
|
||||
@Pc(15) int local15 = this.anIntArray144[local1] >> 16 & 0xFF;
|
||||
for (@Pc(1) int local1 = 0; local1 < this.pallet.length; local1++) {
|
||||
@Pc(15) int local15 = this.pallet[local1] >> 16 & 0xFF;
|
||||
local15 += arg0;
|
||||
if (local15 < 0) {
|
||||
local15 = 0;
|
||||
} else if (local15 > 255) {
|
||||
local15 = 255;
|
||||
}
|
||||
@Pc(38) int local38 = this.anIntArray144[local1] >> 8 & 0xFF;
|
||||
@Pc(38) int local38 = this.pallet[local1] >> 8 & 0xFF;
|
||||
local38 += arg1;
|
||||
if (local38 < 0) {
|
||||
local38 = 0;
|
||||
} else if (local38 > 255) {
|
||||
local38 = 255;
|
||||
}
|
||||
@Pc(59) int local59 = this.anIntArray144[local1] & 0xFF;
|
||||
@Pc(59) int local59 = this.pallet[local1] & 0xFF;
|
||||
local59 += arg2;
|
||||
if (local59 < 0) {
|
||||
local59 = 0;
|
||||
} else if (local59 > 255) {
|
||||
local59 = 255;
|
||||
}
|
||||
this.anIntArray144[local1] = (local15 << 16) + (local38 << 8) + local59;
|
||||
this.pallet[local1] = (local15 << 16) + (local38 << 8) + local59;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
|||
local7 += local21 * local145;
|
||||
local125 += local145;
|
||||
}
|
||||
method1394(SoftwareRaster.pixels, this.pixels, this.anIntArray144, local7, local9, local41, local125, arg2, arg3, local21, local27, local2, arg4);
|
||||
method1394(SoftwareRaster.pixels, this.pixels, this.pallet, local7, local9, local41, local125, arg2, arg3, local21, local27, local2, arg4);
|
||||
}
|
||||
|
||||
@OriginalMember(owner = "client!ek", name = "a", descriptor = "()V")
|
||||
|
|
@ -309,7 +309,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
|||
local27 += local36;
|
||||
}
|
||||
if (local23 > 0 && local20 > 0) {
|
||||
method1397(SoftwareRaster.pixels, this.pixels, this.anIntArray144, local17, local15, local23, local20, local27, local29, arg2);
|
||||
method1397(SoftwareRaster.pixels, this.pixels, this.pallet, local17, local15, local23, local20, local27, local29, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -383,7 +383,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
|||
local7 += local21 * local145;
|
||||
local125 += local145;
|
||||
}
|
||||
method1391(SoftwareRaster.pixels, this.pixels, this.anIntArray144, local7, local9, local41, local125, arg2, arg3, local21, local27, local2);
|
||||
method1391(SoftwareRaster.pixels, this.pixels, this.pallet, local7, local9, local41, local125, arg2, arg3, local21, local27, local2);
|
||||
}
|
||||
|
||||
@OriginalMember(owner = "client!ek", name = "a", descriptor = "(II)V")
|
||||
|
|
@ -424,7 +424,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
|||
local27 += local36;
|
||||
}
|
||||
if (local23 > 0 && local20 > 0) {
|
||||
method1393(SoftwareRaster.pixels, this.pixels, this.anIntArray144, local17, local15, local23, local20, local27, local29);
|
||||
method1393(SoftwareRaster.pixels, this.pixels, this.pallet, local17, local15, local23, local20, local27, local29);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,15 +350,15 @@ public final class client extends GameShell {
|
|||
|
||||
@OriginalMember(owner = "client!pl", name = "a", descriptor = "(II)V")
|
||||
public static void setGameState(@OriginalArg(0) int arg0) {
|
||||
if(arg0 == 30) {
|
||||
PluginRepository.OnLogin();
|
||||
}
|
||||
if (gameState == arg0) {
|
||||
return;
|
||||
}
|
||||
if (gameState == 0) {
|
||||
LoadingBarAwt.clear();
|
||||
}
|
||||
if (gameState == 30) {
|
||||
PluginRepository.OnLogin();
|
||||
}
|
||||
if (arg0 == 40) {
|
||||
LoginManager.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import rt4.*
|
|||
import java.awt.Color
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.roundToInt
|
||||
|
|
@ -19,29 +21,26 @@ import kotlin.math.roundToInt
|
|||
@PluginMeta(
|
||||
author = "downthecrop",
|
||||
description =
|
||||
"""
|
||||
"""
|
||||
Ground Items Overlay. Just like Runelite!
|
||||
cmds ::set(low,med,high,insane,hide), ::(tag,ignore)item ID, ::(reset)groundconfig
|
||||
Special thanks to Chisato for the original skeleton.
|
||||
""",
|
||||
version = 1.1
|
||||
version = 1.2
|
||||
)
|
||||
open class plugin : Plugin() {
|
||||
|
||||
private var lowValue = 5000
|
||||
private var mediumValue = 20000
|
||||
private var highValue = 50000
|
||||
private var insaneValue = 100000
|
||||
private var hideBelowValue = 0
|
||||
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 lateinit var taggedItems: List<Int>
|
||||
private lateinit var ignoredItems: List<Int>
|
||||
private lateinit var kondoExposed_taggedItems: List<Int>
|
||||
private lateinit var kondoExposed_ignoredItems: List<Int>
|
||||
|
||||
private val gePriceMap = try {
|
||||
parseGEPrices(BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8)).useLines { it.joinToString("\n") })
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
private var gePriceMap = loadGEPrices()
|
||||
|
||||
private val colorMap = mapOf(
|
||||
"tagged" to "#AA00FF",
|
||||
|
|
@ -61,22 +60,23 @@ open class plugin : Plugin() {
|
|||
)
|
||||
|
||||
override fun Init() {
|
||||
lowValue = GetData("low-value") as? Int ?: 5000
|
||||
mediumValue = GetData("medium-value") as? Int ?: 20000
|
||||
highValue = GetData("high-value") as? Int ?: 50000
|
||||
insaneValue = GetData("insane-value") as? Int ?: 100000
|
||||
hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
||||
taggedItems = GetData("ground-item-tags")?.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 item_configs.json")
|
||||
kondoExposed_lowValue = GetData("low-value") as? Int ?: 5000
|
||||
kondoExposed_mediumValue = GetData("medium-value") as? Int ?: 20000
|
||||
kondoExposed_highValue = GetData("high-value") as? Int ?: 50000
|
||||
kondoExposed_insaneValue = GetData("insane-value") as? Int ?: 100000
|
||||
kondoExposed_hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
||||
kondoExposed_useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true
|
||||
kondoExposed_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()
|
||||
if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $kondoExposed_useLiveGEPrices")
|
||||
}
|
||||
|
||||
private fun isTagged(itemId: Int): Boolean {
|
||||
return taggedItems.contains(itemId)
|
||||
return kondoExposed_taggedItems.contains(itemId)
|
||||
}
|
||||
|
||||
private fun isHidden(itemId: Int): Boolean {
|
||||
return ignoredItems.contains(itemId)
|
||||
return kondoExposed_ignoredItems.contains(itemId)
|
||||
}
|
||||
|
||||
override fun Draw(timeDelta: Long) = renderGroundItemNames()
|
||||
|
|
@ -141,10 +141,10 @@ open class plugin : Plugin() {
|
|||
val screenY = screenPos[1]
|
||||
val color = when {
|
||||
isTagged(itemDef.id) -> colorMap["tagged"]
|
||||
highestValue < lowValue -> "#FFFFFF"
|
||||
highestValue < mediumValue -> colorMap["lowValue"]
|
||||
highestValue < highValue -> colorMap["mediumValue"]
|
||||
highestValue < insaneValue -> colorMap["highValue"]
|
||||
highestValue < kondoExposed_lowValue -> "#FFFFFF"
|
||||
highestValue < kondoExposed_mediumValue -> colorMap["lowValue"]
|
||||
highestValue < kondoExposed_highValue -> colorMap["mediumValue"]
|
||||
highestValue < kondoExposed_insaneValue -> colorMap["highValue"]
|
||||
else -> colorMap["insaneValue"]
|
||||
} ?: "#FFFFFF"
|
||||
val colorInt = color.drop(1).toInt(16)
|
||||
|
|
@ -164,6 +164,7 @@ open class plugin : Plugin() {
|
|||
}
|
||||
}
|
||||
}
|
||||
InterfaceList.fullRedrawAllInterfaces() // Prevent an overdraw bug
|
||||
}
|
||||
|
||||
private fun getDisplayedStackSize(objstacknodeLL: LinkedList): Int{
|
||||
|
|
@ -182,7 +183,7 @@ open class plugin : Plugin() {
|
|||
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 highestValue = maxOf(haValue, geValue)
|
||||
return !((highestValue < hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
||||
return !((highestValue < kondoExposed_hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
||||
}
|
||||
|
||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||
|
|
@ -199,7 +200,7 @@ open class plugin : Plugin() {
|
|||
|
||||
private fun ignoreItem(itemId: Int): Runnable {
|
||||
return Runnable {
|
||||
val existingIgnores = ignoredItems.toMutableList()
|
||||
val existingIgnores = kondoExposed_ignoredItems.toMutableList()
|
||||
|
||||
if (existingIgnores.contains(itemId)) {
|
||||
existingIgnores.remove(itemId)
|
||||
|
|
@ -214,7 +215,7 @@ open class plugin : Plugin() {
|
|||
|
||||
private fun tagItem(itemId: Int): Runnable {
|
||||
return Runnable {
|
||||
val existingTags = taggedItems.toMutableList()
|
||||
val existingTags = kondoExposed_taggedItems.toMutableList()
|
||||
|
||||
if (existingTags.contains(itemId)) {
|
||||
existingTags.remove(itemId)
|
||||
|
|
@ -229,26 +230,28 @@ open class plugin : Plugin() {
|
|||
|
||||
|
||||
private fun resetConfig() {
|
||||
lowValue = 5000
|
||||
mediumValue = 20000
|
||||
highValue = 50000
|
||||
insaneValue = 100000
|
||||
hideBelowValue = 0
|
||||
kondoExposed_lowValue = 5000
|
||||
kondoExposed_mediumValue = 20000
|
||||
kondoExposed_highValue = 50000
|
||||
kondoExposed_insaneValue = 100000
|
||||
kondoExposed_hideBelowValue = 0
|
||||
kondoExposed_useLiveGEPrices = true
|
||||
StoreData("ground-item-tags","");
|
||||
StoreData("ground-item-ignore","");
|
||||
StoreData("low-value", lowValue)
|
||||
StoreData("medium-value", mediumValue)
|
||||
StoreData("high-value", highValue)
|
||||
StoreData("insane-value", insaneValue)
|
||||
StoreData("hide-below-value", hideBelowValue)
|
||||
StoreData("low-value", kondoExposed_lowValue)
|
||||
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
||||
StoreData("medium-value", kondoExposed_mediumValue)
|
||||
StoreData("high-value", kondoExposed_highValue)
|
||||
StoreData("insane-value", kondoExposed_insaneValue)
|
||||
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
||||
}
|
||||
|
||||
private fun displayRanges() {
|
||||
val low = lowValue
|
||||
val medium = mediumValue
|
||||
val high = highValue
|
||||
val insane = insaneValue
|
||||
val hide = hideBelowValue
|
||||
val low = kondoExposed_lowValue
|
||||
val medium = kondoExposed_mediumValue
|
||||
val high = kondoExposed_highValue
|
||||
val insane = kondoExposed_insaneValue
|
||||
val hide = kondoExposed_hideBelowValue
|
||||
|
||||
SendMessage("== Ground Item Config ==")
|
||||
SendMessage("Low: $low")
|
||||
|
|
@ -257,12 +260,12 @@ open class plugin : Plugin() {
|
|||
SendMessage("Insane: $insane")
|
||||
SendMessage("Hide Below: $hide")
|
||||
SendMessage("-- Ignored Items --")
|
||||
for(item in ignoredItems){
|
||||
for(item in kondoExposed_ignoredItems){
|
||||
val itemDef = ObjTypeList.get(item)
|
||||
SendMessage("Ignored: ${itemDef.name} ${itemDef.id}")
|
||||
}
|
||||
SendMessage("-- Tagged Items --")
|
||||
for(item in taggedItems){
|
||||
for(item in kondoExposed_taggedItems){
|
||||
val itemDef = ObjTypeList.get(item)
|
||||
SendMessage("Tagged: ${itemDef.name} ${itemDef.id}")
|
||||
}
|
||||
|
|
@ -274,19 +277,74 @@ open class plugin : Plugin() {
|
|||
DrawText(FontType.SMALL, fromColor(Color(color)), TextModifier.CENTER, text, x, y)
|
||||
}
|
||||
|
||||
private fun parseGEPrices(json: String): Map<String, String> {
|
||||
return json
|
||||
.trim()
|
||||
.removeSurrounding("[", "]")
|
||||
.split("},")
|
||||
.associate { obj ->
|
||||
val pairs = obj.trim().removeSurrounding("{", "}").split(",")
|
||||
val id = pairs.find { it.trim().startsWith("\"id\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
val grandExchangePrice =
|
||||
pairs.find { it.trim().startsWith("\"grand_exchange_price\"") }?.split(":")?.get(1)?.trim()
|
||||
?.trim('\"')
|
||||
id to grandExchangePrice
|
||||
}.filterKeys { it != null }.filterValues { it != null } as Map<String, String>
|
||||
fun OnKondoValueUpdated() {
|
||||
StoreData("ground-item-tags",kondoExposed_taggedItems);
|
||||
StoreData("ground-item-ignore",kondoExposed_ignoredItems);
|
||||
StoreData("low-value", kondoExposed_lowValue)
|
||||
StoreData("medium-value", kondoExposed_mediumValue)
|
||||
StoreData("high-value", kondoExposed_highValue)
|
||||
StoreData("insane-value", kondoExposed_insaneValue)
|
||||
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
||||
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
||||
gePriceMap = loadGEPrices();
|
||||
}
|
||||
|
||||
fun loadGEPrices(): Map<String, String> {
|
||||
return if (kondoExposed_useLiveGEPrices) {
|
||||
try {
|
||||
println("GroundItems: Loading Remote GE Prices")
|
||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0")
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val inputStream = connection.inputStream
|
||||
val content = inputStream.bufferedReader().use(BufferedReader::readText)
|
||||
|
||||
val items = content.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
val gePrices = mutableMapOf<String, String>()
|
||||
|
||||
for (item in items) {
|
||||
val pairs = item.removeSurrounding("{", "}").split(",")
|
||||
val itemId = pairs.find { it.trim().startsWith("\"item_id\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
val value = pairs.find { it.trim().startsWith("\"value\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
if (itemId != null && value != null) {
|
||||
gePrices[itemId] = value
|
||||
}
|
||||
}
|
||||
|
||||
gePrices
|
||||
} else {
|
||||
emptyMap()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
println("GroundItems: Loading Local GE Prices")
|
||||
BufferedReader(InputStreamReader(GroundItems.plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
||||
.useLines { lines ->
|
||||
val json = lines.joinToString("\n")
|
||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
val gePrices = mutableMapOf<String, String>()
|
||||
|
||||
for (item in items) {
|
||||
val pairs = item.removeSurrounding("{", "}").split(",")
|
||||
val id = pairs.find { it.trim().startsWith("\"id\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
val grandExchangePrice = pairs.find { it.trim().startsWith("\"grand_exchange_price\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
if (id != null && grandExchangePrice != null) {
|
||||
gePrices[id] = grandExchangePrice
|
||||
}
|
||||
}
|
||||
gePrices
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatValue(value: Int): String {
|
||||
|
|
|
|||
90
plugin-playground/src/main/kotlin/KondoKit/Helpers.kt
Normal file
90
plugin-playground/src/main/kotlin/KondoKit/Helpers.kt
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
package KondoKit
|
||||
|
||||
import java.awt.Color
|
||||
import java.awt.Dimension
|
||||
import javax.swing.JPanel
|
||||
|
||||
object Helpers {
|
||||
fun getSpriteId(skillId: Int) : Int {
|
||||
return when (skillId) {
|
||||
0 -> 197
|
||||
1 -> 199
|
||||
2 -> 198
|
||||
3 -> 203
|
||||
4 -> 200
|
||||
5 -> 201
|
||||
6 -> 202
|
||||
7 -> 212
|
||||
8 -> 214
|
||||
9 -> 208
|
||||
10 -> 211
|
||||
11 -> 213
|
||||
12 -> 207
|
||||
13 -> 210
|
||||
14 -> 209
|
||||
15 -> 205
|
||||
16 -> 204
|
||||
17 -> 206
|
||||
18 -> 216
|
||||
19 -> 217
|
||||
20 -> 215
|
||||
21 -> 220
|
||||
22 -> 221
|
||||
23 -> 222
|
||||
else -> 222
|
||||
}
|
||||
}
|
||||
|
||||
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>" +
|
||||
"<span style='color:rgb(${color2.red},${color2.green},${color2.blue});'>$text2</span></html>"
|
||||
}
|
||||
|
||||
fun formatNumber(value: Int): String {
|
||||
return when {
|
||||
value >= 1_000_000 -> String.format("%.2fM", value / 1_000_000.0)
|
||||
value >= 1_000 -> String.format("%.2fK", value / 1_000.0)
|
||||
else -> value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun getProgressBarColor(skillId: Int): Color {
|
||||
// Straight from runelite
|
||||
return when (skillId) {
|
||||
0 -> Color(155, 32, 7) // ATTACK
|
||||
1 -> Color(98, 119, 190) // DEFENCE
|
||||
2 -> Color(4, 149, 90) // STRENGTH
|
||||
3 -> Color(131, 126, 126) // HITPOINTS
|
||||
4 -> Color(109, 144, 23) // RANGED
|
||||
5 -> Color(159, 147, 35) // PRAYER
|
||||
6 -> Color(50, 80, 193) // MAGIC
|
||||
7 -> Color(112, 35, 134) // COOKING
|
||||
8 -> Color(52, 140, 37) // WOODCUTTING
|
||||
9 -> Color(3, 141, 125) // FLETCHING
|
||||
10 -> Color(106, 132, 164) // FISHING
|
||||
11 -> Color(189, 120, 25) // FIREMAKING
|
||||
12 -> Color(151, 110, 77) // CRAFTING
|
||||
13 -> Color(108, 107, 82) // SMITHING
|
||||
14 -> Color(93, 143, 167) // MINING
|
||||
15 -> Color(7, 133, 9) // HERBLORE
|
||||
16 -> Color(58, 60, 137) // AGILITY
|
||||
17 -> Color(108, 52, 87) // THIEVING
|
||||
18 -> Color(100, 100, 100) // SLAYER
|
||||
19 -> Color(101, 152, 63) // FARMING
|
||||
20 -> Color(170, 141, 26) // RUNECRAFT
|
||||
21 -> Color(92, 89, 65) // HUNTER
|
||||
22 -> Color(130, 116, 95) // CONSTRUCTION
|
||||
23 -> Color(150, 50, 50) // Placeholder for any additional skill
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
462
plugin-playground/src/main/kotlin/KondoKit/HiscoresView.kt
Normal file
462
plugin-playground/src/main/kotlin/KondoKit/HiscoresView.kt
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.Constants.COLOR_BACKGROUND_DARK
|
||||
import KondoKit.Helpers.getSpriteId
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import com.google.gson.Gson
|
||||
import plugin.api.API
|
||||
import rt4.Sprites
|
||||
import java.awt.*
|
||||
import java.awt.datatransfer.DataFlavor
|
||||
import java.awt.event.KeyAdapter
|
||||
import java.awt.event.KeyEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import javax.swing.*
|
||||
import javax.swing.border.MatteBorder
|
||||
|
||||
object Constants {
|
||||
// Sprite IDs
|
||||
const val COMBAT_LVL_SPRITE = 168
|
||||
const val IRONMAN_SPRITE = 4
|
||||
const val MAG_SPRITE = 1423
|
||||
const val LVL_BAR_SPRITE = 898
|
||||
|
||||
// Dimensions
|
||||
val SEARCH_FIELD_DIMENSION = Dimension(270, 30)
|
||||
val ICON_DIMENSION_SMALL = Dimension(12, 12)
|
||||
val ICON_DIMENSION_MEDIUM = Dimension(18, 20)
|
||||
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
||||
val HISCORE_PANEL_DIMENSION = Dimension(270, 400)
|
||||
val FILTER_PANEL_DIMENSION = Dimension(270, 30)
|
||||
val SKILLS_PANEL_DIMENSION = Dimension(300, 300)
|
||||
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(270, 30)
|
||||
val SKILL_PANEL_DIMENSION = Dimension(90, 35)
|
||||
val IMAGE_CANVAS_DIMENSION = Dimension(20, 20)
|
||||
val NUMBER_LABEL_DIMENSION = Dimension(30, 20)
|
||||
|
||||
// Colors
|
||||
val COLOR_BACKGROUND_DARK = Color(27, 27, 27)
|
||||
val COLOR_BACKGROUND_MEDIUM = Color(37, 37, 37)
|
||||
val COLOR_BACKGROUND_LIGHT = Color(43, 43, 43)
|
||||
val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200)
|
||||
val COLOR_RED = Color.RED
|
||||
val COLOR_SKILL_PANEL = Color(60, 60, 60)
|
||||
|
||||
// Fonts
|
||||
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
|
||||
val FONT_ARIAL_PLAIN_12 = Font("Arial", Font.PLAIN, 12)
|
||||
val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12)
|
||||
}
|
||||
|
||||
var text: String = ""
|
||||
|
||||
object HiscoresView {
|
||||
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
|
||||
|
||||
private var cursorVisible: Boolean = true
|
||||
private val gson = Gson()
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.MAG_SPRITE))
|
||||
val imageCanvas = bufferedImageSprite.let {
|
||||
ImageCanvas(it).apply {
|
||||
preferredSize = Constants.ICON_DIMENSION_SMALL
|
||||
size = preferredSize
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
preferredSize = Constants.SEARCH_FIELD_DIMENSION
|
||||
background = Constants.COLOR_BACKGROUND_DARK
|
||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||
font = Constants.FONT_ARIAL_PLAIN_14
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
|
||||
addKeyListener(object : KeyAdapter() {
|
||||
override fun keyTyped(e: KeyEvent) {
|
||||
// Prevent null character from being typed on Ctrl+A & Ctrl+V
|
||||
if (e.isControlDown && (e.keyChar == '\u0001' || e.keyChar == '\u0016')) {
|
||||
e.consume()
|
||||
return
|
||||
}
|
||||
if (e.keyChar == '\b') {
|
||||
if (text.isNotEmpty()) {
|
||||
text = text.dropLast(1)
|
||||
}
|
||||
} else if (e.keyChar == '\n') {
|
||||
searchPlayer(text)
|
||||
} else {
|
||||
text += e.keyChar
|
||||
}
|
||||
repaint()
|
||||
}
|
||||
override fun keyPressed(e: KeyEvent) {
|
||||
if (e.isControlDown) {
|
||||
when (e.keyCode) {
|
||||
KeyEvent.VK_A -> {
|
||||
text = ""
|
||||
repaint()
|
||||
}
|
||||
KeyEvent.VK_V -> {
|
||||
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
|
||||
val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String
|
||||
text += pasteText
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent) {
|
||||
if (e.x > width - 20 && e.y < 20) {
|
||||
text = ""
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Timer(500) {
|
||||
cursorVisible = !cursorVisible
|
||||
if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW")
|
||||
repaint()
|
||||
}.start()
|
||||
}
|
||||
|
||||
override fun paint(g: Graphics) {
|
||||
super.paint(g)
|
||||
g.color = foreground
|
||||
g.font = font
|
||||
|
||||
val fm = g.fontMetrics
|
||||
val cursorX = fm.stringWidth(text) + 30
|
||||
|
||||
imageCanvas?.let { canvas ->
|
||||
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
||||
canvas.paint(imgG)
|
||||
imgG.dispose()
|
||||
}
|
||||
|
||||
g.drawString(text, 30, 20)
|
||||
|
||||
if (cursorVisible && hasFocus()) {
|
||||
g.drawLine(cursorX, 5, cursorX, 25)
|
||||
}
|
||||
|
||||
if (text.isNotEmpty()) {
|
||||
g.color = Constants.COLOR_RED
|
||||
g.drawString("x", width - 20, 20)
|
||||
}
|
||||
}
|
||||
|
||||
fun searchPlayer(username: String) {
|
||||
text = username
|
||||
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${username.toLowerCase()}"
|
||||
|
||||
Thread {
|
||||
try {
|
||||
val url = URL(apiUrl)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
||||
val response = reader.use { it.readText() }
|
||||
reader.close()
|
||||
|
||||
SwingUtilities.invokeLater {
|
||||
updatePlayerData(response, username)
|
||||
}
|
||||
} else {
|
||||
SwingUtilities.invokeLater {
|
||||
showError("Player not found!")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
SwingUtilities.invokeLater {
|
||||
showError("Error fetching data!")
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun updatePlayerData(jsonResponse: String, username: String) {
|
||||
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
||||
updateHiscoresView(hiscoresResponse, username)
|
||||
}
|
||||
|
||||
private fun updateHiscoresView(data: HiscoresResponse, username: String) {
|
||||
val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel
|
||||
val ironMode = data.info.iron_mode
|
||||
|
||||
playerNameLabel?.removeAll() // Clear previous components
|
||||
|
||||
if (ironMode != "0") {
|
||||
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
||||
val imageCanvas = ironmanBufferedImage.let {
|
||||
ImageCanvas(it).apply {
|
||||
preferredSize = Constants.IMAGE_CANVAS_DIMENSION
|
||||
size = Constants.IMAGE_CANVAS_DIMENSION
|
||||
}
|
||||
}
|
||||
|
||||
playerNameLabel?.add(imageCanvas)
|
||||
}
|
||||
|
||||
val nameLabel = JLabel(username, JLabel.CENTER).apply {
|
||||
font = Constants.FONT_ARIAL_BOLD_12
|
||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||
}
|
||||
|
||||
playerNameLabel?.add(nameLabel)
|
||||
|
||||
playerNameLabel?.revalidate()
|
||||
playerNameLabel?.repaint()
|
||||
|
||||
// Update skill labels
|
||||
data.skills.forEachIndexed { index, skill ->
|
||||
val labelName = "skillLabel_$index"
|
||||
val numberLabel = findComponentByName(hiscoresPanel, labelName) as? JLabel
|
||||
numberLabel?.text = skill.static
|
||||
}
|
||||
|
||||
updateTotalAndCombatLevel(data.skills, true)
|
||||
|
||||
hiscoresPanel.revalidate()
|
||||
hiscoresPanel.repaint()
|
||||
}
|
||||
|
||||
private fun updateTotalAndCombatLevel(skills: List<Skill>, isMemberWorld: Boolean) {
|
||||
val totalLevel = skills.sumBy { it.static.toInt() }
|
||||
val totalLevelLabel = findComponentByName(hiscoresPanel, "totalLevelLabel") as? JLabel
|
||||
totalLevelLabel?.text = totalLevel.toString()
|
||||
|
||||
val attack = skills.find { it.id == "0" }?.static?.toInt() ?: 1
|
||||
val defence = skills.find { it.id == "1" }?.static?.toInt() ?: 1
|
||||
val strength = skills.find { it.id == "2" }?.static?.toInt() ?: 1
|
||||
val hitpoints = skills.find { it.id == "3" }?.static?.toInt() ?: 1
|
||||
val ranged = skills.find { it.id == "4" }?.static?.toInt() ?: 1
|
||||
val prayer = skills.find { it.id == "5" }?.static?.toInt() ?: 1
|
||||
val magic = skills.find { it.id == "6" }?.static?.toInt() ?: 1
|
||||
val summoning = skills.find { it.id == "23" }?.static?.toInt() ?: 1
|
||||
|
||||
val combatLevel = calculateCombatLevel(attack, defence, strength, hitpoints, prayer, ranged, magic, summoning, true)
|
||||
val combatLevelLabel = findComponentByName(hiscoresPanel, "combatLevelLabel") as? JLabel
|
||||
combatLevelLabel?.text = combatLevel.toString()
|
||||
}
|
||||
|
||||
private fun calculateCombatLevel(
|
||||
attack: Int,
|
||||
defence: Int,
|
||||
strength: Int,
|
||||
hitpoints: Int,
|
||||
prayer: Int,
|
||||
ranged: Int,
|
||||
magic: Int,
|
||||
summoning: Int,
|
||||
isMemberWorld: Boolean
|
||||
): Double {
|
||||
val base = (defence + hitpoints + Math.floor(prayer.toDouble() / 2)) * 0.25
|
||||
val melee = (attack + strength) * 0.325
|
||||
val range = Math.floor(ranged * 1.5) * 0.325
|
||||
val mage = Math.floor(magic * 1.5) * 0.325
|
||||
val maxCombatType = maxOf(melee, range, mage)
|
||||
|
||||
val summoningFactor = if (isMemberWorld) Math.floor(summoning.toDouble() / 8) else 0.0
|
||||
return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0
|
||||
}
|
||||
|
||||
private fun showError(message: String) {
|
||||
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
|
||||
private fun findComponentByName(container: Container, name: String): Component? {
|
||||
for (component in container.components) {
|
||||
if (name == component.name) {
|
||||
return component
|
||||
}
|
||||
if (component is Container) {
|
||||
val child = findComponentByName(component, name)
|
||||
if (child != null) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun createHiscoreSearchView(): JPanel {
|
||||
val hiscorePanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
name = "HISCORE_SEARCH_VIEW"
|
||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||
preferredSize = Constants.HISCORE_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
}
|
||||
|
||||
val customSearchField = CustomSearchField(hiscorePanel)
|
||||
|
||||
val searchFieldWrapper = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.X_AXIS)
|
||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||
preferredSize = Constants.SEARCH_FIELD_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
alignmentX = Component.CENTER_ALIGNMENT
|
||||
add(customSearchField)
|
||||
}
|
||||
|
||||
val searchPanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||
add(searchFieldWrapper)
|
||||
}
|
||||
|
||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
||||
hiscorePanel.add(searchPanel)
|
||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
||||
|
||||
// Adding the player name panel in place of the filterPanel
|
||||
val playerNamePanel = JPanel().apply {
|
||||
layout = FlowLayout(FlowLayout.CENTER)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
name = "playerNameLabel"
|
||||
}
|
||||
|
||||
hiscorePanel.add(playerNamePanel)
|
||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
||||
|
||||
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||
preferredSize = Constants.SKILLS_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
}
|
||||
|
||||
for (i in 0 until 24) {
|
||||
val skillPanel = JPanel().apply {
|
||||
layout = BorderLayout()
|
||||
background = Constants.COLOR_SKILL_PANEL
|
||||
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
border = MatteBorder(5, 0, 0, 0, COLOR_BACKGROUND_DARK)
|
||||
}
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(getSpriteId(i)))
|
||||
|
||||
val imageCanvas = bufferedImageSprite.let {
|
||||
ImageCanvas(it).apply {
|
||||
preferredSize = Constants.IMAGE_CANVAS_DIMENSION
|
||||
size = Constants.IMAGE_CANVAS_DIMENSION
|
||||
}
|
||||
}
|
||||
|
||||
val numberLabel = JLabel("", JLabel.RIGHT).apply {
|
||||
name = "skillLabel_$i"
|
||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||
font = Constants.FONT_ARIAL_PLAIN_12
|
||||
preferredSize = Constants.NUMBER_LABEL_DIMENSION
|
||||
minimumSize = Constants.NUMBER_LABEL_DIMENSION
|
||||
}
|
||||
|
||||
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_DARK
|
||||
add(imageCanvas)
|
||||
add(numberLabel)
|
||||
}
|
||||
|
||||
skillPanel.add(imageContainer, BorderLayout.CENTER)
|
||||
skillsPanel.add(skillPanel)
|
||||
}
|
||||
|
||||
hiscorePanel.add(skillsPanel)
|
||||
|
||||
val totalCombatPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
||||
background = COLOR_BACKGROUND_DARK
|
||||
preferredSize = Constants.TOTAL_COMBAT_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
}
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE));
|
||||
|
||||
val totalLevelIcon = ImageCanvas(bufferedImageSprite).apply {
|
||||
preferredSize = Constants.ICON_DIMENSION_LARGE
|
||||
size = Constants.ICON_DIMENSION_LARGE
|
||||
}
|
||||
|
||||
val totalLevelLabel = JLabel("").apply {
|
||||
name = "totalLevelLabel"
|
||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||
font = Constants.FONT_ARIAL_BOLD_12
|
||||
horizontalAlignment = JLabel.LEFT
|
||||
iconTextGap = 10
|
||||
}
|
||||
|
||||
val totalLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_DARK
|
||||
add(totalLevelIcon)
|
||||
add(totalLevelLabel)
|
||||
}
|
||||
|
||||
val bufferedImageSprite2 = getBufferedImageFromSprite(API.GetSprite(Constants.COMBAT_LVL_SPRITE))
|
||||
|
||||
val combatLevelIcon = ImageCanvas(bufferedImageSprite2).apply {
|
||||
preferredSize = Constants.ICON_DIMENSION_LARGE
|
||||
size = Constants.ICON_DIMENSION_LARGE
|
||||
}
|
||||
|
||||
val combatLevelLabel = JLabel("").apply {
|
||||
name = "combatLevelLabel"
|
||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||
font = Constants.FONT_ARIAL_BOLD_12
|
||||
horizontalAlignment = JLabel.LEFT
|
||||
iconTextGap = 10
|
||||
}
|
||||
|
||||
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_DARK
|
||||
add(combatLevelIcon)
|
||||
add(combatLevelLabel)
|
||||
}
|
||||
|
||||
totalCombatPanel.add(totalLevelPanel)
|
||||
totalCombatPanel.add(combatLevelPanel)
|
||||
hiscorePanel.add(totalCombatPanel)
|
||||
|
||||
return hiscorePanel
|
||||
}
|
||||
|
||||
data class HiscoresResponse(
|
||||
val info: PlayerInfo,
|
||||
val skills: List<Skill>
|
||||
)
|
||||
|
||||
data class PlayerInfo(
|
||||
val exp_multiplier: String,
|
||||
val iron_mode: String
|
||||
)
|
||||
|
||||
data class Skill(
|
||||
val id: String,
|
||||
val dynamic: String,
|
||||
val experience: String,
|
||||
val static: String
|
||||
)
|
||||
}
|
||||
43
plugin-playground/src/main/kotlin/KondoKit/ImageCanvas.kt
Normal file
43
plugin-playground/src/main/kotlin/KondoKit/ImageCanvas.kt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package KondoKit
|
||||
|
||||
import java.awt.Canvas
|
||||
import java.awt.Color
|
||||
import java.awt.Dimension
|
||||
import java.awt.Graphics
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
||||
|
||||
init {
|
||||
// Manually set the alpha value to 255 (fully opaque) only for pixels that are not fully transparent
|
||||
val width = image.width
|
||||
val height = image.height
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
// Retrieve the current pixel color
|
||||
val color = image.getRGB(x, y)
|
||||
|
||||
// Check if the pixel is not fully transparent (i.e., color is not 0)
|
||||
if (color != 0) {
|
||||
// Ensure the alpha is set to 255 (fully opaque)
|
||||
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
||||
|
||||
// Set the pixel with the updated color
|
||||
image.setRGB(x, y, newColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun paint(g: Graphics) {
|
||||
super.paint(g)
|
||||
g.color = Color(27, 27, 27)
|
||||
g.fillRect(0, 0, width, height)
|
||||
g.drawImage(image, 0, 0, width, height, this)
|
||||
}
|
||||
|
||||
override fun getPreferredSize(): Dimension {
|
||||
return Dimension(image.width, image.height)
|
||||
}
|
||||
}
|
||||
100
plugin-playground/src/main/kotlin/KondoKit/KondoKitUtils.kt
Normal file
100
plugin-playground/src/main/kotlin/KondoKit/KondoKitUtils.kt
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
403
plugin-playground/src/main/kotlin/KondoKit/LootTrackerView.kt
Normal file
403
plugin-playground/src/main/kotlin/KondoKit/LootTrackerView.kt
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.Helpers.formatHtmlLabelText
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.XPTrackerView.wrappedWidget
|
||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import plugin.api.API
|
||||
import rt4.NpcTypeList
|
||||
import rt4.ObjStackNode
|
||||
import rt4.Player
|
||||
import rt4.SceneGraph
|
||||
import java.awt.*
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.text.DecimalFormat
|
||||
import javax.swing.*
|
||||
|
||||
object LootTrackerView {
|
||||
private const val SNAPSHOT_LIFESPAN = 10
|
||||
const val BAG_ICON = 900;
|
||||
val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>()
|
||||
var gePriceMap = loadGEPrices()
|
||||
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
||||
private val npcKillCounts = mutableMapOf<String, Int>()
|
||||
private var totalTrackerWidget: XPWidget? = null
|
||||
var lastConfirmedKillNpcId = -1;
|
||||
|
||||
fun loadGEPrices(): Map<String, String> {
|
||||
return if (plugin.kondoExposed_useLiveGEPrices) {
|
||||
try {
|
||||
println("LootTracker: Loading Remote GE Prices")
|
||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0")
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val inputStream = connection.inputStream
|
||||
val content = inputStream.bufferedReader().use(BufferedReader::readText)
|
||||
|
||||
val items = content.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
val gePrices = mutableMapOf<String, String>()
|
||||
|
||||
for (item in items) {
|
||||
val pairs = item.removeSurrounding("{", "}").split(",")
|
||||
val itemId = pairs.find { it.trim().startsWith("\"item_id\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
val value = pairs.find { it.trim().startsWith("\"value\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
if (itemId != null && value != null) {
|
||||
gePrices[itemId] = value
|
||||
}
|
||||
}
|
||||
|
||||
gePrices
|
||||
} else {
|
||||
emptyMap()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
println("LootTracker: Loading Local GE Prices")
|
||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
||||
.useLines { lines ->
|
||||
val json = lines.joinToString("\n")
|
||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
val gePrices = mutableMapOf<String, String>()
|
||||
|
||||
for (item in items) {
|
||||
val pairs = item.removeSurrounding("{", "}").split(",")
|
||||
val id = pairs.find { it.trim().startsWith("\"id\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
val grandExchangePrice = pairs.find { it.trim().startsWith("\"grand_exchange_price\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
if (id != null && grandExchangePrice != null) {
|
||||
gePrices[id] = grandExchangePrice
|
||||
}
|
||||
}
|
||||
|
||||
gePrices
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun createLootTrackerView(): JPanel {
|
||||
return JPanel().apply {
|
||||
layout = FlowLayout(FlowLayout.CENTER, 0, 5)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
preferredSize = Dimension(270, 700)
|
||||
maximumSize = Dimension(270, 700)
|
||||
minimumSize = Dimension(270, 700)
|
||||
add(Box.createVerticalStrut(5))
|
||||
totalTrackerWidget = createTotalLootWidget()
|
||||
add(wrappedWidget(totalTrackerWidget!!.panel))
|
||||
add(Helpers.Spacer(height = 15))
|
||||
revalidate()
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTotalKills() {
|
||||
totalTrackerWidget?.let {
|
||||
it.totalXpGained += 1
|
||||
it.xpGainedLabel.text = formatHtmlLabelText("Total Count: ", primaryColor, it.totalXpGained.toString(), secondaryColor)
|
||||
it.xpGainedLabel.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTotalValue(newVal: Int) {
|
||||
totalTrackerWidget?.let {
|
||||
it.previousXp += newVal
|
||||
it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor)
|
||||
it.panel.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTotalLootWidget(): XPWidget {
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(BAG_ICON))
|
||||
val l1 = createLabel(formatHtmlLabelText("Total Value: ", primaryColor, "0" + " gp", secondaryColor))
|
||||
val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor))
|
||||
return XPWidget(
|
||||
skillId = -1,
|
||||
panel = createWidgetPanel(bufferedImageSprite,l2,l1),
|
||||
xpGainedLabel = l2,
|
||||
xpLeftLabel = JLabel(),
|
||||
actionsRemainingLabel = JLabel(),
|
||||
xpPerHourLabel = l1,
|
||||
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
|
||||
totalXpGained = 0,
|
||||
startTime = System.currentTimeMillis(),
|
||||
previousXp = 0
|
||||
)
|
||||
}
|
||||
|
||||
private fun createWidgetPanel(bufferedImageSprite: BufferedImage, l1 : JLabel, l2 : JLabel): Panel {
|
||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||
size = Dimension(width, height)
|
||||
background = WIDGET_COLOR
|
||||
}
|
||||
|
||||
val imageContainer = Panel(FlowLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
add(imageCanvas)
|
||||
}
|
||||
|
||||
return Panel(BorderLayout(5, 5)).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = TOTAL_XP_WIDGET_SIZE
|
||||
add(imageContainer, BorderLayout.WEST)
|
||||
add(createTextPanel(l1,l2), BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTextPanel(l1 : JLabel, l2: JLabel): Panel {
|
||||
return Panel(GridLayout(2, 1, 5, 5)).apply {
|
||||
background = WIDGET_COLOR
|
||||
add(l1)
|
||||
add(l2)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLabel(text: String): JLabel {
|
||||
return JLabel(text).apply {
|
||||
font = Font("Arial", Font.PLAIN, 11)
|
||||
horizontalAlignment = JLabel.LEFT
|
||||
}
|
||||
}
|
||||
|
||||
private fun addItemToLootPanel(lootTrackerView: JPanel, drop: Item, npcName: String) {
|
||||
findLootItemsPanel(lootTrackerView, npcName)?.let { lootPanel ->
|
||||
val itemQuantities = lootItemPanels.getOrPut(npcName) { mutableMapOf() }
|
||||
val newQuantity = itemQuantities.merge(drop.id, drop.quantity, Int::plus) ?: drop.quantity
|
||||
|
||||
lootPanel.components.filterIsInstance<JPanel>().find { it.getClientProperty("itemId") == drop.id }
|
||||
?.let { updateItemPanelIcon(it, drop.id, newQuantity) }
|
||||
?: addNewItemToPanel(lootPanel, drop, newQuantity)
|
||||
|
||||
// Recalculate lootPanel size based on the number of unique items.
|
||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1)
|
||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
||||
|
||||
lootPanel.revalidate()
|
||||
lootPanel.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addNewItemToPanel(lootPanel: JPanel, drop: Item, newQuantity: Int) {
|
||||
val itemPanel = createItemPanel(drop.id, newQuantity)
|
||||
lootPanel.add(itemPanel)
|
||||
}
|
||||
|
||||
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 0, 0))
|
||||
return FixedSizePanel(Dimension(36, 32)).apply {
|
||||
preferredSize = Dimension(36, 32)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
add(ImageCanvas(bufferedImageSprite).apply {
|
||||
preferredSize = Dimension(36, 32)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
}, BorderLayout.CENTER)
|
||||
putClientProperty("itemId", itemId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateItemPanelIcon(panel: JPanel, itemId: Int, quantity: Int) {
|
||||
panel.removeAll()
|
||||
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
|
||||
panel.revalidate()
|
||||
panel.repaint()
|
||||
}
|
||||
|
||||
private fun updateKillCountLabel(lootTrackerPanel: JPanel, npcName: String) {
|
||||
lootTrackerPanel.components.filterIsInstance<JPanel>().forEach { childFramePanel ->
|
||||
(childFramePanel.components.firstOrNull { it is JPanel } as? JPanel)?.components
|
||||
?.filterIsInstance<JLabel>()?.find { it.name == "killCountLabel_$npcName" }
|
||||
?.apply {
|
||||
text = formatHtmlLabelText(npcName, secondaryColor, " x ${npcKillCounts[npcName]}", primaryColor)
|
||||
revalidate()
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateValueLabel(lootTrackerPanel: JPanel, valueOfNewDrops: String, npcName: String) {
|
||||
lootTrackerPanel.components.filterIsInstance<JPanel>().forEach { childFramePanel ->
|
||||
(childFramePanel.components.firstOrNull { it is JPanel } as? JPanel)?.components
|
||||
?.filterIsInstance<JLabel>()?.find { it.name == "valueLabel_$npcName" }
|
||||
?.apply {
|
||||
val newValue = (getClientProperty("val") as? Int ?: 0) + valueOfNewDrops.toInt()
|
||||
text = "${formatValue(newValue)} gp"
|
||||
putClientProperty("val", newValue)
|
||||
revalidate()
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatValue(value: Int): String {
|
||||
return when {
|
||||
value >= 1_000_000 -> "%.1fM".format(value / 1_000_000.0)
|
||||
value >= 10_000 -> "%.1fK".format(value / 1_000.0)
|
||||
else -> DecimalFormat("#,###").format(value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findLootItemsPanel(container: Container, npcName: String): JPanel? {
|
||||
return container.components.filterIsInstance<JPanel>().firstOrNull { it.name == "LOOT_ITEMS_$npcName" }
|
||||
?: container.components.filterIsInstance<Container>().mapNotNull { findLootItemsPanel(it, npcName) }.firstOrNull()
|
||||
}
|
||||
|
||||
|
||||
fun onPostClientTick(lootTrackerView: JPanel) {
|
||||
val toRemove = mutableListOf<Int>()
|
||||
|
||||
npcDeathSnapshots.entries.forEach { (npcId, snapshot) ->
|
||||
val postDeathSnapshot = takeGroundSnapshot(Pair(snapshot.location.first, snapshot.location.second))
|
||||
val newDrops = postDeathSnapshot.subtract(snapshot.items)
|
||||
|
||||
if (newDrops.isNotEmpty()) {
|
||||
val npcName = NpcTypeList.get(npcId).name
|
||||
lastConfirmedKillNpcId = npcId;
|
||||
handleNewDrops(npcName.toString(), newDrops, lootTrackerView)
|
||||
toRemove.add(npcId)
|
||||
} else if (snapshot.age >= SNAPSHOT_LIFESPAN) {
|
||||
toRemove.add(npcId)
|
||||
} else {
|
||||
snapshot.age++
|
||||
}
|
||||
}
|
||||
|
||||
toRemove.forEach { npcDeathSnapshots.remove(it) }
|
||||
}
|
||||
|
||||
|
||||
fun takeGroundSnapshot(location: Pair<Int, Int>): Set<Item> {
|
||||
return getGroundItemsAt(location.first, location.second).toSet()
|
||||
}
|
||||
|
||||
private fun getGroundItemsAt(x: Int, z: Int): List<Item> {
|
||||
val objstacknodeLL = SceneGraph.objStacks[Player.plane][x][z] ?: return emptyList()
|
||||
val items = mutableListOf<Item>()
|
||||
var itemNode = objstacknodeLL.head() as ObjStackNode?
|
||||
while (itemNode != null) {
|
||||
items.add(Item(itemNode.value.type, itemNode.value.amount))
|
||||
itemNode = objstacknodeLL.next() as ObjStackNode?
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
private fun handleNewDrops(npcName: String, newDrops: Set<Item>, lootTrackerView: JPanel) {
|
||||
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
||||
} ?: run {
|
||||
// Panel doesn't exist, so create and add it
|
||||
lootTrackerView.add(createLootFrame(npcName))
|
||||
lootTrackerView.add(Helpers.Spacer(height = 15))
|
||||
lootTrackerView.revalidate()
|
||||
lootTrackerView.repaint()
|
||||
}
|
||||
|
||||
npcKillCounts[npcName] = npcKillCounts.getOrDefault(npcName, 0) + 1
|
||||
updateKillCountLabel(lootTrackerView, npcName)
|
||||
updateTotalKills()
|
||||
newDrops.forEach { drop ->
|
||||
val geValue = (gePriceMap[drop.id.toString()]?.toInt() ?: 0) * drop.quantity
|
||||
updateValueLabel(lootTrackerView, geValue.toString(), npcName)
|
||||
addItemToLootPanel(lootTrackerView, drop, npcName)
|
||||
updateTotalValue(geValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLootFrame(npcName: String): JPanel {
|
||||
val childFramePanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = Dimension(270, 0)
|
||||
maximumSize = Dimension(270, 700)
|
||||
}
|
||||
|
||||
val labelPanel = JPanel(BorderLayout()).apply {
|
||||
background = Color(21, 21, 21)
|
||||
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
||||
maximumSize = Dimension(270, 24)
|
||||
minimumSize = maximumSize
|
||||
preferredSize = maximumSize
|
||||
}
|
||||
|
||||
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
|
||||
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
|
||||
foreground = Color(200, 200, 200)
|
||||
font = Font("Arial", Font.PLAIN, 12)
|
||||
horizontalAlignment = JLabel.LEFT
|
||||
name = "killCountLabel_$npcName"
|
||||
}
|
||||
|
||||
val valueLabel = JLabel("0 gp").apply {
|
||||
foreground = Color(200, 200, 200)
|
||||
font = Font("Arial", Font.PLAIN, 12)
|
||||
horizontalAlignment = JLabel.RIGHT
|
||||
name = "valueLabel_$npcName"
|
||||
}
|
||||
|
||||
labelPanel.add(countLabel, BorderLayout.WEST)
|
||||
labelPanel.add(valueLabel, BorderLayout.EAST)
|
||||
|
||||
// Panel to hold loot items, using GridLayout to manage rows and columns.
|
||||
val lootPanel = JPanel().apply {
|
||||
background = WIDGET_COLOR
|
||||
border = BorderFactory.createLineBorder(WIDGET_COLOR, 5)
|
||||
name = "LOOT_ITEMS_$npcName"
|
||||
layout = GridLayout(0, 6, 1, 1) // 6 columns, rows will be added dynamically
|
||||
}
|
||||
|
||||
lootItemPanels[npcName] = mutableMapOf()
|
||||
|
||||
// Determine number of items and adjust size of lootPanel
|
||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1) // Height per row = 36 + spacing
|
||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
||||
|
||||
childFramePanel.add(labelPanel)
|
||||
childFramePanel.add(lootPanel)
|
||||
childFramePanel.add(lootPanel)
|
||||
|
||||
return childFramePanel
|
||||
}
|
||||
|
||||
|
||||
class FixedSizePanel(private val fixedSize: Dimension) : JPanel() {
|
||||
override fun getPreferredSize(): Dimension {
|
||||
return fixedSize
|
||||
}
|
||||
|
||||
override fun getMinimumSize(): Dimension {
|
||||
return fixedSize
|
||||
}
|
||||
|
||||
override fun getMaximumSize(): Dimension {
|
||||
return fixedSize
|
||||
}
|
||||
}
|
||||
|
||||
data class GroundSnapshot(val items: Set<Item>, val location: Pair<Int, Int>, var age: Int)
|
||||
data class Item(val id: Int, val quantity: Int)
|
||||
}
|
||||
53
plugin-playground/src/main/kotlin/KondoKit/ProgressBar.kt
Normal file
53
plugin-playground/src/main/kotlin/KondoKit/ProgressBar.kt
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package KondoKit
|
||||
|
||||
import java.awt.Canvas
|
||||
import java.awt.Color
|
||||
import java.awt.Font
|
||||
import java.awt.Graphics
|
||||
|
||||
class ProgressBar(
|
||||
private var progress: Double,
|
||||
private val barColor: Color,
|
||||
private var currentLevel: Int = 0,
|
||||
private var nextLevel: Int = 1
|
||||
) : Canvas() {
|
||||
|
||||
init {
|
||||
font = Font("Arial", Font.PLAIN, 12)
|
||||
}
|
||||
|
||||
override fun paint(g: Graphics) {
|
||||
super.paint(g)
|
||||
|
||||
// Draw the filled part of the progress bar
|
||||
g.color = barColor
|
||||
val width = (progress * this.width / 100).toInt()
|
||||
g.fillRect(0, 0, width, this.height)
|
||||
|
||||
// Draw the unfilled part of the progress bar
|
||||
g.color = Color(100, 100, 100)
|
||||
g.fillRect(width, 0, this.width - width, this.height)
|
||||
|
||||
// Draw the current level on the far left
|
||||
g.color = Color(255, 255, 255)
|
||||
g.drawString("Lvl. $currentLevel", 5, this.height / 2 + 4)
|
||||
|
||||
// Draw the percentage in the middle
|
||||
val percentageText = String.format("%.2f%%", progress)
|
||||
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
|
||||
g.drawString(percentageText, (this.width - percentageWidth) / 2, this.height / 2 + 4)
|
||||
|
||||
// Draw the next level on the far right
|
||||
val nextLevelText = "Lvl. $nextLevel"
|
||||
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
|
||||
g.drawString(nextLevelText, this.width - nextLevelWidth - 5, this.height / 2 + 4)
|
||||
}
|
||||
|
||||
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
|
||||
this.progress = newProgress
|
||||
this.currentLevel = currentLevel
|
||||
this.nextLevel = nextLevel
|
||||
if(isVisible)
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.KondoKitUtils.convertValue
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import plugin.Plugin
|
||||
import plugin.PluginRepository
|
||||
import java.awt.*
|
||||
import java.lang.reflect.Field
|
||||
import java.util.*
|
||||
import java.util.Timer
|
||||
import javax.swing.*
|
||||
|
||||
object ReflectiveEditorView {
|
||||
fun createReflectiveEditorView(): JPanel {
|
||||
val reflectiveEditorPanel = JPanel(BorderLayout())
|
||||
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
||||
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
||||
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
||||
return reflectiveEditorPanel
|
||||
}
|
||||
|
||||
fun addPlugins(reflectiveEditorView: JPanel) {
|
||||
try {
|
||||
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
||||
loadedPluginsField.isAccessible = true
|
||||
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
||||
|
||||
for ((_, plugin) in loadedPlugins) {
|
||||
addPluginToEditor(reflectiveEditorView, plugin as Plugin)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
reflectiveEditorView.revalidate()
|
||||
reflectiveEditorView.repaint()
|
||||
}
|
||||
|
||||
private fun addPluginToEditor(reflectiveEditorView: JPanel, plugin: Any) {
|
||||
reflectiveEditorView.layout = BoxLayout(reflectiveEditorView, BoxLayout.Y_AXIS)
|
||||
|
||||
val fieldNotifier = KondoKitUtils.FieldNotifier(plugin)
|
||||
val exposedFields = KondoKitUtils.getKondoExposedFields(plugin)
|
||||
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
val packageName = plugin.javaClass.`package`.name
|
||||
val labelPanel = JPanel(BorderLayout())
|
||||
labelPanel.maximumSize = Dimension(Int.MAX_VALUE, 30) // Adjust height to be minimal
|
||||
labelPanel.background = VIEW_BACKGROUND_COLOR // Ensure it matches the overall background
|
||||
labelPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
||||
|
||||
val label = JLabel("$packageName", SwingConstants.CENTER)
|
||||
label.foreground = primaryColor
|
||||
label.font = Font("Arial", Font.BOLD, 14)
|
||||
labelPanel.add(label, BorderLayout.CENTER)
|
||||
reflectiveEditorView.add(labelPanel)
|
||||
}
|
||||
|
||||
for (field in exposedFields) {
|
||||
field.isAccessible = true
|
||||
|
||||
val fieldPanel = JPanel()
|
||||
fieldPanel.layout = GridBagLayout()
|
||||
fieldPanel.background = WIDGET_COLOR // Match the background for minimal borders
|
||||
fieldPanel.foreground = secondaryColor
|
||||
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0) // No visible border, just spacing
|
||||
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
||||
|
||||
val gbc = GridBagConstraints()
|
||||
gbc.insets = Insets(0, 5, 0, 5) // Less padding, more minimal spacing
|
||||
|
||||
val label = JLabel(field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).capitalize())
|
||||
label.foreground = secondaryColor
|
||||
gbc.gridx = 0
|
||||
gbc.gridy = 0
|
||||
gbc.weightx = 0.0
|
||||
gbc.anchor = GridBagConstraints.WEST
|
||||
fieldPanel.add(label, gbc)
|
||||
|
||||
val textField = JTextField(field.get(plugin)?.toString() ?: "")
|
||||
textField.background = VIEW_BACKGROUND_COLOR
|
||||
textField.foreground = secondaryColor
|
||||
textField.border = BorderFactory.createLineBorder(WIDGET_COLOR, 1) // Subtle border
|
||||
gbc.gridx = 1
|
||||
gbc.gridy = 0
|
||||
gbc.weightx = 1.0
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL
|
||||
fieldPanel.add(textField, gbc)
|
||||
|
||||
val applyButton = JButton("Apply")
|
||||
applyButton.background = primaryColor
|
||||
applyButton.foreground = VIEW_BACKGROUND_COLOR
|
||||
applyButton.border = BorderFactory.createLineBorder(primaryColor, 1)
|
||||
gbc.gridx = 2
|
||||
gbc.gridy = 0
|
||||
gbc.weightx = 0.0
|
||||
gbc.fill = GridBagConstraints.NONE
|
||||
applyButton.addActionListener {
|
||||
try {
|
||||
val newValue = convertValue(field.type, field.genericType, textField.text)
|
||||
fieldNotifier.setFieldValue(field, newValue)
|
||||
JOptionPane.showMessageDialog(
|
||||
null,
|
||||
"${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)} updated successfully!"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
JOptionPane.showMessageDialog(
|
||||
null,
|
||||
"Failed to update ${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)}: ${e.message}",
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fieldPanel.add(applyButton, gbc)
|
||||
reflectiveEditorView.add(fieldPanel)
|
||||
|
||||
var previousValue = field.get(plugin)?.toString()
|
||||
val timer = Timer()
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
val currentValue = field.get(plugin)?.toString()
|
||||
if (currentValue != previousValue) {
|
||||
previousValue = currentValue
|
||||
SwingUtilities.invokeLater {
|
||||
fieldNotifier.notifyFieldChange(field, currentValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0, 1000)
|
||||
|
||||
fieldNotifier.addObserver(object : KondoKitUtils.FieldObserver {
|
||||
override fun onFieldChange(field: Field, newValue: Any?) {
|
||||
if (field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).equals(label.text, ignoreCase = true)) {
|
||||
textField.text = newValue?.toString() ?: ""
|
||||
textField.revalidate()
|
||||
textField.repaint()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
reflectiveEditorView.add(Box.createVerticalStrut(10))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package KondoKit
|
||||
|
||||
import rt4.GlIndexedSprite
|
||||
import rt4.GlSprite
|
||||
import rt4.SoftwareIndexedSprite
|
||||
import rt4.SoftwareSprite
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
object SpriteToBufferedImage {
|
||||
/**
|
||||
* Converts a SoftwareSprite back into a BufferedImage.
|
||||
*
|
||||
* @param sprite The sprite to be converted.
|
||||
* @return The BufferedImage created from the sprite.
|
||||
*/
|
||||
fun convertToBufferedImage(sprite: SoftwareSprite): BufferedImage {
|
||||
val width = sprite.width
|
||||
val height = sprite.height
|
||||
val pixels = sprite.pixels
|
||||
|
||||
// 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
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
// Get the index from the sprite's pixel array
|
||||
val index = pixels[y * width + x].toInt() and 0xFF
|
||||
// Map the index to a color in the palette
|
||||
val color = palette[index]
|
||||
// Set the ARGB color in the BufferedImage
|
||||
image.setRGB(x, y, color)
|
||||
}
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
fun convertToBufferedImage(sprite: GlIndexedSprite): 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
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
// Get the index from the sprite's pixel array
|
||||
val index = pixels[y * width + x].toInt() and 0xFF
|
||||
// Map the index to a color in the palette
|
||||
val color = palette[index]
|
||||
// Set the ARGB color in the BufferedImage
|
||||
image.setRGB(x, y, color)
|
||||
}
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
|
||||
fun convertToBufferedImage(sprite: GlSprite): BufferedImage {
|
||||
val width = sprite.width
|
||||
val height = sprite.height
|
||||
val pixels = sprite.pixels
|
||||
|
||||
// Create a BufferedImage with ARGB color model
|
||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
||||
|
||||
if(pixels == null) {
|
||||
return image
|
||||
}
|
||||
|
||||
// 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 getBufferedImageFromSprite(sprite: Any?) : BufferedImage {
|
||||
return when(sprite){
|
||||
is GlSprite -> convertToBufferedImage(sprite)
|
||||
is SoftwareSprite -> convertToBufferedImage(sprite)
|
||||
is SoftwareIndexedSprite -> convertToBufferedImage(sprite)
|
||||
is GlIndexedSprite -> convertToBufferedImage(sprite)
|
||||
else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
|
||||
}
|
||||
}
|
||||
}
|
||||
357
plugin-playground/src/main/kotlin/KondoKit/XPTrackerView.kt
Normal file
357
plugin-playground/src/main/kotlin/KondoKit/XPTrackerView.kt
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.Helpers.formatHtmlLabelText
|
||||
import KondoKit.Helpers.formatNumber
|
||||
import KondoKit.Helpers.getProgressBarColor
|
||||
import KondoKit.Helpers.getSpriteId
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.plugin.Companion.IMAGE_SIZE
|
||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_SIZE
|
||||
import KondoKit.plugin.Companion.kondoExposed_playerXPMultiplier
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import KondoKit.plugin.StateManager.totalXPWidget
|
||||
import XPGlobesPlugin.XPTable
|
||||
import plugin.api.API
|
||||
import java.awt.*
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.swing.Box
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
|
||||
object XPTrackerView {
|
||||
|
||||
private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4)
|
||||
|
||||
val npcHitpointsMap: Map<Int, Int> = try {
|
||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
||||
.useLines { lines ->
|
||||
val json = lines.joinToString("\n")
|
||||
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
||||
val map = mutableMapOf<Int, Int>()
|
||||
|
||||
for (pair in pairs) {
|
||||
val keyValue = pair.split(":")
|
||||
val id = keyValue[0].trim().trim('\"').toIntOrNull()
|
||||
val hitpoints = keyValue[1].trim()
|
||||
|
||||
if (id != null && hitpoints.isNotEmpty()) {
|
||||
map[id] = hitpoints.toIntOrNull() ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("XPTracker Error parsing NPC HP: ${e.message}")
|
||||
emptyMap()
|
||||
}
|
||||
|
||||
fun updateWidget(xpWidget: XPWidget, xp: Int) {
|
||||
val (currentLevel, xpGainedSinceLastLevel) = XPTable.getLevelForXp(xp)
|
||||
|
||||
var xpGainedSinceLastUpdate = xp - xpWidget.previousXp
|
||||
xpWidget.totalXpGained += xpGainedSinceLastUpdate
|
||||
updateTotalXPWidget(xpGainedSinceLastUpdate)
|
||||
|
||||
val progress: Double
|
||||
if (currentLevel >= 99) {
|
||||
progress = 100.0 // Set progress to 100% if the level is 99 or above
|
||||
xpWidget.xpLeftLabel.text = "" // Hide XP Left when level is 99
|
||||
xpWidget.actionsRemainingLabel.text = ""
|
||||
} else {
|
||||
val nextLevelXp = XPTable.getXpRequiredForLevel(currentLevel + 1)
|
||||
val xpLeft = nextLevelXp - xp
|
||||
progress = xpGainedSinceLastLevel.toDouble() / (nextLevelXp - XPTable.getXpRequiredForLevel(currentLevel)) * 100
|
||||
val xpLeftstr = formatNumber(xpLeft)
|
||||
xpWidget.xpLeftLabel.text = formatHtmlLabelText("XP Left: ", primaryColor, xpLeftstr, secondaryColor)
|
||||
if(COMBAT_SKILLS.contains(xpWidget.skillId)) {
|
||||
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
||||
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
||||
val xpPerKill = when (xpWidget.skillId) {
|
||||
3 -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
||||
else -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
||||
}
|
||||
val remainingKills = xpLeft / xpPerKill
|
||||
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
||||
}
|
||||
} else {
|
||||
if(xpGainedSinceLastUpdate == 0)
|
||||
xpGainedSinceLastUpdate = 1 // Avoid possible divide by 0
|
||||
val remainingActions = (xpLeft / xpGainedSinceLastUpdate).coerceAtLeast(1)
|
||||
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Actions: ", primaryColor, remainingActions.toString(), secondaryColor)
|
||||
}
|
||||
}
|
||||
|
||||
val formattedXp = formatNumber(xpWidget.totalXpGained)
|
||||
xpWidget.xpGainedLabel.text = formatHtmlLabelText("XP Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||
|
||||
// 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.previousXp = xp
|
||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
xpWidget.panel.repaint()
|
||||
}
|
||||
|
||||
|
||||
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
||||
val totalXPWidget = plugin.StateManager.totalXPWidget ?: return
|
||||
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
||||
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
||||
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
totalXPWidget.panel.repaint()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun createTotalXPWidget(): XPWidget {
|
||||
val widgetPanel = Panel().apply {
|
||||
layout = BorderLayout(5, 5)
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = TOTAL_XP_WIDGET_SIZE
|
||||
maximumSize = TOTAL_XP_WIDGET_SIZE
|
||||
minimumSize = TOTAL_XP_WIDGET_SIZE
|
||||
}
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(898))
|
||||
|
||||
|
||||
val imageContainer = Panel(FlowLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = IMAGE_SIZE
|
||||
maximumSize = IMAGE_SIZE
|
||||
minimumSize = IMAGE_SIZE
|
||||
size = IMAGE_SIZE
|
||||
}
|
||||
|
||||
bufferedImageSprite.let { image ->
|
||||
val imageCanvas = ImageCanvas(image).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = Dimension(image.width, image.height)
|
||||
maximumSize = Dimension(image.width, image.height)
|
||||
minimumSize = Dimension(image.width, image.height)
|
||||
size = Dimension(image.width, image.height)
|
||||
}
|
||||
|
||||
imageContainer.add(imageCanvas)
|
||||
imageContainer.size = Dimension(image.width, image.height)
|
||||
|
||||
imageContainer.revalidate()
|
||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
imageContainer.repaint()
|
||||
}
|
||||
|
||||
val textPanel = Panel().apply {
|
||||
layout = GridLayout(2, 1, 5, 5)
|
||||
background = WIDGET_COLOR
|
||||
}
|
||||
|
||||
val font = Font("Arial", Font.PLAIN, 11)
|
||||
|
||||
val xpGainedLabel = JLabel(
|
||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
}
|
||||
|
||||
val xpPerHourLabel = JLabel(
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
}
|
||||
|
||||
textPanel.add(xpGainedLabel)
|
||||
textPanel.add(xpPerHourLabel)
|
||||
|
||||
widgetPanel.add(imageContainer, BorderLayout.WEST)
|
||||
widgetPanel.add(textPanel, BorderLayout.CENTER)
|
||||
|
||||
return XPWidget(
|
||||
skillId = -1,
|
||||
panel = widgetPanel,
|
||||
xpGainedLabel = xpGainedLabel,
|
||||
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
},
|
||||
xpPerHourLabel = xpPerHourLabel,
|
||||
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
|
||||
totalXpGained = 0,
|
||||
startTime = System.currentTimeMillis(),
|
||||
previousXp = 0,
|
||||
actionsRemainingLabel = JLabel(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun createXPTrackerView(): JPanel? {
|
||||
val widgetViewPanel = JPanel()
|
||||
widgetViewPanel.layout = BoxLayout(widgetViewPanel, BoxLayout.Y_AXIS)
|
||||
widgetViewPanel.background = VIEW_BACKGROUND_COLOR
|
||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||
|
||||
totalXPWidget = createTotalXPWidget()
|
||||
widgetViewPanel.add(wrappedWidget(totalXPWidget!!.panel))
|
||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||
|
||||
return widgetViewPanel
|
||||
}
|
||||
|
||||
fun createXPWidget(skillId: Int, previousXp: Int): XPWidget {
|
||||
val widgetPanel = Panel().apply {
|
||||
layout = BorderLayout(5, 5)
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = WIDGET_SIZE
|
||||
maximumSize = WIDGET_SIZE
|
||||
minimumSize = WIDGET_SIZE
|
||||
}
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(getSpriteId(skillId)))
|
||||
val imageContainer = Panel(FlowLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = IMAGE_SIZE
|
||||
maximumSize = IMAGE_SIZE
|
||||
minimumSize = IMAGE_SIZE
|
||||
size = IMAGE_SIZE
|
||||
}
|
||||
|
||||
bufferedImageSprite.let { image ->
|
||||
val imageCanvas = ImageCanvas(image).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = Dimension(image.width, image.height)
|
||||
maximumSize = Dimension(image.width, image.height)
|
||||
minimumSize = Dimension(image.width, image.height)
|
||||
size = Dimension(image.width, image.height) // Explicitly set the size
|
||||
}
|
||||
|
||||
imageContainer.add(imageCanvas)
|
||||
imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size
|
||||
|
||||
imageContainer.revalidate()
|
||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
imageContainer.repaint()
|
||||
}
|
||||
|
||||
val textPanel = Panel().apply {
|
||||
layout = GridLayout(2, 2, 5, 5)
|
||||
background = WIDGET_COLOR
|
||||
}
|
||||
|
||||
val font = Font("Arial", Font.PLAIN, 11)
|
||||
|
||||
val xpGainedLabel = JLabel(
|
||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
}
|
||||
|
||||
val xpLeftLabel = JLabel(
|
||||
formatHtmlLabelText("XP Left: ", primaryColor, "0K", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
}
|
||||
|
||||
val xpPerHourLabel = JLabel(
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
}
|
||||
|
||||
val killsLabel = JLabel(
|
||||
formatHtmlLabelText("Kills: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
}
|
||||
|
||||
val levelPanel = Panel().apply {
|
||||
layout = BorderLayout(5, 0)
|
||||
background = Color(43, 43, 43)
|
||||
}
|
||||
|
||||
val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply {
|
||||
preferredSize = Dimension(160, 22)
|
||||
}
|
||||
|
||||
levelPanel.add(progressBarPanel, BorderLayout.CENTER)
|
||||
|
||||
textPanel.add(xpGainedLabel)
|
||||
textPanel.add(xpLeftLabel)
|
||||
textPanel.add(xpPerHourLabel)
|
||||
textPanel.add(killsLabel)
|
||||
|
||||
widgetPanel.add(imageContainer, BorderLayout.WEST)
|
||||
widgetPanel.add(textPanel, BorderLayout.CENTER)
|
||||
widgetPanel.add(levelPanel, BorderLayout.SOUTH)
|
||||
|
||||
widgetPanel.revalidate()
|
||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
widgetPanel.repaint()
|
||||
|
||||
return XPWidget(
|
||||
skillId = skillId,
|
||||
panel = widgetPanel,
|
||||
xpGainedLabel = xpGainedLabel,
|
||||
xpLeftLabel = xpLeftLabel,
|
||||
xpPerHourLabel = xpPerHourLabel,
|
||||
progressBar = progressBarPanel,
|
||||
totalXpGained = 0,
|
||||
actionsRemainingLabel = killsLabel,
|
||||
startTime = System.currentTimeMillis(),
|
||||
previousXp = previousXp
|
||||
)
|
||||
}
|
||||
|
||||
fun wrappedWidget(component: Component, padding: Int = 7): Panel {
|
||||
val outerPanelSize = Dimension(
|
||||
component.preferredSize.width + 2 * padding,
|
||||
component.preferredSize.height + 2 * padding
|
||||
)
|
||||
val outerPanel = Panel(GridBagLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = outerPanelSize
|
||||
maximumSize = outerPanelSize
|
||||
minimumSize = outerPanelSize
|
||||
}
|
||||
val innerPanel = Panel(BorderLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = component.preferredSize
|
||||
maximumSize = component.preferredSize
|
||||
minimumSize = component.preferredSize
|
||||
add(component, BorderLayout.CENTER)
|
||||
}
|
||||
val gbc = GridBagConstraints().apply {
|
||||
anchor = GridBagConstraints.CENTER
|
||||
}
|
||||
outerPanel.add(innerPanel, gbc)
|
||||
return outerPanel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
data class XPWidget(
|
||||
val panel: Panel,
|
||||
val skillId: Int,
|
||||
val xpGainedLabel: JLabel,
|
||||
val xpLeftLabel: JLabel,
|
||||
val xpPerHourLabel: JLabel,
|
||||
val actionsRemainingLabel: JLabel,
|
||||
val progressBar: ProgressBar,
|
||||
var totalXpGained: Int = 0,
|
||||
var startTime: Long = System.currentTimeMillis(),
|
||||
var previousXp: Int = 0
|
||||
)
|
||||
130818
plugin-playground/src/main/kotlin/KondoKit/item_configs.json
Normal file
130818
plugin-playground/src/main/kotlin/KondoKit/item_configs.json
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
381
plugin-playground/src/main/kotlin/KondoKit/plugin.kt
Normal file
381
plugin-playground/src/main/kotlin/KondoKit/plugin.kt
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.Constants.COMBAT_LVL_SPRITE
|
||||
import KondoKit.Helpers.formatHtmlLabelText
|
||||
import KondoKit.Helpers.formatNumber
|
||||
import KondoKit.Helpers.getSpriteId
|
||||
import KondoKit.HiscoresView.createHiscoreSearchView
|
||||
import KondoKit.LootTrackerView.BAG_ICON
|
||||
import KondoKit.LootTrackerView.createLootTrackerView
|
||||
import KondoKit.LootTrackerView.npcDeathSnapshots
|
||||
import KondoKit.LootTrackerView.onPostClientTick
|
||||
import KondoKit.LootTrackerView.takeGroundSnapshot
|
||||
import KondoKit.ReflectiveEditorView.addPlugins
|
||||
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.XPTrackerView.createTotalXPWidget
|
||||
import KondoKit.XPTrackerView.createXPTrackerView
|
||||
import KondoKit.XPTrackerView.createXPWidget
|
||||
import KondoKit.XPTrackerView.updateWidget
|
||||
import KondoKit.XPTrackerView.wrappedWidget
|
||||
import KondoKit.plugin.StateManager.initialXP
|
||||
import KondoKit.plugin.StateManager.totalXPWidget
|
||||
import KondoKit.plugin.StateManager.xpWidgets
|
||||
import plugin.Plugin
|
||||
import plugin.annotations.PluginMeta
|
||||
import plugin.api.*
|
||||
import plugin.api.API.*
|
||||
import plugin.api.FontColor.fromColor
|
||||
import rt4.GameShell
|
||||
import rt4.GameShell.frame
|
||||
import rt4.GlRenderer
|
||||
import rt4.InterfaceList
|
||||
import rt4.Player
|
||||
import rt4.client.js5Archive8
|
||||
import rt4.client.mainLoadState
|
||||
import java.awt.*
|
||||
import java.awt.event.ActionListener
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.*
|
||||
|
||||
@PluginMeta(
|
||||
author = "downthecrop",
|
||||
description = "A plugin that adds a right-side panel with custom widgets and navigation.",
|
||||
version = 1.0
|
||||
)
|
||||
|
||||
class plugin : Plugin() {
|
||||
companion object {
|
||||
val WIDGET_SIZE = Dimension(270, 55)
|
||||
val TOTAL_XP_WIDGET_SIZE = Dimension(270, 30)
|
||||
val IMAGE_SIZE = Dimension(20, 20)
|
||||
val WIDGET_COLOR = Color(27, 27, 27)
|
||||
val VIEW_BACKGROUND_COLOR = Color(37, 37, 37)
|
||||
val primaryColor = Color(129, 129, 129) // Color for "XP Gained:"
|
||||
val secondaryColor = Color(226, 226, 226) // Color for "0"
|
||||
var kondoExposed_useLiveGEPrices = true
|
||||
var kondoExposed_playerXPMultiplier = 5
|
||||
const val FIXED_WIDTH = 782
|
||||
const val SCROLLPANE_WIDTH = 340
|
||||
private const val WRENCH_ICON = 907
|
||||
private const val LVL_ICON = 898
|
||||
private const val LOOT_ICON = 777
|
||||
private const val MAG_SPRITE = 1423
|
||||
private lateinit var cardLayout: CardLayout
|
||||
private lateinit var mainContentPanel: Panel
|
||||
private var scrollPane: JScrollPane? = null
|
||||
private var hiScoreView: JPanel? = null
|
||||
private var reflectiveEditorView: JPanel? = null
|
||||
private var lootTrackerView: JPanel? = null
|
||||
private var xpTrackerView: JPanel? = null
|
||||
private var accumulatedTime = 0L
|
||||
private const val tickInterval = 600L
|
||||
private var pluginsReloaded = false
|
||||
private var loginScreen = 160;
|
||||
private var lastLogin = ""
|
||||
private var initialized = false;
|
||||
}
|
||||
|
||||
fun allSpritesLoaded() : Boolean {
|
||||
// Check all skill sprites
|
||||
try{
|
||||
for (i in 0 until 24) {
|
||||
if(!js5Archive8.isFileReady(getSpriteId(i))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
val otherIcons = arrayOf(LVL_ICON, MAG_SPRITE, LOOT_ICON, WRENCH_ICON, COMBAT_LVL_SPRITE, BAG_ICON);
|
||||
for (icon in otherIcons) {
|
||||
if(!js5Archive8.isFileReady(icon)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (e : Exception){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
override fun OnLogin() {
|
||||
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
|
||||
// if we logged in with a new character
|
||||
// we need to reset the trackers
|
||||
xpTrackerView?.removeAll()
|
||||
totalXPWidget = createTotalXPWidget()
|
||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||
xpTrackerView?.add(wrappedWidget(totalXPWidget!!.panel))
|
||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||
initialXP.clear()
|
||||
xpWidgets.clear()
|
||||
|
||||
xpTrackerView?.revalidate()
|
||||
if (StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
xpTrackerView?.repaint()
|
||||
}
|
||||
lastLogin = Player.usernameInput.toString()
|
||||
}
|
||||
|
||||
private fun UpdateDisplaySettings() {
|
||||
val mode = GetWindowMode()
|
||||
when (mode) {
|
||||
WindowMode.FIXED -> {
|
||||
if (frame.width < FIXED_WIDTH + SCROLLPANE_WIDTH) {
|
||||
frame.setSize(FIXED_WIDTH + SCROLLPANE_WIDTH, frame.height)
|
||||
}
|
||||
val difference = frame.width - (FIXED_WIDTH + SCROLLPANE_WIDTH)
|
||||
GameShell.leftMargin = difference / 2
|
||||
}
|
||||
WindowMode.RESIZABLE -> {
|
||||
GameShell.canvasWidth -= SCROLLPANE_WIDTH
|
||||
}
|
||||
}
|
||||
scrollPane?.revalidate()
|
||||
scrollPane?.repaint()
|
||||
}
|
||||
|
||||
fun OnKondoValueUpdated(){
|
||||
StoreData("kondoUseRemoteGE", kondoExposed_useLiveGEPrices)
|
||||
StoreData("kondoPlayerXPMultiplier", kondoExposed_playerXPMultiplier)
|
||||
LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices()
|
||||
}
|
||||
|
||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||
if (currentEntries != null) {
|
||||
for ((index, entry) in currentEntries.withIndex()) {
|
||||
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
|
||||
val input = entry.subject
|
||||
val username = input
|
||||
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "")
|
||||
.replace(Regex("<img=\\d+>"), "")
|
||||
.split(" ") // Split by spaces
|
||||
.first() // Take the first part, which is the username
|
||||
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(username.replace(" ","_")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchHiscore(username: String): Runnable {
|
||||
return Runnable {
|
||||
cardLayout.show(mainContentPanel, "HISCORE_SEARCH_VIEW")
|
||||
StateManager.focusedView = "HISCORE_SEARCH_VIEW"
|
||||
val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) }
|
||||
|
||||
customSearchField?.searchPlayer(username) ?: run {
|
||||
println("searchView is null or CustomSearchField creation failed.")
|
||||
}
|
||||
hiScoreView?.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun OnPluginsReloaded(): Boolean {
|
||||
if (!initialized) return true
|
||||
|
||||
UpdateDisplaySettings()
|
||||
|
||||
frame.remove(scrollPane)
|
||||
frame.layout = BorderLayout()
|
||||
frame.add(scrollPane, BorderLayout.EAST)
|
||||
|
||||
// Clear or regenerate the reflectiveEditorView
|
||||
reflectiveEditorView?.removeAll()
|
||||
reflectiveEditorView?.revalidate()
|
||||
if(StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW")
|
||||
reflectiveEditorView?.repaint()
|
||||
|
||||
frame.revalidate()
|
||||
frame.repaint()
|
||||
pluginsReloaded = true
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
||||
if (!initialXP.containsKey(skillId)) {
|
||||
initialXP[skillId] = xp
|
||||
return
|
||||
}
|
||||
|
||||
var xpWidget = xpWidgets[skillId]
|
||||
|
||||
if (xpWidget != null) {
|
||||
updateWidget(xpWidget, xp)
|
||||
} else {
|
||||
val previousXp = initialXP[skillId] ?: xp
|
||||
if (xp == initialXP[skillId]) return
|
||||
|
||||
xpWidget = createXPWidget(skillId, previousXp)
|
||||
xpWidgets[skillId] = xpWidget
|
||||
|
||||
xpTrackerView?.add(wrappedWidget(xpWidget.panel))
|
||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||
|
||||
xpTrackerView?.revalidate()
|
||||
if(StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
xpTrackerView?.repaint()
|
||||
|
||||
updateWidget(xpWidget, xp)
|
||||
}
|
||||
}
|
||||
|
||||
override fun Draw(timeDelta: Long) {
|
||||
if (GlRenderer.enabled && GlRenderer.canvasWidth != GameShell.canvasWidth) {
|
||||
GlRenderer.canvasWidth = GameShell.canvasWidth
|
||||
GlRenderer.setViewportBounds(0, 0, GameShell.canvasWidth, GameShell.canvasHeight)
|
||||
}
|
||||
|
||||
if (pluginsReloaded) {
|
||||
InterfaceList.method3712(true) // Gets the resize working correctly
|
||||
reflectiveEditorView?.let { addPlugins(it) }
|
||||
pluginsReloaded = false
|
||||
}
|
||||
|
||||
accumulatedTime += timeDelta
|
||||
if (accumulatedTime >= tickInterval) {
|
||||
lootTrackerView?.let { onPostClientTick(it) }
|
||||
accumulatedTime = 0L
|
||||
}
|
||||
|
||||
|
||||
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
||||
if(!initialized && mainLoadState >= loginScreen) {
|
||||
initKondoUI()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun initKondoUI(){
|
||||
DrawText(FontType.LARGE, fromColor(Color(16777215)), TextModifier.CENTER, "KondoKit Loading Sprites...", GameShell.canvasWidth/2, GameShell.canvasHeight/2)
|
||||
if(!allSpritesLoaded()) return;
|
||||
val frame: Frame? = GameShell.frame
|
||||
if (frame != null) {
|
||||
kondoExposed_useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true
|
||||
kondoExposed_playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5
|
||||
cardLayout = CardLayout()
|
||||
mainContentPanel = Panel(cardLayout)
|
||||
mainContentPanel.background = VIEW_BACKGROUND_COLOR
|
||||
|
||||
xpTrackerView = createXPTrackerView()
|
||||
hiScoreView = createHiscoreSearchView()
|
||||
lootTrackerView = createLootTrackerView()
|
||||
reflectiveEditorView = createReflectiveEditorView()
|
||||
mainContentPanel.add(xpTrackerView, "XP_TRACKER_VIEW")
|
||||
mainContentPanel.add(hiScoreView, "HISCORE_SEARCH_VIEW")
|
||||
mainContentPanel.add(lootTrackerView, "LOOT_TRACKER_VIEW")
|
||||
mainContentPanel.add(reflectiveEditorView, "REFLECTIVE_EDITOR_VIEW")
|
||||
|
||||
val navPanel = Panel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = Dimension(42, frame.height)
|
||||
}
|
||||
|
||||
navPanel.add(createNavButton(LVL_ICON, "XP_TRACKER_VIEW"))
|
||||
navPanel.add(createNavButton(MAG_SPRITE, "HISCORE_SEARCH_VIEW"))
|
||||
navPanel.add(createNavButton(LOOT_ICON, "LOOT_TRACKER_VIEW"))
|
||||
navPanel.add(createNavButton(WRENCH_ICON, "REFLECTIVE_EDITOR_VIEW"))
|
||||
|
||||
val rightPanel = Panel(BorderLayout()).apply {
|
||||
add(mainContentPanel, BorderLayout.CENTER)
|
||||
add(navPanel, BorderLayout.EAST)
|
||||
}
|
||||
|
||||
scrollPane = JScrollPane(rightPanel).apply {
|
||||
preferredSize = Dimension(SCROLLPANE_WIDTH, frame.height)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
border = BorderFactory.createEmptyBorder() // Removes the border completely
|
||||
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||
verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_NEVER
|
||||
}
|
||||
|
||||
frame.layout = BorderLayout()
|
||||
scrollPane?.let { frame.add(it, BorderLayout.EAST) }
|
||||
|
||||
frame.revalidate()
|
||||
frame.repaint()
|
||||
|
||||
StateManager.focusedView = "XP_TRACKER_VIEW"
|
||||
initialized = true
|
||||
pluginsReloaded = true
|
||||
UpdateDisplaySettings()
|
||||
}
|
||||
}
|
||||
|
||||
override fun Update() {
|
||||
xpWidgets.values.forEach { xpWidget ->
|
||||
val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||
val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||
val formattedXpPerHour = formatNumber(xpPerHour)
|
||||
xpWidget.xpPerHourLabel.text =
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
||||
xpWidget.panel.repaint()
|
||||
}
|
||||
|
||||
totalXPWidget?.let { totalXPWidget ->
|
||||
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
||||
totalXPWidget.xpPerHourLabel.text =
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
||||
totalXPWidget.panel.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
||||
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
||||
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
||||
}
|
||||
|
||||
|
||||
private fun createNavButton(spriteId: Int, viewName: String): JButton {
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId))
|
||||
val buttonSize = Dimension(42, 42)
|
||||
val imageSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||
|
||||
val actionListener = ActionListener {
|
||||
cardLayout.show(mainContentPanel, viewName)
|
||||
StateManager.focusedView = viewName
|
||||
}
|
||||
|
||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = imageSize
|
||||
maximumSize = imageSize
|
||||
minimumSize = imageSize
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent?) {
|
||||
actionListener.actionPerformed(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val button = JButton().apply {
|
||||
layout = GridBagLayout()
|
||||
preferredSize = buttonSize
|
||||
maximumSize = buttonSize
|
||||
minimumSize = buttonSize
|
||||
background = WIDGET_COLOR
|
||||
isFocusPainted = false
|
||||
isBorderPainted = false
|
||||
|
||||
val gbc = GridBagConstraints().apply {
|
||||
anchor = GridBagConstraints.CENTER
|
||||
}
|
||||
|
||||
add(imageCanvas, gbc)
|
||||
addActionListener(actionListener)
|
||||
}
|
||||
|
||||
return button
|
||||
}
|
||||
|
||||
object StateManager {
|
||||
val initialXP: MutableMap<Int, Int> = HashMap()
|
||||
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
||||
var totalXPWidget: XPWidget? = null
|
||||
var focusedView: String = ""
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue