mirror of
https://gitlab.com/2009scape/rt4-client.git
synced 2025-12-09 16:45:46 -07:00
Compare commits
52 commits
637e5bf68a
...
5cf2f18786
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cf2f18786 | ||
|
|
f668d14be6 | ||
|
|
5d59aea830 | ||
|
|
77c5528fc1 | ||
|
|
3a33995006 | ||
|
|
1de6638a12 | ||
|
|
4ebba194dc | ||
|
|
e05f5829d7 | ||
|
|
cf5ccbf1ac | ||
|
|
cb9758f7d5 | ||
|
|
1cca6611ee | ||
|
|
52d07d5795 | ||
|
|
f2d4c17569 | ||
|
|
c24df5ab46 | ||
|
|
07f4dc9349 | ||
|
|
c307db1e11 | ||
|
|
8b89a2bb8c | ||
|
|
c5ba4ecc94 | ||
|
|
341d6758c1 | ||
|
|
1f1718d917 | ||
|
|
8180e20281 | ||
|
|
e7f46f1006 | ||
|
|
83fe4805ac | ||
|
|
71afb2b385 | ||
|
|
15deb6216f | ||
|
|
4f8bf464c5 | ||
|
|
b3c2adc51f | ||
|
|
838acc57ff | ||
|
|
1eb7483d35 | ||
|
|
af4dafee89 | ||
|
|
8e554b573c | ||
|
|
668345b81e | ||
|
|
c917981ef8 | ||
|
|
fce9b4eea7 | ||
|
|
5948810ca7 | ||
|
|
2a059d1c02 | ||
|
|
72bf9a8297 | ||
|
|
f09de70a30 | ||
|
|
d779f65db4 | ||
|
|
1eee2a531b | ||
|
|
0ab6ed3868 | ||
|
|
59100a036d | ||
|
|
f8dcbfafdb | ||
|
|
5e90004474 | ||
|
|
b009601848 | ||
|
|
de5cbda4f9 | ||
|
|
413601d0e5 | ||
|
|
a31437ef82 | ||
|
|
310c88600a | ||
|
|
8076568083 | ||
|
|
8b42b58b87 | ||
|
|
b8c7a1150f |
37 changed files with 2509 additions and 874 deletions
|
|
@ -13,6 +13,7 @@ import rt4.Tile;
|
|||
*/
|
||||
public abstract class Plugin {
|
||||
long timeOfLastDraw;
|
||||
long timeOfLastLateDraw;
|
||||
|
||||
void _init() {
|
||||
Init();
|
||||
|
|
@ -24,6 +25,12 @@ public abstract class Plugin {
|
|||
timeOfLastDraw = nowTime;
|
||||
}
|
||||
|
||||
void _lateDraw() {
|
||||
long nowTime = System.currentTimeMillis();
|
||||
LateDraw(nowTime - timeOfLastLateDraw);
|
||||
timeOfLastLateDraw = nowTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw() is called by the client rendering loop so that plugins can draw information onto the screen.
|
||||
* This will be called once per frame, meaning it is framerate bound.
|
||||
|
|
@ -31,6 +38,14 @@ public abstract class Plugin {
|
|||
*/
|
||||
public void Draw(long timeDelta) {}
|
||||
|
||||
|
||||
/**
|
||||
* LateDraw() is called at the end of a finalized frame
|
||||
* This will be called once per frame, meaning it is framerate bound.
|
||||
* @param timeDelta the time (ms) elapsed since the last draw call.
|
||||
*/
|
||||
public void LateDraw(long timeDelta) {}
|
||||
|
||||
/**
|
||||
* Init() is called when the plugin is first loaded
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import java.util.Properties;
|
|||
* A data class for storing information about plugins.
|
||||
* @author ceikry
|
||||
*/
|
||||
class PluginInfo {
|
||||
double version;
|
||||
String author;
|
||||
String description;
|
||||
public class PluginInfo {
|
||||
public double version;
|
||||
public String author;
|
||||
public String description;
|
||||
|
||||
public PluginInfo(String author, String description, double version) {
|
||||
this.version = version;
|
||||
|
|
|
|||
|
|
@ -160,6 +160,11 @@ public class PluginRepository {
|
|||
pluginsSnapshot.forEach(Plugin::_draw);
|
||||
}
|
||||
|
||||
public static void LateDraw() {
|
||||
List<Plugin> pluginsSnapshot = new ArrayList<>(loadedPlugins.values());
|
||||
pluginsSnapshot.forEach(Plugin::_lateDraw);
|
||||
}
|
||||
|
||||
public static void NPCOverheadDraw(Npc npc, int screenX, int screenY) {
|
||||
loadedPlugins.values().forEach((plugin) -> plugin.NPCOverheadDraw(npc, screenX, screenY));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,6 +307,7 @@ public class API {
|
|||
public static int[] CalculateSceneGraphScreenPosition(int entityX, int entityZ, int yOffset) {
|
||||
final int HALF_FIXED_WIDTH = 256;
|
||||
final int HALF_FIXED_HEIGHT = 167;
|
||||
final int RESIZABLE_SD_OFFSET = 500;
|
||||
|
||||
int elevation = SceneGraph.getTileHeight(plane, entityX, entityZ) - yOffset;
|
||||
entityX -= SceneGraph.cameraX;
|
||||
|
|
@ -329,15 +330,27 @@ public class API {
|
|||
int[] screenPos = new int[2]; // X,Y
|
||||
|
||||
if (entityZ >= 50) {
|
||||
if(GetWindowMode() == WindowMode.FIXED) {
|
||||
if (GetWindowMode() == WindowMode.FIXED) {
|
||||
screenPos[0] = HALF_FIXED_WIDTH + ((entityX << 9) / entityZ);
|
||||
screenPos[1] = HALF_FIXED_HEIGHT + ((elevation << 9) / entityZ);
|
||||
} else {
|
||||
Dimension canvas = GetWindowDimensions();
|
||||
double newViewDistH = (canvas.width / 2) / Math.tan(Math.toRadians(GlRenderer.hFOV) / 2);
|
||||
double newViewDistV = (canvas.height / 2) / Math.tan(Math.toRadians(GlRenderer.vFOV) / 2);
|
||||
screenPos[0] = canvas.width / 2 + (int)((entityX * newViewDistH) / entityZ);
|
||||
screenPos[1] = canvas.height / 2 + (int)((elevation * newViewDistV) / entityZ);
|
||||
double newViewDistH;
|
||||
double newViewDistV;
|
||||
|
||||
if (API.IsHD()) {
|
||||
newViewDistH = (canvas.width / 2) / Math.tan(Math.toRadians(GlRenderer.hFOV) / 2);
|
||||
newViewDistV = (canvas.height / 2) / Math.tan(Math.toRadians(GlRenderer.vFOV) / 2);
|
||||
} else {
|
||||
double aspectRatio = (double) canvas.width / canvas.height;
|
||||
double vFOV = 2 * Math.toDegrees(Math.atan((double) canvas.height / (2 * RESIZABLE_SD_OFFSET)));
|
||||
double hFOV = 2 * Math.toDegrees(Math.atan(Math.tan(Math.toRadians(vFOV / 2)) * aspectRatio));
|
||||
|
||||
newViewDistH = (canvas.width / 2) / Math.tan(Math.toRadians(hFOV) / 2);
|
||||
newViewDistV = (canvas.height / 2) / Math.tan(Math.toRadians(vFOV) / 2);
|
||||
}
|
||||
screenPos[0] = canvas.width / 2 + (int) ((entityX * newViewDistH) / entityZ);
|
||||
screenPos[1] = canvas.height / 2 + (int) ((elevation * newViewDistV) / entityZ);
|
||||
}
|
||||
} else {
|
||||
screenPos[0] = -1;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import java.awt.*;
|
|||
public abstract class FrameBuffer {
|
||||
|
||||
@OriginalMember(owner = "client!vk", name = "e", descriptor = "[I")
|
||||
protected int[] pixels;
|
||||
public int[] pixels;
|
||||
|
||||
@OriginalMember(owner = "client!vk", name = "g", descriptor = "Ljava/awt/Image;")
|
||||
protected Image image;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import org.openrs2.deob.annotation.OriginalMember;
|
|||
import org.openrs2.deob.annotation.Pc;
|
||||
|
||||
import java.awt.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
|
@ -24,6 +25,11 @@ public final class GlRenderer {
|
|||
public static float hFOV = 0;
|
||||
|
||||
|
||||
|
||||
private static ByteBuffer pixelByteBuffer;
|
||||
private static IntBuffer pixelIntBuffer;
|
||||
public static int[] pixelData;
|
||||
|
||||
@OriginalMember(owner = "client!tf", name = "c", descriptor = "F")
|
||||
private static float aFloat30;
|
||||
|
||||
|
|
@ -200,10 +206,34 @@ public final class GlRenderer {
|
|||
public static void swapBuffers() {
|
||||
try {
|
||||
drawable.swapBuffers();
|
||||
readPixels();
|
||||
} catch (@Pc(3) Exception local3) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void initializePixelBuffer(int width, int height) {
|
||||
// Allocate ByteBuffer for BGRA pixels (4 bytes per pixel)
|
||||
pixelByteBuffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder());
|
||||
pixelIntBuffer = pixelByteBuffer.asIntBuffer();
|
||||
pixelData = new int[width * height];
|
||||
}
|
||||
|
||||
public static void readPixels() {
|
||||
// Ensure the pixel buffer is initialized with the correct size
|
||||
if (pixelByteBuffer == null || pixelIntBuffer.capacity() != canvasWidth * canvasHeight) {
|
||||
initializePixelBuffer(canvasWidth, canvasHeight);
|
||||
}
|
||||
|
||||
// Read pixels into the direct ByteBuffer
|
||||
gl.glReadPixels(0, 0, canvasWidth, canvasHeight, GL2.GL_BGRA,
|
||||
GlRenderer.bigEndian ? GL2.GL_UNSIGNED_INT_8_8_8_8_REV : GL2.GL_UNSIGNED_BYTE,
|
||||
pixelByteBuffer);
|
||||
|
||||
// Convert to int array if needed
|
||||
pixelIntBuffer.rewind(); // Prepare the IntBuffer for reading
|
||||
pixelIntBuffer.get(pixelData, 0, pixelData.length); // Transfer to pixelData array if necessary
|
||||
}
|
||||
|
||||
@OriginalMember(owner = "client!tf", name = "a", descriptor = "(Z)V")
|
||||
public static void setFogEnabled(@OriginalArg(0) boolean enabled) {
|
||||
if (enabled == fogEnabled) {
|
||||
|
|
|
|||
|
|
@ -921,6 +921,7 @@ public final class client extends GameShell {
|
|||
Preferences.safeMode = false;
|
||||
Preferences.write(GameShell.signLink);
|
||||
}
|
||||
PluginRepository.LateDraw();
|
||||
}
|
||||
|
||||
@OriginalMember(owner = "client!client", name = "c", descriptor = "(B)V")
|
||||
|
|
|
|||
|
|
@ -110,4 +110,10 @@ task buildPlugins(type: Copy, dependsOn: classes) {
|
|||
from "build/classes/kotlin/main"
|
||||
into pluginsPath
|
||||
}
|
||||
|
||||
// Find and copy any 'res' directories from 'src/main/kotlin/**/res/'
|
||||
copy {
|
||||
from fileTree(dir: "src/main/kotlin", include: "**/res/**")
|
||||
into pluginsPath
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package GroundItems
|
||||
|
||||
import KondoKit.Exposed
|
||||
import plugin.Plugin
|
||||
import plugin.annotations.PluginMeta
|
||||
import plugin.api.API.*
|
||||
import plugin.api.FontColor.fromColor
|
||||
import plugin.api.FontType
|
||||
|
|
@ -18,27 +18,26 @@ import java.nio.charset.StandardCharsets
|
|||
import java.text.DecimalFormat
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@PluginMeta(
|
||||
author = "downthecrop",
|
||||
description =
|
||||
"""
|
||||
Ground Items Overlay. Just like Runelite!
|
||||
cmds ::set(low,med,high,insane,hide), ::(tag,ignore)item ID, ::(reset)groundconfig
|
||||
Special thanks to Chisato for the original skeleton.
|
||||
""",
|
||||
version = 1.2
|
||||
)
|
||||
open class plugin : Plugin() {
|
||||
class plugin : Plugin() {
|
||||
|
||||
@Exposed(description = "Default: true, Use Local JSON or the prices from the Live/Stable server API")
|
||||
private var useLiveGEPrices = true
|
||||
@Exposed( "Default: 5,000 (blue)")
|
||||
private var lowValue = 5000
|
||||
@Exposed( "Default: 20,000 (green)")
|
||||
private var mediumValue = 20000
|
||||
@Exposed( "Default: 50,000 (orange)")
|
||||
private var highValue = 50000
|
||||
@Exposed( "Default: 100,000 (pink)")
|
||||
private var insaneValue = 100000
|
||||
@Exposed("Default: 0, No labels will be drawn for items below this value (unless tagged)")
|
||||
private var hideBelowValue = 0
|
||||
@Exposed("Tag Items (purple) add/remove with Ctrl+RightClick Tag/Ignore")
|
||||
private lateinit var taggedItems: List<Int>
|
||||
@Exposed("Ignore items add/remove with Ctrl+RightClick Tag/Ignore")
|
||||
private lateinit var ignoredItems: List<Int>
|
||||
|
||||
private var kondoExposed_lowValue = 5000
|
||||
private var kondoExposed_mediumValue = 20000
|
||||
private var kondoExposed_highValue = 50000
|
||||
private var kondoExposed_insaneValue = 100000
|
||||
private var kondoExposed_hideBelowValue = 0
|
||||
private var kondoExposed_useLiveGEPrices = true
|
||||
private val coindId = 995
|
||||
private lateinit var kondoExposed_taggedItems: List<Int>
|
||||
private lateinit var kondoExposed_ignoredItems: List<Int>
|
||||
|
||||
private var gePriceMap = loadGEPrices()
|
||||
|
||||
|
|
@ -60,23 +59,23 @@ open class plugin : Plugin() {
|
|||
)
|
||||
|
||||
override fun Init() {
|
||||
kondoExposed_lowValue = GetData("low-value") as? Int ?: 5000
|
||||
kondoExposed_mediumValue = GetData("medium-value") as? Int ?: 20000
|
||||
kondoExposed_highValue = GetData("high-value") as? Int ?: 50000
|
||||
kondoExposed_insaneValue = GetData("insane-value") as? Int ?: 100000
|
||||
kondoExposed_hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
||||
kondoExposed_useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true
|
||||
kondoExposed_taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||
kondoExposed_ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||
if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $kondoExposed_useLiveGEPrices")
|
||||
lowValue = GetData("low-value") as? Int ?: 5000
|
||||
mediumValue = GetData("medium-value") as? Int ?: 20000
|
||||
highValue = GetData("high-value") as? Int ?: 50000
|
||||
insaneValue = GetData("insane-value") as? Int ?: 100000
|
||||
hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
||||
useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true
|
||||
taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||
ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||
if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $useLiveGEPrices")
|
||||
}
|
||||
|
||||
private fun isTagged(itemId: Int): Boolean {
|
||||
return kondoExposed_taggedItems.contains(itemId)
|
||||
return taggedItems.contains(itemId)
|
||||
}
|
||||
|
||||
private fun isHidden(itemId: Int): Boolean {
|
||||
return kondoExposed_ignoredItems.contains(itemId)
|
||||
return ignoredItems.contains(itemId)
|
||||
}
|
||||
|
||||
override fun Draw(timeDelta: Long) = renderGroundItemNames()
|
||||
|
|
@ -141,10 +140,10 @@ open class plugin : Plugin() {
|
|||
val screenY = screenPos[1]
|
||||
val color = when {
|
||||
isTagged(itemDef.id) -> colorMap["tagged"]
|
||||
highestValue < kondoExposed_lowValue -> "#FFFFFF"
|
||||
highestValue < kondoExposed_mediumValue -> colorMap["lowValue"]
|
||||
highestValue < kondoExposed_highValue -> colorMap["mediumValue"]
|
||||
highestValue < kondoExposed_insaneValue -> colorMap["highValue"]
|
||||
highestValue < lowValue -> "#FFFFFF"
|
||||
highestValue < mediumValue -> colorMap["lowValue"]
|
||||
highestValue < highValue -> colorMap["mediumValue"]
|
||||
highestValue < insaneValue -> colorMap["highValue"]
|
||||
else -> colorMap["insaneValue"]
|
||||
} ?: "#FFFFFF"
|
||||
val colorInt = color.drop(1).toInt(16)
|
||||
|
|
@ -183,7 +182,7 @@ open class plugin : Plugin() {
|
|||
val haValue = if (itemDef.id == coindId) item.value.amount else (itemDef.cost * 0.6 * item.value.amount).roundToInt()
|
||||
val geValue = (gePriceMap[itemDef.id.toString()]?.toInt() ?: 0) * item.value.amount
|
||||
val highestValue = maxOf(haValue, geValue)
|
||||
return !((highestValue < kondoExposed_hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
||||
return !((highestValue < hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
||||
}
|
||||
|
||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||
|
|
@ -200,7 +199,7 @@ open class plugin : Plugin() {
|
|||
|
||||
private fun ignoreItem(itemId: Int): Runnable {
|
||||
return Runnable {
|
||||
val existingIgnores = kondoExposed_ignoredItems.toMutableList()
|
||||
val existingIgnores = ignoredItems.toMutableList()
|
||||
|
||||
if (existingIgnores.contains(itemId)) {
|
||||
existingIgnores.remove(itemId)
|
||||
|
|
@ -215,7 +214,7 @@ open class plugin : Plugin() {
|
|||
|
||||
private fun tagItem(itemId: Int): Runnable {
|
||||
return Runnable {
|
||||
val existingTags = kondoExposed_taggedItems.toMutableList()
|
||||
val existingTags = taggedItems.toMutableList()
|
||||
|
||||
if (existingTags.contains(itemId)) {
|
||||
existingTags.remove(itemId)
|
||||
|
|
@ -230,28 +229,28 @@ open class plugin : Plugin() {
|
|||
|
||||
|
||||
private fun resetConfig() {
|
||||
kondoExposed_lowValue = 5000
|
||||
kondoExposed_mediumValue = 20000
|
||||
kondoExposed_highValue = 50000
|
||||
kondoExposed_insaneValue = 100000
|
||||
kondoExposed_hideBelowValue = 0
|
||||
kondoExposed_useLiveGEPrices = true
|
||||
lowValue = 5000
|
||||
mediumValue = 20000
|
||||
highValue = 50000
|
||||
insaneValue = 100000
|
||||
hideBelowValue = 0
|
||||
useLiveGEPrices = true
|
||||
StoreData("ground-item-tags","");
|
||||
StoreData("ground-item-ignore","");
|
||||
StoreData("low-value", kondoExposed_lowValue)
|
||||
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
||||
StoreData("medium-value", kondoExposed_mediumValue)
|
||||
StoreData("high-value", kondoExposed_highValue)
|
||||
StoreData("insane-value", kondoExposed_insaneValue)
|
||||
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
||||
StoreData("low-value", lowValue)
|
||||
StoreData("ground-item-use-remote", useLiveGEPrices)
|
||||
StoreData("medium-value", mediumValue)
|
||||
StoreData("high-value", highValue)
|
||||
StoreData("insane-value", insaneValue)
|
||||
StoreData("hide-below-value", hideBelowValue)
|
||||
}
|
||||
|
||||
private fun displayRanges() {
|
||||
val low = kondoExposed_lowValue
|
||||
val medium = kondoExposed_mediumValue
|
||||
val high = kondoExposed_highValue
|
||||
val insane = kondoExposed_insaneValue
|
||||
val hide = kondoExposed_hideBelowValue
|
||||
val low = lowValue
|
||||
val medium = mediumValue
|
||||
val high = highValue
|
||||
val insane = insaneValue
|
||||
val hide = hideBelowValue
|
||||
|
||||
SendMessage("== Ground Item Config ==")
|
||||
SendMessage("Low: $low")
|
||||
|
|
@ -260,12 +259,12 @@ open class plugin : Plugin() {
|
|||
SendMessage("Insane: $insane")
|
||||
SendMessage("Hide Below: $hide")
|
||||
SendMessage("-- Ignored Items --")
|
||||
for(item in kondoExposed_ignoredItems){
|
||||
for(item in ignoredItems){
|
||||
val itemDef = ObjTypeList.get(item)
|
||||
SendMessage("Ignored: ${itemDef.name} ${itemDef.id}")
|
||||
}
|
||||
SendMessage("-- Tagged Items --")
|
||||
for(item in kondoExposed_taggedItems){
|
||||
for(item in taggedItems){
|
||||
val itemDef = ObjTypeList.get(item)
|
||||
SendMessage("Tagged: ${itemDef.name} ${itemDef.id}")
|
||||
}
|
||||
|
|
@ -278,19 +277,19 @@ open class plugin : Plugin() {
|
|||
}
|
||||
|
||||
fun OnKondoValueUpdated() {
|
||||
StoreData("ground-item-tags",kondoExposed_taggedItems);
|
||||
StoreData("ground-item-ignore",kondoExposed_ignoredItems);
|
||||
StoreData("low-value", kondoExposed_lowValue)
|
||||
StoreData("medium-value", kondoExposed_mediumValue)
|
||||
StoreData("high-value", kondoExposed_highValue)
|
||||
StoreData("insane-value", kondoExposed_insaneValue)
|
||||
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
||||
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
||||
StoreData("ground-item-tags",taggedItems);
|
||||
StoreData("ground-item-ignore",ignoredItems);
|
||||
StoreData("low-value", lowValue)
|
||||
StoreData("medium-value", mediumValue)
|
||||
StoreData("high-value", highValue)
|
||||
StoreData("insane-value", insaneValue)
|
||||
StoreData("ground-item-use-remote", useLiveGEPrices)
|
||||
StoreData("hide-below-value", hideBelowValue)
|
||||
gePriceMap = loadGEPrices();
|
||||
}
|
||||
|
||||
fun loadGEPrices(): Map<String, String> {
|
||||
return if (kondoExposed_useLiveGEPrices) {
|
||||
return if (useLiveGEPrices) {
|
||||
try {
|
||||
println("GroundItems: Loading Remote GE Prices")
|
||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||
|
|
@ -325,7 +324,7 @@ open class plugin : Plugin() {
|
|||
} else {
|
||||
try {
|
||||
println("GroundItems: Loading Local GE Prices")
|
||||
BufferedReader(InputStreamReader(GroundItems.plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8))
|
||||
.useLines { lines ->
|
||||
val json = lines.joinToString("\n")
|
||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ Commands:\
|
|||
::(tag,ignore)item ID\
|
||||
::(reset)groundconfig\
|
||||
Special thanks to Chisato for the original skeleton.
|
||||
VERSION=1.2
|
||||
VERSION=1.3
|
||||
166
plugin-playground/src/main/kotlin/KondoKit/AltCanvas.kt
Normal file
166
plugin-playground/src/main/kotlin/KondoKit/AltCanvas.kt
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.plugin.Companion.FIXED_HEIGHT
|
||||
import KondoKit.plugin.Companion.FIXED_WIDTH
|
||||
import plugin.api.API.IsHD
|
||||
import rt4.GameShell.canvas
|
||||
import rt4.GlRenderer
|
||||
import rt4.SoftwareRaster
|
||||
import java.awt.*
|
||||
import java.awt.event.*
|
||||
import java.awt.geom.AffineTransform
|
||||
import java.awt.image.AffineTransformOp
|
||||
import java.awt.image.BufferedImage
|
||||
import java.awt.image.VolatileImage
|
||||
|
||||
class AltCanvas : Canvas() {
|
||||
private var gameImage: VolatileImage? = null
|
||||
private var op: AffineTransformOp? = null
|
||||
private var transform: AffineTransform? = null
|
||||
|
||||
private var flippedImage: BufferedImage? = null
|
||||
private var bufferImage = BufferedImage(FIXED_WIDTH, FIXED_HEIGHT, BufferedImage.TYPE_INT_BGR)
|
||||
|
||||
private var lastImageWidth = -1
|
||||
private var lastImageHeight = -1
|
||||
|
||||
init {
|
||||
isFocusable = true
|
||||
requestFocusInWindow()
|
||||
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent) = relayMouseEvent(e)
|
||||
override fun mouseReleased(e: MouseEvent) = relayMouseEvent(e)
|
||||
override fun mouseClicked(e: MouseEvent) = relayMouseEvent(e)
|
||||
})
|
||||
|
||||
addMouseMotionListener(object : MouseMotionAdapter() {
|
||||
override fun mouseMoved(e: MouseEvent) = relayMouseEvent(e)
|
||||
override fun mouseDragged(e: MouseEvent) = relayMouseEvent(e)
|
||||
})
|
||||
|
||||
addKeyListener(object : KeyAdapter() {
|
||||
override fun keyPressed(e: KeyEvent) = relayKeyEvent { it.keyPressed(e) }
|
||||
override fun keyReleased(e: KeyEvent) = relayKeyEvent { it.keyReleased(e) }
|
||||
override fun keyTyped(e: KeyEvent) = relayKeyEvent { it.keyTyped(e) }
|
||||
})
|
||||
|
||||
addMouseWheelListener(MouseWheelListener { e -> relayMouseWheelEvent(e) })
|
||||
}
|
||||
|
||||
override fun update(g: Graphics) = paint(g)
|
||||
|
||||
override fun addNotify() {
|
||||
super.addNotify()
|
||||
createBufferStrategy(2) // Double-buffering
|
||||
validateGameImage()
|
||||
}
|
||||
|
||||
private fun validateGameImage() {
|
||||
val gc = GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice.defaultConfiguration
|
||||
gameImage?.let {
|
||||
when (it.validate(gc)) {
|
||||
VolatileImage.IMAGE_INCOMPATIBLE -> createGameImage(gc)
|
||||
VolatileImage.IMAGE_RESTORED -> renderGameImage()
|
||||
}
|
||||
} ?: createGameImage(gc)
|
||||
}
|
||||
|
||||
private fun createGameImage(gc: GraphicsConfiguration) {
|
||||
gameImage = gc.createCompatibleVolatileImage(FIXED_WIDTH, FIXED_HEIGHT, Transparency.OPAQUE)
|
||||
renderGameImage()
|
||||
}
|
||||
|
||||
private fun renderGameImage() {
|
||||
gameImage?.createGraphics()?.apply {
|
||||
color = Color.DARK_GRAY
|
||||
fillRect(0, 0, gameImage!!.width, gameImage!!.height)
|
||||
color = Color.BLACK
|
||||
drawString("KondoKit Scaled Fixed Canvas", 20, 20)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun paint(g: Graphics) {
|
||||
bufferStrategy?.let { strategy ->
|
||||
val g2d = strategy.drawGraphics as? Graphics2D ?: return
|
||||
|
||||
g2d.color = Color.BLACK
|
||||
g2d.fillRect(0, 0, width, height)
|
||||
|
||||
gameImage?.let { image ->
|
||||
val scale = minOf(width.toDouble() / image.width, height.toDouble() / image.height)
|
||||
val x = ((width - image.width * scale) / 2).toInt()
|
||||
val y = ((height - image.height * scale) / 2).toInt()
|
||||
g2d.drawImage(image, x, y, (image.width * scale).toInt(), (image.height * scale).toInt(), null)
|
||||
}
|
||||
|
||||
g2d.dispose() // Release the graphics context
|
||||
strategy.show() // Display the buffer
|
||||
}
|
||||
}
|
||||
|
||||
private fun relayMouseEvent(e: MouseEvent) {
|
||||
requestFocusInWindow()
|
||||
val scale = minOf(width.toDouble() / gameImage!!.width, height.toDouble() / gameImage!!.height)
|
||||
val xOffset = ((width - gameImage!!.width * scale) / 2)
|
||||
val yOffset = ((height - gameImage!!.height * scale) / 2)
|
||||
|
||||
val adjustedX = ((e.x - xOffset) / scale).toInt().coerceIn(0, gameImage!!.width - 1)
|
||||
val adjustedY = ((e.y - yOffset) / scale).toInt().coerceIn(0, gameImage!!.height - 1)
|
||||
|
||||
canvas.dispatchEvent(MouseEvent(this, e.id, e.`when`, e.modifiersEx, adjustedX, adjustedY, e.clickCount, e.isPopupTrigger, e.button))
|
||||
}
|
||||
|
||||
private fun relayKeyEvent(action: (KeyListener) -> Unit) {
|
||||
for (listener in canvas.keyListeners) action(listener)
|
||||
}
|
||||
|
||||
private fun relayMouseWheelEvent(e: MouseWheelEvent) {
|
||||
val scale = minOf(width.toDouble() / gameImage!!.width, height.toDouble() / gameImage!!.height)
|
||||
val xOffset = ((width - gameImage!!.width * scale) / 2)
|
||||
val yOffset = ((height - gameImage!!.height * scale) / 2)
|
||||
|
||||
val adjustedX = ((e.x - xOffset) / scale).toInt().coerceIn(0, gameImage!!.width - 1)
|
||||
val adjustedY = ((e.y - yOffset) / scale).toInt().coerceIn(0, gameImage!!.height - 1)
|
||||
|
||||
canvas.dispatchEvent(MouseWheelEvent(this, e.id, e.`when`, e.modifiersEx, adjustedX, adjustedY, e.clickCount, e.isPopupTrigger, e.scrollType, e.scrollAmount, e.wheelRotation))
|
||||
}
|
||||
|
||||
fun updateGameImage() {
|
||||
if (IsHD()) renderGlRaster() else renderSoftwareRaster()
|
||||
repaint()
|
||||
}
|
||||
|
||||
private fun renderGlRaster() {
|
||||
val width = gameImage!!.width
|
||||
val height = gameImage!!.height
|
||||
|
||||
bufferImage.setRGB(0, 0, width, height, GlRenderer.pixelData, 0, width)
|
||||
|
||||
if (width != lastImageWidth || height != lastImageHeight) {
|
||||
transform = AffineTransform.getScaleInstance(1.0, -1.0).apply {
|
||||
translate(0.0, -height.toDouble())
|
||||
}
|
||||
op = AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR)
|
||||
flippedImage = BufferedImage(width, height, bufferImage.type)
|
||||
lastImageWidth = width
|
||||
lastImageHeight = height
|
||||
}
|
||||
|
||||
op!!.filter(bufferImage, flippedImage)
|
||||
|
||||
gameImage?.createGraphics()?.apply {
|
||||
drawImage(flippedImage, 0, 0, null)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderSoftwareRaster() {
|
||||
gameImage?.createGraphics()?.apply {
|
||||
SoftwareRaster.frameBuffer.draw(this)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,159 @@
|
|||
package KondoKit
|
||||
|
||||
import java.awt.Color
|
||||
import java.awt.Dimension
|
||||
import javax.swing.JPanel
|
||||
import rt4.GameShell
|
||||
import java.awt.*
|
||||
import java.awt.event.MouseListener
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
import java.util.*
|
||||
import java.util.Timer
|
||||
import javax.swing.*
|
||||
|
||||
object Helpers {
|
||||
|
||||
fun convertValue(type: Class<*>, genericType: Type?, value: String): Any {
|
||||
return when {
|
||||
type == Int::class.java -> value.toInt()
|
||||
type == Double::class.java -> value.toDouble()
|
||||
type == Boolean::class.java -> value.toBoolean()
|
||||
type == Color::class.java -> convertToColor(value)
|
||||
type == List::class.java && genericType is ParameterizedType -> {
|
||||
val actualTypeArgument = genericType.actualTypeArguments.firstOrNull()
|
||||
when {
|
||||
value.isBlank() -> emptyList<Any>() // Handle empty string by returning an empty list
|
||||
actualTypeArgument == Int::class.javaObjectType -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim().toInt() }
|
||||
actualTypeArgument == String::class.java -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim() }
|
||||
else -> throw IllegalArgumentException("Unsupported List type: $actualTypeArgument")
|
||||
}
|
||||
}
|
||||
else -> value // Default to String
|
||||
}
|
||||
}
|
||||
|
||||
fun showToast(
|
||||
parentComponent: Component?,
|
||||
message: String,
|
||||
messageType: Int = JOptionPane.INFORMATION_MESSAGE
|
||||
) {
|
||||
SwingUtilities.invokeLater {
|
||||
val toast = JWindow()
|
||||
toast.type = Window.Type.POPUP
|
||||
toast.background = Color(0, 0, 0, 0)
|
||||
|
||||
val panel = JPanel()
|
||||
panel.isOpaque = false
|
||||
panel.layout = BoxLayout(panel, BoxLayout.Y_AXIS)
|
||||
|
||||
val label = JLabel(message)
|
||||
label.foreground = Color.WHITE
|
||||
|
||||
label.background = when (messageType) {
|
||||
JOptionPane.ERROR_MESSAGE -> Color(220, 20, 60, 230) // Crimson for errors
|
||||
JOptionPane.INFORMATION_MESSAGE -> Color(0, 128, 0, 230) // Green for success
|
||||
JOptionPane.WARNING_MESSAGE -> Color(255, 165, 0, 230) // Orange for warnings
|
||||
else -> Color(0, 0, 0, 170) // Default semi-transparent black
|
||||
}
|
||||
|
||||
label.isOpaque = true
|
||||
label.border = BorderFactory.createEmptyBorder(10, 20, 10, 20)
|
||||
label.maximumSize = Dimension(242, 50)
|
||||
label.preferredSize = Dimension(242, 50)
|
||||
panel.add(label)
|
||||
|
||||
|
||||
|
||||
toast.contentPane.add(panel)
|
||||
toast.pack()
|
||||
|
||||
|
||||
// Adjust for parent component location if it exists
|
||||
if (parentComponent != null && GameShell.canvas.isShowing) {
|
||||
val parentLocation = parentComponent.locationOnScreen
|
||||
val x = parentLocation.x
|
||||
val y = GameShell.canvas.locationOnScreen.y
|
||||
toast.setLocation(x, y)
|
||||
} else {
|
||||
// Fallback to screen center if no parent is provided
|
||||
val screenSize = Toolkit.getDefaultToolkit().screenSize
|
||||
val x = (screenSize.width - toast.width) / 2
|
||||
val y = screenSize.height - toast.height - 50
|
||||
toast.setLocation(x, y)
|
||||
}
|
||||
|
||||
toast.isVisible = true
|
||||
|
||||
Timer().schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
SwingUtilities.invokeLater {
|
||||
toast.isVisible = false
|
||||
toast.dispose()
|
||||
}
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convertToColor(value: String): Color {
|
||||
return Color.decode(value)
|
||||
}
|
||||
|
||||
fun colorToHex(color: Color): String {
|
||||
return "#%02x%02x%02x".format(color.red, color.green, color.blue)
|
||||
}
|
||||
|
||||
fun colorToIntArray(color: Color): IntArray {
|
||||
return intArrayOf(color.red, color.green, color.blue)
|
||||
}
|
||||
|
||||
interface FieldObserver {
|
||||
fun onFieldChange(field: Field, newValue: Any?)
|
||||
}
|
||||
|
||||
fun addMouseListenerToAll(container: Container, listener: MouseListener) {
|
||||
// Recursively go through all components within the container
|
||||
for (component in container.components) {
|
||||
// Add the passed MouseListener to the component
|
||||
if (component is JComponent || component is Canvas) {
|
||||
component.addMouseListener(listener)
|
||||
}
|
||||
|
||||
// If the component is a container, recursively call this function
|
||||
if (component is Container) {
|
||||
addMouseListenerToAll(component, listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class FieldNotifier(private val plugin: Any) {
|
||||
private val observers = mutableListOf<FieldObserver>()
|
||||
|
||||
private fun notifyFieldChange(field: Field, newValue: Any?) {
|
||||
for (observer in observers) {
|
||||
observer.onFieldChange(field, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
fun setFieldValue(field: Field, value: Any?) {
|
||||
field.isAccessible = true
|
||||
field.set(plugin, value)
|
||||
notifyFieldChange(field, value)
|
||||
|
||||
try {
|
||||
val onUpdateMethod = plugin::class.java.getMethod("OnKondoValueUpdated")
|
||||
onUpdateMethod.invoke(plugin)
|
||||
} catch (e: NoSuchMethodException) {
|
||||
// The method doesn't exist
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSpriteId(skillId: Int) : Int {
|
||||
return when (skillId) {
|
||||
0 -> 197
|
||||
|
|
@ -35,9 +184,15 @@ object Helpers {
|
|||
}
|
||||
}
|
||||
|
||||
fun showAlert(message: String, title: String, type: Int){
|
||||
JOptionPane.showMessageDialog(null, message, title, type)
|
||||
}
|
||||
|
||||
fun formatHtmlLabelText(text1: String, color1: Color, text2: String, color2: Color): String {
|
||||
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>"
|
||||
return "<html><div style='white-space:nowrap;'>" +
|
||||
"<span style='color:rgb(${color1.red},${color1.green},${color1.blue});'>$text1</span>" +
|
||||
"<span style='color:rgb(${color2.red},${color2.green},${color2.blue});'>$text2</span>" +
|
||||
"</div></html>"
|
||||
}
|
||||
|
||||
fun formatNumber(value: Int): String {
|
||||
|
|
@ -78,13 +233,4 @@ object Helpers {
|
|||
else -> Color(128, 128, 128) // Default grey for unhandled skill IDs
|
||||
}
|
||||
}
|
||||
|
||||
class Spacer(width: Int = 0, height: Int = 0) : JPanel() {
|
||||
init {
|
||||
preferredSize = Dimension(width, height)
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
isOpaque = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,19 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.Constants.COLOR_BACKGROUND_DARK
|
||||
import KondoKit.Constants.SKILL_DISPLAY_ORDER
|
||||
import KondoKit.Constants.SKILL_SPRITE_DIMENSION
|
||||
import KondoKit.Helpers.formatHtmlLabelText
|
||||
import KondoKit.Helpers.getSpriteId
|
||||
import KondoKit.Helpers.showToast
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import KondoKit.plugin.StateManager.focusedView
|
||||
import com.google.gson.Gson
|
||||
import plugin.api.API
|
||||
import rt4.Sprites
|
||||
|
|
@ -16,9 +26,11 @@ import java.awt.event.MouseEvent
|
|||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.SocketTimeoutException
|
||||
import java.net.URL
|
||||
import javax.swing.*
|
||||
import javax.swing.border.MatteBorder
|
||||
import kotlin.math.floor
|
||||
|
||||
object Constants {
|
||||
// Sprite IDs
|
||||
|
|
@ -28,47 +40,49 @@ object Constants {
|
|||
const val LVL_BAR_SPRITE = 898
|
||||
|
||||
// Dimensions
|
||||
val SEARCH_FIELD_DIMENSION = Dimension(270, 30)
|
||||
val SEARCH_FIELD_DIMENSION = Dimension(230, 30)
|
||||
val ICON_DIMENSION_SMALL = Dimension(12, 12)
|
||||
val ICON_DIMENSION_MEDIUM = Dimension(18, 20)
|
||||
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
||||
val HISCORE_PANEL_DIMENSION = Dimension(270, 400)
|
||||
val FILTER_PANEL_DIMENSION = Dimension(270, 30)
|
||||
val SKILLS_PANEL_DIMENSION = Dimension(300, 300)
|
||||
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(270, 30)
|
||||
val SKILL_PANEL_DIMENSION = Dimension(90, 35)
|
||||
val HISCORE_PANEL_DIMENSION = Dimension(230, 500)
|
||||
val FILTER_PANEL_DIMENSION = Dimension(230, 30)
|
||||
val SKILLS_PANEL_DIMENSION = Dimension(230, 290)
|
||||
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(230, 30)
|
||||
val SKILL_PANEL_DIMENSION = Dimension(76, 35)
|
||||
val IMAGE_CANVAS_DIMENSION = Dimension(20, 20)
|
||||
val NUMBER_LABEL_DIMENSION = Dimension(30, 20)
|
||||
val SKILL_SPRITE_DIMENSION = Dimension(14, 14)
|
||||
val NUMBER_LABEL_DIMENSION = Dimension(20, 20)
|
||||
|
||||
// Colors
|
||||
val COLOR_BACKGROUND_DARK = Color(27, 27, 27)
|
||||
val COLOR_BACKGROUND_MEDIUM = Color(37, 37, 37)
|
||||
val COLOR_BACKGROUND_LIGHT = Color(43, 43, 43)
|
||||
val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200)
|
||||
val COLOR_RED = Color.RED
|
||||
val COLOR_SKILL_PANEL = Color(60, 60, 60)
|
||||
val COLOR_BACKGROUND_DARK = WIDGET_COLOR
|
||||
val COLOR_BACKGROUND_MEDIUM = VIEW_BACKGROUND_COLOR
|
||||
val COLOR_FOREGROUND_LIGHT = POPUP_FOREGROUND
|
||||
|
||||
// Fonts
|
||||
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
|
||||
val FONT_ARIAL_PLAIN_12 = Font("Arial", Font.PLAIN, 12)
|
||||
val FONT_ARIAL_PLAIN_12 = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12)
|
||||
val SKILL_DISPLAY_ORDER = arrayOf(0,3,14,2,16,13,1,15,10,4,17,7,5,12,11,6,9,8,20,18,19,22,21,23)
|
||||
}
|
||||
|
||||
var text: String = ""
|
||||
|
||||
object HiscoresView {
|
||||
|
||||
const val VIEW_NAME = "HISCORE_SEARCH_VIEW"
|
||||
var hiScoreView: JPanel? = null
|
||||
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 {
|
||||
private val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.MAG_SPRITE))
|
||||
private val imageCanvas = bufferedImageSprite.let {
|
||||
ImageCanvas(it).apply {
|
||||
preferredSize = Constants.ICON_DIMENSION_SMALL
|
||||
size = preferredSize
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
fillColor = COLOR_BACKGROUND_DARK
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +110,9 @@ object HiscoresView {
|
|||
} else {
|
||||
text += e.keyChar
|
||||
}
|
||||
repaint()
|
||||
SwingUtilities.invokeLater {
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
override fun keyPressed(e: KeyEvent) {
|
||||
if (e.isControlDown) {
|
||||
|
|
@ -109,7 +125,9 @@ object HiscoresView {
|
|||
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
|
||||
val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String
|
||||
text += pasteText
|
||||
repaint()
|
||||
SwingUtilities.invokeLater {
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -120,15 +138,19 @@ object HiscoresView {
|
|||
override fun mouseClicked(e: MouseEvent) {
|
||||
if (e.x > width - 20 && e.y < 20) {
|
||||
text = ""
|
||||
repaint()
|
||||
SwingUtilities.invokeLater {
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Timer(500) {
|
||||
cursorVisible = !cursorVisible
|
||||
if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW")
|
||||
repaint()
|
||||
if(focusedView == VIEW_NAME)
|
||||
SwingUtilities.invokeLater {
|
||||
repaint()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +162,7 @@ object HiscoresView {
|
|||
val fm = g.fontMetrics
|
||||
val cursorX = fm.stringWidth(text) + 30
|
||||
|
||||
imageCanvas?.let { canvas ->
|
||||
imageCanvas.let { canvas ->
|
||||
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
||||
canvas.paint(imgG)
|
||||
imgG.dispose()
|
||||
|
|
@ -153,14 +175,16 @@ object HiscoresView {
|
|||
}
|
||||
|
||||
if (text.isNotEmpty()) {
|
||||
g.color = Constants.COLOR_RED
|
||||
g.color = 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()}"
|
||||
text = username.replace(" ", "_")
|
||||
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${text.toLowerCase()}"
|
||||
|
||||
updateHiscoresView(null, "Searching...")
|
||||
|
||||
Thread {
|
||||
try {
|
||||
|
|
@ -168,6 +192,10 @@ object HiscoresView {
|
|||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
|
||||
// If a request take longer than 5 seconds timeout.
|
||||
connection.connectTimeout = 5000
|
||||
connection.readTimeout = 5000
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
||||
|
|
@ -179,27 +207,45 @@ object HiscoresView {
|
|||
}
|
||||
} else {
|
||||
SwingUtilities.invokeLater {
|
||||
showError("Player not found!")
|
||||
showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
} catch (e: SocketTimeoutException) {
|
||||
SwingUtilities.invokeLater {
|
||||
showError("Error fetching data!")
|
||||
showToast(hiscoresPanel, "Request timed out", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Handle other errors
|
||||
SwingUtilities.invokeLater {
|
||||
showToast(hiscoresPanel, "Error fetching data!", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
|
||||
private fun updatePlayerData(jsonResponse: String, username: String) {
|
||||
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
||||
updateHiscoresView(hiscoresResponse, username)
|
||||
}
|
||||
|
||||
private fun updateHiscoresView(data: HiscoresResponse, username: String) {
|
||||
private fun updateHiscoresView(data: HiscoresResponse?, username: String) {
|
||||
val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel
|
||||
val ironMode = data.info.iron_mode
|
||||
|
||||
playerNameLabel?.removeAll() // Clear previous components
|
||||
var nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, "", primaryColor), JLabel.CENTER).apply {
|
||||
font = Constants.FONT_ARIAL_BOLD_12
|
||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||
}
|
||||
playerNameLabel?.add(nameLabel)
|
||||
playerNameLabel?.revalidate()
|
||||
playerNameLabel?.repaint()
|
||||
|
||||
if(data == null) return
|
||||
|
||||
playerNameLabel?.removeAll()
|
||||
|
||||
val ironMode = data.info.iron_mode
|
||||
|
||||
if (ironMode != "0") {
|
||||
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
||||
|
|
@ -213,11 +259,14 @@ object HiscoresView {
|
|||
playerNameLabel?.add(imageCanvas)
|
||||
}
|
||||
|
||||
val nameLabel = JLabel(username, JLabel.CENTER).apply {
|
||||
val exp_multiplier = data.info.exp_multiplier
|
||||
nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, " (${exp_multiplier}x)", primaryColor), JLabel.CENTER).apply {
|
||||
font = Constants.FONT_ARIAL_BOLD_12
|
||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||
}
|
||||
|
||||
|
||||
playerNameLabel?.add(nameLabel)
|
||||
|
||||
playerNameLabel?.revalidate()
|
||||
|
|
@ -266,20 +315,16 @@ object HiscoresView {
|
|||
summoning: Int,
|
||||
isMemberWorld: Boolean
|
||||
): Double {
|
||||
val base = (defence + hitpoints + Math.floor(prayer.toDouble() / 2)) * 0.25
|
||||
val base = (defence + hitpoints + floor(prayer.toDouble() / 2)) * 0.25
|
||||
val melee = (attack + strength) * 0.325
|
||||
val range = Math.floor(ranged * 1.5) * 0.325
|
||||
val mage = Math.floor(magic * 1.5) * 0.325
|
||||
val range = floor(ranged * 1.5) * 0.325
|
||||
val mage = floor(magic * 1.5) * 0.325
|
||||
val maxCombatType = maxOf(melee, range, mage)
|
||||
|
||||
val summoningFactor = if (isMemberWorld) Math.floor(summoning.toDouble() / 8) else 0.0
|
||||
val summoningFactor = if (isMemberWorld) floor(summoning.toDouble() / 8) else 0.0
|
||||
return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
@ -296,10 +341,10 @@ object HiscoresView {
|
|||
}
|
||||
}
|
||||
|
||||
fun createHiscoreSearchView(): JPanel {
|
||||
fun createHiscoreSearchView() {
|
||||
val hiscorePanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
name = "HISCORE_SEARCH_VIEW"
|
||||
name = VIEW_NAME
|
||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||
preferredSize = Constants.HISCORE_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
|
|
@ -324,14 +369,13 @@ object HiscoresView {
|
|||
add(searchFieldWrapper)
|
||||
}
|
||||
|
||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
||||
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||
hiscorePanel.add(searchPanel)
|
||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
||||
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||
|
||||
// Adding the player name panel in place of the filterPanel
|
||||
val playerNamePanel = JPanel().apply {
|
||||
layout = FlowLayout(FlowLayout.CENTER)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
layout = GridBagLayout() // This will center the JLabel both vertically and horizontally
|
||||
background = TOOLTIP_BACKGROUND.darker()
|
||||
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
|
|
@ -339,7 +383,7 @@ object HiscoresView {
|
|||
}
|
||||
|
||||
hiscorePanel.add(playerNamePanel)
|
||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
||||
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||
|
||||
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||
|
|
@ -348,10 +392,10 @@ object HiscoresView {
|
|||
minimumSize = preferredSize
|
||||
}
|
||||
|
||||
for (i in 0 until 24) {
|
||||
for (i in SKILL_DISPLAY_ORDER) {
|
||||
val skillPanel = JPanel().apply {
|
||||
layout = BorderLayout()
|
||||
background = Constants.COLOR_SKILL_PANEL
|
||||
background = COLOR_BACKGROUND_DARK
|
||||
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
|
|
@ -362,8 +406,9 @@ object HiscoresView {
|
|||
|
||||
val imageCanvas = bufferedImageSprite.let {
|
||||
ImageCanvas(it).apply {
|
||||
preferredSize = Constants.IMAGE_CANVAS_DIMENSION
|
||||
size = Constants.IMAGE_CANVAS_DIMENSION
|
||||
preferredSize = SKILL_SPRITE_DIMENSION
|
||||
size = SKILL_SPRITE_DIMENSION
|
||||
fillColor = COLOR_BACKGROUND_DARK
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +421,7 @@ object HiscoresView {
|
|||
}
|
||||
|
||||
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_DARK
|
||||
background = COLOR_BACKGROUND_DARK
|
||||
add(imageCanvas)
|
||||
add(numberLabel)
|
||||
}
|
||||
|
|
@ -394,9 +439,10 @@ object HiscoresView {
|
|||
minimumSize = preferredSize
|
||||
}
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE));
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE))
|
||||
|
||||
val totalLevelIcon = ImageCanvas(bufferedImageSprite).apply {
|
||||
fillColor = COLOR_BACKGROUND_DARK
|
||||
preferredSize = Constants.ICON_DIMENSION_LARGE
|
||||
size = Constants.ICON_DIMENSION_LARGE
|
||||
}
|
||||
|
|
@ -418,6 +464,7 @@ object HiscoresView {
|
|||
val bufferedImageSprite2 = getBufferedImageFromSprite(API.GetSprite(Constants.COMBAT_LVL_SPRITE))
|
||||
|
||||
val combatLevelIcon = ImageCanvas(bufferedImageSprite2).apply {
|
||||
fillColor = COLOR_BACKGROUND_DARK
|
||||
preferredSize = Constants.ICON_DIMENSION_LARGE
|
||||
size = Constants.ICON_DIMENSION_LARGE
|
||||
}
|
||||
|
|
@ -431,7 +478,7 @@ object HiscoresView {
|
|||
}
|
||||
|
||||
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
||||
background = Constants.COLOR_BACKGROUND_DARK
|
||||
background = COLOR_BACKGROUND_DARK
|
||||
add(combatLevelIcon)
|
||||
add(combatLevelLabel)
|
||||
}
|
||||
|
|
@ -439,8 +486,9 @@ object HiscoresView {
|
|||
totalCombatPanel.add(totalLevelPanel)
|
||||
totalCombatPanel.add(combatLevelPanel)
|
||||
hiscorePanel.add(totalCombatPanel)
|
||||
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||
|
||||
return hiscorePanel
|
||||
hiScoreView = hiscorePanel
|
||||
}
|
||||
|
||||
data class HiscoresResponse(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import java.awt.Canvas
|
||||
import java.awt.Color
|
||||
import java.awt.Dimension
|
||||
|
|
@ -8,31 +9,25 @@ import java.awt.image.BufferedImage
|
|||
|
||||
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
||||
|
||||
var fillColor: Color = WIDGET_COLOR
|
||||
|
||||
init {
|
||||
// Manually set the alpha value to 255 (fully opaque) only for pixels that are not fully transparent
|
||||
val width = image.width
|
||||
val height = image.height
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
// Retrieve the current pixel color
|
||||
val color = image.getRGB(x, y)
|
||||
|
||||
// Check if the pixel is not fully transparent (i.e., color is not 0)
|
||||
if (color != 0) {
|
||||
// Ensure the alpha is set to 255 (fully opaque)
|
||||
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
||||
|
||||
// Set the pixel with the updated color
|
||||
image.setRGB(x, y, newColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun paint(g: Graphics) {
|
||||
super.paint(g)
|
||||
g.color = Color(27, 27, 27)
|
||||
g.color = fillColor
|
||||
g.fillRect(0, 0, width, height)
|
||||
g.drawImage(image, 0, 0, width, height, this)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
package KondoKit
|
||||
|
||||
import java.awt.Color
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
|
||||
|
||||
/*
|
||||
This is used for the runtime editing of plugin variables.
|
||||
To expose fields name them starting with `kondoExposed_`
|
||||
When they are applied this will trigger an invoke of OnKondoValueUpdated()
|
||||
if it is implemented. Check GroundItems plugin for an example.
|
||||
*/
|
||||
|
||||
object KondoKitUtils {
|
||||
const val KONDO_PREFIX = "kondoExposed_"
|
||||
|
||||
fun isKondoExposed(field: Field): Boolean {
|
||||
return field.name.startsWith(KONDO_PREFIX)
|
||||
}
|
||||
|
||||
fun getKondoExposedFields(instance: Any): List<Field> {
|
||||
val exposedFields: MutableList<Field> = ArrayList()
|
||||
for (field in instance.javaClass.declaredFields) {
|
||||
if (isKondoExposed(field)) {
|
||||
exposedFields.add(field)
|
||||
}
|
||||
}
|
||||
return exposedFields
|
||||
}
|
||||
|
||||
fun convertValue(type: Class<*>, genericType: Type?, value: String): Any {
|
||||
return when {
|
||||
type == Int::class.java -> value.toInt()
|
||||
type == Double::class.java -> value.toDouble()
|
||||
type == Boolean::class.java -> value.toBoolean()
|
||||
type == Color::class.java -> convertToColor(value)
|
||||
type == List::class.java && genericType is ParameterizedType -> {
|
||||
val actualTypeArgument = genericType.actualTypeArguments.firstOrNull()
|
||||
when {
|
||||
value.isBlank() -> emptyList<Any>() // Handle empty string by returning an empty list
|
||||
actualTypeArgument == Int::class.javaObjectType -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim().toInt() }
|
||||
actualTypeArgument == String::class.java -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim() }
|
||||
else -> throw IllegalArgumentException("Unsupported List type: $actualTypeArgument")
|
||||
}
|
||||
}
|
||||
else -> value // Default to String
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun convertToColor(value: String): Color {
|
||||
val color = Color.decode(value) // Assumes value is in format "#RRGGBB" or "0xRRGGBB"
|
||||
return color
|
||||
}
|
||||
|
||||
fun colorToHex(color: Color): String {
|
||||
return "#%02x%02x%02x".format(color.red, color.green, color.blue)
|
||||
}
|
||||
|
||||
fun colorToIntArray(color: Color): IntArray {
|
||||
return intArrayOf(color.red, color.green, color.blue)
|
||||
}
|
||||
|
||||
interface FieldObserver {
|
||||
fun onFieldChange(field: Field, newValue: Any?)
|
||||
}
|
||||
|
||||
class FieldNotifier(private val plugin: Any) {
|
||||
private val observers = mutableListOf<FieldObserver>()
|
||||
|
||||
fun addObserver(observer: FieldObserver) {
|
||||
observers.add(observer)
|
||||
}
|
||||
|
||||
fun notifyFieldChange(field: Field, newValue: Any?) {
|
||||
for (observer in observers) {
|
||||
observer.onFieldChange(field, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
fun setFieldValue(field: Field, value: Any?) {
|
||||
field.isAccessible = true
|
||||
field.set(plugin, value)
|
||||
notifyFieldChange(field, value)
|
||||
|
||||
try {
|
||||
val onUpdateMethod = plugin::class.java.getMethod("OnKondoValueUpdated")
|
||||
onUpdateMethod.invoke(plugin)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +1,25 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.Helpers.addMouseListenerToAll
|
||||
import KondoKit.Helpers.formatHtmlLabelText
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.XPTrackerView.wrappedWidget
|
||||
import KondoKit.plugin.Companion.POPUP_BACKGROUND
|
||||
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
|
||||
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import KondoKit.plugin.StateManager.focusedView
|
||||
import plugin.api.API
|
||||
import rt4.NpcTypeList
|
||||
import rt4.ObjStackNode
|
||||
import rt4.Player
|
||||
import rt4.SceneGraph
|
||||
import rt4.*
|
||||
import java.awt.*
|
||||
import java.awt.Font
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
|
|
@ -22,19 +28,23 @@ import java.net.URL
|
|||
import java.nio.charset.StandardCharsets
|
||||
import java.text.DecimalFormat
|
||||
import javax.swing.*
|
||||
import kotlin.math.ceil
|
||||
|
||||
object LootTrackerView {
|
||||
private const val SNAPSHOT_LIFESPAN = 10
|
||||
const val BAG_ICON = 900;
|
||||
const val BAG_ICON = 900
|
||||
val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>()
|
||||
var gePriceMap = loadGEPrices()
|
||||
const val VIEW_NAME = "LOOT_TRACKER_VIEW"
|
||||
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
||||
private val npcKillCounts = mutableMapOf<String, Int>()
|
||||
private var totalTrackerWidget: XPWidget? = null
|
||||
var lastConfirmedKillNpcId = -1;
|
||||
var lastConfirmedKillNpcId = -1
|
||||
private var customToolTipWindow: JWindow? = null
|
||||
var lootTrackerView: JPanel? = null
|
||||
|
||||
fun loadGEPrices(): Map<String, String> {
|
||||
return if (plugin.kondoExposed_useLiveGEPrices) {
|
||||
return if (plugin.useLiveGEPrices) {
|
||||
try {
|
||||
println("LootTracker: Loading Remote GE Prices")
|
||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||
|
|
@ -69,7 +79,7 @@ object LootTrackerView {
|
|||
} else {
|
||||
try {
|
||||
println("LootTracker: Loading Local GE Prices")
|
||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8))
|
||||
.useLines { lines ->
|
||||
val json = lines.joinToString("\n")
|
||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
|
|
@ -94,19 +104,37 @@ object LootTrackerView {
|
|||
|
||||
|
||||
|
||||
fun createLootTrackerView(): JPanel {
|
||||
return JPanel().apply {
|
||||
layout = FlowLayout(FlowLayout.CENTER, 0, 5)
|
||||
fun createLootTrackerView() {
|
||||
lootTrackerView = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS) // Use BoxLayout on Y axis to stack widgets vertically
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
preferredSize = Dimension(270, 700)
|
||||
maximumSize = Dimension(270, 700)
|
||||
minimumSize = Dimension(270, 700)
|
||||
add(Box.createVerticalStrut(5))
|
||||
totalTrackerWidget = createTotalLootWidget()
|
||||
add(wrappedWidget(totalTrackerWidget!!.panel))
|
||||
add(Helpers.Spacer(height = 15))
|
||||
|
||||
val wrapped = wrappedWidget(totalTrackerWidget!!.container)
|
||||
val popupMenu = resetLootTrackerMenu()
|
||||
|
||||
// Create a custom MouseListener
|
||||
val rightClickListener = object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseReleased(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
addMouseListenerToAll(wrapped,rightClickListener)
|
||||
wrapped.addMouseListener(rightClickListener)
|
||||
add(wrapped)
|
||||
add(Box.createVerticalStrut(8))
|
||||
revalidate()
|
||||
repaint()
|
||||
if(focusedView == VIEW_NAME)
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +150,7 @@ object LootTrackerView {
|
|||
totalTrackerWidget?.let {
|
||||
it.previousXp += newVal
|
||||
it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor)
|
||||
it.panel.repaint()
|
||||
it.container.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,12 +160,12 @@ object LootTrackerView {
|
|||
val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor))
|
||||
return XPWidget(
|
||||
skillId = -1,
|
||||
panel = createWidgetPanel(bufferedImageSprite,l2,l1),
|
||||
container = createWidgetPanel(bufferedImageSprite,l2,l1),
|
||||
xpGainedLabel = l2,
|
||||
xpLeftLabel = JLabel(),
|
||||
actionsRemainingLabel = JLabel(),
|
||||
xpPerHourLabel = l1,
|
||||
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
|
||||
progressBar = ProgressBar(0.0, Color(0,0,0)), // unused.
|
||||
totalXpGained = 0,
|
||||
startTime = System.currentTimeMillis(),
|
||||
previousXp = 0
|
||||
|
|
@ -146,16 +174,19 @@ object LootTrackerView {
|
|||
|
||||
private fun createWidgetPanel(bufferedImageSprite: BufferedImage, l1 : JLabel, l2 : JLabel): Panel {
|
||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||
size = Dimension(width, height)
|
||||
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
size = preferredSize
|
||||
background = WIDGET_COLOR
|
||||
}
|
||||
|
||||
val imageContainer = Panel(FlowLayout()).apply {
|
||||
val imageContainer = Panel(BorderLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
add(imageCanvas)
|
||||
add(imageCanvas, BorderLayout.NORTH)
|
||||
}
|
||||
|
||||
return Panel(BorderLayout(5, 5)).apply {
|
||||
return Panel(BorderLayout(5, 0)).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = TOTAL_XP_WIDGET_SIZE
|
||||
add(imageContainer, BorderLayout.WEST)
|
||||
|
|
@ -164,7 +195,7 @@ object LootTrackerView {
|
|||
}
|
||||
|
||||
private fun createTextPanel(l1 : JLabel, l2: JLabel): Panel {
|
||||
return Panel(GridLayout(2, 1, 5, 5)).apply {
|
||||
return Panel(GridLayout(2, 1, 5, 0)).apply {
|
||||
background = WIDGET_COLOR
|
||||
add(l1)
|
||||
add(l2)
|
||||
|
|
@ -173,7 +204,7 @@ object LootTrackerView {
|
|||
|
||||
private fun createLabel(text: String): JLabel {
|
||||
return JLabel(text).apply {
|
||||
font = Font("Arial", Font.PLAIN, 11)
|
||||
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
horizontalAlignment = JLabel.LEFT
|
||||
}
|
||||
}
|
||||
|
|
@ -189,12 +220,19 @@ object LootTrackerView {
|
|||
|
||||
// Recalculate lootPanel size based on the number of unique items.
|
||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1)
|
||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
||||
val rowsNeeded = ceil(totalItems / 6.0).toInt()
|
||||
val lootPanelHeight = rowsNeeded * (40)
|
||||
|
||||
val size = Dimension(lootPanel.width,lootPanelHeight+32)
|
||||
lootPanel.parent.preferredSize = size
|
||||
lootPanel.parent.minimumSize = size
|
||||
lootPanel.parent.maximumSize = size
|
||||
lootPanel.parent.revalidate()
|
||||
lootPanel.parent.repaint()
|
||||
lootPanel.revalidate()
|
||||
lootPanel.repaint()
|
||||
|
||||
if(focusedView == VIEW_NAME)
|
||||
lootPanel.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,27 +242,93 @@ object LootTrackerView {
|
|||
}
|
||||
|
||||
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 0, 0))
|
||||
return FixedSizePanel(Dimension(36, 32)).apply {
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 1, 3153952))
|
||||
|
||||
// Create the panel for the item
|
||||
val itemPanel = FixedSizePanel(Dimension(36, 32)).apply {
|
||||
preferredSize = Dimension(36, 32)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
add(ImageCanvas(bufferedImageSprite).apply {
|
||||
|
||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||
preferredSize = Dimension(36, 32)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = preferredSize
|
||||
maximumSize = preferredSize
|
||||
}, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
// Add the imageCanvas to the panel
|
||||
add(imageCanvas, BorderLayout.CENTER)
|
||||
|
||||
// Put the itemId as a property for reference
|
||||
putClientProperty("itemId", itemId)
|
||||
|
||||
// Add mouse listener for custom hover text
|
||||
imageCanvas.addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseEntered(e: MouseEvent) {
|
||||
// Show custom tooltip when the mouse enters the component
|
||||
showCustomToolTip(e.point, itemId,quantity,imageCanvas)
|
||||
}
|
||||
|
||||
override fun mouseExited(e: MouseEvent) {
|
||||
// Hide tooltip when mouse exits
|
||||
hideCustomToolTip()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return itemPanel
|
||||
}
|
||||
|
||||
// Function to show the custom tooltip
|
||||
fun showCustomToolTip(location: Point, itemId: Int, quantity: Int, parentComponent: ImageCanvas) {
|
||||
val itemDef = ObjTypeList.get(itemId)
|
||||
val gePricePerItem = gePriceMap[itemDef.id.toString()]?.toInt() ?: 0
|
||||
val totalGePrice = gePricePerItem * quantity
|
||||
val totalHaPrice = itemDef.cost * quantity
|
||||
val geText = if (quantity > 1) " (${formatValue(gePricePerItem)} ea)" else ""
|
||||
val haText = if (quantity > 1) " (${formatValue(itemDef.cost)} ea)" else ""
|
||||
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
|
||||
val textColor = Helpers.colorToHex(secondaryColor)
|
||||
val text = "<html><div style='color: "+textColor+"; background-color: "+bgColor+"; padding: 3px;'>" +
|
||||
"${itemDef.name} x $quantity<br>" +
|
||||
"GE: ${formatValue(totalGePrice)} ${geText}<br>" +
|
||||
"HA: ${formatValue(totalHaPrice)} ${haText}</div></html>"
|
||||
|
||||
val _font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
if (customToolTipWindow == null) {
|
||||
customToolTipWindow = JWindow().apply {
|
||||
contentPane = JLabel(text).apply {
|
||||
border = BorderFactory.createLineBorder(Color.BLACK)
|
||||
isOpaque = true
|
||||
background = TOOLTIP_BACKGROUND
|
||||
foreground = Color.WHITE
|
||||
font = _font
|
||||
}
|
||||
pack()
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the tooltip location relative to the parent component
|
||||
val screenLocation = parentComponent.locationOnScreen
|
||||
customToolTipWindow!!.setLocation(screenLocation.x + location.x, screenLocation.y + location.y + 20)
|
||||
customToolTipWindow!!.isVisible = true
|
||||
}
|
||||
|
||||
// Function to hide the custom tooltip
|
||||
fun hideCustomToolTip() {
|
||||
customToolTipWindow?.isVisible = false
|
||||
customToolTipWindow = null // Nullify the global instance
|
||||
}
|
||||
|
||||
|
||||
private fun updateItemPanelIcon(panel: JPanel, itemId: Int, quantity: Int) {
|
||||
panel.removeAll()
|
||||
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
|
||||
panel.revalidate()
|
||||
panel.repaint()
|
||||
if(focusedView == VIEW_NAME)
|
||||
panel.repaint()
|
||||
}
|
||||
|
||||
private fun updateKillCountLabel(lootTrackerPanel: JPanel, npcName: String) {
|
||||
|
|
@ -246,9 +350,11 @@ object LootTrackerView {
|
|||
?.apply {
|
||||
val newValue = (getClientProperty("val") as? Int ?: 0) + valueOfNewDrops.toInt()
|
||||
text = "${formatValue(newValue)} gp"
|
||||
foreground = primaryColor
|
||||
putClientProperty("val", newValue)
|
||||
revalidate()
|
||||
repaint()
|
||||
if(focusedView == VIEW_NAME)
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -276,7 +382,7 @@ object LootTrackerView {
|
|||
|
||||
if (newDrops.isNotEmpty()) {
|
||||
val npcName = NpcTypeList.get(npcId).name
|
||||
lastConfirmedKillNpcId = npcId;
|
||||
lastConfirmedKillNpcId = npcId
|
||||
handleNewDrops(npcName.toString(), newDrops, lootTrackerView)
|
||||
toRemove.add(npcId)
|
||||
} else if (snapshot.age >= SNAPSHOT_LIFESPAN) {
|
||||
|
|
@ -308,11 +414,11 @@ object LootTrackerView {
|
|||
private fun handleNewDrops(npcName: String, newDrops: Set<Item>, lootTrackerView: JPanel) {
|
||||
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
||||
} ?: run {
|
||||
// Panel doesn't exist, so create and add it
|
||||
lootTrackerView.add(createLootFrame(npcName))
|
||||
lootTrackerView.add(Helpers.Spacer(height = 15))
|
||||
lootTrackerView.add(Box.createVerticalStrut(8))
|
||||
lootTrackerView.revalidate()
|
||||
lootTrackerView.repaint()
|
||||
if(focusedView == VIEW_NAME)
|
||||
lootTrackerView.repaint()
|
||||
}
|
||||
|
||||
npcKillCounts[npcName] = npcKillCounts.getOrDefault(npcName, 0) + 1
|
||||
|
|
@ -321,7 +427,7 @@ object LootTrackerView {
|
|||
newDrops.forEach { drop ->
|
||||
val geValue = (gePriceMap[drop.id.toString()]?.toInt() ?: 0) * drop.quantity
|
||||
updateValueLabel(lootTrackerView, geValue.toString(), npcName)
|
||||
addItemToLootPanel(lootTrackerView, drop, npcName)
|
||||
plugin.registerDrawAction { addItemToLootPanel(lootTrackerView, drop, npcName) }
|
||||
updateTotalValue(geValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -330,29 +436,30 @@ object LootTrackerView {
|
|||
val childFramePanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = WIDGET_COLOR
|
||||
minimumSize = Dimension(270, 0)
|
||||
maximumSize = Dimension(270, 700)
|
||||
minimumSize = Dimension(230, 0)
|
||||
maximumSize = Dimension(230, 700)
|
||||
name = "HELLO_WORLD"
|
||||
}
|
||||
|
||||
val labelPanel = JPanel(BorderLayout()).apply {
|
||||
background = Color(21, 21, 21)
|
||||
background = TITLE_BAR_COLOR
|
||||
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
||||
maximumSize = Dimension(270, 24)
|
||||
maximumSize = Dimension(230, 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)
|
||||
foreground = secondaryColor
|
||||
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
horizontalAlignment = JLabel.LEFT
|
||||
name = "killCountLabel_$npcName"
|
||||
}
|
||||
|
||||
val valueLabel = JLabel("0 gp").apply {
|
||||
foreground = Color(200, 200, 200)
|
||||
font = Font("Arial", Font.PLAIN, 12)
|
||||
foreground = secondaryColor
|
||||
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
horizontalAlignment = JLabel.RIGHT
|
||||
name = "valueLabel_$npcName"
|
||||
}
|
||||
|
|
@ -370,19 +477,120 @@ object LootTrackerView {
|
|||
|
||||
lootItemPanels[npcName] = mutableMapOf()
|
||||
|
||||
// Determine number of items and adjust size of lootPanel
|
||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1) // Height per row = 36 + spacing
|
||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
||||
|
||||
childFramePanel.add(labelPanel)
|
||||
childFramePanel.add(lootPanel)
|
||||
childFramePanel.add(lootPanel)
|
||||
|
||||
val popupMenu = removeLootFrameMenu(childFramePanel, npcName)
|
||||
|
||||
// Create a custom MouseListener
|
||||
val rightClickListener = object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseReleased(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labelPanel.addMouseListener(rightClickListener)
|
||||
return childFramePanel
|
||||
}
|
||||
|
||||
private fun removeLootFrameMenu(toRemove: JPanel, npcName: String): JPopupMenu {
|
||||
// Create a popup menu
|
||||
val popupMenu = JPopupMenu()
|
||||
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
|
||||
popupMenu.background = POPUP_BACKGROUND
|
||||
|
||||
// Create menu items with custom font and colors
|
||||
val menuItem1 = JMenuItem("Remove").apply {
|
||||
font = rFont // Set custom font
|
||||
background = POPUP_BACKGROUND // Dark background for item
|
||||
foreground = POPUP_FOREGROUND // Light text color for item
|
||||
}
|
||||
popupMenu.add(menuItem1)
|
||||
menuItem1.addActionListener {
|
||||
lootItemPanels[npcName]?.clear()
|
||||
npcKillCounts[npcName] = 0
|
||||
lootTrackerView?.let { parent ->
|
||||
val components = parent.components
|
||||
val toRemoveIndex = components.indexOf(toRemove)
|
||||
if (toRemoveIndex >= 0 && toRemoveIndex < components.size - 1) {
|
||||
val nextComponent = components[toRemoveIndex + 1]
|
||||
if (nextComponent is Box.Filler) {
|
||||
// Nasty way to remove the Box.createVerticalStrut(8) after
|
||||
// the lootpanel.
|
||||
parent.remove(nextComponent)
|
||||
}
|
||||
}
|
||||
parent.remove(toRemove)
|
||||
parent.revalidate()
|
||||
parent.repaint()
|
||||
}
|
||||
}
|
||||
return popupMenu
|
||||
}
|
||||
|
||||
|
||||
private fun resetLootTrackerMenu(): JPopupMenu {
|
||||
// Create a popup menu
|
||||
val popupMenu = JPopupMenu()
|
||||
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
|
||||
popupMenu.background = POPUP_BACKGROUND
|
||||
|
||||
// Create menu items with custom font and colors
|
||||
val menuItem1 = JMenuItem("Reset Loot Tracker").apply {
|
||||
font = rFont // Set custom font
|
||||
background = POPUP_BACKGROUND // Dark background for item
|
||||
foreground = POPUP_FOREGROUND // Light text color for item
|
||||
}
|
||||
popupMenu.add(menuItem1)
|
||||
menuItem1.addActionListener {
|
||||
plugin.registerDrawAction {
|
||||
resetLootTracker()
|
||||
}
|
||||
}
|
||||
return popupMenu
|
||||
}
|
||||
|
||||
private fun resetLootTracker(){
|
||||
lootTrackerView?.removeAll()
|
||||
npcKillCounts.clear()
|
||||
lootItemPanels.clear()
|
||||
totalTrackerWidget = createTotalLootWidget()
|
||||
|
||||
val wrapped = wrappedWidget(totalTrackerWidget!!.container)
|
||||
val _popupMenu = resetLootTrackerMenu()
|
||||
|
||||
// Create a custom MouseListener
|
||||
val rightClickListener = object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
_popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseReleased(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
_popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
addMouseListenerToAll(wrapped,rightClickListener)
|
||||
wrapped.addMouseListener(rightClickListener)
|
||||
lootTrackerView?.add(Box.createVerticalStrut(5))
|
||||
lootTrackerView?.add(wrapped)
|
||||
lootTrackerView?.add(Box.createVerticalStrut(8))
|
||||
lootTrackerView?.revalidate()
|
||||
lootTrackerView?.repaint()
|
||||
}
|
||||
|
||||
class FixedSizePanel(private val fixedSize: Dimension) : JPanel() {
|
||||
override fun getPreferredSize(): Dimension {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.plugin.Companion.PROGRESS_BAR_FILL
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import java.awt.Canvas
|
||||
import java.awt.Color
|
||||
import java.awt.Dimension
|
||||
import java.awt.Font
|
||||
import java.awt.Graphics
|
||||
|
||||
class ProgressBar(
|
||||
private var progress: Double,
|
||||
private val barColor: Color,
|
||||
private var currentLevel: Int = 0,
|
||||
private var nextLevel: Int = 1
|
||||
private var progress: Double,
|
||||
private val barColor: Color,
|
||||
private var currentLevel: Int = 0,
|
||||
private var nextLevel: Int = 1
|
||||
) : Canvas() {
|
||||
|
||||
init {
|
||||
font = Font("Arial", Font.PLAIN, 12)
|
||||
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
}
|
||||
|
||||
override fun paint(g: Graphics) {
|
||||
|
|
@ -25,22 +28,32 @@ class ProgressBar(
|
|||
g.fillRect(0, 0, width, this.height)
|
||||
|
||||
// Draw the unfilled part of the progress bar
|
||||
g.color = Color(100, 100, 100)
|
||||
g.color = PROGRESS_BAR_FILL
|
||||
g.fillRect(width, 0, this.width - width, this.height)
|
||||
|
||||
// Variables for text position
|
||||
val textY = this.height / 2 + 6
|
||||
|
||||
// Draw the current level on the far left
|
||||
g.color = Color(255, 255, 255)
|
||||
g.drawString("Lvl. $currentLevel", 5, this.height / 2 + 4)
|
||||
drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, secondaryColor)
|
||||
|
||||
// Draw the percentage in the middle
|
||||
val percentageText = String.format("%.2f%%", progress)
|
||||
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
|
||||
g.drawString(percentageText, (this.width - percentageWidth) / 2, this.height / 2 + 4)
|
||||
drawTextWithShadow(g, percentageText, (this.width - percentageWidth) / 2, textY, secondaryColor)
|
||||
|
||||
// Draw the next level on the far right
|
||||
val nextLevelText = "Lvl. $nextLevel"
|
||||
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
|
||||
g.drawString(nextLevelText, this.width - nextLevelWidth - 5, this.height / 2 + 4)
|
||||
drawTextWithShadow(g, nextLevelText, this.width - nextLevelWidth - 5, textY, secondaryColor)
|
||||
}
|
||||
|
||||
override fun getPreferredSize(): Dimension {
|
||||
return Dimension(220, 16) // Force the height to 16px, width can be anything appropriate
|
||||
}
|
||||
|
||||
override fun getMinimumSize(): Dimension {
|
||||
return Dimension(220, 16) // Force the minimum height to 16px, width can be smaller
|
||||
}
|
||||
|
||||
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
|
||||
|
|
@ -50,4 +63,15 @@ class ProgressBar(
|
|||
if(isVisible)
|
||||
repaint()
|
||||
}
|
||||
|
||||
// Helper function to draw text with a shadow effect
|
||||
private fun drawTextWithShadow(g: Graphics, text: String, x: Int, y: Int, textColor: Color) {
|
||||
// Draw shadow (black text with -1 x and -1 y offset)
|
||||
g.color = Color(0, 0, 0)
|
||||
g.drawString(text, x + 1, y + 1)
|
||||
|
||||
// Draw actual text on top
|
||||
g.color = textColor
|
||||
g.drawString(text, x, y)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,149 +1,324 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.KondoKitUtils.convertValue
|
||||
import KondoKit.Helpers.convertValue
|
||||
import KondoKit.Helpers.showToast
|
||||
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
|
||||
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import KondoKit.plugin.StateManager.focusedView
|
||||
import plugin.Plugin
|
||||
import plugin.PluginInfo
|
||||
import plugin.PluginRepository
|
||||
import java.awt.*
|
||||
import java.lang.reflect.Field
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.util.*
|
||||
import java.util.Timer
|
||||
import javax.swing.*
|
||||
import kotlin.math.ceil
|
||||
|
||||
/*
|
||||
This is used for the runtime editing of plugin variables.
|
||||
To expose fields add the @Exposed annotation.
|
||||
When they are applied this will trigger an invoke of OnKondoValueUpdated()
|
||||
if it is implemented. Check GroundItems plugin for an example.
|
||||
*/
|
||||
|
||||
|
||||
object ReflectiveEditorView {
|
||||
fun createReflectiveEditorView(): JPanel {
|
||||
var reflectiveEditorView: JPanel? = null
|
||||
private val loadedPlugins: MutableList<String> = mutableListOf()
|
||||
const val VIEW_NAME = "REFLECTIVE_EDITOR_VIEW"
|
||||
fun createReflectiveEditorView() {
|
||||
val reflectiveEditorPanel = JPanel(BorderLayout())
|
||||
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
||||
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
||||
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
||||
return reflectiveEditorPanel
|
||||
reflectiveEditorView = reflectiveEditorPanel
|
||||
addPlugins(reflectiveEditorView!!)
|
||||
}
|
||||
|
||||
fun addPlugins(reflectiveEditorView: JPanel) {
|
||||
reflectiveEditorView.removeAll() // clear previous
|
||||
loadedPlugins.clear()
|
||||
try {
|
||||
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
||||
loadedPluginsField.isAccessible = true
|
||||
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
||||
|
||||
for ((_, plugin) in loadedPlugins) {
|
||||
addPluginToEditor(reflectiveEditorView, plugin as Plugin)
|
||||
for ((pluginInfo, plugin) in loadedPlugins) {
|
||||
addPluginToEditor(reflectiveEditorView, pluginInfo as PluginInfo, plugin as Plugin)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
reflectiveEditorView.revalidate()
|
||||
reflectiveEditorView.repaint()
|
||||
}
|
||||
|
||||
private fun addPluginToEditor(reflectiveEditorView: JPanel, plugin: Any) {
|
||||
reflectiveEditorView.layout = BoxLayout(reflectiveEditorView, BoxLayout.Y_AXIS)
|
||||
// Add a centered box for plugins that have no exposed fields
|
||||
if (loadedPlugins.isNotEmpty()) {
|
||||
val noExposedPanel = JPanel(BorderLayout())
|
||||
noExposedPanel.background = VIEW_BACKGROUND_COLOR
|
||||
noExposedPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
||||
|
||||
val 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)
|
||||
val label = JLabel("Loaded Plugins without Exposed Fields", SwingConstants.CENTER)
|
||||
label.font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||
label.foreground = primaryColor
|
||||
label.font = Font("Arial", Font.BOLD, 14)
|
||||
labelPanel.add(label, BorderLayout.CENTER)
|
||||
reflectiveEditorView.add(labelPanel)
|
||||
}
|
||||
noExposedPanel.add(label, BorderLayout.NORTH)
|
||||
|
||||
for (field in exposedFields) {
|
||||
field.isAccessible = true
|
||||
val pluginsList = JList(loadedPlugins.toTypedArray())
|
||||
pluginsList.background = WIDGET_COLOR
|
||||
pluginsList.foreground = secondaryColor
|
||||
pluginsList.font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
// Wrap the JList in a JScrollPane with a fixed height
|
||||
val maxScrollPaneHeight = 200
|
||||
val scrollPane = JScrollPane(pluginsList).apply {
|
||||
verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
|
||||
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||
}
|
||||
|
||||
fieldPanel.add(applyButton, gbc)
|
||||
reflectiveEditorView.add(fieldPanel)
|
||||
// Create a wrapper panel with BoxLayout to constrain the scroll pane
|
||||
val scrollPaneWrapper = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
add(scrollPane)
|
||||
}
|
||||
|
||||
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)
|
||||
noExposedPanel.add(scrollPaneWrapper, BorderLayout.CENTER)
|
||||
|
||||
// Center the panel within the reflectiveEditorView
|
||||
val centeredPanel = JPanel().apply {
|
||||
preferredSize = Dimension(240, maxScrollPaneHeight)
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
}
|
||||
centeredPanel.layout = BoxLayout(centeredPanel, BoxLayout.Y_AXIS)
|
||||
centeredPanel.add(Box.createVerticalGlue())
|
||||
centeredPanel.add(noExposedPanel)
|
||||
centeredPanel.add(Box.createVerticalGlue())
|
||||
|
||||
reflectiveEditorView.add(Box.createVerticalStrut(10))
|
||||
reflectiveEditorView.add(centeredPanel)
|
||||
}
|
||||
|
||||
|
||||
reflectiveEditorView.revalidate()
|
||||
if(focusedView == VIEW_NAME)
|
||||
reflectiveEditorView.repaint()
|
||||
}
|
||||
|
||||
private fun addPluginToEditor(reflectiveEditorView: JPanel, pluginInfo : PluginInfo, plugin: Plugin) {
|
||||
reflectiveEditorView.layout = BoxLayout(reflectiveEditorView, BoxLayout.Y_AXIS)
|
||||
|
||||
val fieldNotifier = Helpers.FieldNotifier(plugin)
|
||||
val exposedFields = plugin.javaClass.declaredFields.filter { field ->
|
||||
field.annotations.any { annotation ->
|
||||
annotation.annotationClass.simpleName == "Exposed"
|
||||
}
|
||||
}
|
||||
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
|
||||
val packageName = plugin.javaClass.`package`.name
|
||||
val version = pluginInfo.version
|
||||
val labelPanel = JPanel(BorderLayout())
|
||||
labelPanel.maximumSize = Dimension(Int.MAX_VALUE, 30)
|
||||
labelPanel.background = VIEW_BACKGROUND_COLOR
|
||||
labelPanel.border = BorderFactory.createEmptyBorder(5, 0, 0, 0)
|
||||
|
||||
val label = JLabel("$packageName v$version", SwingConstants.CENTER)
|
||||
label.foreground = primaryColor
|
||||
label.font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
labelPanel.add(label, BorderLayout.CENTER)
|
||||
label.isOpaque = true
|
||||
label.background = TITLE_BAR_COLOR
|
||||
reflectiveEditorView.add(labelPanel)
|
||||
|
||||
for (field in exposedFields) {
|
||||
field.isAccessible = true
|
||||
|
||||
// Get the "Exposed" annotation specifically and retrieve its description, if available
|
||||
val exposedAnnotation = field.annotations.firstOrNull { annotation ->
|
||||
annotation.annotationClass.simpleName == "Exposed"
|
||||
}
|
||||
|
||||
val description = exposedAnnotation?.let { annotation ->
|
||||
try {
|
||||
val descriptionField = annotation.annotationClass.java.getMethod("description")
|
||||
descriptionField.invoke(annotation) as String
|
||||
} catch (e: NoSuchMethodException) {
|
||||
"" // No description method, return empty string
|
||||
}
|
||||
} ?: ""
|
||||
|
||||
val fieldPanel = JPanel()
|
||||
fieldPanel.layout = GridBagLayout()
|
||||
fieldPanel.background = WIDGET_COLOR
|
||||
fieldPanel.foreground = secondaryColor
|
||||
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
||||
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
||||
|
||||
val gbc = GridBagConstraints()
|
||||
gbc.insets = Insets(0, 5, 0, 5)
|
||||
|
||||
val label = JLabel(field.name.capitalize())
|
||||
label.foreground = secondaryColor
|
||||
gbc.gridx = 0
|
||||
gbc.gridy = 0
|
||||
gbc.weightx = 0.0
|
||||
label.font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
gbc.anchor = GridBagConstraints.WEST
|
||||
fieldPanel.add(label, gbc)
|
||||
|
||||
// Create appropriate input component based on field type
|
||||
val inputComponent: JComponent = when {
|
||||
field.type == Boolean::class.javaPrimitiveType || field.type == java.lang.Boolean::class.java -> JCheckBox().apply {
|
||||
isSelected = field.get(plugin) as Boolean
|
||||
}
|
||||
|
||||
field.type.isEnum -> JComboBox((field.type.enumConstants as Array<Enum<*>>)).apply {
|
||||
selectedItem = field.get(plugin)
|
||||
}
|
||||
|
||||
field.type == Int::class.javaPrimitiveType || field.type == Integer::class.java -> JSpinner(SpinnerNumberModel(field.get(plugin) as Int, Int.MIN_VALUE, Int.MAX_VALUE, 1))
|
||||
field.type == Float::class.javaPrimitiveType || field.type == Double::class.javaPrimitiveType || field.type == java.lang.Float::class.java || field.type == java.lang.Double::class.java -> JSpinner(SpinnerNumberModel((field.get(plugin) as Number).toDouble(), -Double.MAX_VALUE, Double.MAX_VALUE, 0.1))
|
||||
else -> JTextField(field.get(plugin)?.toString() ?: "")
|
||||
}
|
||||
|
||||
// Add mouse listener to the label only if a description is available
|
||||
if (description.isNotBlank()) {
|
||||
label.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
|
||||
label.addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseEntered(e: MouseEvent) {
|
||||
showCustomToolTip(description, label)
|
||||
}
|
||||
|
||||
override fun mouseExited(e: MouseEvent) {
|
||||
customToolTipWindow?.isVisible = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
gbc.gridx = 1
|
||||
gbc.gridy = 0
|
||||
gbc.weightx = 1.0
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL
|
||||
fieldPanel.add(inputComponent, gbc)
|
||||
|
||||
val applyButton = JButton("\u2714").apply {
|
||||
maximumSize = Dimension(Int.MAX_VALUE, 8)
|
||||
}
|
||||
gbc.gridx = 2
|
||||
gbc.gridy = 0
|
||||
gbc.weightx = 0.0
|
||||
gbc.fill = GridBagConstraints.NONE
|
||||
applyButton.addActionListener {
|
||||
try {
|
||||
val newValue = when (inputComponent) {
|
||||
is JCheckBox -> inputComponent.isSelected
|
||||
is JComboBox<*> -> inputComponent.selectedItem
|
||||
is JSpinner -> inputComponent.value
|
||||
is JTextField -> convertValue(field.type, field.genericType, inputComponent.text)
|
||||
else -> throw IllegalArgumentException("Unsupported input component type")
|
||||
}
|
||||
fieldNotifier.setFieldValue(field, newValue)
|
||||
showToast(
|
||||
reflectiveEditorView,
|
||||
"${field.name} updated successfully!"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
showToast(
|
||||
reflectiveEditorView,
|
||||
"Failed to update ${field.name}: ${e.message}",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fieldPanel.add(applyButton, gbc)
|
||||
reflectiveEditorView.add(fieldPanel)
|
||||
|
||||
// Track field changes in real-time and update UI
|
||||
var previousValue = field.get(plugin)?.toString()
|
||||
val timer = Timer()
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
val currentValue = field.get(plugin)?.toString()
|
||||
if (currentValue != previousValue) {
|
||||
previousValue = currentValue
|
||||
SwingUtilities.invokeLater {
|
||||
// Update the inputComponent based on the new value
|
||||
when (inputComponent) {
|
||||
is JCheckBox -> inputComponent.isSelected = field.get(plugin) as Boolean
|
||||
is JComboBox<*> -> inputComponent.selectedItem = field.get(plugin)
|
||||
is JSpinner -> inputComponent.value = field.get(plugin)
|
||||
is JTextField -> inputComponent.text = field.get(plugin)?.toString() ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0, 1000)
|
||||
}, 0, 1000) // Poll every 1000 milliseconds (1 second)
|
||||
}
|
||||
|
||||
fieldNotifier.addObserver(object : KondoKitUtils.FieldObserver {
|
||||
override fun onFieldChange(field: Field, newValue: Any?) {
|
||||
if (field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).equals(label.text, ignoreCase = true)) {
|
||||
textField.text = newValue?.toString() ?: ""
|
||||
textField.revalidate()
|
||||
textField.repaint()
|
||||
}
|
||||
}
|
||||
})
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
reflectiveEditorView.add(Box.createVerticalStrut(5))
|
||||
}
|
||||
}
|
||||
if (exposedFields.isNotEmpty()) {
|
||||
reflectiveEditorView.add(Box.createVerticalStrut(10))
|
||||
else {
|
||||
loadedPlugins.add(plugin.javaClass.`package`.name)
|
||||
}
|
||||
}
|
||||
|
||||
var customToolTipWindow: JWindow? = null
|
||||
|
||||
fun showCustomToolTip(text: String, component: JComponent) {
|
||||
val _font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||
val maxWidth = 150
|
||||
val lineHeight = 16
|
||||
|
||||
// Create a dummy JLabel to get FontMetrics for the font used in the tooltip
|
||||
val dummyLabel = JLabel()
|
||||
dummyLabel.font = _font
|
||||
val fontMetrics = dummyLabel.getFontMetrics(_font)
|
||||
|
||||
// Calculate the approximate width of the text
|
||||
val textWidth = fontMetrics.stringWidth(text)
|
||||
|
||||
// Calculate the number of lines required based on the text width and max tooltip width
|
||||
val numberOfLines = ceil(textWidth.toDouble() / maxWidth).toInt()
|
||||
|
||||
// Calculate the required height of the tooltip
|
||||
val requiredHeight = numberOfLines * lineHeight + 6 // Adding some padding
|
||||
|
||||
if (customToolTipWindow == null) {
|
||||
customToolTipWindow = JWindow().apply {
|
||||
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
|
||||
val textColor = Helpers.colorToHex(secondaryColor)
|
||||
contentPane = JLabel("<html><div style='color: $textColor; background-color: $bgColor; padding: 3px; word-break: break-all;'>$text</div></html>").apply {
|
||||
border = BorderFactory.createLineBorder(Color.BLACK)
|
||||
isOpaque = true
|
||||
background = TOOLTIP_BACKGROUND
|
||||
foreground = Color.WHITE
|
||||
font = _font
|
||||
maximumSize = Dimension(maxWidth, Int.MAX_VALUE)
|
||||
preferredSize = Dimension(maxWidth, requiredHeight)
|
||||
}
|
||||
pack()
|
||||
}
|
||||
} else {
|
||||
// Update the tooltip text
|
||||
val label = customToolTipWindow!!.contentPane as JLabel
|
||||
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
|
||||
val textColor = Helpers.colorToHex(secondaryColor)
|
||||
label.text = "<html><div style='color: $textColor; background-color: $bgColor; padding: 3px; word-break: break-all;'>$text</div></html>"
|
||||
label.preferredSize = Dimension(maxWidth, requiredHeight)
|
||||
customToolTipWindow!!.pack()
|
||||
}
|
||||
|
||||
// Position the tooltip near the component
|
||||
val locationOnScreen = component.locationOnScreen
|
||||
customToolTipWindow!!.setLocation(locationOnScreen.x, locationOnScreen.y + 15)
|
||||
customToolTipWindow!!.isVisible = true
|
||||
}
|
||||
}
|
||||
162
plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt
Normal file
162
plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.plugin.Companion.SCROLL_BAR_COLOR
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import rt4.GameShell.frame
|
||||
import java.awt.Graphics
|
||||
import java.awt.Graphics2D
|
||||
import java.awt.Rectangle
|
||||
import java.awt.event.*
|
||||
import java.util.*
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
class ScrollablePanel(private val content: JPanel) : JPanel() {
|
||||
private var lastMouseY = 0
|
||||
private var currentOffsetY = 0
|
||||
private var scrollbarHeight = 0
|
||||
private var scrollbarY = 0
|
||||
private var showScrollbar = false
|
||||
private var draggingScrollPill = false
|
||||
private var lastSize = 0
|
||||
|
||||
// Define a buffer for the view height (extra space for smoother scrolling)
|
||||
private val viewBuffer = -30
|
||||
|
||||
init {
|
||||
layout = null
|
||||
background = VIEW_BACKGROUND_COLOR // Color.red color can be set to debug
|
||||
|
||||
// Initial content bounds
|
||||
content.bounds = Rectangle(0, 0, 242, content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer))
|
||||
add(content)
|
||||
|
||||
// Add listeners for scrolling interactions
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent) {
|
||||
lastMouseY = e.y
|
||||
if (showScrollbar && e.x in (242 - 10)..242 && e.y in scrollbarY..(scrollbarY + scrollbarHeight)) {
|
||||
draggingScrollPill = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseReleased(e: MouseEvent) {
|
||||
draggingScrollPill = false
|
||||
}
|
||||
})
|
||||
|
||||
addMouseMotionListener(object : MouseMotionAdapter() {
|
||||
override fun mouseDragged(e: MouseEvent) {
|
||||
val deltaY = e.y - lastMouseY
|
||||
if (draggingScrollPill && showScrollbar) {
|
||||
val viewHeight = frame.height
|
||||
val contentHeight = content.height
|
||||
val scrollRatio = contentHeight.toDouble() / viewHeight
|
||||
scrollContent((deltaY * scrollRatio).toInt())
|
||||
} else if (showScrollbar) {
|
||||
scrollContent(deltaY)
|
||||
}
|
||||
lastMouseY = e.y
|
||||
}
|
||||
})
|
||||
|
||||
addMouseWheelListener { e ->
|
||||
if (showScrollbar) {
|
||||
scrollContent(-e.wheelRotation * 20)
|
||||
}
|
||||
}
|
||||
|
||||
// Timer to periodically check and update scrollbar status
|
||||
Timer().schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
updateScrollbar()
|
||||
if(lastSize != content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer))
|
||||
handleResize()
|
||||
}
|
||||
}, 0, 1000)
|
||||
|
||||
// Component listener for resizing the frame
|
||||
frame.addComponentListener(object : ComponentAdapter() {
|
||||
override fun componentResized(e: ComponentEvent) {
|
||||
handleResize()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun handleResize() {
|
||||
SwingUtilities.invokeLater{
|
||||
// Ensure the ScrollablePanel resizes with the frame
|
||||
bounds = Rectangle(0, 0, 242, frame.height)
|
||||
|
||||
// Dynamically update content bounds and scrollbar on frame resize with buffer
|
||||
lastSize = content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer)
|
||||
content.bounds = Rectangle(0, 0, 242, lastSize)
|
||||
showScrollbar = content.height > frame.height
|
||||
|
||||
currentOffsetY = 0
|
||||
|
||||
content.setLocation(0, currentOffsetY)
|
||||
updateScrollbar()
|
||||
|
||||
revalidate()
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollContent(deltaY: Int) {
|
||||
if (!showScrollbar) {
|
||||
currentOffsetY = 0
|
||||
content.setLocation(0, currentOffsetY)
|
||||
return
|
||||
}
|
||||
SwingUtilities.invokeLater {
|
||||
currentOffsetY += deltaY
|
||||
|
||||
// Apply buffer to maxOffset
|
||||
val maxOffset = (frame.height - content.height + viewBuffer).coerceAtMost(0)
|
||||
currentOffsetY = currentOffsetY.coerceAtMost(0).coerceAtLeast(maxOffset)
|
||||
|
||||
content.setLocation(0, currentOffsetY)
|
||||
|
||||
val contentHeight = content.height
|
||||
val viewHeight = frame.height + viewBuffer
|
||||
val scrollableRatio = viewHeight.toDouble() / contentHeight
|
||||
scrollbarY = ((-currentOffsetY / contentHeight.toDouble()) * viewHeight).toInt()
|
||||
scrollbarHeight = (viewHeight * scrollableRatio).toInt().coerceAtLeast(20)
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateScrollbar() {
|
||||
SwingUtilities.invokeLater {
|
||||
showScrollbar = content.height > frame.height
|
||||
|
||||
val contentHeight = content.height
|
||||
val viewHeight = frame.height + viewBuffer
|
||||
|
||||
if (showScrollbar) {
|
||||
val scrollableRatio = viewHeight.toDouble() / contentHeight
|
||||
scrollbarY = ((-currentOffsetY / contentHeight.toDouble()) * viewHeight).toInt()
|
||||
scrollbarHeight = (viewHeight * scrollableRatio).toInt().coerceAtLeast(20)
|
||||
} else {
|
||||
scrollbarY = 0
|
||||
scrollbarHeight = 0
|
||||
}
|
||||
repaint()
|
||||
}
|
||||
}
|
||||
|
||||
override fun paintComponent(g: Graphics) {
|
||||
super.paintComponent(g)
|
||||
}
|
||||
|
||||
override fun paintChildren(g: Graphics) {
|
||||
super.paintChildren(g)
|
||||
if (showScrollbar) {
|
||||
val g2 = g as Graphics2D
|
||||
val scrollbarX = 238
|
||||
g2.color = SCROLL_BAR_COLOR
|
||||
g2.fillRect(scrollbarX, scrollbarY, 2, scrollbarHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,109 +4,196 @@ import rt4.GlIndexedSprite
|
|||
import rt4.GlSprite
|
||||
import rt4.SoftwareIndexedSprite
|
||||
import rt4.SoftwareSprite
|
||||
import java.awt.Color
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
// Define interfaces for common sprite types
|
||||
interface BaseSprite {
|
||||
val width: Int
|
||||
val height: Int
|
||||
}
|
||||
|
||||
interface IndexedSprite : BaseSprite {
|
||||
val pixels: ByteArray
|
||||
val palette: IntArray
|
||||
}
|
||||
|
||||
interface NonIndexedSprite : BaseSprite {
|
||||
val pixels: IntArray?
|
||||
}
|
||||
|
||||
// Adapter functions for existing sprite types
|
||||
fun adaptSoftwareSprite(sprite: SoftwareSprite): NonIndexedSprite {
|
||||
return object : NonIndexedSprite {
|
||||
override val width: Int = sprite.width
|
||||
override val height: Int = sprite.height
|
||||
override val pixels: IntArray? = sprite.pixels
|
||||
}
|
||||
}
|
||||
|
||||
fun adaptSoftwareIndexedSprite(sprite: SoftwareIndexedSprite): IndexedSprite {
|
||||
return object : IndexedSprite {
|
||||
override val width: Int = sprite.width
|
||||
override val height: Int = sprite.height
|
||||
override val pixels: ByteArray = sprite.pixels
|
||||
override val palette: IntArray = sprite.pallet
|
||||
}
|
||||
}
|
||||
|
||||
fun adaptGlSprite(sprite: GlSprite): NonIndexedSprite {
|
||||
return object : NonIndexedSprite {
|
||||
override val width: Int = sprite.width
|
||||
override val height: Int = sprite.height
|
||||
override val pixels: IntArray? = sprite.pixels
|
||||
}
|
||||
}
|
||||
|
||||
fun adaptGlIndexedSprite(sprite: GlIndexedSprite): IndexedSprite {
|
||||
return object : IndexedSprite {
|
||||
override val width: Int = sprite.width
|
||||
override val height: Int = sprite.height
|
||||
override val pixels: ByteArray = sprite.pixels
|
||||
override val palette: IntArray = sprite.pallet
|
||||
}
|
||||
}
|
||||
|
||||
object SpriteToBufferedImage {
|
||||
/**
|
||||
* Converts a SoftwareSprite back into a BufferedImage.
|
||||
* Converts a BaseSprite into a BufferedImage.
|
||||
*
|
||||
* Handles both indexed and non-indexed sprites, with optional tinting and grayscale.
|
||||
*
|
||||
* @param sprite The sprite to be converted.
|
||||
* @param tint An optional Color to tint the image.
|
||||
* @param grayscale If true, converts the image to grayscale.
|
||||
* @param brightnessBoost A multiplier to boost the brightness of the image.
|
||||
* @return The BufferedImage created from the sprite.
|
||||
*/
|
||||
fun convertToBufferedImage(sprite: SoftwareSprite): BufferedImage {
|
||||
private fun convertToBufferedImage(
|
||||
sprite: BaseSprite,
|
||||
tint: Color? = null,
|
||||
grayscale: Boolean = false,
|
||||
brightnessBoost: Float = 1.0f
|
||||
): 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)
|
||||
when (sprite) {
|
||||
is IndexedSprite -> {
|
||||
val pixels = sprite.pixels
|
||||
val palette = sprite.palette
|
||||
|
||||
// Manually set pixels using the palette
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val index = pixels[y * width + x].toInt() and 0xFF
|
||||
val color = palette[index]
|
||||
|
||||
// Apply grayscale or tint if provided
|
||||
val finalColor = if (grayscale) {
|
||||
applyGrayscale(Color(color, true), brightnessBoost)
|
||||
} else if (tint != null) {
|
||||
applyTint(Color(color, true), tint, brightnessBoost)
|
||||
} else {
|
||||
applyBrightness(Color(color, true), brightnessBoost)
|
||||
}
|
||||
|
||||
image.setRGB(x, y, finalColor.rgb)
|
||||
}
|
||||
}
|
||||
}
|
||||
is NonIndexedSprite -> {
|
||||
val pixels = sprite.pixels ?: return image // Handle null case for GlSprite
|
||||
|
||||
// Manually set pixels directly
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val color = pixels[y * width + x]
|
||||
|
||||
// Apply grayscale or tint if provided
|
||||
val finalColor = if (grayscale) {
|
||||
applyGrayscale(Color(color, true), brightnessBoost)
|
||||
} else if (tint != null) {
|
||||
applyTint(Color(color, true), tint, brightnessBoost)
|
||||
} else {
|
||||
applyBrightness(Color(color, true), brightnessBoost)
|
||||
}
|
||||
|
||||
image.setRGB(x, y, finalColor.rgb)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
/**
|
||||
* Applies a tint to a given color using the tint's alpha value to control the intensity.
|
||||
*
|
||||
* @param original The original color.
|
||||
* @param tint The tint color to be applied.
|
||||
* @param brightnessBoost A multiplier to boost the brightness of the image.
|
||||
* @return The tinted color.
|
||||
*/
|
||||
private fun applyTint(original: Color, tint: Color, brightnessBoost: Float): Color {
|
||||
val boostedColor = applyBrightness(original, brightnessBoost)
|
||||
val r = (boostedColor.red * tint.red / 255).coerceIn(0, 255)
|
||||
val g = (boostedColor.green * tint.green / 255).coerceIn(0, 255)
|
||||
val b = (boostedColor.blue * tint.blue / 255).coerceIn(0, 255)
|
||||
return Color(r, g, b, boostedColor.alpha)
|
||||
}
|
||||
|
||||
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
|
||||
/**
|
||||
* Boosts the brightness of a given color.
|
||||
*
|
||||
* @param original The original color.
|
||||
* @param factor The multiplier to boost the brightness.
|
||||
* @return The color with boosted brightness.
|
||||
*/
|
||||
private fun applyBrightness(original: Color, factor: Float): Color {
|
||||
val r = (original.red * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||
val g = (original.green * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||
val b = (original.blue * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||
return Color(r, g, b, original.alpha)
|
||||
}
|
||||
|
||||
|
||||
fun 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
|
||||
/**
|
||||
* Converts a color to grayscale and applies a brightness boost.
|
||||
*
|
||||
* @param original The original color.
|
||||
* @param brightnessBoost A multiplier to boost the brightness.
|
||||
* @return The grayscale version of the color with boosted brightness.
|
||||
*/
|
||||
private fun applyGrayscale(original: Color, brightnessBoost: Float): Color {
|
||||
// Calculate the grayscale value using the luminosity method
|
||||
val grayValue = (0.3 * original.red + 0.59 * original.green + 0.11 * original.blue).toInt()
|
||||
val boostedGray = (grayValue * brightnessBoost).coerceIn(0.0f, 255.0f).toInt()
|
||||
return Color(boostedGray, boostedGray, boostedGray, original.alpha)
|
||||
}
|
||||
|
||||
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)
|
||||
/**
|
||||
* Converts an unknown sprite object into a BufferedImage if it matches a known sprite type.
|
||||
*
|
||||
* @param sprite The sprite object to be converted.
|
||||
* @param tint An optional Color to tint the image.
|
||||
* @param grayscale If true, converts the image to grayscale.
|
||||
* @param brightnessBoost A multiplier to boost the brightness of the image.
|
||||
* @return The BufferedImage created from the sprite or a default image if unsupported.
|
||||
*/
|
||||
fun getBufferedImageFromSprite(
|
||||
sprite: Any?,
|
||||
tint: Color? = null,
|
||||
grayscale: Boolean = false,
|
||||
brightnessBoost: Float = 1.0f
|
||||
): BufferedImage {
|
||||
return when (sprite) {
|
||||
is SoftwareSprite -> convertToBufferedImage(adaptSoftwareSprite(sprite), tint, grayscale, brightnessBoost)
|
||||
is SoftwareIndexedSprite -> convertToBufferedImage(adaptSoftwareIndexedSprite(sprite), tint, grayscale, brightnessBoost)
|
||||
is GlSprite -> convertToBufferedImage(adaptGlSprite(sprite), tint, grayscale, brightnessBoost)
|
||||
is GlIndexedSprite -> convertToBufferedImage(adaptGlIndexedSprite(sprite), tint, grayscale, brightnessBoost)
|
||||
else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) // Default empty image for unsupported types
|
||||
}
|
||||
}
|
||||
}
|
||||
94
plugin-playground/src/main/kotlin/KondoKit/Themes.kt
Normal file
94
plugin-playground/src/main/kotlin/KondoKit/Themes.kt
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package KondoKit
|
||||
|
||||
import java.awt.Color
|
||||
|
||||
object Themes {
|
||||
enum class ThemeType {
|
||||
RUNELITE,
|
||||
DARKER_RUNELITE,
|
||||
SARADOMIN,
|
||||
ORION,
|
||||
}
|
||||
|
||||
fun getTheme(themeType: ThemeType): Theme {
|
||||
return when (themeType) {
|
||||
ThemeType.RUNELITE -> Theme(
|
||||
widgetColor = Color(30, 30, 30),
|
||||
titleBarColor = Color(21, 21, 21),
|
||||
viewBackgroundColor = Color(40, 40, 40),
|
||||
primaryColor = Color(165, 165, 165),
|
||||
secondaryColor = Color(255, 255, 255),
|
||||
popupBackground = Color(45, 45, 45),
|
||||
popupForeground = Color(220, 220, 220),
|
||||
tooltipBackground = Color(50, 50, 50),
|
||||
scrollBarColor = Color(64, 64, 64),
|
||||
progressBarFill = Color(61, 56, 49),
|
||||
navTint = null,
|
||||
navGreyScale = false,
|
||||
boost = 1f
|
||||
)
|
||||
ThemeType.DARKER_RUNELITE -> Theme(
|
||||
widgetColor = Color(15, 15, 15),
|
||||
titleBarColor = Color(10, 10, 10),
|
||||
viewBackgroundColor = Color(20, 20, 20),
|
||||
primaryColor = Color(140, 140, 140),
|
||||
secondaryColor = Color(210, 210, 210),
|
||||
popupBackground = Color(25, 25, 25),
|
||||
popupForeground = Color(200, 200, 200),
|
||||
tooltipBackground = Color(30, 30, 30),
|
||||
scrollBarColor = Color(40, 40, 40),
|
||||
progressBarFill = Color(45, 40, 35),
|
||||
navTint = null,
|
||||
navGreyScale = false,
|
||||
boost = 0.8f
|
||||
)
|
||||
ThemeType.ORION -> Theme(
|
||||
widgetColor = Color(50, 50, 50),
|
||||
titleBarColor = Color(35, 35, 35),
|
||||
viewBackgroundColor = Color(60, 60, 60),
|
||||
primaryColor = Color(180, 180, 180),
|
||||
secondaryColor = Color(210, 210, 210),
|
||||
popupBackground = Color(45, 45, 45),
|
||||
popupForeground = Color(230, 230, 230),
|
||||
tooltipBackground = Color(55, 55, 55),
|
||||
scrollBarColor = Color(75, 75, 75),
|
||||
progressBarFill = Color(100, 100, 100),
|
||||
navTint = null,
|
||||
navGreyScale = true,
|
||||
boost = 1.3f
|
||||
)
|
||||
ThemeType.SARADOMIN -> Theme(
|
||||
widgetColor = Color(62, 53, 41),
|
||||
titleBarColor = Color(111, 93, 69).darker(),
|
||||
viewBackgroundColor = Color(101, 85, 63),
|
||||
primaryColor = Color(180, 150, 120),
|
||||
secondaryColor = Color(230, 210, 190),
|
||||
popupBackground = Color(70, 56, 42),
|
||||
popupForeground = Color(220, 200, 180),
|
||||
tooltipBackground = Color(80, 65, 50),
|
||||
scrollBarColor = Color(100, 85, 70),
|
||||
progressBarFill = Color(130, 110, 90),
|
||||
navTint = null,
|
||||
navGreyScale = false,
|
||||
boost = 1f
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class Theme(
|
||||
val widgetColor: Color,
|
||||
val titleBarColor: Color,
|
||||
val viewBackgroundColor: Color,
|
||||
val primaryColor: Color,
|
||||
val secondaryColor: Color,
|
||||
val popupBackground: Color,
|
||||
val popupForeground: Color,
|
||||
val tooltipBackground: Color,
|
||||
val scrollBarColor: Color,
|
||||
val progressBarFill: Color,
|
||||
val navTint: Color?,
|
||||
val navGreyScale: Boolean,
|
||||
val boost: Float
|
||||
)
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@ import rt4.Node
|
|||
|
||||
object XPTable {
|
||||
|
||||
const val MAX_LEVEL = 99
|
||||
const val INVALID_LEVEL = -1
|
||||
const val SKILLS_XP_TABLE = 716
|
||||
private const val MAX_LEVEL = 99
|
||||
private const val INVALID_LEVEL = -1
|
||||
private const val SKILLS_XP_TABLE = 716
|
||||
|
||||
private var xpTable: MutableList<Int> = mutableListOf()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,43 @@
|
|||
package KondoKit
|
||||
|
||||
import KondoKit.Helpers.addMouseListenerToAll
|
||||
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.LVL_ICON
|
||||
import KondoKit.plugin.Companion.POPUP_BACKGROUND
|
||||
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||
import KondoKit.plugin.Companion.WIDGET_SIZE
|
||||
import KondoKit.plugin.Companion.kondoExposed_playerXPMultiplier
|
||||
import KondoKit.plugin.Companion.playerXPMultiplier
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import KondoKit.plugin.StateManager.totalXPWidget
|
||||
import KondoKit.plugin.StateManager.focusedView
|
||||
import plugin.api.API
|
||||
import java.awt.*
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.swing.Box
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.*
|
||||
|
||||
object XPTrackerView {
|
||||
|
||||
private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4)
|
||||
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
||||
var totalXPWidget: XPWidget? = null
|
||||
val initialXP: MutableMap<Int, Int> = HashMap()
|
||||
var xpTrackerView: JPanel? = null
|
||||
const val VIEW_NAME = "XP_TRACKER_VIEW"
|
||||
|
||||
|
||||
val npcHitpointsMap: Map<Int, Int> = try {
|
||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
||||
.useLines { lines ->
|
||||
val json = lines.joinToString("\n")
|
||||
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
||||
|
|
@ -74,8 +82,8 @@ object XPTrackerView {
|
|||
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
||||
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
||||
val xpPerKill = when (xpWidget.skillId) {
|
||||
3 -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
||||
else -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
||||
3 -> playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
||||
else -> playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
||||
}
|
||||
val remainingKills = xpLeft / xpPerKill
|
||||
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
||||
|
|
@ -92,24 +100,61 @@ object XPTrackerView {
|
|||
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.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, focusedView == VIEW_NAME)
|
||||
|
||||
xpWidget.previousXp = xp
|
||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
xpWidget.panel.repaint()
|
||||
if (focusedView == VIEW_NAME)
|
||||
xpWidget.container.repaint()
|
||||
}
|
||||
|
||||
|
||||
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
||||
val totalXPWidget = plugin.StateManager.totalXPWidget ?: return
|
||||
val totalXPWidget = totalXPWidget ?: return
|
||||
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
||||
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
||||
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
totalXPWidget.panel.repaint()
|
||||
if (focusedView == VIEW_NAME)
|
||||
totalXPWidget.container.repaint()
|
||||
}
|
||||
|
||||
|
||||
fun resetXPTracker(xpTrackerView : JPanel){
|
||||
|
||||
// Redo logic here
|
||||
xpTrackerView.removeAll()
|
||||
val popupMenu = createResetMenu()
|
||||
|
||||
// Create a custom MouseListener
|
||||
val rightClickListener = object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseReleased(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the XP widget
|
||||
totalXPWidget = createTotalXPWidget()
|
||||
val wrapped = wrappedWidget(totalXPWidget!!.container)
|
||||
addMouseListenerToAll(wrapped,rightClickListener)
|
||||
wrapped.addMouseListener(rightClickListener)
|
||||
xpTrackerView.add(Box.createVerticalStrut(5))
|
||||
xpTrackerView.add(wrapped)
|
||||
xpTrackerView.add(Box.createVerticalStrut(5))
|
||||
|
||||
initialXP.clear()
|
||||
xpWidgets.clear()
|
||||
|
||||
xpTrackerView.revalidate()
|
||||
if (focusedView == VIEW_NAME)
|
||||
xpTrackerView.repaint()
|
||||
}
|
||||
|
||||
fun createTotalXPWidget(): XPWidget {
|
||||
val widgetPanel = Panel().apply {
|
||||
|
|
@ -120,43 +165,39 @@ object XPTrackerView {
|
|||
minimumSize = TOTAL_XP_WIDGET_SIZE
|
||||
}
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(898))
|
||||
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(LVL_ICON))
|
||||
|
||||
val imageContainer = Panel(FlowLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = IMAGE_SIZE
|
||||
maximumSize = IMAGE_SIZE
|
||||
minimumSize = IMAGE_SIZE
|
||||
size = IMAGE_SIZE
|
||||
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
size = preferredSize
|
||||
}
|
||||
|
||||
bufferedImageSprite.let { image ->
|
||||
val imageCanvas = ImageCanvas(image).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = Dimension(image.width, image.height)
|
||||
maximumSize = Dimension(image.width, image.height)
|
||||
minimumSize = Dimension(image.width, image.height)
|
||||
size = Dimension(image.width, image.height)
|
||||
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||
maximumSize = preferredSize
|
||||
minimumSize = preferredSize
|
||||
size = preferredSize
|
||||
}
|
||||
|
||||
imageContainer.add(imageCanvas)
|
||||
imageContainer.size = Dimension(image.width, image.height)
|
||||
imageContainer.size = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||
|
||||
imageContainer.revalidate()
|
||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
if(focusedView == VIEW_NAME)
|
||||
imageContainer.repaint()
|
||||
}
|
||||
|
||||
val textPanel = Panel().apply {
|
||||
layout = GridLayout(2, 1, 5, 5)
|
||||
background = WIDGET_COLOR
|
||||
layout = GridLayout(2, 1, 5, 0)
|
||||
}
|
||||
|
||||
val font = Font("Arial", Font.PLAIN, 11)
|
||||
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
|
||||
val xpGainedLabel = JLabel(
|
||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||
formatHtmlLabelText("Gained: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
|
|
@ -177,14 +218,14 @@ object XPTrackerView {
|
|||
|
||||
return XPWidget(
|
||||
skillId = -1,
|
||||
panel = widgetPanel,
|
||||
container = widgetPanel,
|
||||
xpGainedLabel = xpGainedLabel,
|
||||
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
},
|
||||
xpPerHourLabel = xpPerHourLabel,
|
||||
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
|
||||
progressBar = ProgressBar(0.0, Color.BLACK), // Unused
|
||||
totalXpGained = 0,
|
||||
startTime = System.currentTimeMillis(),
|
||||
previousXp = 0,
|
||||
|
|
@ -193,19 +234,66 @@ object XPTrackerView {
|
|||
}
|
||||
|
||||
|
||||
fun createXPTrackerView(): JPanel? {
|
||||
val widgetViewPanel = JPanel()
|
||||
widgetViewPanel.layout = BoxLayout(widgetViewPanel, BoxLayout.Y_AXIS)
|
||||
widgetViewPanel.background = VIEW_BACKGROUND_COLOR
|
||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||
fun createXPTrackerView(){
|
||||
val widgetViewPanel = JPanel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
}
|
||||
|
||||
val popupMenu = createResetMenu()
|
||||
|
||||
// Create a custom MouseListener
|
||||
val rightClickListener = object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseReleased(e: MouseEvent) {
|
||||
if (e.isPopupTrigger) {
|
||||
popupMenu.show(e.component, e.x, e.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the XP widget
|
||||
totalXPWidget = createTotalXPWidget()
|
||||
widgetViewPanel.add(wrappedWidget(totalXPWidget!!.panel))
|
||||
val wrapped = wrappedWidget(totalXPWidget!!.container)
|
||||
addMouseListenerToAll(wrapped,rightClickListener)
|
||||
wrapped.addMouseListener(rightClickListener)
|
||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||
widgetViewPanel.add(wrapped)
|
||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
||||
|
||||
return widgetViewPanel
|
||||
xpTrackerView = widgetViewPanel
|
||||
}
|
||||
|
||||
|
||||
fun createResetMenu(): JPopupMenu {
|
||||
// Create a popup menu
|
||||
val popupMenu = JPopupMenu()
|
||||
|
||||
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
|
||||
popupMenu.background = POPUP_BACKGROUND
|
||||
|
||||
// Create menu items with custom font and colors
|
||||
val menuItem1 = JMenuItem("Reset Tracker").apply {
|
||||
font = rFont // Set custom font
|
||||
background = POPUP_BACKGROUND // Dark background for item
|
||||
foreground = POPUP_FOREGROUND // Light text color for item
|
||||
}
|
||||
|
||||
// Add menu items to the popup menu
|
||||
popupMenu.add(menuItem1)
|
||||
|
||||
// Add action listeners to each menu item (optional)
|
||||
menuItem1.addActionListener { plugin.registerDrawAction { resetXPTracker(xpTrackerView!!) } }
|
||||
return popupMenu
|
||||
}
|
||||
|
||||
|
||||
fun createXPWidget(skillId: Int, previousXp: Int): XPWidget {
|
||||
val widgetPanel = Panel().apply {
|
||||
layout = BorderLayout(5, 5)
|
||||
|
|
@ -237,19 +325,19 @@ object XPTrackerView {
|
|||
imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size
|
||||
|
||||
imageContainer.revalidate()
|
||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
if(focusedView == VIEW_NAME)
|
||||
imageContainer.repaint()
|
||||
}
|
||||
|
||||
val textPanel = Panel().apply {
|
||||
layout = GridLayout(2, 2, 5, 5)
|
||||
layout = GridLayout(2, 2, 5, 0)
|
||||
background = WIDGET_COLOR
|
||||
}
|
||||
|
||||
val font = Font("Arial", Font.PLAIN, 11)
|
||||
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||
|
||||
val xpGainedLabel = JLabel(
|
||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||
).apply {
|
||||
this.horizontalAlignment = JLabel.LEFT
|
||||
this.font = font
|
||||
|
|
@ -278,7 +366,7 @@ object XPTrackerView {
|
|||
|
||||
val levelPanel = Panel().apply {
|
||||
layout = BorderLayout(5, 0)
|
||||
background = Color(43, 43, 43)
|
||||
background = WIDGET_COLOR
|
||||
}
|
||||
|
||||
val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply {
|
||||
|
|
@ -297,12 +385,12 @@ object XPTrackerView {
|
|||
widgetPanel.add(levelPanel, BorderLayout.SOUTH)
|
||||
|
||||
widgetPanel.revalidate()
|
||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
if(focusedView == VIEW_NAME)
|
||||
widgetPanel.repaint()
|
||||
|
||||
return XPWidget(
|
||||
skillId = skillId,
|
||||
panel = widgetPanel,
|
||||
container = widgetPanel,
|
||||
xpGainedLabel = xpGainedLabel,
|
||||
xpLeftLabel = xpLeftLabel,
|
||||
xpPerHourLabel = xpPerHourLabel,
|
||||
|
|
@ -314,18 +402,18 @@ object XPTrackerView {
|
|||
)
|
||||
}
|
||||
|
||||
fun wrappedWidget(component: Component, padding: Int = 7): Panel {
|
||||
fun wrappedWidget(component: Component, padding: Int = 7): Container {
|
||||
val outerPanelSize = Dimension(
|
||||
component.preferredSize.width + 2 * padding,
|
||||
component.preferredSize.height + 2 * padding
|
||||
component.preferredSize.width + 2 * padding,
|
||||
component.preferredSize.height + 2 * padding
|
||||
)
|
||||
val outerPanel = Panel(GridBagLayout()).apply {
|
||||
val outerPanel = JPanel(GridBagLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = outerPanelSize
|
||||
maximumSize = outerPanelSize
|
||||
minimumSize = outerPanelSize
|
||||
}
|
||||
val innerPanel = Panel(BorderLayout()).apply {
|
||||
val innerPanel = JPanel(BorderLayout()).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = component.preferredSize
|
||||
maximumSize = component.preferredSize
|
||||
|
|
@ -343,14 +431,14 @@ object XPTrackerView {
|
|||
|
||||
|
||||
data class XPWidget(
|
||||
val panel: Panel,
|
||||
val skillId: Int,
|
||||
val xpGainedLabel: JLabel,
|
||||
val xpLeftLabel: JLabel,
|
||||
val xpPerHourLabel: JLabel,
|
||||
val actionsRemainingLabel: JLabel,
|
||||
val progressBar: ProgressBar,
|
||||
var totalXpGained: Int = 0,
|
||||
var startTime: Long = System.currentTimeMillis(),
|
||||
var previousXp: Int = 0
|
||||
val container: Container,
|
||||
val skillId: Int,
|
||||
val xpGainedLabel: JLabel,
|
||||
val xpLeftLabel: JLabel,
|
||||
val xpPerHourLabel: JLabel,
|
||||
val actionsRemainingLabel: JLabel,
|
||||
val progressBar: ProgressBar,
|
||||
var totalXpGained: Int = 0,
|
||||
var startTime: Long = System.currentTimeMillis(),
|
||||
var previousXp: Int = 0
|
||||
)
|
||||
|
|
@ -4,214 +4,201 @@ import KondoKit.Constants.COMBAT_LVL_SPRITE
|
|||
import KondoKit.Helpers.formatHtmlLabelText
|
||||
import KondoKit.Helpers.formatNumber
|
||||
import KondoKit.Helpers.getSpriteId
|
||||
import KondoKit.Helpers.showAlert
|
||||
import KondoKit.HiscoresView.createHiscoreSearchView
|
||||
import KondoKit.HiscoresView.hiScoreView
|
||||
import KondoKit.LootTrackerView.BAG_ICON
|
||||
import KondoKit.LootTrackerView.createLootTrackerView
|
||||
import KondoKit.LootTrackerView.lootTrackerView
|
||||
import KondoKit.LootTrackerView.npcDeathSnapshots
|
||||
import KondoKit.LootTrackerView.onPostClientTick
|
||||
import KondoKit.LootTrackerView.takeGroundSnapshot
|
||||
import KondoKit.ReflectiveEditorView.addPlugins
|
||||
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
||||
import KondoKit.ReflectiveEditorView.reflectiveEditorView
|
||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||
import KondoKit.XPTrackerView.createTotalXPWidget
|
||||
import KondoKit.Themes.Theme
|
||||
import KondoKit.Themes.ThemeType
|
||||
import KondoKit.Themes.getTheme
|
||||
import KondoKit.XPTrackerView.createXPTrackerView
|
||||
import KondoKit.XPTrackerView.createXPWidget
|
||||
import KondoKit.XPTrackerView.initialXP
|
||||
import KondoKit.XPTrackerView.resetXPTracker
|
||||
import KondoKit.XPTrackerView.totalXPWidget
|
||||
import KondoKit.XPTrackerView.updateWidget
|
||||
import KondoKit.XPTrackerView.wrappedWidget
|
||||
import KondoKit.plugin.StateManager.initialXP
|
||||
import KondoKit.plugin.StateManager.totalXPWidget
|
||||
import KondoKit.plugin.StateManager.xpWidgets
|
||||
import KondoKit.XPTrackerView.xpTrackerView
|
||||
import KondoKit.XPTrackerView.xpWidgets
|
||||
import KondoKit.plugin.StateManager.focusedView
|
||||
import plugin.Plugin
|
||||
import plugin.api.*
|
||||
import plugin.api.API.*
|
||||
import plugin.api.FontColor.fromColor
|
||||
import rt4.GameShell
|
||||
import rt4.*
|
||||
import rt4.DisplayMode
|
||||
import rt4.GameShell.canvas
|
||||
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.Font
|
||||
import java.awt.event.ActionListener
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.*
|
||||
|
||||
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Exposed(val description: String = "")
|
||||
|
||||
class plugin : Plugin() {
|
||||
companion object {
|
||||
val WIDGET_SIZE = Dimension(270, 55)
|
||||
val TOTAL_XP_WIDGET_SIZE = Dimension(270, 30)
|
||||
val IMAGE_SIZE = Dimension(20, 20)
|
||||
val WIDGET_COLOR = Color(27, 27, 27)
|
||||
val VIEW_BACKGROUND_COLOR = Color(37, 37, 37)
|
||||
val primaryColor = Color(129, 129, 129) // Color for "XP Gained:"
|
||||
val secondaryColor = Color(226, 226, 226) // Color for "0"
|
||||
var kondoExposed_useLiveGEPrices = true
|
||||
var kondoExposed_playerXPMultiplier = 5
|
||||
const val FIXED_WIDTH = 782
|
||||
const val SCROLLPANE_WIDTH = 340
|
||||
val WIDGET_SIZE = Dimension(220, 50)
|
||||
val TOTAL_XP_WIDGET_SIZE = Dimension(220, 30)
|
||||
val IMAGE_SIZE = Dimension(25, 23)
|
||||
|
||||
// Default Theme Colors
|
||||
var WIDGET_COLOR = Color(30, 30, 30)
|
||||
var TITLE_BAR_COLOR = Color(21, 21, 21)
|
||||
var VIEW_BACKGROUND_COLOR = Color(40, 40, 40)
|
||||
var primaryColor = Color(165, 165, 165) // Color for "XP Gained:"
|
||||
var secondaryColor = Color(255, 255, 255) // Color for "0"
|
||||
var POPUP_BACKGROUND = Color(45, 45, 45)
|
||||
var POPUP_FOREGROUND = Color(220, 220, 220)
|
||||
var TOOLTIP_BACKGROUND = Color(50,50,50)
|
||||
var SCROLL_BAR_COLOR = Color(64, 64, 64)
|
||||
var PROGRESS_BAR_FILL = Color(61, 56, 49)
|
||||
var NAV_TINT: Color? = null
|
||||
var NAV_GREYSCALE = false
|
||||
var BOOST = 1f
|
||||
|
||||
var appliedTheme = ThemeType.RUNELITE
|
||||
|
||||
@Exposed("Theme colors for KondoKit, requires a relaunch to apply.")
|
||||
var theme = ThemeType.RUNELITE
|
||||
|
||||
@Exposed("Default: true, Use Local JSON or the prices from the Live/Stable server API")
|
||||
var useLiveGEPrices = true
|
||||
|
||||
@Exposed("Used to calculate Combat Actions until next level.")
|
||||
var playerXPMultiplier = 5
|
||||
|
||||
@Exposed("Start minimized/collapsed by default")
|
||||
var launchMinimized = false
|
||||
|
||||
@Exposed("Default 16 on Windows, 0 Linux/macOS. If Kondo is not " +
|
||||
"perfectly snapped to the edge of the game due to window chrome you can update this to fix it")
|
||||
var uiOffset = 0
|
||||
|
||||
@Exposed("Stretched/Scaled Fixed Mode Support")
|
||||
var useScaledFixed = false
|
||||
|
||||
const val FIXED_WIDTH = 765
|
||||
const val FIXED_HEIGHT = 503
|
||||
private const val NAVBAR_WIDTH = 30
|
||||
private const val MAIN_CONTENT_WIDTH = 242
|
||||
private const val WRENCH_ICON = 907
|
||||
private const val LVL_ICON = 898
|
||||
private const val LOOT_ICON = 777
|
||||
private const val MAG_SPRITE = 1423
|
||||
const val LVL_ICON = 898
|
||||
private lateinit var cardLayout: CardLayout
|
||||
private lateinit var mainContentPanel: Panel
|
||||
private var scrollPane: JScrollPane? = null
|
||||
private var hiScoreView: JPanel? = null
|
||||
private var reflectiveEditorView: JPanel? = null
|
||||
private var lootTrackerView: JPanel? = null
|
||||
private var xpTrackerView: JPanel? = null
|
||||
private lateinit var mainContentPanel: JPanel
|
||||
private var rightPanelWrapper: JScrollPane? = null
|
||||
private var accumulatedTime = 0L
|
||||
private const val tickInterval = 600L
|
||||
private var reloadInterfaces = false
|
||||
private const val TICK_INTERVAL = 600L
|
||||
private var pluginsReloaded = false
|
||||
private var loginScreen = 160;
|
||||
private var loginScreen = 160
|
||||
private var lastLogin = ""
|
||||
private var initialized = false;
|
||||
private var initialized = false
|
||||
private var lastClickTime = 0L
|
||||
private var lastUIOffset = 0
|
||||
private var themeName = "RUNELITE"
|
||||
private const val HIDDEN_VIEW = "HIDDEN"
|
||||
private var altCanvas: AltCanvas? = null
|
||||
private val drawActions = mutableListOf<() -> Unit>()
|
||||
|
||||
fun registerDrawAction(action: () -> Unit) {
|
||||
synchronized(drawActions) {
|
||||
drawActions.add(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 Init() {
|
||||
// Disable Font AA
|
||||
System.setProperty("sun.java2d.opengl", "false")
|
||||
System.setProperty("awt.useSystemAAFontSettings", "off")
|
||||
System.setProperty("swing.aatext", "false")
|
||||
}
|
||||
|
||||
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()
|
||||
xpTrackerView?.let { resetXPTracker(it) }
|
||||
}
|
||||
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(" ","_")))
|
||||
// Trim spaces, clean up tags, and remove the level info
|
||||
val cleanedInput = input
|
||||
.trim() // Remove any leading/trailing spaces
|
||||
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "") // Remove color tags
|
||||
.replace(Regex("<img=\\d+>"), "") // Remove image tags
|
||||
.replace(Regex("\\(level: \\d+\\)"), "") // Remove level text e.g. (level: 44)
|
||||
.trim() // Trim again to remove extra spaces after removing level text
|
||||
|
||||
// Proceed with the full cleaned username
|
||||
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(cleanedInput))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
updateDisplaySettings()
|
||||
frame.remove(rightPanelWrapper)
|
||||
frame.layout = BorderLayout()
|
||||
frame.add(scrollPane, BorderLayout.EAST)
|
||||
|
||||
// Clear or regenerate the reflectiveEditorView
|
||||
reflectiveEditorView?.removeAll()
|
||||
reflectiveEditorView?.revalidate()
|
||||
if(StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW")
|
||||
reflectiveEditorView?.repaint()
|
||||
|
||||
rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) }
|
||||
frame.revalidate()
|
||||
frame.repaint()
|
||||
pluginsReloaded = true
|
||||
reloadInterfaces = true
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
||||
if (!initialXP.containsKey(skillId)) {
|
||||
initialXP[skillId] = xp
|
||||
return
|
||||
}
|
||||
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
|
||||
|
||||
var xpWidget = xpWidgets[skillId]
|
||||
xpWidget = createXPWidget(skillId, previousXp)
|
||||
xpWidgets[skillId] = xpWidget
|
||||
|
||||
if (xpWidget != null) {
|
||||
updateWidget(xpWidget, xp)
|
||||
} else {
|
||||
val previousXp = initialXP[skillId] ?: xp
|
||||
if (xp == initialXP[skillId]) return
|
||||
xpTrackerView?.add(wrappedWidget(xpWidget.container))
|
||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||
|
||||
xpWidget = createXPWidget(skillId, previousXp)
|
||||
xpWidgets[skillId] = xpWidget
|
||||
if(focusedView == XPTrackerView.VIEW_NAME) {
|
||||
xpTrackerView?.revalidate()
|
||||
xpTrackerView?.repaint()
|
||||
}
|
||||
|
||||
xpTrackerView?.add(wrappedWidget(xpWidget.panel))
|
||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||
|
||||
xpTrackerView?.revalidate()
|
||||
if(StateManager.focusedView == "XP_TRACKER_VIEW")
|
||||
xpTrackerView?.repaint()
|
||||
|
||||
updateWidget(xpWidget, xp)
|
||||
}
|
||||
updateWidget(xpWidget, xp)
|
||||
}
|
||||
}
|
||||
|
||||
override fun Draw(timeDelta: Long) {
|
||||
|
|
@ -221,155 +208,498 @@ class plugin : Plugin() {
|
|||
}
|
||||
|
||||
if (pluginsReloaded) {
|
||||
InterfaceList.method3712(true) // Gets the resize working correctly
|
||||
reflectiveEditorView?.let { addPlugins(it) }
|
||||
pluginsReloaded = false
|
||||
}
|
||||
|
||||
if (reloadInterfaces){
|
||||
InterfaceList.method3712(true) // Gets the resize working correctly
|
||||
reloadInterfaces = false
|
||||
}
|
||||
|
||||
accumulatedTime += timeDelta
|
||||
if (accumulatedTime >= tickInterval) {
|
||||
if (accumulatedTime >= TICK_INTERVAL) {
|
||||
lootTrackerView?.let { onPostClientTick(it) }
|
||||
accumulatedTime = 0L
|
||||
}
|
||||
|
||||
// Draw synced actions (that require to be done between glBegin and glEnd)
|
||||
if (drawActions.isNotEmpty()) {
|
||||
synchronized(drawActions) {
|
||||
val actionsCopy = drawActions.toList()
|
||||
drawActions.clear()
|
||||
for (action in actionsCopy) {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
||||
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 LateDraw(timeDelta: Long) {
|
||||
if (!initialized) return
|
||||
if(GameShell.fullScreenFrame != null) {
|
||||
DisplayMode.setWindowMode(true, 0, FIXED_WIDTH, FIXED_HEIGHT)
|
||||
showAlert("Fullscreen is not supported by KondoKit. Disable the plugin first.",
|
||||
"Error",
|
||||
JOptionPane.INFORMATION_MESSAGE
|
||||
)
|
||||
return
|
||||
}
|
||||
if(!useScaledFixed) return
|
||||
if(GetWindowMode() == WindowMode.FIXED){
|
||||
moveAltCanvasToFront()
|
||||
} else {
|
||||
moveCanvasToFront()
|
||||
}
|
||||
altCanvas?.updateGameImage() // Update the game image as needed
|
||||
}
|
||||
|
||||
override fun Update() {
|
||||
xpWidgets.values.forEach { xpWidget ->
|
||||
|
||||
val widgets = xpWidgets.values
|
||||
val totalXP = totalXPWidget
|
||||
|
||||
widgets.forEach { xpWidget ->
|
||||
val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||
val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||
val formattedXpPerHour = formatNumber(xpPerHour)
|
||||
xpWidget.xpPerHourLabel.text =
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
||||
xpWidget.panel.repaint()
|
||||
xpWidget.container.repaint()
|
||||
}
|
||||
|
||||
totalXPWidget?.let { totalXPWidget ->
|
||||
totalXP?.let { totalXPWidget ->
|
||||
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
||||
totalXPWidget.xpPerHourLabel.text =
|
||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
||||
totalXPWidget.panel.repaint()
|
||||
totalXPWidget.container.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
||||
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
||||
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
||||
}
|
||||
|
||||
private fun 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
|
||||
}
|
||||
|
||||
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)
|
||||
private fun updateDisplaySettings() {
|
||||
val mode = GetWindowMode()
|
||||
val currentScrollPaneWidth = if (mainContentPanel.isVisible) NAVBAR_WIDTH + MAIN_CONTENT_WIDTH else NAVBAR_WIDTH
|
||||
lastUIOffset = uiOffset
|
||||
|
||||
val actionListener = ActionListener {
|
||||
cardLayout.show(mainContentPanel, viewName)
|
||||
StateManager.focusedView = viewName
|
||||
if(mode != WindowMode.FIXED) {
|
||||
destroyAltCanvas()
|
||||
} else if (useScaledFixed && altCanvas == null) {
|
||||
initAltCanvas()
|
||||
}
|
||||
|
||||
when (mode) {
|
||||
WindowMode.FIXED -> {
|
||||
if (frame.width < FIXED_WIDTH + currentScrollPaneWidth + uiOffset) {
|
||||
frame.setSize(FIXED_WIDTH + currentScrollPaneWidth + uiOffset, frame.height)
|
||||
}
|
||||
|
||||
val difference = frame.width - (uiOffset + currentScrollPaneWidth)
|
||||
|
||||
if (useScaledFixed) {
|
||||
GameShell.leftMargin = 0
|
||||
val canvasWidth = difference + uiOffset / 2
|
||||
val canvasHeight = frame.height - canvas.y // Restricting height to frame height
|
||||
|
||||
altCanvas?.size = Dimension(canvasWidth, canvasHeight)
|
||||
altCanvas?.setLocation(0, canvas.y)
|
||||
canvas.setLocation(0, canvas.y)
|
||||
} else {
|
||||
val difference = frame.width - (FIXED_WIDTH + uiOffset + currentScrollPaneWidth)
|
||||
GameShell.leftMargin = difference / 2
|
||||
}
|
||||
}
|
||||
|
||||
WindowMode.RESIZABLE -> {
|
||||
GameShell.canvasWidth = frame.width - (currentScrollPaneWidth + uiOffset)
|
||||
}
|
||||
}
|
||||
|
||||
rightPanelWrapper?.preferredSize = Dimension(currentScrollPaneWidth, frame.height)
|
||||
rightPanelWrapper?.isDoubleBuffered = true
|
||||
rightPanelWrapper?.revalidate()
|
||||
rightPanelWrapper?.repaint()
|
||||
}
|
||||
|
||||
fun OnKondoValueUpdated(){
|
||||
StoreData("kondoUseRemoteGE", useLiveGEPrices)
|
||||
StoreData("kondoTheme", theme.toString())
|
||||
if(appliedTheme != theme) {
|
||||
showAlert(
|
||||
"KondoKit Theme changes require a relaunch.",
|
||||
"KondoKit",
|
||||
JOptionPane.INFORMATION_MESSAGE
|
||||
)
|
||||
}
|
||||
StoreData("kondoPlayerXPMultiplier", playerXPMultiplier)
|
||||
LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices()
|
||||
StoreData("kondoLaunchMinimized", launchMinimized)
|
||||
StoreData("kondoUIOffset", uiOffset)
|
||||
StoreData("kondoScaledFixed", useScaledFixed)
|
||||
if(lastUIOffset != uiOffset){
|
||||
reloadInterfaces = true
|
||||
}
|
||||
updateDisplaySettings()
|
||||
}
|
||||
|
||||
private fun initAltCanvas(){
|
||||
if(GetWindowMode() != WindowMode.FIXED || altCanvas != null) return
|
||||
if (frame != null) {
|
||||
altCanvas = AltCanvas().apply {
|
||||
preferredSize = Dimension(FIXED_WIDTH, FIXED_HEIGHT)
|
||||
}
|
||||
altCanvas?.let { frame.add(it) }
|
||||
moveAltCanvasToFront()
|
||||
frame.setComponentZOrder(rightPanelWrapper, 2)
|
||||
}
|
||||
}
|
||||
|
||||
private fun destroyAltCanvas(){
|
||||
if (altCanvas == null) return
|
||||
moveCanvasToFront()
|
||||
frame.remove(altCanvas)
|
||||
altCanvas = null
|
||||
}
|
||||
|
||||
private fun moveAltCanvasToFront(){
|
||||
if (altCanvas == null) return
|
||||
frame.setComponentZOrder(canvas, 2)
|
||||
frame.setComponentZOrder(altCanvas, 1)
|
||||
frame.setComponentZOrder(rightPanelWrapper, 0)
|
||||
}
|
||||
|
||||
private fun moveCanvasToFront(){
|
||||
if (altCanvas == null) return
|
||||
frame.setComponentZOrder(altCanvas, 2)
|
||||
frame.setComponentZOrder(canvas, 1)
|
||||
frame.setComponentZOrder(rightPanelWrapper, 0)
|
||||
}
|
||||
|
||||
private fun searchHiscore(username: String): Runnable {
|
||||
return Runnable {
|
||||
setActiveView(HiscoresView.VIEW_NAME)
|
||||
val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) }
|
||||
|
||||
customSearchField?.searchPlayer(username) ?: run {
|
||||
println("searchView is null or CustomSearchField creation failed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreSettings(){
|
||||
themeName = (GetData("kondoTheme") as? String) ?: "RUNELITE"
|
||||
useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true
|
||||
playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5
|
||||
val osName = System.getProperty("os.name").toLowerCase()
|
||||
uiOffset = (GetData("kondoUIOffset") as? Int) ?: if (osName.contains("win")) 16 else 0
|
||||
launchMinimized = (GetData("kondoLaunchMinimized") as? Boolean) ?: false
|
||||
useScaledFixed = (GetData("kondoScaledFixed") as? Boolean) ?: false
|
||||
}
|
||||
|
||||
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) {
|
||||
restoreSettings()
|
||||
theme = ThemeType.valueOf(themeName)
|
||||
applyTheme(getTheme(theme))
|
||||
appliedTheme = theme
|
||||
configureLookAndFeel()
|
||||
|
||||
cardLayout = CardLayout()
|
||||
mainContentPanel = JPanel(cardLayout).apply {
|
||||
border = BorderFactory.createEmptyBorder(0, 0, 0, 0) // Removes any default border or padding
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
preferredSize = Dimension(MAIN_CONTENT_WIDTH, frame.height)
|
||||
isOpaque = true
|
||||
}
|
||||
|
||||
// Register Views
|
||||
createXPTrackerView()
|
||||
createHiscoreSearchView()
|
||||
createLootTrackerView()
|
||||
createReflectiveEditorView()
|
||||
|
||||
mainContentPanel.add(ScrollablePanel(xpTrackerView!!), XPTrackerView.VIEW_NAME)
|
||||
mainContentPanel.add(ScrollablePanel(hiScoreView!!), HiscoresView.VIEW_NAME)
|
||||
mainContentPanel.add(ScrollablePanel(lootTrackerView!!), LootTrackerView.VIEW_NAME)
|
||||
mainContentPanel.add(ScrollablePanel(reflectiveEditorView!!), ReflectiveEditorView.VIEW_NAME)
|
||||
|
||||
val navPanel = Panel().apply {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = Dimension(NAVBAR_WIDTH, frame.height)
|
||||
}
|
||||
|
||||
navPanel.add(createNavButton(LVL_ICON, XPTrackerView.VIEW_NAME))
|
||||
navPanel.add(createNavButton(MAG_SPRITE, HiscoresView.VIEW_NAME))
|
||||
navPanel.add(createNavButton(LOOT_ICON, LootTrackerView.VIEW_NAME))
|
||||
navPanel.add(createNavButton(WRENCH_ICON, ReflectiveEditorView.VIEW_NAME))
|
||||
|
||||
val rightPanel = Panel(BorderLayout()).apply {
|
||||
add(mainContentPanel, BorderLayout.CENTER)
|
||||
add(navPanel, BorderLayout.EAST)
|
||||
}
|
||||
|
||||
rightPanelWrapper = JScrollPane(rightPanel).apply {
|
||||
preferredSize = Dimension(NAVBAR_WIDTH + MAIN_CONTENT_WIDTH, frame.height)
|
||||
background = VIEW_BACKGROUND_COLOR
|
||||
border = BorderFactory.createEmptyBorder()
|
||||
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||
verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_NEVER
|
||||
}
|
||||
|
||||
frame.layout = BorderLayout()
|
||||
rightPanelWrapper?.let {
|
||||
frame.add(it, BorderLayout.EAST)
|
||||
}
|
||||
|
||||
if(launchMinimized){
|
||||
setActiveView(HIDDEN_VIEW)
|
||||
} else {
|
||||
setActiveView(XPTrackerView.VIEW_NAME)
|
||||
}
|
||||
initialized = true
|
||||
pluginsReloaded = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun setActiveView(viewName: String) {
|
||||
// Handle the visibility of the main content panel
|
||||
if (viewName == HIDDEN_VIEW) {
|
||||
mainContentPanel.isVisible = false
|
||||
} else {
|
||||
if (!mainContentPanel.isVisible) {
|
||||
mainContentPanel.isVisible = true
|
||||
}
|
||||
cardLayout.show(mainContentPanel, viewName)
|
||||
}
|
||||
|
||||
reloadInterfaces = true
|
||||
updateDisplaySettings()
|
||||
|
||||
// Revalidate and repaint necessary panels
|
||||
mainContentPanel.revalidate()
|
||||
rightPanelWrapper?.revalidate()
|
||||
frame?.revalidate()
|
||||
|
||||
mainContentPanel.repaint()
|
||||
rightPanelWrapper?.repaint()
|
||||
frame?.repaint()
|
||||
|
||||
focusedView = viewName
|
||||
}
|
||||
|
||||
private fun createNavButton(spriteId: Int, viewName: String): JPanel {
|
||||
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId), NAV_TINT, NAV_GREYSCALE, BOOST)
|
||||
val buttonSize = Dimension(NAVBAR_WIDTH, 32)
|
||||
val imageSize = Dimension((bufferedImageSprite.width / 1.2f).toInt(), (bufferedImageSprite.height / 1.2f).toInt())
|
||||
val cooldownDuration = 100L
|
||||
|
||||
val actionListener = ActionListener {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime < cooldownDuration) {
|
||||
return@ActionListener
|
||||
}
|
||||
lastClickTime = currentTime
|
||||
|
||||
if (focusedView == viewName) {
|
||||
setActiveView("HIDDEN")
|
||||
} else {
|
||||
setActiveView(viewName)
|
||||
}
|
||||
}
|
||||
|
||||
// ImageCanvas with forced size
|
||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||
background = WIDGET_COLOR
|
||||
preferredSize = imageSize
|
||||
maximumSize = imageSize
|
||||
minimumSize = imageSize
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent?) {
|
||||
actionListener.actionPerformed(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val button = JButton().apply {
|
||||
// Wrapping the ImageCanvas in another JPanel to prevent stretching
|
||||
val imageCanvasWrapper = JPanel().apply {
|
||||
layout = GridBagLayout() // Keeps the layout of the wrapped panel minimal
|
||||
preferredSize = imageSize
|
||||
maximumSize = imageSize
|
||||
minimumSize = imageSize
|
||||
isOpaque = false // No background for the wrapper
|
||||
add(imageCanvas) // Adding ImageCanvas directly, layout won't stretch it
|
||||
}
|
||||
|
||||
val panelButton = JPanel().apply {
|
||||
layout = GridBagLayout()
|
||||
preferredSize = buttonSize
|
||||
maximumSize = buttonSize
|
||||
minimumSize = buttonSize
|
||||
background = WIDGET_COLOR
|
||||
isFocusPainted = false
|
||||
isBorderPainted = false
|
||||
isOpaque = true
|
||||
|
||||
val gbc = GridBagConstraints().apply {
|
||||
anchor = GridBagConstraints.CENTER
|
||||
fill = GridBagConstraints.NONE // Prevents stretching
|
||||
}
|
||||
|
||||
add(imageCanvas, gbc)
|
||||
addActionListener(actionListener)
|
||||
add(imageCanvasWrapper, gbc)
|
||||
|
||||
// Hover and click behavior
|
||||
val hoverListener = object : MouseAdapter() {
|
||||
override fun mouseEntered(e: MouseEvent?) {
|
||||
background = WIDGET_COLOR.darker()
|
||||
imageCanvas.fillColor = WIDGET_COLOR.darker()
|
||||
imageCanvas.repaint()
|
||||
repaint()
|
||||
}
|
||||
|
||||
override fun mouseExited(e: MouseEvent?) {
|
||||
background = WIDGET_COLOR
|
||||
imageCanvas.fillColor = WIDGET_COLOR
|
||||
imageCanvas.repaint()
|
||||
repaint()
|
||||
}
|
||||
|
||||
override fun mouseClicked(e: MouseEvent?) {
|
||||
actionListener.actionPerformed(null)
|
||||
}
|
||||
}
|
||||
|
||||
addMouseListener(hoverListener)
|
||||
imageCanvas.addMouseListener(hoverListener)
|
||||
}
|
||||
|
||||
return button
|
||||
return panelButton
|
||||
}
|
||||
|
||||
private fun configureLookAndFeel(){
|
||||
loadFont()
|
||||
try {
|
||||
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel")
|
||||
|
||||
// Modify the UI properties to match theme
|
||||
UIManager.put("control", VIEW_BACKGROUND_COLOR)
|
||||
UIManager.put("info", VIEW_BACKGROUND_COLOR)
|
||||
UIManager.put("nimbusBase", WIDGET_COLOR)
|
||||
UIManager.put("nimbusBlueGrey", TITLE_BAR_COLOR)
|
||||
|
||||
UIManager.put("nimbusDisabledText", primaryColor)
|
||||
UIManager.put("nimbusSelectedText", secondaryColor)
|
||||
UIManager.put("text", secondaryColor)
|
||||
|
||||
UIManager.put("nimbusFocus", TITLE_BAR_COLOR)
|
||||
UIManager.put("nimbusInfoBlue", POPUP_BACKGROUND)
|
||||
UIManager.put("nimbusLightBackground", WIDGET_COLOR)
|
||||
UIManager.put("nimbusSelectionBackground", PROGRESS_BAR_FILL)
|
||||
|
||||
UIManager.put("Button.background", WIDGET_COLOR)
|
||||
UIManager.put("Button.foreground", secondaryColor)
|
||||
|
||||
UIManager.put("CheckBox.background", VIEW_BACKGROUND_COLOR)
|
||||
UIManager.put("CheckBox.foreground", secondaryColor)
|
||||
UIManager.put("CheckBox.icon", UIManager.getIcon("CheckBox.icon"))
|
||||
|
||||
UIManager.put("ComboBox.background", WIDGET_COLOR)
|
||||
UIManager.put("ComboBox.foreground", secondaryColor)
|
||||
UIManager.put("ComboBox.selectionBackground", PROGRESS_BAR_FILL)
|
||||
UIManager.put("ComboBox.selectionForeground", primaryColor)
|
||||
UIManager.put("ComboBox.buttonBackground", WIDGET_COLOR)
|
||||
|
||||
UIManager.put("Spinner.background", WIDGET_COLOR)
|
||||
UIManager.put("Spinner.foreground", secondaryColor)
|
||||
UIManager.put("Spinner.border", BorderFactory.createLineBorder(TITLE_BAR_COLOR))
|
||||
|
||||
UIManager.put("TextField.background", WIDGET_COLOR)
|
||||
UIManager.put("TextField.foreground", secondaryColor)
|
||||
UIManager.put("TextField.caretForeground", secondaryColor)
|
||||
UIManager.put("TextField.border", BorderFactory.createLineBorder(TITLE_BAR_COLOR))
|
||||
|
||||
UIManager.put("ScrollBar.thumb", WIDGET_COLOR)
|
||||
UIManager.put("ScrollBar.track", VIEW_BACKGROUND_COLOR)
|
||||
UIManager.put("ScrollBar.thumbHighlight", TITLE_BAR_COLOR)
|
||||
|
||||
UIManager.put("ProgressBar.foreground", PROGRESS_BAR_FILL)
|
||||
UIManager.put("ProgressBar.background", WIDGET_COLOR)
|
||||
UIManager.put("ProgressBar.border", BorderFactory.createLineBorder(TITLE_BAR_COLOR))
|
||||
|
||||
UIManager.put("ToolTip.background", VIEW_BACKGROUND_COLOR)
|
||||
UIManager.put("ToolTip.foreground", secondaryColor)
|
||||
UIManager.put("ToolTip.border", BorderFactory.createLineBorder(TITLE_BAR_COLOR))
|
||||
|
||||
// Update component tree UI to apply the new theme
|
||||
SwingUtilities.updateComponentTreeUI(frame)
|
||||
frame.background = Color.BLACK
|
||||
} catch (e : Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFont(): Font? {
|
||||
val fontStream = plugin::class.java.getResourceAsStream("res/runescape_small.ttf")
|
||||
return if (fontStream != null) {
|
||||
try {
|
||||
val font = Font.createFont(Font.TRUETYPE_FONT, fontStream)
|
||||
val ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||
ge.registerFont(font) // Register the font in the graphics environment
|
||||
font
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
} else {
|
||||
println("Font not found!")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
object StateManager {
|
||||
val initialXP: MutableMap<Int, Int> = HashMap()
|
||||
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
||||
var totalXPWidget: XPWidget? = null
|
||||
var focusedView: String = ""
|
||||
}
|
||||
|
||||
private fun applyTheme(theme: Theme) {
|
||||
WIDGET_COLOR = theme.widgetColor
|
||||
TITLE_BAR_COLOR = theme.titleBarColor
|
||||
VIEW_BACKGROUND_COLOR = theme.viewBackgroundColor
|
||||
primaryColor = theme.primaryColor
|
||||
secondaryColor = theme.secondaryColor
|
||||
POPUP_BACKGROUND = theme.popupBackground
|
||||
POPUP_FOREGROUND = theme.popupForeground
|
||||
TOOLTIP_BACKGROUND = theme.tooltipBackground
|
||||
SCROLL_BAR_COLOR = theme.scrollBarColor
|
||||
PROGRESS_BAR_FILL = theme.progressBarFill
|
||||
NAV_TINT = theme.navTint
|
||||
NAV_GREYSCALE = theme.navGreyScale
|
||||
BOOST = theme.boost
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
AUTHOR='downthecrop'
|
||||
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.'
|
||||
VERSION=1.1
|
||||
VERSION=2.0
|
||||
Binary file not shown.
|
|
@ -25,6 +25,7 @@ class plugin : Plugin() {
|
|||
|
||||
private var timeMode = TIME_MODE_INITIALIZATION
|
||||
private var initTime: Long = 0
|
||||
private var logoutFlag = true
|
||||
private var displayMessageCounter = 0
|
||||
|
||||
private var component: Component? = null
|
||||
|
|
@ -35,6 +36,20 @@ class plugin : Plugin() {
|
|||
displayMessageCounter = 0
|
||||
}
|
||||
|
||||
override fun OnPluginsReloaded(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun OnLogin() {
|
||||
if(logoutFlag)
|
||||
initTime = System.currentTimeMillis()
|
||||
logoutFlag = false
|
||||
}
|
||||
|
||||
override fun OnLogout() {
|
||||
logoutFlag = true
|
||||
}
|
||||
|
||||
override fun Draw(timeDelta: Long) {
|
||||
if (component == null)
|
||||
return
|
||||
|
|
@ -134,6 +149,9 @@ class plugin : Plugin() {
|
|||
API.InsertMiniMenuEntry("Disable Timer", "") {
|
||||
timeMode = DEFAULT_TIME_MODE
|
||||
}
|
||||
API.InsertMiniMenuEntry("Reset Play Time", "") {
|
||||
initTime = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
AUTHOR='Woahscam, Ceikry'
|
||||
DESCRIPTION='Displays the session time played, system time, or no time over the "Report Abuse" button.'
|
||||
VERSION=1.2
|
||||
VERSION=1.1
|
||||
|
|
@ -1,52 +1,56 @@
|
|||
package ToggleResizableSD
|
||||
|
||||
import KondoKit.Exposed
|
||||
import plugin.Plugin
|
||||
import plugin.annotations.PluginMeta
|
||||
import plugin.api.API
|
||||
import rt4.*
|
||||
import plugin.api.API.StoreData
|
||||
import rt4.DisplayMode
|
||||
import rt4.GameShell
|
||||
import rt4.InterfaceList
|
||||
import rt4.client
|
||||
import java.awt.event.KeyAdapter
|
||||
import java.awt.event.KeyEvent
|
||||
|
||||
class plugin : Plugin() {
|
||||
var toggleResize = false
|
||||
var wantHd = false //Setting wantHd to true hides the black screen on logout (when resize SD is enabled), by enabling HD on logout
|
||||
|
||||
@Exposed("Use Resizable SD")
|
||||
var useResizable = false
|
||||
|
||||
@Exposed("Setting wantHd to true hides the black screen on logout (when resize SD is enabled), by enabling HD on logout ")
|
||||
var wantHd = false
|
||||
|
||||
override fun Init() {
|
||||
API.AddKeyboardListener(object : KeyAdapter() {
|
||||
override fun keyPressed(e: KeyEvent) {
|
||||
if (e.keyCode == KeyEvent.VK_F12) {
|
||||
toggleResize = true
|
||||
toggleResizableSd()
|
||||
}
|
||||
}
|
||||
})
|
||||
if (!DisplayMode.resizableSD && API.GetData("use-resizable-sd") == true) {
|
||||
toggleResize = true
|
||||
}
|
||||
var osNameLowerCase: String = ""
|
||||
var osName: String
|
||||
|
||||
try {
|
||||
osName = System.getProperty("os.name")
|
||||
} catch (e: Exception) {
|
||||
osName = "Unknown"
|
||||
useResizable = DisplayMode.resizableSD
|
||||
if (API.GetData("use-resizable-sd") == true) {
|
||||
useResizable = true
|
||||
}
|
||||
|
||||
osNameLowerCase = osName.toLowerCase()
|
||||
var osNameLowerCase: String = System.getProperty("os.name").toLowerCase()
|
||||
if (!osNameLowerCase.startsWith("mac")) {
|
||||
wantHd = true
|
||||
}
|
||||
|
||||
if (API.GetData("want-hd") == false) {
|
||||
wantHd = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun ProcessCommand(commandStr: String, args: Array<out String>?) {
|
||||
when(commandStr.toLowerCase()) {
|
||||
when (commandStr.toLowerCase()) {
|
||||
"::toggleresizablesd", "::resizablesd", "::togglersd", "::rsd" -> {
|
||||
toggleResize = true //We could call toggleResizableSd() directly here, but it's not necessary.
|
||||
toggleResizableSd()
|
||||
}
|
||||
"::toggleresizablesdhd", "::resizablesdhd", "::togglersdhd", "::rsdhd", -> {
|
||||
"::toggleresizablesdhd", "::resizablesdhd", "::togglersdhd", "::rsdhd" -> {
|
||||
wantHd = !wantHd
|
||||
API.StoreData("want-hd", wantHd)
|
||||
StoreData("want-hd", wantHd)
|
||||
API.SendMessage("You have turned login screen HD " + (if (wantHd) "on" else "off"))
|
||||
}
|
||||
}
|
||||
|
|
@ -57,25 +61,29 @@ class plugin : Plugin() {
|
|||
if (InterfaceList.aClass13_26 == null || client.gameState != 30) {
|
||||
return
|
||||
}
|
||||
toggleResize = false
|
||||
DisplayMode.resizableSD = !DisplayMode.resizableSD;
|
||||
if(!DisplayMode.resizableSD){
|
||||
//Revert to fixed
|
||||
API.StoreData("use-resizable-sd", false) //Note: It is important to call StoreData before setWindowMode because setWindowMode causes all plugins to reload.
|
||||
|
||||
DisplayMode.resizableSD = !DisplayMode.resizableSD
|
||||
useResizable = DisplayMode.resizableSD
|
||||
StoreData("use-resizable-sd", useResizable)
|
||||
|
||||
if (!DisplayMode.resizableSD) {
|
||||
DisplayMode.setWindowMode(true, 0, -1, -1)
|
||||
} else {
|
||||
//Use resizable
|
||||
API.StoreData("use-resizable-sd", true) //Note: It is important to call StoreData before setWindowMode because setWindowMode causes all plugins to reload.
|
||||
DisplayMode.setWindowMode(true, 0, GameShell.frameWidth, GameShell.frameHeight)
|
||||
}
|
||||
}
|
||||
|
||||
override fun Draw(timeDelta: Long) {
|
||||
if (toggleResize) {
|
||||
if (useResizable != DisplayMode.resizableSD) {
|
||||
toggleResizableSd()
|
||||
}
|
||||
}
|
||||
|
||||
fun OnKondoValueUpdated() {
|
||||
StoreData("want-hd", wantHd)
|
||||
StoreData("use-resizable-sd", useResizable)
|
||||
}
|
||||
|
||||
override fun OnLogout() {
|
||||
if (DisplayMode.resizableSD && wantHd) {
|
||||
//Because resizable SD always uses the "HD" size canvas/window mode (check the in-game Graphics Options with resizeable SD enabled if you don't believe me!), useHD becomes true when logging out, so logging out with resizeSD enabled means "HD" will always be enabled on the login screen after logging out, so we might as well fix the HD flyover by setting resizableSD to false first, and then calling setWindowMode to replace the canvas and set newMode to 2.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
AUTHOR='ipkpjersi'
|
||||
DESCRIPTION='Allows you to use F12 to toggle resizable SD.'
|
||||
VERSION=1.0
|
||||
VERSION=1.1
|
||||
|
|
@ -1,12 +1,34 @@
|
|||
package XPDropPlugin;
|
||||
package XPDropPlugin
|
||||
|
||||
import KondoKit.Exposed
|
||||
import plugin.Plugin
|
||||
import plugin.annotations.PluginMeta
|
||||
import plugin.api.*
|
||||
import plugin.api.API
|
||||
import plugin.api.API.*
|
||||
import plugin.api.FontColor.fromColor
|
||||
import plugin.api.FontType
|
||||
import plugin.api.TextModifier
|
||||
import plugin.api.WindowMode
|
||||
import rt4.Sprite
|
||||
import rt4.client
|
||||
import java.awt.Color
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.InputStream
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.math.ceil
|
||||
|
||||
|
||||
class plugin : Plugin() {
|
||||
|
||||
enum class Theme {
|
||||
DEFAULT, RUNELITE
|
||||
}
|
||||
|
||||
@Exposed
|
||||
private var theme = Theme.DEFAULT
|
||||
|
||||
@Exposed
|
||||
private var alwaysShow = false
|
||||
|
||||
private val displayTimeout = 10000L // 10 seconds
|
||||
private val drawStart = 175
|
||||
private val drawPadding = 25
|
||||
|
|
@ -15,41 +37,50 @@ class plugin : Plugin() {
|
|||
private var totalXp = 0
|
||||
private val activeGains = ArrayList<XPGain>()
|
||||
private var lastGain = 0L
|
||||
private val IN_GAME = 30
|
||||
|
||||
private val spriteCache = HashMap<Int, Sprite?>()
|
||||
|
||||
override fun Init() {
|
||||
val themeIndex = (GetData("xp-drop-theme") as? String) ?: "DEFAULT"
|
||||
theme = Theme.valueOf(themeIndex)
|
||||
alwaysShow = (GetData("xp-drop-alwaysShow") as? Boolean) ?: false
|
||||
}
|
||||
|
||||
override fun Draw(deltaTime: Long) {
|
||||
if (System.currentTimeMillis() - lastGain >= displayTimeout && activeGains.isEmpty())
|
||||
return
|
||||
if (shouldSkipDrawing()) return
|
||||
|
||||
drawTotalXPBox()
|
||||
|
||||
val removeList = ArrayList<XPGain>()
|
||||
var posX = API.GetWindowDimensions().width / 2
|
||||
|
||||
if (API.GetWindowMode() == WindowMode.FIXED)
|
||||
posX += 60
|
||||
val movementSpeedFactor = deltaTime / 16.666 // 60 FPS
|
||||
|
||||
for(gain in activeGains) {
|
||||
gain.currentPos -= ceil(deltaTime / 20.0).toInt()
|
||||
for (gain in activeGains) {
|
||||
gain.currentPos -= ceil(movementSpeedFactor).toInt() // Adjust movement based on deltaTime
|
||||
if (gain.currentPos <= drawClear) {
|
||||
removeList.add(gain)
|
||||
totalXp += gain.xp
|
||||
} else if (gain.currentPos <= drawStart){
|
||||
val sprite = XPSprites.getSpriteForSkill(skillId = gain.skill)
|
||||
sprite?.render(posX - 25, gain.currentPos - 20)
|
||||
API.DrawText(
|
||||
FontType.SMALL,
|
||||
FontColor.fromColor(Color.WHITE),
|
||||
TextModifier.LEFT,
|
||||
addCommas(gain.xp.toString()),
|
||||
posX,
|
||||
gain.currentPos
|
||||
)
|
||||
} else if (gain.currentPos <= drawStart) {
|
||||
drawXPDrops(gain)
|
||||
}
|
||||
}
|
||||
|
||||
activeGains.removeAll(removeList.toSet())
|
||||
}
|
||||
|
||||
private fun shouldSkipDrawing(): Boolean {
|
||||
return client.gameState < IN_GAME || (!alwaysShow && isDisplayTimeoutExpired() && activeGains.isEmpty())
|
||||
}
|
||||
|
||||
fun OnKondoValueUpdated() {
|
||||
StoreData("xp-drop-theme",theme.toString())
|
||||
StoreData("xp-drop-alwaysShow",alwaysShow)
|
||||
}
|
||||
|
||||
private fun isDisplayTimeoutExpired(): Boolean {
|
||||
return System.currentTimeMillis() - lastGain >= displayTimeout
|
||||
}
|
||||
|
||||
override fun OnXPUpdate(skill: Int, xp: Int) {
|
||||
if (xp == lastXp[skill]) return
|
||||
|
||||
|
|
@ -81,6 +112,54 @@ class plugin : Plugin() {
|
|||
}
|
||||
|
||||
private fun drawTotalXPBox() {
|
||||
when (theme) {
|
||||
Theme.DEFAULT -> drawDefaultXPBox()
|
||||
Theme.RUNELITE -> drawRuneliteXPBox()
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawXPDrops(gain : XPGain) {
|
||||
when (theme) {
|
||||
Theme.DEFAULT -> drawDefaultXPDrop(gain)
|
||||
Theme.RUNELITE -> drawRuneliteXPDrops(gain)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawDefaultXPDrop(gain: XPGain) {
|
||||
var posX = API.GetWindowDimensions().width / 2
|
||||
if (API.GetWindowMode() == WindowMode.FIXED)
|
||||
posX += 60
|
||||
val sprite = spriteCache.getOrPut(gain.skill) { XPSprites.getSpriteForSkill(skillId = gain.skill) }
|
||||
sprite?.render(posX - 25, gain.currentPos - 20)
|
||||
DrawText(
|
||||
FontType.SMALL,
|
||||
fromColor(Color.WHITE),
|
||||
TextModifier.LEFT,
|
||||
addCommas(gain.xp.toString()),
|
||||
posX,
|
||||
gain.currentPos
|
||||
)
|
||||
}
|
||||
|
||||
private fun drawRuneliteXPDrops(gain: XPGain) {
|
||||
val w = API.GetWindowDimensions().width
|
||||
val offset = if(API.GetWindowMode() == WindowMode.FIXED) 251 else 225
|
||||
val extra = 2;
|
||||
val posX = w - (offset + extra)
|
||||
|
||||
val str = addCommas(gain.xp.toString())
|
||||
val fontCharWidth = 4
|
||||
val displace = str.length*fontCharWidth + 30
|
||||
|
||||
// should be scaled https://github.com/runelite/runelite/blob/0500906f8de9cd20875c168a7a59e5e066ed5058/runelite-client/src/main/java/net/runelite/client/game/SkillIconManager.java#L50
|
||||
// but for now this is good enough
|
||||
|
||||
val sprite = spriteCache.getOrPut(gain.skill) { XPSprites.getSpriteForSkill(skillId = gain.skill) }
|
||||
sprite?.render(posX - displace, gain.currentPos - 20)
|
||||
drawTextWithDropShadow(posX, gain.currentPos, Color.WHITE, addCommas(gain.xp.toString()))
|
||||
}
|
||||
|
||||
private fun drawDefaultXPBox() {
|
||||
var posX = API.GetWindowDimensions().width / 2
|
||||
val posY = API.GetWindowDimensions().height / 4
|
||||
|
||||
|
|
@ -89,13 +168,13 @@ class plugin : Plugin() {
|
|||
|
||||
API.ClipRect(0, 0, posX * 2, posY * 4)
|
||||
|
||||
val horizontal = API.GetSprite(822)
|
||||
val horizontalTop = API.GetSprite(820)
|
||||
val tlCorner = API.GetSprite(824)
|
||||
val blCorner = API.GetSprite(826)
|
||||
val trCorner = API.GetSprite(825)
|
||||
val brCorner = API.GetSprite(827)
|
||||
val bg = API.GetSprite(657)
|
||||
val horizontal = spriteCache.getOrPut(822) { API.GetSprite(822) }
|
||||
val horizontalTop = spriteCache.getOrPut(820) { API.GetSprite(820) }
|
||||
val tlCorner = spriteCache.getOrPut(824) { API.GetSprite(824) }
|
||||
val blCorner = spriteCache.getOrPut(826) { API.GetSprite(826) }
|
||||
val trCorner = spriteCache.getOrPut(825) { API.GetSprite(825) }
|
||||
val brCorner = spriteCache.getOrPut(827) { API.GetSprite(827) }
|
||||
val bg = spriteCache.getOrPut(657) { API.GetSprite(657) }
|
||||
|
||||
bg?.render(posX - 77, 10)
|
||||
API.FillRect(posX - 75, 5, 140, 30, 0, 64)
|
||||
|
|
@ -112,26 +191,58 @@ class plugin : Plugin() {
|
|||
horizontalTop?.render(posX + 9, -8)
|
||||
horizontal?.render(posX + 9, 22)
|
||||
|
||||
API.DrawText(
|
||||
FontType.SMALL,
|
||||
FontColor.fromColor(Color.WHITE),
|
||||
TextModifier.LEFT,
|
||||
"Total Xp: ${addCommas(totalXp.toString())}",
|
||||
posX - 65,
|
||||
28
|
||||
DrawText(
|
||||
FontType.SMALL,
|
||||
fromColor(Color.WHITE),
|
||||
TextModifier.LEFT,
|
||||
"Total Xp: ${addCommas(totalXp.toString())}",
|
||||
posX - 65,
|
||||
28
|
||||
)
|
||||
}
|
||||
|
||||
private fun drawRuneliteXPBox() {
|
||||
val boxHeight = 29
|
||||
val boxWidth = 119
|
||||
val posX = API.GetWindowDimensions().width
|
||||
|
||||
val innerBorderColor = Color(90, 82, 69).rgb
|
||||
val outerBorderColor = Color(56,48,35).rgb
|
||||
|
||||
val offset = if(API.GetWindowMode() == WindowMode.FIXED) 251 else 225
|
||||
val boxStart = posX - (offset + boxWidth)
|
||||
val yOffset = if(API.GetWindowMode() == WindowMode.FIXED) 4 else 0
|
||||
|
||||
val lvlIcon = 898;
|
||||
val sprite = spriteCache.getOrPut(lvlIcon){
|
||||
val imageStream: InputStream = plugin::class.java.getResourceAsStream("res/rl-lvls.png")
|
||||
imageStream.use { imageStream ->
|
||||
val image: BufferedImage = ImageIO.read(imageStream)
|
||||
API.GetSpriteFromPNG(image)
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a simple rectangle instead of the default box design
|
||||
API.FillRect(boxStart, yOffset, boxWidth, boxHeight, innerBorderColor, 150)
|
||||
drawTextWithDropShadow(boxStart+boxWidth-4, 18+yOffset, Color.WHITE, addCommas(totalXp.toString()))
|
||||
|
||||
// Inner Border
|
||||
API.DrawRect(boxStart+1, 1+yOffset, boxWidth-2, boxHeight-2, innerBorderColor)
|
||||
// redraw around the border
|
||||
API.DrawRect(boxStart, yOffset, boxWidth, boxHeight, outerBorderColor)
|
||||
sprite?.render(boxStart + 3, 3+yOffset)
|
||||
}
|
||||
|
||||
data class XPGain(val skill: Int, val xp: Int, var currentPos: Int)
|
||||
|
||||
fun addCommas(num: String): String{
|
||||
fun addCommas(num: String): String {
|
||||
var newString = ""
|
||||
if(num.length > 9){
|
||||
if (num.length > 9) {
|
||||
return "Lots!"
|
||||
}
|
||||
var counter = 1
|
||||
num.reversed().forEach {
|
||||
if(counter % 3 == 0 && counter != num.length){
|
||||
if (counter % 3 == 0 && counter != num.length) {
|
||||
newString += "$it,"
|
||||
} else {
|
||||
newString += it
|
||||
|
|
@ -140,4 +251,10 @@ class plugin : Plugin() {
|
|||
}
|
||||
return newString.reversed()
|
||||
}
|
||||
|
||||
private fun drawTextWithDropShadow(x: Int, y: Int, color: Color, text: String, mod : TextModifier = TextModifier.RIGHT) {
|
||||
DrawText(FontType.SMALL, fromColor(Color(0)), mod, text, x + 1, y + 1)
|
||||
DrawText(FontType.SMALL, fromColor(color), mod, text, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
AUTHOR='Ceikry'
|
||||
DESCRIPTION='Draws nice and clean experience drops onto the screen.'
|
||||
VERSION=1.2
|
||||
VERSION=1.3
|
||||
|
|
|
|||
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png
Normal file
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 348 B |
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png
Normal file
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 329 B |
Loading…
Add table
Add a link
Reference in a new issue