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() {}
|
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.
|
* OnLogout is called when the client logs out. This should be used to clear player-relevant plugin state.
|
||||||
*/
|
*/
|
||||||
public void OnLogout() {}
|
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.
|
* DrawMiniMenu is called when a MiniMenu entry has been created.
|
||||||
* @param entry the entry
|
* @param entry the entry
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,23 @@ public class PluginRepository {
|
||||||
API.registeredWheelListeners.clear();
|
API.registeredWheelListeners.clear();
|
||||||
API.registeredMouseListeners.clear();
|
API.registeredMouseListeners.clear();
|
||||||
API.registeredKeyListeners.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.clear();
|
||||||
|
loadedPlugins.putAll(pluginsToKeep);
|
||||||
SaveStorage();
|
SaveStorage();
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
@ -98,6 +114,11 @@ public class PluginRepository {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loadedPlugins.containsKey(info)) {
|
||||||
|
System.out.println("Skipping reloading of plugin " + file.getName() + " as it already exists and has OnPluginsReloaded.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Plugin thisPlugin = (Plugin) clazz.newInstance();
|
Plugin thisPlugin = (Plugin) clazz.newInstance();
|
||||||
thisPlugin._init();
|
thisPlugin._init();
|
||||||
|
|
@ -179,6 +200,10 @@ public class PluginRepository {
|
||||||
loadedPlugins.values().forEach((plugin) -> plugin.OnLogin());
|
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() {
|
public static void SaveStorage() {
|
||||||
if (pluginStorage.containsKey("_keystoreDirty")) {
|
if (pluginStorage.containsKey("_keystoreDirty")) {
|
||||||
pluginStorage.remove("_keystoreDirty");
|
pluginStorage.remove("_keystoreDirty");
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ public final class GlIndexedSprite extends IndexedSprite {
|
||||||
@OriginalMember(owner = "client!oh", name = "q", descriptor = "I")
|
@OriginalMember(owner = "client!oh", name = "q", descriptor = "I")
|
||||||
private int anInt4284 = 0;
|
private int anInt4284 = 0;
|
||||||
|
|
||||||
|
public byte[] pixels;
|
||||||
|
|
||||||
|
public int[] pallet;
|
||||||
|
|
||||||
@OriginalMember(owner = "client!oh", name = "<init>", descriptor = "(IIIIII[B[I)V")
|
@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) {
|
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;
|
this.innerWidth = arg0;
|
||||||
|
|
@ -40,6 +44,8 @@ public final class GlIndexedSprite extends IndexedSprite {
|
||||||
this.yOffset = arg3;
|
this.yOffset = arg3;
|
||||||
this.width = arg4;
|
this.width = arg4;
|
||||||
this.height = arg5;
|
this.height = arg5;
|
||||||
|
this.pixels = arg6;
|
||||||
|
this.pallet = arg7;
|
||||||
this.method3337(arg6, arg7);
|
this.method3337(arg6, arg7);
|
||||||
this.method3339();
|
this.method3339();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ public class GlSprite extends Sprite {
|
||||||
@OriginalMember(owner = "client!cf", name = "ab", descriptor = "I")
|
@OriginalMember(owner = "client!cf", name = "ab", descriptor = "I")
|
||||||
public int textureId = -1;
|
public int textureId = -1;
|
||||||
|
|
||||||
|
public int[] pixels;
|
||||||
|
|
||||||
@OriginalMember(owner = "client!cf", name = "Z", descriptor = "I")
|
@OriginalMember(owner = "client!cf", name = "Z", descriptor = "I")
|
||||||
private int anInt1871 = -1;
|
private int anInt1871 = -1;
|
||||||
|
|
||||||
|
|
@ -40,6 +42,7 @@ public class GlSprite extends Sprite {
|
||||||
this.anInt1861 = arg3;
|
this.anInt1861 = arg3;
|
||||||
this.width = arg4;
|
this.width = arg4;
|
||||||
this.height = arg5;
|
this.height = arg5;
|
||||||
|
this.pixels = arg6;
|
||||||
this.method1430(arg6);
|
this.method1430(arg6);
|
||||||
this.method1431();
|
this.method1431();
|
||||||
}
|
}
|
||||||
|
|
@ -52,6 +55,7 @@ public class GlSprite extends Sprite {
|
||||||
this.anInt1861 = arg0.anInt1861;
|
this.anInt1861 = arg0.anInt1861;
|
||||||
this.width = arg0.width;
|
this.width = arg0.width;
|
||||||
this.height = arg0.height;
|
this.height = arg0.height;
|
||||||
|
this.pixels = arg0.pixels;
|
||||||
this.method1430(arg0.pixels);
|
this.method1430(arg0.pixels);
|
||||||
this.method1431();
|
this.method1431();
|
||||||
}
|
}
|
||||||
|
|
@ -515,4 +519,4 @@ public class GlSprite extends Sprite {
|
||||||
gl.glEnd();
|
gl.glEnd();
|
||||||
gl.glEndList();
|
gl.glEndList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3034,6 +3034,7 @@ public class Protocol {
|
||||||
|
|
||||||
boolean isKillingBlow = (local18 & 0x80) != 0;
|
boolean isKillingBlow = (local18 & 0x80) != 0;
|
||||||
if (isKillingBlow) {
|
if (isKillingBlow) {
|
||||||
|
PluginRepository.OnKillingBlowNPC(npc.type.id,npc.movementQueueX[0],npc.movementQueueZ[0]);
|
||||||
local43 = inboundBuffer.g2add();
|
local43 = inboundBuffer.g2add();
|
||||||
if (local43 == 65535) {
|
if (local43 == 65535) {
|
||||||
local43 = -1;
|
local43 = -1;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
||||||
public byte[] pixels;
|
public byte[] pixels;
|
||||||
|
|
||||||
@OriginalMember(owner = "client!ek", name = "n", descriptor = "[I")
|
@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")
|
@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) {
|
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.width = arg4;
|
||||||
this.height = arg5;
|
this.height = arg5;
|
||||||
this.pixels = arg6;
|
this.pixels = arg6;
|
||||||
this.anIntArray144 = arg7;
|
this.pallet = arg7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@OriginalMember(owner = "client!ek", name = "<init>", descriptor = "(III)V")
|
@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.innerHeight = this.height = arg1;
|
||||||
this.xOffset = this.yOffset = 0;
|
this.xOffset = this.yOffset = 0;
|
||||||
this.pixels = new byte[arg0 * arg1];
|
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")
|
@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")
|
@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) {
|
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++) {
|
for (@Pc(1) int local1 = 0; local1 < this.pallet.length; local1++) {
|
||||||
@Pc(15) int local15 = this.anIntArray144[local1] >> 16 & 0xFF;
|
@Pc(15) int local15 = this.pallet[local1] >> 16 & 0xFF;
|
||||||
local15 += arg0;
|
local15 += arg0;
|
||||||
if (local15 < 0) {
|
if (local15 < 0) {
|
||||||
local15 = 0;
|
local15 = 0;
|
||||||
} else if (local15 > 255) {
|
} else if (local15 > 255) {
|
||||||
local15 = 255;
|
local15 = 255;
|
||||||
}
|
}
|
||||||
@Pc(38) int local38 = this.anIntArray144[local1] >> 8 & 0xFF;
|
@Pc(38) int local38 = this.pallet[local1] >> 8 & 0xFF;
|
||||||
local38 += arg1;
|
local38 += arg1;
|
||||||
if (local38 < 0) {
|
if (local38 < 0) {
|
||||||
local38 = 0;
|
local38 = 0;
|
||||||
} else if (local38 > 255) {
|
} else if (local38 > 255) {
|
||||||
local38 = 255;
|
local38 = 255;
|
||||||
}
|
}
|
||||||
@Pc(59) int local59 = this.anIntArray144[local1] & 0xFF;
|
@Pc(59) int local59 = this.pallet[local1] & 0xFF;
|
||||||
local59 += arg2;
|
local59 += arg2;
|
||||||
if (local59 < 0) {
|
if (local59 < 0) {
|
||||||
local59 = 0;
|
local59 = 0;
|
||||||
} else if (local59 > 255) {
|
} else if (local59 > 255) {
|
||||||
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;
|
local7 += local21 * local145;
|
||||||
local125 += 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")
|
@OriginalMember(owner = "client!ek", name = "a", descriptor = "()V")
|
||||||
|
|
@ -309,7 +309,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
||||||
local27 += local36;
|
local27 += local36;
|
||||||
}
|
}
|
||||||
if (local23 > 0 && local20 > 0) {
|
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;
|
local7 += local21 * local145;
|
||||||
local125 += 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")
|
@OriginalMember(owner = "client!ek", name = "a", descriptor = "(II)V")
|
||||||
|
|
@ -424,7 +424,7 @@ public final class SoftwareIndexedSprite extends IndexedSprite {
|
||||||
local27 += local36;
|
local27 += local36;
|
||||||
}
|
}
|
||||||
if (local23 > 0 && local20 > 0) {
|
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")
|
@OriginalMember(owner = "client!pl", name = "a", descriptor = "(II)V")
|
||||||
public static void setGameState(@OriginalArg(0) int arg0) {
|
public static void setGameState(@OriginalArg(0) int arg0) {
|
||||||
|
if(arg0 == 30) {
|
||||||
|
PluginRepository.OnLogin();
|
||||||
|
}
|
||||||
if (gameState == arg0) {
|
if (gameState == arg0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gameState == 0) {
|
if (gameState == 0) {
|
||||||
LoadingBarAwt.clear();
|
LoadingBarAwt.clear();
|
||||||
}
|
}
|
||||||
if (gameState == 30) {
|
|
||||||
PluginRepository.OnLogin();
|
|
||||||
}
|
|
||||||
if (arg0 == 40) {
|
if (arg0 == 40) {
|
||||||
LoginManager.clear();
|
LoginManager.clear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import rt4.*
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URL
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
@ -19,29 +21,26 @@ import kotlin.math.roundToInt
|
||||||
@PluginMeta(
|
@PluginMeta(
|
||||||
author = "downthecrop",
|
author = "downthecrop",
|
||||||
description =
|
description =
|
||||||
"""
|
"""
|
||||||
Ground Items Overlay. Just like Runelite!
|
Ground Items Overlay. Just like Runelite!
|
||||||
cmds ::set(low,med,high,insane,hide), ::(tag,ignore)item ID, ::(reset)groundconfig
|
cmds ::set(low,med,high,insane,hide), ::(tag,ignore)item ID, ::(reset)groundconfig
|
||||||
Special thanks to Chisato for the original skeleton.
|
Special thanks to Chisato for the original skeleton.
|
||||||
""",
|
""",
|
||||||
version = 1.1
|
version = 1.2
|
||||||
)
|
)
|
||||||
open class plugin : Plugin() {
|
open class plugin : Plugin() {
|
||||||
|
|
||||||
private var lowValue = 5000
|
private var kondoExposed_lowValue = 5000
|
||||||
private var mediumValue = 20000
|
private var kondoExposed_mediumValue = 20000
|
||||||
private var highValue = 50000
|
private var kondoExposed_highValue = 50000
|
||||||
private var insaneValue = 100000
|
private var kondoExposed_insaneValue = 100000
|
||||||
private var hideBelowValue = 0
|
private var kondoExposed_hideBelowValue = 0
|
||||||
|
private var kondoExposed_useLiveGEPrices = true
|
||||||
private val coindId = 995
|
private val coindId = 995
|
||||||
private lateinit var taggedItems: List<Int>
|
private lateinit var kondoExposed_taggedItems: List<Int>
|
||||||
private lateinit var ignoredItems: List<Int>
|
private lateinit var kondoExposed_ignoredItems: List<Int>
|
||||||
|
|
||||||
private val gePriceMap = try {
|
private var gePriceMap = loadGEPrices()
|
||||||
parseGEPrices(BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8)).useLines { it.joinToString("\n") })
|
|
||||||
} catch (e: Exception) {
|
|
||||||
emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val colorMap = mapOf(
|
private val colorMap = mapOf(
|
||||||
"tagged" to "#AA00FF",
|
"tagged" to "#AA00FF",
|
||||||
|
|
@ -61,22 +60,23 @@ open class plugin : Plugin() {
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun Init() {
|
override fun Init() {
|
||||||
lowValue = GetData("low-value") as? Int ?: 5000
|
kondoExposed_lowValue = GetData("low-value") as? Int ?: 5000
|
||||||
mediumValue = GetData("medium-value") as? Int ?: 20000
|
kondoExposed_mediumValue = GetData("medium-value") as? Int ?: 20000
|
||||||
highValue = GetData("high-value") as? Int ?: 50000
|
kondoExposed_highValue = GetData("high-value") as? Int ?: 50000
|
||||||
insaneValue = GetData("insane-value") as? Int ?: 100000
|
kondoExposed_insaneValue = GetData("insane-value") as? Int ?: 100000
|
||||||
hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
kondoExposed_hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
||||||
taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
kondoExposed_useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true
|
||||||
ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
kondoExposed_taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||||
if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load item_configs.json")
|
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 {
|
private fun isTagged(itemId: Int): Boolean {
|
||||||
return taggedItems.contains(itemId)
|
return kondoExposed_taggedItems.contains(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isHidden(itemId: Int): Boolean {
|
private fun isHidden(itemId: Int): Boolean {
|
||||||
return ignoredItems.contains(itemId)
|
return kondoExposed_ignoredItems.contains(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) = renderGroundItemNames()
|
override fun Draw(timeDelta: Long) = renderGroundItemNames()
|
||||||
|
|
@ -141,10 +141,10 @@ open class plugin : Plugin() {
|
||||||
val screenY = screenPos[1]
|
val screenY = screenPos[1]
|
||||||
val color = when {
|
val color = when {
|
||||||
isTagged(itemDef.id) -> colorMap["tagged"]
|
isTagged(itemDef.id) -> colorMap["tagged"]
|
||||||
highestValue < lowValue -> "#FFFFFF"
|
highestValue < kondoExposed_lowValue -> "#FFFFFF"
|
||||||
highestValue < mediumValue -> colorMap["lowValue"]
|
highestValue < kondoExposed_mediumValue -> colorMap["lowValue"]
|
||||||
highestValue < highValue -> colorMap["mediumValue"]
|
highestValue < kondoExposed_highValue -> colorMap["mediumValue"]
|
||||||
highestValue < insaneValue -> colorMap["highValue"]
|
highestValue < kondoExposed_insaneValue -> colorMap["highValue"]
|
||||||
else -> colorMap["insaneValue"]
|
else -> colorMap["insaneValue"]
|
||||||
} ?: "#FFFFFF"
|
} ?: "#FFFFFF"
|
||||||
val colorInt = color.drop(1).toInt(16)
|
val colorInt = color.drop(1).toInt(16)
|
||||||
|
|
@ -164,6 +164,7 @@ open class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
InterfaceList.fullRedrawAllInterfaces() // Prevent an overdraw bug
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDisplayedStackSize(objstacknodeLL: LinkedList): Int{
|
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 haValue = if (itemDef.id == coindId) item.value.amount else (itemDef.cost * 0.6 * item.value.amount).roundToInt()
|
||||||
val geValue = (gePriceMap[itemDef.id.toString()]?.toInt() ?: 0) * item.value.amount
|
val geValue = (gePriceMap[itemDef.id.toString()]?.toInt() ?: 0) * item.value.amount
|
||||||
val highestValue = maxOf(haValue, geValue)
|
val highestValue = maxOf(haValue, geValue)
|
||||||
return !((highestValue < hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
return !((highestValue < kondoExposed_hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||||
|
|
@ -199,7 +200,7 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
private fun ignoreItem(itemId: Int): Runnable {
|
private fun ignoreItem(itemId: Int): Runnable {
|
||||||
return Runnable {
|
return Runnable {
|
||||||
val existingIgnores = ignoredItems.toMutableList()
|
val existingIgnores = kondoExposed_ignoredItems.toMutableList()
|
||||||
|
|
||||||
if (existingIgnores.contains(itemId)) {
|
if (existingIgnores.contains(itemId)) {
|
||||||
existingIgnores.remove(itemId)
|
existingIgnores.remove(itemId)
|
||||||
|
|
@ -214,7 +215,7 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
private fun tagItem(itemId: Int): Runnable {
|
private fun tagItem(itemId: Int): Runnable {
|
||||||
return Runnable {
|
return Runnable {
|
||||||
val existingTags = taggedItems.toMutableList()
|
val existingTags = kondoExposed_taggedItems.toMutableList()
|
||||||
|
|
||||||
if (existingTags.contains(itemId)) {
|
if (existingTags.contains(itemId)) {
|
||||||
existingTags.remove(itemId)
|
existingTags.remove(itemId)
|
||||||
|
|
@ -229,26 +230,28 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
|
|
||||||
private fun resetConfig() {
|
private fun resetConfig() {
|
||||||
lowValue = 5000
|
kondoExposed_lowValue = 5000
|
||||||
mediumValue = 20000
|
kondoExposed_mediumValue = 20000
|
||||||
highValue = 50000
|
kondoExposed_highValue = 50000
|
||||||
insaneValue = 100000
|
kondoExposed_insaneValue = 100000
|
||||||
hideBelowValue = 0
|
kondoExposed_hideBelowValue = 0
|
||||||
|
kondoExposed_useLiveGEPrices = true
|
||||||
StoreData("ground-item-tags","");
|
StoreData("ground-item-tags","");
|
||||||
StoreData("ground-item-ignore","");
|
StoreData("ground-item-ignore","");
|
||||||
StoreData("low-value", lowValue)
|
StoreData("low-value", kondoExposed_lowValue)
|
||||||
StoreData("medium-value", mediumValue)
|
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
||||||
StoreData("high-value", highValue)
|
StoreData("medium-value", kondoExposed_mediumValue)
|
||||||
StoreData("insane-value", insaneValue)
|
StoreData("high-value", kondoExposed_highValue)
|
||||||
StoreData("hide-below-value", hideBelowValue)
|
StoreData("insane-value", kondoExposed_insaneValue)
|
||||||
|
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun displayRanges() {
|
private fun displayRanges() {
|
||||||
val low = lowValue
|
val low = kondoExposed_lowValue
|
||||||
val medium = mediumValue
|
val medium = kondoExposed_mediumValue
|
||||||
val high = highValue
|
val high = kondoExposed_highValue
|
||||||
val insane = insaneValue
|
val insane = kondoExposed_insaneValue
|
||||||
val hide = hideBelowValue
|
val hide = kondoExposed_hideBelowValue
|
||||||
|
|
||||||
SendMessage("== Ground Item Config ==")
|
SendMessage("== Ground Item Config ==")
|
||||||
SendMessage("Low: $low")
|
SendMessage("Low: $low")
|
||||||
|
|
@ -257,12 +260,12 @@ open class plugin : Plugin() {
|
||||||
SendMessage("Insane: $insane")
|
SendMessage("Insane: $insane")
|
||||||
SendMessage("Hide Below: $hide")
|
SendMessage("Hide Below: $hide")
|
||||||
SendMessage("-- Ignored Items --")
|
SendMessage("-- Ignored Items --")
|
||||||
for(item in ignoredItems){
|
for(item in kondoExposed_ignoredItems){
|
||||||
val itemDef = ObjTypeList.get(item)
|
val itemDef = ObjTypeList.get(item)
|
||||||
SendMessage("Ignored: ${itemDef.name} ${itemDef.id}")
|
SendMessage("Ignored: ${itemDef.name} ${itemDef.id}")
|
||||||
}
|
}
|
||||||
SendMessage("-- Tagged Items --")
|
SendMessage("-- Tagged Items --")
|
||||||
for(item in taggedItems){
|
for(item in kondoExposed_taggedItems){
|
||||||
val itemDef = ObjTypeList.get(item)
|
val itemDef = ObjTypeList.get(item)
|
||||||
SendMessage("Tagged: ${itemDef.name} ${itemDef.id}")
|
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)
|
DrawText(FontType.SMALL, fromColor(Color(color)), TextModifier.CENTER, text, x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseGEPrices(json: String): Map<String, String> {
|
fun OnKondoValueUpdated() {
|
||||||
return json
|
StoreData("ground-item-tags",kondoExposed_taggedItems);
|
||||||
.trim()
|
StoreData("ground-item-ignore",kondoExposed_ignoredItems);
|
||||||
.removeSurrounding("[", "]")
|
StoreData("low-value", kondoExposed_lowValue)
|
||||||
.split("},")
|
StoreData("medium-value", kondoExposed_mediumValue)
|
||||||
.associate { obj ->
|
StoreData("high-value", kondoExposed_highValue)
|
||||||
val pairs = obj.trim().removeSurrounding("{", "}").split(",")
|
StoreData("insane-value", kondoExposed_insaneValue)
|
||||||
val id = pairs.find { it.trim().startsWith("\"id\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
||||||
val grandExchangePrice =
|
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
||||||
pairs.find { it.trim().startsWith("\"grand_exchange_price\"") }?.split(":")?.get(1)?.trim()
|
gePriceMap = loadGEPrices();
|
||||||
?.trim('\"')
|
}
|
||||||
id to grandExchangePrice
|
|
||||||
}.filterKeys { it != null }.filterValues { it != null } as Map<String, String>
|
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 {
|
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