mirror of
https://gitlab.com/2009scape/rt4-client.git
synced 2025-12-09 16:45:46 -07:00
Compare commits
54 commits
fb41691c3c
...
834cf0112a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
834cf0112a | ||
|
|
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 | ||
|
|
886014a889 |
42 changed files with 2542 additions and 907 deletions
|
|
@ -13,6 +13,7 @@ import rt4.Tile;
|
||||||
*/
|
*/
|
||||||
public abstract class Plugin {
|
public abstract class Plugin {
|
||||||
long timeOfLastDraw;
|
long timeOfLastDraw;
|
||||||
|
long timeOfLastLateDraw;
|
||||||
|
|
||||||
void _init() {
|
void _init() {
|
||||||
Init();
|
Init();
|
||||||
|
|
@ -24,6 +25,12 @@ public abstract class Plugin {
|
||||||
timeOfLastDraw = nowTime;
|
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.
|
* 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.
|
* 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) {}
|
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
|
* 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.
|
* A data class for storing information about plugins.
|
||||||
* @author ceikry
|
* @author ceikry
|
||||||
*/
|
*/
|
||||||
class PluginInfo {
|
public class PluginInfo {
|
||||||
double version;
|
public double version;
|
||||||
String author;
|
public String author;
|
||||||
String description;
|
public String description;
|
||||||
|
|
||||||
public PluginInfo(String author, String description, double version) {
|
public PluginInfo(String author, String description, double version) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,11 @@ public class PluginRepository {
|
||||||
pluginsSnapshot.forEach(Plugin::_draw);
|
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) {
|
public static void NPCOverheadDraw(Npc npc, int screenX, int screenY) {
|
||||||
loadedPlugins.values().forEach((plugin) -> plugin.NPCOverheadDraw(npc, screenX, 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) {
|
public static int[] CalculateSceneGraphScreenPosition(int entityX, int entityZ, int yOffset) {
|
||||||
final int HALF_FIXED_WIDTH = 256;
|
final int HALF_FIXED_WIDTH = 256;
|
||||||
final int HALF_FIXED_HEIGHT = 167;
|
final int HALF_FIXED_HEIGHT = 167;
|
||||||
|
final int RESIZABLE_SD_OFFSET = 500;
|
||||||
|
|
||||||
int elevation = SceneGraph.getTileHeight(plane, entityX, entityZ) - yOffset;
|
int elevation = SceneGraph.getTileHeight(plane, entityX, entityZ) - yOffset;
|
||||||
entityX -= SceneGraph.cameraX;
|
entityX -= SceneGraph.cameraX;
|
||||||
|
|
@ -329,15 +330,27 @@ public class API {
|
||||||
int[] screenPos = new int[2]; // X,Y
|
int[] screenPos = new int[2]; // X,Y
|
||||||
|
|
||||||
if (entityZ >= 50) {
|
if (entityZ >= 50) {
|
||||||
if(GetWindowMode() == WindowMode.FIXED) {
|
if (GetWindowMode() == WindowMode.FIXED) {
|
||||||
screenPos[0] = HALF_FIXED_WIDTH + ((entityX << 9) / entityZ);
|
screenPos[0] = HALF_FIXED_WIDTH + ((entityX << 9) / entityZ);
|
||||||
screenPos[1] = HALF_FIXED_HEIGHT + ((elevation << 9) / entityZ);
|
screenPos[1] = HALF_FIXED_HEIGHT + ((elevation << 9) / entityZ);
|
||||||
} else {
|
} else {
|
||||||
Dimension canvas = GetWindowDimensions();
|
Dimension canvas = GetWindowDimensions();
|
||||||
double newViewDistH = (canvas.width / 2) / Math.tan(Math.toRadians(GlRenderer.hFOV) / 2);
|
double newViewDistH;
|
||||||
double newViewDistV = (canvas.height / 2) / Math.tan(Math.toRadians(GlRenderer.vFOV) / 2);
|
double newViewDistV;
|
||||||
screenPos[0] = canvas.width / 2 + (int)((entityX * newViewDistH) / entityZ);
|
|
||||||
screenPos[1] = canvas.height / 2 + (int)((elevation * newViewDistV) / entityZ);
|
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 {
|
} else {
|
||||||
screenPos[0] = -1;
|
screenPos[0] = -1;
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ public class Cheat {
|
||||||
|
|
||||||
@OriginalMember(owner = "client!en", name = "a", descriptor = "(IIIB)V")
|
@OriginalMember(owner = "client!en", name = "a", descriptor = "(IIIB)V")
|
||||||
public static void teleport(@OriginalArg(0) int arg0, @OriginalArg(1) int arg1, @OriginalArg(2) int arg2) {
|
public static void teleport(@OriginalArg(0) int arg0, @OriginalArg(1) int arg1, @OriginalArg(2) int arg2) {
|
||||||
@Pc(66) JagString local66 = JagString.concatenate(new JagString[]{aClass100_521, JagString.parseInt(arg2), JagString.aClass100_760, JagString.parseInt(arg0 >> 6), JagString.aClass100_760, JagString.parseInt(arg1 >> 6), JagString.aClass100_760, JagString.parseInt(arg0 & 0x3F), JagString.aClass100_760, JagString.parseInt(arg1 & 0x3F)});
|
@Pc(66) JagString local66 = JagString.concatenate(new JagString[]{aClass100_521, JagString.parseInt(arg2), JagString.COMMA, JagString.parseInt(arg0 >> 6), JagString.COMMA, JagString.parseInt(arg1 >> 6), JagString.COMMA, JagString.parseInt(arg0 & 0x3F), JagString.COMMA, JagString.parseInt(arg1 & 0x3F)});
|
||||||
local66.print();
|
local66.print();
|
||||||
execute(local66);
|
execute(local66);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1034,7 +1034,7 @@ public class Cs1ScriptRunner {
|
||||||
local3297 = interpolate(component, local3297);
|
local3297 = interpolate(component, local3297);
|
||||||
@Pc(3325) JagString local3325;
|
@Pc(3325) JagString local3325;
|
||||||
while (local3297.length() > 0) {
|
while (local3297.length() > 0) {
|
||||||
cardMemory = local3297.indexOf(JagString.aClass100_556);
|
cardMemory = local3297.indexOf(JagString.LINE_BREAK);
|
||||||
if (cardMemory == -1) {
|
if (cardMemory == -1) {
|
||||||
local3325 = local3297;
|
local3325 = local3297;
|
||||||
local3297 = JagString.EMPTY;
|
local3297 = JagString.EMPTY;
|
||||||
|
|
@ -1072,7 +1072,7 @@ public class Cs1ScriptRunner {
|
||||||
objId = local556 + local3299.lineHeight + 2;
|
objId = local556 + local3299.lineHeight + 2;
|
||||||
local3297 = interpolate(component, local3297);
|
local3297 = interpolate(component, local3297);
|
||||||
while (local3297.length() > 0) {
|
while (local3297.length() > 0) {
|
||||||
local563 = local3297.indexOf(JagString.aClass100_556);
|
local563 = local3297.indexOf(JagString.LINE_BREAK);
|
||||||
if (local563 == -1) {
|
if (local563 == -1) {
|
||||||
local3325 = local3297;
|
local3325 = local3297;
|
||||||
local3297 = JagString.EMPTY;
|
local3297 = JagString.EMPTY;
|
||||||
|
|
@ -1246,14 +1246,14 @@ public class Cs1ScriptRunner {
|
||||||
public static JagString method1548(@OriginalArg(1) int arg0) {
|
public static JagString method1548(@OriginalArg(1) int arg0) {
|
||||||
@Pc(9) JagString local9 = JagString.parseInt(arg0);
|
@Pc(9) JagString local9 = JagString.parseInt(arg0);
|
||||||
for (@Pc(21) int local21 = local9.length() - 3; local21 > 0; local21 -= 3) {
|
for (@Pc(21) int local21 = local9.length() - 3; local21 > 0; local21 -= 3) {
|
||||||
local9 = JagString.concatenate(new JagString[]{local9.substring(local21, 0), JagString.aClass100_760, local9.substring(local21)});
|
local9 = JagString.concatenate(new JagString[]{local9.substring(local21, 0), JagString.COMMA, local9.substring(local21)});
|
||||||
}
|
}
|
||||||
if (local9.length() > 9) {
|
if (local9.length() > 9) {
|
||||||
return JagString.concatenate(new JagString[]{JagString.aClass100_1043, local9.substring(local9.length() - 8, 0), LocalizedText.MILLION_SHORT, MiniMenu.OPEN_PARENTHESIS, local9, JagString.aClass100_583});
|
return JagString.concatenate(new JagString[]{JagString.LIGHT_GREEN, local9.substring(local9.length() - 8, 0), LocalizedText.MILLION_SHORT, MiniMenu.OPEN_PARENTHESIS, local9, JagString.aClass100_583});
|
||||||
} else if (local9.length() > 6) {
|
} else if (local9.length() > 6) {
|
||||||
return JagString.concatenate(new JagString[]{JagString.aClass100_589, local9.substring(local9.length() - 4, 0), LocalizedText.THOUSAND_SHORT, MiniMenu.OPEN_PARENTHESIS, local9, JagString.aClass100_583});
|
return JagString.concatenate(new JagString[]{JagString.WHITE, local9.substring(local9.length() - 4, 0), LocalizedText.THOUSAND_SHORT, MiniMenu.OPEN_PARENTHESIS, local9, JagString.aClass100_583});
|
||||||
} else {
|
} else {
|
||||||
return JagString.concatenate(new JagString[]{JagString.aClass100_1101, local9, JagString.aClass100_978});
|
return JagString.concatenate(new JagString[]{JagString.YELLOW, local9, JagString.aClass100_978});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,6 @@ public final class DateUtil {
|
||||||
@Pc(36) int local36 = calendar.get(Calendar.HOUR_OF_DAY);
|
@Pc(36) int local36 = calendar.get(Calendar.HOUR_OF_DAY);
|
||||||
@Pc(40) int local40 = calendar.get(Calendar.MINUTE);
|
@Pc(40) int local40 = calendar.get(Calendar.MINUTE);
|
||||||
@Pc(44) int local44 = calendar.get(Calendar.SECOND);
|
@Pc(44) int local44 = calendar.get(Calendar.SECOND);
|
||||||
return JagString.concatenate(new JagString[]{DAYS[local13 - 1], JagString.aClass100_461, JagString.parseInt(local17 / 10), JagString.parseInt(local17 % 10), JagString.aClass100_1089, MONTHS[local21], JagString.aClass100_1089, JagString.parseInt(local32), JagString.SPACE, JagString.parseInt(local36 / 10), JagString.parseInt(local36 % 10), JagString.COLON, JagString.parseInt(local40 / 10), JagString.parseInt(local40 % 10), JagString.COLON, JagString.parseInt(local44 / 10), JagString.parseInt(local44 % 10), JagString.TIMEZONE});
|
return JagString.concatenate(new JagString[]{DAYS[local13 - 1], JagString.COMMA_SPACE, JagString.parseInt(local17 / 10), JagString.parseInt(local17 % 10), JagString.HYPHEN, MONTHS[local21], JagString.HYPHEN, JagString.parseInt(local32), JagString.SPACE, JagString.parseInt(local36 / 10), JagString.parseInt(local36 % 10), JagString.COLON, JagString.parseInt(local40 / 10), JagString.parseInt(local40 % 10), JagString.COLON, JagString.parseInt(local44 / 10), JagString.parseInt(local44 % 10), JagString.TIMEZONE});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import java.awt.*;
|
||||||
public abstract class FrameBuffer {
|
public abstract class FrameBuffer {
|
||||||
|
|
||||||
@OriginalMember(owner = "client!vk", name = "e", descriptor = "[I")
|
@OriginalMember(owner = "client!vk", name = "e", descriptor = "[I")
|
||||||
protected int[] pixels;
|
public int[] pixels;
|
||||||
|
|
||||||
@OriginalMember(owner = "client!vk", name = "g", descriptor = "Ljava/awt/Image;")
|
@OriginalMember(owner = "client!vk", name = "g", descriptor = "Ljava/awt/Image;")
|
||||||
protected Image image;
|
protected Image image;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import org.openrs2.deob.annotation.OriginalMember;
|
||||||
import org.openrs2.deob.annotation.Pc;
|
import org.openrs2.deob.annotation.Pc;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
@ -24,6 +25,11 @@ public final class GlRenderer {
|
||||||
public static float hFOV = 0;
|
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")
|
@OriginalMember(owner = "client!tf", name = "c", descriptor = "F")
|
||||||
private static float aFloat30;
|
private static float aFloat30;
|
||||||
|
|
||||||
|
|
@ -200,10 +206,34 @@ public final class GlRenderer {
|
||||||
public static void swapBuffers() {
|
public static void swapBuffers() {
|
||||||
try {
|
try {
|
||||||
drawable.swapBuffers();
|
drawable.swapBuffers();
|
||||||
|
readPixels();
|
||||||
} catch (@Pc(3) Exception local3) {
|
} 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")
|
@OriginalMember(owner = "client!tf", name = "a", descriptor = "(Z)V")
|
||||||
public static void setFogEnabled(@OriginalArg(0) boolean enabled) {
|
public static void setFogEnabled(@OriginalArg(0) boolean enabled) {
|
||||||
if (enabled == fogEnabled) {
|
if (enabled == fogEnabled) {
|
||||||
|
|
|
||||||
|
|
@ -17,35 +17,35 @@ public final class JagString implements StringInterface {
|
||||||
@OriginalMember(owner = "client!pa", name = "O", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!pa", name = "O", descriptor = "Lclient!na;")
|
||||||
public static final JagString EMPTY = parse("");
|
public static final JagString EMPTY = parse("");
|
||||||
@OriginalMember(owner = "client!pi", name = "Q", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!pi", name = "Q", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_853 = parse("null");
|
public static final JagString NULL = parse("null");
|
||||||
@OriginalMember(owner = "client!t", name = "C", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!t", name = "C", descriptor = "Lclient!na;")
|
||||||
public static final JagString PERIOD = parse(")3");
|
public static final JagString PERIOD = parse(")3");
|
||||||
@OriginalMember(owner = "client!vk", name = "a", descriptor = "[I")
|
@OriginalMember(owner = "client!vk", name = "a", descriptor = "[I")
|
||||||
public static final int[] anIntArray471 = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 73, 74, 76, 78, 83, 84, 85, 86, 91, 92, 93, 94, 95, 97, 103, 104, 105, 106, 107, 108, 113, 114, 115, 116, 118, 119, 120, 121, 122, 123, 124, 125, 133, 134, 136, 138, 143, 144, 145, 146, 151, 152, 153, 154, 155, 157, 163, 164, 165, 166, 168, 169, 174, 175, 176, 177, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 97, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 157, 215, 216, 117, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 66, 66, 66, 66, 66, 66, 65, 75, 79, 79, 79, 79, 87, 87, 87, 87, 77, 96, 98, 98, 98, 98, 98, 250, 251, 109, 109, 109, 109, 117, 252, 167, 126, 126, 126, 126, 126, 126, 125, 135, 139, 139, 139, 139, 147, 147, 147, 147, 137, 156, 158, 158, 158, 158, 158, 253, 254, 170, 170, 170, 170, 178, 255, 178};
|
public static final int[] anIntArray471 = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 73, 74, 76, 78, 83, 84, 85, 86, 91, 92, 93, 94, 95, 97, 103, 104, 105, 106, 107, 108, 113, 114, 115, 116, 118, 119, 120, 121, 122, 123, 124, 125, 133, 134, 136, 138, 143, 144, 145, 146, 151, 152, 153, 154, 155, 157, 163, 164, 165, 166, 168, 169, 174, 175, 176, 177, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 97, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 157, 215, 216, 117, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 66, 66, 66, 66, 66, 66, 65, 75, 79, 79, 79, 79, 87, 87, 87, 87, 77, 96, 98, 98, 98, 98, 98, 250, 251, 109, 109, 109, 109, 117, 252, 167, 126, 126, 126, 126, 126, 126, 125, 135, 139, 139, 139, 139, 147, 147, 147, 147, 137, 156, 158, 158, 158, 158, 158, 253, 254, 170, 170, 170, 170, 178, 255, 178};
|
||||||
@OriginalMember(owner = "client!sh", name = "e", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!sh", name = "e", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_967 = parse("");
|
public static final JagString EMPTY2 = parse("");
|
||||||
@OriginalMember(owner = "client!dm", name = "n", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!dm", name = "n", descriptor = "Lclient!na;")
|
||||||
public static final JagString PERCENT_SIGN = parse("(U");
|
public static final JagString PERCENT_SIGN = parse("(U");
|
||||||
@OriginalMember(owner = "client!wa", name = "pb", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!wa", name = "pb", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_556 = parse("<br>");
|
public static final JagString LINE_BREAK = parse("<br>");
|
||||||
@OriginalMember(owner = "client!ed", name = "H", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!ed", name = "H", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_375 = parse("<)4col> x");
|
public static final JagString aClass100_375 = parse("<)4col> x");
|
||||||
@OriginalMember(owner = "client!je", name = "db", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!je", name = "db", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_589 = parse(" <col=ffffff>");
|
public static final JagString WHITE = parse(" <col=ffffff>");
|
||||||
@OriginalMember(owner = "client!uf", name = "s", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!uf", name = "s", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_1043 = parse(" <col=00ff80>");
|
public static final JagString LIGHT_GREEN = parse(" <col=00ff80>");
|
||||||
@OriginalMember(owner = "client!wj", name = "b", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!wj", name = "b", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_1101 = parse(" <col=ffff00>");
|
public static final JagString YELLOW = parse(" <col=ffff00>");
|
||||||
@OriginalMember(owner = "client!mi", name = "R", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!mi", name = "R", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_760 = parse(")1");
|
public static final JagString COMMA = parse(")1");
|
||||||
@OriginalMember(owner = "client!sj", name = "w", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!sj", name = "w", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_978 = parse("<)4col>");
|
public static final JagString aClass100_978 = parse("<)4col>");
|
||||||
@OriginalMember(owner = "client!jb", name = "c", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!jb", name = "c", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_583 = parse("(Y<)4col>");
|
public static final JagString aClass100_583 = parse("(Y<)4col>");
|
||||||
@OriginalMember(owner = "client!fn", name = "Z", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!fn", name = "Z", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_461 = parse(")1 ");
|
public static final JagString COMMA_SPACE = parse(")1 ");
|
||||||
@OriginalMember(owner = "client!wb", name = "a", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!wb", name = "a", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_1089 = parse(")2");
|
public static final JagString HYPHEN = parse(")2");
|
||||||
@OriginalMember(owner = "client!dm", name = "j", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!dm", name = "j", descriptor = "Lclient!na;")
|
||||||
public static final JagString SPACE = parse(" ");
|
public static final JagString SPACE = parse(" ");
|
||||||
@OriginalMember(owner = "client!vh", name = "c", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!vh", name = "c", descriptor = "Lclient!na;")
|
||||||
|
|
@ -76,7 +76,7 @@ public final class JagString implements StringInterface {
|
||||||
@Pc(5) int local5 = 0;
|
@Pc(5) int local5 = 0;
|
||||||
for (@Pc(7) int local7 = 0; local7 < arg1; local7++) {
|
for (@Pc(7) int local7 = 0; local7 < arg1; local7++) {
|
||||||
if (arg2[arg0 + local7] == null) {
|
if (arg2[arg0 + local7] == null) {
|
||||||
arg2[local7 + arg0] = aClass100_853;
|
arg2[local7 + arg0] = NULL;
|
||||||
}
|
}
|
||||||
local5 += arg2[local7 + arg0].length;
|
local5 += arg2[local7 + arg0].length;
|
||||||
}
|
}
|
||||||
|
|
@ -1077,7 +1077,7 @@ public final class JagString implements StringInterface {
|
||||||
@OriginalMember(owner = "client!na", name = "k", descriptor = "(I)Lclient!na;")
|
@OriginalMember(owner = "client!na", name = "k", descriptor = "(I)Lclient!na;")
|
||||||
public final JagString method3159() {
|
public final JagString method3159() {
|
||||||
@Pc(9) JagString local9 = Base37.decode37(this.encode37());
|
@Pc(9) JagString local9 = Base37.decode37(this.encode37());
|
||||||
return local9 == null ? aClass100_967 : local9;
|
return local9 == null ? EMPTY2 : local9;
|
||||||
}
|
}
|
||||||
|
|
||||||
@OriginalMember(owner = "client!na", name = "a", descriptor = "(Z[BIII)I")
|
@OriginalMember(owner = "client!na", name = "a", descriptor = "(Z[BIII)I")
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,17 @@ import org.openrs2.deob.annotation.Pc;
|
||||||
|
|
||||||
public class StringUtils {
|
public class StringUtils {
|
||||||
@OriginalMember(owner = "client!ag", name = "ab", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!ag", name = "ab", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_17 = JagString.parse("0");
|
public static final JagString ZERO = JagString.parse("0");
|
||||||
@OriginalMember(owner = "client!ca", name = "cb", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!ca", name = "cb", descriptor = "Lclient!na;")
|
||||||
public static final JagString nonBreakingSpace = JagString.getNbsp();
|
public static final JagString nonBreakingSpace = JagString.getNbsp();
|
||||||
@OriginalMember(owner = "client!rm", name = "i", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!rm", name = "i", descriptor = "Lclient!na;")
|
||||||
public static final JagString ASTERISK = JagString.parse("(Z");
|
public static final JagString ASTERISK = JagString.parse("(Z");
|
||||||
@OriginalMember(owner = "client!fm", name = "W", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!fm", name = "W", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_453 = JagString.parse(")2");
|
public static final JagString HYPHEN = JagString.parse(")2");
|
||||||
@OriginalMember(owner = "client!cg", name = "h", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!cg", name = "h", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_185 = JagString.parse(")3");
|
public static final JagString PERIOD = JagString.parse(")3");
|
||||||
@OriginalMember(owner = "client!tl", name = "i", descriptor = "Lclient!na;")
|
@OriginalMember(owner = "client!tl", name = "i", descriptor = "Lclient!na;")
|
||||||
public static final JagString aClass100_1017 = JagString.parse(")1");
|
public static final JagString COMMA = JagString.parse(")1");
|
||||||
|
|
||||||
@OriginalMember(owner = "client!vf", name = "a", descriptor = "(IB)Lclient!na;")
|
@OriginalMember(owner = "client!vf", name = "a", descriptor = "(IB)Lclient!na;")
|
||||||
public static JagString toString(@OriginalArg(0) int arg0) {
|
public static JagString toString(@OriginalArg(0) int arg0) {
|
||||||
|
|
@ -28,21 +28,21 @@ public class StringUtils {
|
||||||
@Pc(9) JagString local9 = JagString.allocate(0);
|
@Pc(9) JagString local9 = JagString.allocate(0);
|
||||||
if (arg3 < 0L) {
|
if (arg3 < 0L) {
|
||||||
arg3 = -arg3;
|
arg3 = -arg3;
|
||||||
local9.method3113(aClass100_453);
|
local9.method3113(HYPHEN);
|
||||||
}
|
}
|
||||||
@Pc(26) JagString local26 = aClass100_1017;
|
@Pc(26) JagString local26 = COMMA;
|
||||||
@Pc(28) JagString local28 = aClass100_185;
|
@Pc(28) JagString local28 = PERIOD;
|
||||||
if (arg0 == 1) {
|
if (arg0 == 1) {
|
||||||
local26 = aClass100_185;
|
local26 = PERIOD;
|
||||||
local28 = aClass100_1017;
|
local28 = COMMA;
|
||||||
}
|
}
|
||||||
if (arg0 == 2) {
|
if (arg0 == 2) {
|
||||||
local28 = aClass100_1017;
|
local28 = COMMA;
|
||||||
local26 = nonBreakingSpace;
|
local26 = nonBreakingSpace;
|
||||||
}
|
}
|
||||||
if (arg0 == 3) {
|
if (arg0 == 3) {
|
||||||
local26 = aClass100_185;
|
local26 = PERIOD;
|
||||||
local28 = aClass100_1017;
|
local28 = COMMA;
|
||||||
}
|
}
|
||||||
@Pc(59) JagString local59 = JagString.allocate(0);
|
@Pc(59) JagString local59 = JagString.allocate(0);
|
||||||
@Pc(61) int local61;
|
@Pc(61) int local61;
|
||||||
|
|
@ -53,7 +53,7 @@ public class StringUtils {
|
||||||
local61 = 0;
|
local61 = 0;
|
||||||
@Pc(137) JagString local137;
|
@Pc(137) JagString local137;
|
||||||
if (arg3 == 0L) {
|
if (arg3 == 0L) {
|
||||||
local137 = aClass100_17;
|
local137 = ZERO;
|
||||||
} else {
|
} else {
|
||||||
@Pc(95) JagString local95 = JagString.allocate(0);
|
@Pc(95) JagString local95 = JagString.allocate(0);
|
||||||
while (arg3 > 0L) {
|
while (arg3 > 0L) {
|
||||||
|
|
|
||||||
|
|
@ -881,7 +881,7 @@ public final class client extends GameShell {
|
||||||
} else if (gameState == 30) {
|
} else if (gameState == 30) {
|
||||||
LoginManager.method1841();
|
LoginManager.method1841();
|
||||||
} else if (gameState == 40) {
|
} else if (gameState == 40) {
|
||||||
Fonts.drawTextOnScreen(false, JagString.concatenate(new JagString[]{LocalizedText.CONLOST, JagString.aClass100_556, LocalizedText.ATTEMPT_TO_REESTABLISH}));
|
Fonts.drawTextOnScreen(false, JagString.concatenate(new JagString[]{LocalizedText.CONLOST, JagString.LINE_BREAK, LocalizedText.ATTEMPT_TO_REESTABLISH}));
|
||||||
}
|
}
|
||||||
if (GlRenderer.enabled && gameState != 0) {
|
if (GlRenderer.enabled && gameState != 0) {
|
||||||
GlRenderer.swapBuffers();
|
GlRenderer.swapBuffers();
|
||||||
|
|
@ -921,6 +921,7 @@ public final class client extends GameShell {
|
||||||
Preferences.safeMode = false;
|
Preferences.safeMode = false;
|
||||||
Preferences.write(GameShell.signLink);
|
Preferences.write(GameShell.signLink);
|
||||||
}
|
}
|
||||||
|
PluginRepository.LateDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@OriginalMember(owner = "client!client", name = "c", descriptor = "(B)V")
|
@OriginalMember(owner = "client!client", name = "c", descriptor = "(B)V")
|
||||||
|
|
|
||||||
|
|
@ -110,4 +110,10 @@ task buildPlugins(type: Copy, dependsOn: classes) {
|
||||||
from "build/classes/kotlin/main"
|
from "build/classes/kotlin/main"
|
||||||
into pluginsPath
|
into pluginsPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find and copy any 'res' directories from 'src/main/kotlin/**/res/'
|
||||||
|
copy {
|
||||||
|
from fileTree(dir: "src/main/kotlin", include: "**/res/**")
|
||||||
|
into pluginsPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package GroundItems
|
package GroundItems
|
||||||
|
|
||||||
|
import KondoKit.Exposed
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.annotations.PluginMeta
|
|
||||||
import plugin.api.API.*
|
import plugin.api.API.*
|
||||||
import plugin.api.FontColor.fromColor
|
import plugin.api.FontColor.fromColor
|
||||||
import plugin.api.FontType
|
import plugin.api.FontType
|
||||||
|
|
@ -18,27 +18,26 @@ import java.nio.charset.StandardCharsets
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@PluginMeta(
|
class plugin : Plugin() {
|
||||||
author = "downthecrop",
|
|
||||||
description =
|
@Exposed(description = "Default: true, Use Local JSON or the prices from the Live/Stable server API")
|
||||||
"""
|
private var useLiveGEPrices = true
|
||||||
Ground Items Overlay. Just like Runelite!
|
@Exposed( "Default: 5,000 (blue)")
|
||||||
cmds ::set(low,med,high,insane,hide), ::(tag,ignore)item ID, ::(reset)groundconfig
|
private var lowValue = 5000
|
||||||
Special thanks to Chisato for the original skeleton.
|
@Exposed( "Default: 20,000 (green)")
|
||||||
""",
|
private var mediumValue = 20000
|
||||||
version = 1.2
|
@Exposed( "Default: 50,000 (orange)")
|
||||||
)
|
private var highValue = 50000
|
||||||
open class plugin : Plugin() {
|
@Exposed( "Default: 100,000 (pink)")
|
||||||
|
private var insaneValue = 100000
|
||||||
|
@Exposed("Default: 0, No labels will be drawn for items below this value (unless tagged)")
|
||||||
|
private var hideBelowValue = 0
|
||||||
|
@Exposed("Tag Items (purple) add/remove with Ctrl+RightClick Tag/Ignore")
|
||||||
|
private lateinit var taggedItems: List<Int>
|
||||||
|
@Exposed("Ignore items add/remove with Ctrl+RightClick Tag/Ignore")
|
||||||
|
private lateinit var ignoredItems: List<Int>
|
||||||
|
|
||||||
private var kondoExposed_lowValue = 5000
|
|
||||||
private var kondoExposed_mediumValue = 20000
|
|
||||||
private var kondoExposed_highValue = 50000
|
|
||||||
private var kondoExposed_insaneValue = 100000
|
|
||||||
private var kondoExposed_hideBelowValue = 0
|
|
||||||
private var kondoExposed_useLiveGEPrices = true
|
|
||||||
private val coindId = 995
|
private val coindId = 995
|
||||||
private lateinit var kondoExposed_taggedItems: List<Int>
|
|
||||||
private lateinit var kondoExposed_ignoredItems: List<Int>
|
|
||||||
|
|
||||||
private var gePriceMap = loadGEPrices()
|
private var gePriceMap = loadGEPrices()
|
||||||
|
|
||||||
|
|
@ -60,23 +59,23 @@ open class plugin : Plugin() {
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun Init() {
|
override fun Init() {
|
||||||
kondoExposed_lowValue = GetData("low-value") as? Int ?: 5000
|
lowValue = GetData("low-value") as? Int ?: 5000
|
||||||
kondoExposed_mediumValue = GetData("medium-value") as? Int ?: 20000
|
mediumValue = GetData("medium-value") as? Int ?: 20000
|
||||||
kondoExposed_highValue = GetData("high-value") as? Int ?: 50000
|
highValue = GetData("high-value") as? Int ?: 50000
|
||||||
kondoExposed_insaneValue = GetData("insane-value") as? Int ?: 100000
|
insaneValue = GetData("insane-value") as? Int ?: 100000
|
||||||
kondoExposed_hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
hideBelowValue = GetData("hide-below-value") as? Int ?: 0
|
||||||
kondoExposed_useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true
|
useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true
|
||||||
kondoExposed_taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||||
kondoExposed_ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList()
|
||||||
if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $kondoExposed_useLiveGEPrices")
|
if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $useLiveGEPrices")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isTagged(itemId: Int): Boolean {
|
private fun isTagged(itemId: Int): Boolean {
|
||||||
return kondoExposed_taggedItems.contains(itemId)
|
return taggedItems.contains(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isHidden(itemId: Int): Boolean {
|
private fun isHidden(itemId: Int): Boolean {
|
||||||
return kondoExposed_ignoredItems.contains(itemId)
|
return ignoredItems.contains(itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) = renderGroundItemNames()
|
override fun Draw(timeDelta: Long) = renderGroundItemNames()
|
||||||
|
|
@ -141,10 +140,10 @@ open class plugin : Plugin() {
|
||||||
val screenY = screenPos[1]
|
val screenY = screenPos[1]
|
||||||
val color = when {
|
val color = when {
|
||||||
isTagged(itemDef.id) -> colorMap["tagged"]
|
isTagged(itemDef.id) -> colorMap["tagged"]
|
||||||
highestValue < kondoExposed_lowValue -> "#FFFFFF"
|
highestValue < lowValue -> "#FFFFFF"
|
||||||
highestValue < kondoExposed_mediumValue -> colorMap["lowValue"]
|
highestValue < mediumValue -> colorMap["lowValue"]
|
||||||
highestValue < kondoExposed_highValue -> colorMap["mediumValue"]
|
highestValue < highValue -> colorMap["mediumValue"]
|
||||||
highestValue < kondoExposed_insaneValue -> colorMap["highValue"]
|
highestValue < insaneValue -> colorMap["highValue"]
|
||||||
else -> colorMap["insaneValue"]
|
else -> colorMap["insaneValue"]
|
||||||
} ?: "#FFFFFF"
|
} ?: "#FFFFFF"
|
||||||
val colorInt = color.drop(1).toInt(16)
|
val colorInt = color.drop(1).toInt(16)
|
||||||
|
|
@ -183,7 +182,7 @@ open class plugin : Plugin() {
|
||||||
val haValue = if (itemDef.id == coindId) item.value.amount else (itemDef.cost * 0.6 * item.value.amount).roundToInt()
|
val haValue = if (itemDef.id == coindId) item.value.amount else (itemDef.cost * 0.6 * item.value.amount).roundToInt()
|
||||||
val geValue = (gePriceMap[itemDef.id.toString()]?.toInt() ?: 0) * item.value.amount
|
val geValue = (gePriceMap[itemDef.id.toString()]?.toInt() ?: 0) * item.value.amount
|
||||||
val highestValue = maxOf(haValue, geValue)
|
val highestValue = maxOf(haValue, geValue)
|
||||||
return !((highestValue < kondoExposed_hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
return !((highestValue < hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||||
|
|
@ -200,7 +199,7 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
private fun ignoreItem(itemId: Int): Runnable {
|
private fun ignoreItem(itemId: Int): Runnable {
|
||||||
return Runnable {
|
return Runnable {
|
||||||
val existingIgnores = kondoExposed_ignoredItems.toMutableList()
|
val existingIgnores = ignoredItems.toMutableList()
|
||||||
|
|
||||||
if (existingIgnores.contains(itemId)) {
|
if (existingIgnores.contains(itemId)) {
|
||||||
existingIgnores.remove(itemId)
|
existingIgnores.remove(itemId)
|
||||||
|
|
@ -215,7 +214,7 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
private fun tagItem(itemId: Int): Runnable {
|
private fun tagItem(itemId: Int): Runnable {
|
||||||
return Runnable {
|
return Runnable {
|
||||||
val existingTags = kondoExposed_taggedItems.toMutableList()
|
val existingTags = taggedItems.toMutableList()
|
||||||
|
|
||||||
if (existingTags.contains(itemId)) {
|
if (existingTags.contains(itemId)) {
|
||||||
existingTags.remove(itemId)
|
existingTags.remove(itemId)
|
||||||
|
|
@ -230,28 +229,28 @@ open class plugin : Plugin() {
|
||||||
|
|
||||||
|
|
||||||
private fun resetConfig() {
|
private fun resetConfig() {
|
||||||
kondoExposed_lowValue = 5000
|
lowValue = 5000
|
||||||
kondoExposed_mediumValue = 20000
|
mediumValue = 20000
|
||||||
kondoExposed_highValue = 50000
|
highValue = 50000
|
||||||
kondoExposed_insaneValue = 100000
|
insaneValue = 100000
|
||||||
kondoExposed_hideBelowValue = 0
|
hideBelowValue = 0
|
||||||
kondoExposed_useLiveGEPrices = true
|
useLiveGEPrices = true
|
||||||
StoreData("ground-item-tags","");
|
StoreData("ground-item-tags","");
|
||||||
StoreData("ground-item-ignore","");
|
StoreData("ground-item-ignore","");
|
||||||
StoreData("low-value", kondoExposed_lowValue)
|
StoreData("low-value", lowValue)
|
||||||
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
StoreData("ground-item-use-remote", useLiveGEPrices)
|
||||||
StoreData("medium-value", kondoExposed_mediumValue)
|
StoreData("medium-value", mediumValue)
|
||||||
StoreData("high-value", kondoExposed_highValue)
|
StoreData("high-value", highValue)
|
||||||
StoreData("insane-value", kondoExposed_insaneValue)
|
StoreData("insane-value", insaneValue)
|
||||||
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
StoreData("hide-below-value", hideBelowValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun displayRanges() {
|
private fun displayRanges() {
|
||||||
val low = kondoExposed_lowValue
|
val low = lowValue
|
||||||
val medium = kondoExposed_mediumValue
|
val medium = mediumValue
|
||||||
val high = kondoExposed_highValue
|
val high = highValue
|
||||||
val insane = kondoExposed_insaneValue
|
val insane = insaneValue
|
||||||
val hide = kondoExposed_hideBelowValue
|
val hide = hideBelowValue
|
||||||
|
|
||||||
SendMessage("== Ground Item Config ==")
|
SendMessage("== Ground Item Config ==")
|
||||||
SendMessage("Low: $low")
|
SendMessage("Low: $low")
|
||||||
|
|
@ -260,12 +259,12 @@ open class plugin : Plugin() {
|
||||||
SendMessage("Insane: $insane")
|
SendMessage("Insane: $insane")
|
||||||
SendMessage("Hide Below: $hide")
|
SendMessage("Hide Below: $hide")
|
||||||
SendMessage("-- Ignored Items --")
|
SendMessage("-- Ignored Items --")
|
||||||
for(item in kondoExposed_ignoredItems){
|
for(item in ignoredItems){
|
||||||
val itemDef = ObjTypeList.get(item)
|
val itemDef = ObjTypeList.get(item)
|
||||||
SendMessage("Ignored: ${itemDef.name} ${itemDef.id}")
|
SendMessage("Ignored: ${itemDef.name} ${itemDef.id}")
|
||||||
}
|
}
|
||||||
SendMessage("-- Tagged Items --")
|
SendMessage("-- Tagged Items --")
|
||||||
for(item in kondoExposed_taggedItems){
|
for(item in taggedItems){
|
||||||
val itemDef = ObjTypeList.get(item)
|
val itemDef = ObjTypeList.get(item)
|
||||||
SendMessage("Tagged: ${itemDef.name} ${itemDef.id}")
|
SendMessage("Tagged: ${itemDef.name} ${itemDef.id}")
|
||||||
}
|
}
|
||||||
|
|
@ -278,19 +277,19 @@ open class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun OnKondoValueUpdated() {
|
fun OnKondoValueUpdated() {
|
||||||
StoreData("ground-item-tags",kondoExposed_taggedItems);
|
StoreData("ground-item-tags",taggedItems);
|
||||||
StoreData("ground-item-ignore",kondoExposed_ignoredItems);
|
StoreData("ground-item-ignore",ignoredItems);
|
||||||
StoreData("low-value", kondoExposed_lowValue)
|
StoreData("low-value", lowValue)
|
||||||
StoreData("medium-value", kondoExposed_mediumValue)
|
StoreData("medium-value", mediumValue)
|
||||||
StoreData("high-value", kondoExposed_highValue)
|
StoreData("high-value", highValue)
|
||||||
StoreData("insane-value", kondoExposed_insaneValue)
|
StoreData("insane-value", insaneValue)
|
||||||
StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices)
|
StoreData("ground-item-use-remote", useLiveGEPrices)
|
||||||
StoreData("hide-below-value", kondoExposed_hideBelowValue)
|
StoreData("hide-below-value", hideBelowValue)
|
||||||
gePriceMap = loadGEPrices();
|
gePriceMap = loadGEPrices();
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadGEPrices(): Map<String, String> {
|
fun loadGEPrices(): Map<String, String> {
|
||||||
return if (kondoExposed_useLiveGEPrices) {
|
return if (useLiveGEPrices) {
|
||||||
try {
|
try {
|
||||||
println("GroundItems: Loading Remote GE Prices")
|
println("GroundItems: Loading Remote GE Prices")
|
||||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||||
|
|
@ -325,7 +324,7 @@ open class plugin : Plugin() {
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
println("GroundItems: Loading Local GE Prices")
|
println("GroundItems: Loading Local GE Prices")
|
||||||
BufferedReader(InputStreamReader(GroundItems.plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8))
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
val json = lines.joinToString("\n")
|
val json = lines.joinToString("\n")
|
||||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ Commands:\
|
||||||
::(tag,ignore)item ID\
|
::(tag,ignore)item ID\
|
||||||
::(reset)groundconfig\
|
::(reset)groundconfig\
|
||||||
Special thanks to Chisato for the original skeleton.
|
Special thanks to Chisato for the original skeleton.
|
||||||
VERSION=1.2
|
VERSION=1.3
|
||||||
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
|
package KondoKit
|
||||||
|
|
||||||
import java.awt.Color
|
import rt4.GameShell
|
||||||
import java.awt.Dimension
|
import java.awt.*
|
||||||
import javax.swing.JPanel
|
import java.awt.event.MouseListener
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.util.*
|
||||||
|
import java.util.Timer
|
||||||
|
import javax.swing.*
|
||||||
|
|
||||||
object Helpers {
|
object Helpers {
|
||||||
|
|
||||||
|
fun convertValue(type: Class<*>, genericType: Type?, value: String): Any {
|
||||||
|
return when {
|
||||||
|
type == Int::class.java -> value.toInt()
|
||||||
|
type == Double::class.java -> value.toDouble()
|
||||||
|
type == Boolean::class.java -> value.toBoolean()
|
||||||
|
type == Color::class.java -> convertToColor(value)
|
||||||
|
type == List::class.java && genericType is ParameterizedType -> {
|
||||||
|
val actualTypeArgument = genericType.actualTypeArguments.firstOrNull()
|
||||||
|
when {
|
||||||
|
value.isBlank() -> emptyList<Any>() // Handle empty string by returning an empty list
|
||||||
|
actualTypeArgument == Int::class.javaObjectType -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim().toInt() }
|
||||||
|
actualTypeArgument == String::class.java -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim() }
|
||||||
|
else -> throw IllegalArgumentException("Unsupported List type: $actualTypeArgument")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> value // Default to String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showToast(
|
||||||
|
parentComponent: Component?,
|
||||||
|
message: String,
|
||||||
|
messageType: Int = JOptionPane.INFORMATION_MESSAGE
|
||||||
|
) {
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
val toast = JWindow()
|
||||||
|
toast.type = Window.Type.POPUP
|
||||||
|
toast.background = Color(0, 0, 0, 0)
|
||||||
|
|
||||||
|
val panel = JPanel()
|
||||||
|
panel.isOpaque = false
|
||||||
|
panel.layout = BoxLayout(panel, BoxLayout.Y_AXIS)
|
||||||
|
|
||||||
|
val label = JLabel(message)
|
||||||
|
label.foreground = Color.WHITE
|
||||||
|
|
||||||
|
label.background = when (messageType) {
|
||||||
|
JOptionPane.ERROR_MESSAGE -> Color(220, 20, 60, 230) // Crimson for errors
|
||||||
|
JOptionPane.INFORMATION_MESSAGE -> Color(0, 128, 0, 230) // Green for success
|
||||||
|
JOptionPane.WARNING_MESSAGE -> Color(255, 165, 0, 230) // Orange for warnings
|
||||||
|
else -> Color(0, 0, 0, 170) // Default semi-transparent black
|
||||||
|
}
|
||||||
|
|
||||||
|
label.isOpaque = true
|
||||||
|
label.border = BorderFactory.createEmptyBorder(10, 20, 10, 20)
|
||||||
|
label.maximumSize = Dimension(242, 50)
|
||||||
|
label.preferredSize = Dimension(242, 50)
|
||||||
|
panel.add(label)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
toast.contentPane.add(panel)
|
||||||
|
toast.pack()
|
||||||
|
|
||||||
|
|
||||||
|
// Adjust for parent component location if it exists
|
||||||
|
if (parentComponent != null && 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 {
|
fun getSpriteId(skillId: Int) : Int {
|
||||||
return when (skillId) {
|
return when (skillId) {
|
||||||
0 -> 197
|
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 {
|
fun formatHtmlLabelText(text1: String, color1: Color, text2: String, color2: Color): String {
|
||||||
return "<html><span style='color:rgb(${color1.red},${color1.green},${color1.blue});'>$text1</span>" +
|
return "<html><div style='white-space:nowrap;'>" +
|
||||||
"<span style='color:rgb(${color2.red},${color2.green},${color2.blue});'>$text2</span></html>"
|
"<span style='color:rgb(${color1.red},${color1.green},${color1.blue});'>$text1</span>" +
|
||||||
|
"<span style='color:rgb(${color2.red},${color2.green},${color2.blue});'>$text2</span>" +
|
||||||
|
"</div></html>"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatNumber(value: Int): String {
|
fun formatNumber(value: Int): String {
|
||||||
|
|
@ -78,13 +233,4 @@ object Helpers {
|
||||||
else -> Color(128, 128, 128) // Default grey for unhandled skill IDs
|
else -> Color(128, 128, 128) // Default grey for unhandled skill IDs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Spacer(width: Int = 0, height: Int = 0) : JPanel() {
|
|
||||||
init {
|
|
||||||
preferredSize = Dimension(width, height)
|
|
||||||
maximumSize = preferredSize
|
|
||||||
minimumSize = preferredSize
|
|
||||||
isOpaque = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,19 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
import KondoKit.Constants.COLOR_BACKGROUND_DARK
|
import KondoKit.Constants.COLOR_BACKGROUND_DARK
|
||||||
|
import KondoKit.Constants.SKILL_DISPLAY_ORDER
|
||||||
|
import KondoKit.Constants.SKILL_SPRITE_DIMENSION
|
||||||
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
|
import KondoKit.Helpers.showToast
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
|
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||||
|
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import rt4.Sprites
|
import rt4.Sprites
|
||||||
|
|
@ -16,9 +26,11 @@ import java.awt.event.MouseEvent
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
import javax.swing.border.MatteBorder
|
import javax.swing.border.MatteBorder
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
// Sprite IDs
|
// Sprite IDs
|
||||||
|
|
@ -28,47 +40,49 @@ object Constants {
|
||||||
const val LVL_BAR_SPRITE = 898
|
const val LVL_BAR_SPRITE = 898
|
||||||
|
|
||||||
// Dimensions
|
// Dimensions
|
||||||
val SEARCH_FIELD_DIMENSION = Dimension(270, 30)
|
val SEARCH_FIELD_DIMENSION = Dimension(230, 30)
|
||||||
val ICON_DIMENSION_SMALL = Dimension(12, 12)
|
val ICON_DIMENSION_SMALL = Dimension(12, 12)
|
||||||
val ICON_DIMENSION_MEDIUM = Dimension(18, 20)
|
|
||||||
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
val ICON_DIMENSION_LARGE = Dimension(30, 30)
|
||||||
val HISCORE_PANEL_DIMENSION = Dimension(270, 400)
|
val HISCORE_PANEL_DIMENSION = Dimension(230, 500)
|
||||||
val FILTER_PANEL_DIMENSION = Dimension(270, 30)
|
val FILTER_PANEL_DIMENSION = Dimension(230, 30)
|
||||||
val SKILLS_PANEL_DIMENSION = Dimension(300, 300)
|
val SKILLS_PANEL_DIMENSION = Dimension(230, 290)
|
||||||
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(270, 30)
|
val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(230, 30)
|
||||||
val SKILL_PANEL_DIMENSION = Dimension(90, 35)
|
val SKILL_PANEL_DIMENSION = Dimension(76, 35)
|
||||||
val IMAGE_CANVAS_DIMENSION = Dimension(20, 20)
|
val IMAGE_CANVAS_DIMENSION = Dimension(20, 20)
|
||||||
val NUMBER_LABEL_DIMENSION = Dimension(30, 20)
|
val SKILL_SPRITE_DIMENSION = Dimension(14, 14)
|
||||||
|
val NUMBER_LABEL_DIMENSION = Dimension(20, 20)
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
val COLOR_BACKGROUND_DARK = Color(27, 27, 27)
|
val COLOR_BACKGROUND_DARK = WIDGET_COLOR
|
||||||
val COLOR_BACKGROUND_MEDIUM = Color(37, 37, 37)
|
val COLOR_BACKGROUND_MEDIUM = VIEW_BACKGROUND_COLOR
|
||||||
val COLOR_BACKGROUND_LIGHT = Color(43, 43, 43)
|
val COLOR_FOREGROUND_LIGHT = POPUP_FOREGROUND
|
||||||
val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200)
|
|
||||||
val COLOR_RED = Color.RED
|
|
||||||
val COLOR_SKILL_PANEL = Color(60, 60, 60)
|
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
|
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
|
||||||
val FONT_ARIAL_PLAIN_12 = Font("Arial", Font.PLAIN, 12)
|
val FONT_ARIAL_PLAIN_12 = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12)
|
val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12)
|
||||||
|
val SKILL_DISPLAY_ORDER = arrayOf(0,3,14,2,16,13,1,15,10,4,17,7,5,12,11,6,9,8,20,18,19,22,21,23)
|
||||||
}
|
}
|
||||||
|
|
||||||
var text: String = ""
|
var text: String = ""
|
||||||
|
|
||||||
object HiscoresView {
|
object HiscoresView {
|
||||||
|
|
||||||
|
const val VIEW_NAME = "HISCORE_SEARCH_VIEW"
|
||||||
|
var hiScoreView: JPanel? = null
|
||||||
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
|
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
|
||||||
|
|
||||||
private var cursorVisible: Boolean = true
|
private var cursorVisible: Boolean = true
|
||||||
private val gson = Gson()
|
private val gson = Gson()
|
||||||
|
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.MAG_SPRITE))
|
private val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.MAG_SPRITE))
|
||||||
val imageCanvas = bufferedImageSprite.let {
|
private val imageCanvas = bufferedImageSprite.let {
|
||||||
ImageCanvas(it).apply {
|
ImageCanvas(it).apply {
|
||||||
preferredSize = Constants.ICON_DIMENSION_SMALL
|
preferredSize = Constants.ICON_DIMENSION_SMALL
|
||||||
size = preferredSize
|
size = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +110,9 @@ object HiscoresView {
|
||||||
} else {
|
} else {
|
||||||
text += e.keyChar
|
text += e.keyChar
|
||||||
}
|
}
|
||||||
repaint()
|
SwingUtilities.invokeLater {
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
override fun keyPressed(e: KeyEvent) {
|
override fun keyPressed(e: KeyEvent) {
|
||||||
if (e.isControlDown) {
|
if (e.isControlDown) {
|
||||||
|
|
@ -109,7 +125,9 @@ object HiscoresView {
|
||||||
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
|
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
|
||||||
val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String
|
val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String
|
||||||
text += pasteText
|
text += pasteText
|
||||||
repaint()
|
SwingUtilities.invokeLater {
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -120,15 +138,19 @@ object HiscoresView {
|
||||||
override fun mouseClicked(e: MouseEvent) {
|
override fun mouseClicked(e: MouseEvent) {
|
||||||
if (e.x > width - 20 && e.y < 20) {
|
if (e.x > width - 20 && e.y < 20) {
|
||||||
text = ""
|
text = ""
|
||||||
repaint()
|
SwingUtilities.invokeLater {
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Timer(500) {
|
Timer(500) {
|
||||||
cursorVisible = !cursorVisible
|
cursorVisible = !cursorVisible
|
||||||
if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW")
|
if(focusedView == VIEW_NAME)
|
||||||
repaint()
|
SwingUtilities.invokeLater {
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,7 +162,7 @@ object HiscoresView {
|
||||||
val fm = g.fontMetrics
|
val fm = g.fontMetrics
|
||||||
val cursorX = fm.stringWidth(text) + 30
|
val cursorX = fm.stringWidth(text) + 30
|
||||||
|
|
||||||
imageCanvas?.let { canvas ->
|
imageCanvas.let { canvas ->
|
||||||
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
val imgG = g.create(5, 5, canvas.width, canvas.height)
|
||||||
canvas.paint(imgG)
|
canvas.paint(imgG)
|
||||||
imgG.dispose()
|
imgG.dispose()
|
||||||
|
|
@ -153,14 +175,16 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.isNotEmpty()) {
|
if (text.isNotEmpty()) {
|
||||||
g.color = Constants.COLOR_RED
|
g.color = Color.RED
|
||||||
g.drawString("x", width - 20, 20)
|
g.drawString("x", width - 20, 20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchPlayer(username: String) {
|
fun searchPlayer(username: String) {
|
||||||
text = username
|
text = username.replace(" ", "_")
|
||||||
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${username.toLowerCase()}"
|
val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${text.toLowerCase()}"
|
||||||
|
|
||||||
|
updateHiscoresView(null, "Searching...")
|
||||||
|
|
||||||
Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
|
|
@ -168,6 +192,10 @@ object HiscoresView {
|
||||||
val connection = url.openConnection() as HttpURLConnection
|
val connection = url.openConnection() as HttpURLConnection
|
||||||
connection.requestMethod = "GET"
|
connection.requestMethod = "GET"
|
||||||
|
|
||||||
|
// If a request take longer than 5 seconds timeout.
|
||||||
|
connection.connectTimeout = 5000
|
||||||
|
connection.readTimeout = 5000
|
||||||
|
|
||||||
val responseCode = connection.responseCode
|
val responseCode = connection.responseCode
|
||||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
||||||
|
|
@ -179,27 +207,45 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SwingUtilities.invokeLater {
|
SwingUtilities.invokeLater {
|
||||||
showError("Player not found!")
|
showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: SocketTimeoutException) {
|
||||||
SwingUtilities.invokeLater {
|
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()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updatePlayerData(jsonResponse: String, username: String) {
|
private fun updatePlayerData(jsonResponse: String, username: String) {
|
||||||
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
||||||
updateHiscoresView(hiscoresResponse, username)
|
updateHiscoresView(hiscoresResponse, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateHiscoresView(data: HiscoresResponse, username: String) {
|
private fun updateHiscoresView(data: HiscoresResponse?, username: String) {
|
||||||
val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel
|
val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel
|
||||||
val ironMode = data.info.iron_mode
|
|
||||||
|
|
||||||
playerNameLabel?.removeAll() // Clear previous components
|
playerNameLabel?.removeAll() // Clear previous components
|
||||||
|
var nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, "", primaryColor), JLabel.CENTER).apply {
|
||||||
|
font = Constants.FONT_ARIAL_BOLD_12
|
||||||
|
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||||
|
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||||
|
}
|
||||||
|
playerNameLabel?.add(nameLabel)
|
||||||
|
playerNameLabel?.revalidate()
|
||||||
|
playerNameLabel?.repaint()
|
||||||
|
|
||||||
|
if(data == null) return
|
||||||
|
|
||||||
|
playerNameLabel?.removeAll()
|
||||||
|
|
||||||
|
val ironMode = data.info.iron_mode
|
||||||
|
|
||||||
if (ironMode != "0") {
|
if (ironMode != "0") {
|
||||||
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1])
|
||||||
|
|
@ -213,11 +259,14 @@ object HiscoresView {
|
||||||
playerNameLabel?.add(imageCanvas)
|
playerNameLabel?.add(imageCanvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nameLabel = JLabel(username, JLabel.CENTER).apply {
|
val exp_multiplier = data.info.exp_multiplier
|
||||||
|
nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, " (${exp_multiplier}x)", primaryColor), JLabel.CENTER).apply {
|
||||||
font = Constants.FONT_ARIAL_BOLD_12
|
font = Constants.FONT_ARIAL_BOLD_12
|
||||||
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
foreground = Constants.COLOR_FOREGROUND_LIGHT
|
||||||
|
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
playerNameLabel?.add(nameLabel)
|
playerNameLabel?.add(nameLabel)
|
||||||
|
|
||||||
playerNameLabel?.revalidate()
|
playerNameLabel?.revalidate()
|
||||||
|
|
@ -266,20 +315,16 @@ object HiscoresView {
|
||||||
summoning: Int,
|
summoning: Int,
|
||||||
isMemberWorld: Boolean
|
isMemberWorld: Boolean
|
||||||
): Double {
|
): Double {
|
||||||
val base = (defence + hitpoints + Math.floor(prayer.toDouble() / 2)) * 0.25
|
val base = (defence + hitpoints + floor(prayer.toDouble() / 2)) * 0.25
|
||||||
val melee = (attack + strength) * 0.325
|
val melee = (attack + strength) * 0.325
|
||||||
val range = Math.floor(ranged * 1.5) * 0.325
|
val range = floor(ranged * 1.5) * 0.325
|
||||||
val mage = Math.floor(magic * 1.5) * 0.325
|
val mage = floor(magic * 1.5) * 0.325
|
||||||
val maxCombatType = maxOf(melee, range, mage)
|
val maxCombatType = maxOf(melee, range, mage)
|
||||||
|
|
||||||
val summoningFactor = if (isMemberWorld) Math.floor(summoning.toDouble() / 8) else 0.0
|
val summoningFactor = if (isMemberWorld) floor(summoning.toDouble() / 8) else 0.0
|
||||||
return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0
|
return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showError(message: String) {
|
|
||||||
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findComponentByName(container: Container, name: String): Component? {
|
private fun findComponentByName(container: Container, name: String): Component? {
|
||||||
for (component in container.components) {
|
for (component in container.components) {
|
||||||
if (name == component.name) {
|
if (name == component.name) {
|
||||||
|
|
@ -296,10 +341,10 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createHiscoreSearchView(): JPanel {
|
fun createHiscoreSearchView() {
|
||||||
val hiscorePanel = JPanel().apply {
|
val hiscorePanel = JPanel().apply {
|
||||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
name = "HISCORE_SEARCH_VIEW"
|
name = VIEW_NAME
|
||||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||||
preferredSize = Constants.HISCORE_PANEL_DIMENSION
|
preferredSize = Constants.HISCORE_PANEL_DIMENSION
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
|
|
@ -324,14 +369,13 @@ object HiscoresView {
|
||||||
add(searchFieldWrapper)
|
add(searchFieldWrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
hiscorePanel.add(searchPanel)
|
hiscorePanel.add(searchPanel)
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
// Adding the player name panel in place of the filterPanel
|
|
||||||
val playerNamePanel = JPanel().apply {
|
val playerNamePanel = JPanel().apply {
|
||||||
layout = FlowLayout(FlowLayout.CENTER)
|
layout = GridBagLayout() // This will center the JLabel both vertically and horizontally
|
||||||
background = VIEW_BACKGROUND_COLOR
|
background = TOOLTIP_BACKGROUND.darker()
|
||||||
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
preferredSize = Constants.FILTER_PANEL_DIMENSION
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
|
|
@ -339,7 +383,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
hiscorePanel.add(playerNamePanel)
|
hiscorePanel.add(playerNamePanel)
|
||||||
hiscorePanel.add(Helpers.Spacer(height = 10))
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_MEDIUM
|
background = Constants.COLOR_BACKGROUND_MEDIUM
|
||||||
|
|
@ -348,10 +392,10 @@ object HiscoresView {
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until 24) {
|
for (i in SKILL_DISPLAY_ORDER) {
|
||||||
val skillPanel = JPanel().apply {
|
val skillPanel = JPanel().apply {
|
||||||
layout = BorderLayout()
|
layout = BorderLayout()
|
||||||
background = Constants.COLOR_SKILL_PANEL
|
background = COLOR_BACKGROUND_DARK
|
||||||
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
preferredSize = Constants.SKILL_PANEL_DIMENSION
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
|
|
@ -362,8 +406,9 @@ object HiscoresView {
|
||||||
|
|
||||||
val imageCanvas = bufferedImageSprite.let {
|
val imageCanvas = bufferedImageSprite.let {
|
||||||
ImageCanvas(it).apply {
|
ImageCanvas(it).apply {
|
||||||
preferredSize = Constants.IMAGE_CANVAS_DIMENSION
|
preferredSize = SKILL_SPRITE_DIMENSION
|
||||||
size = Constants.IMAGE_CANVAS_DIMENSION
|
size = SKILL_SPRITE_DIMENSION
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,7 +421,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_DARK
|
background = COLOR_BACKGROUND_DARK
|
||||||
add(imageCanvas)
|
add(imageCanvas)
|
||||||
add(numberLabel)
|
add(numberLabel)
|
||||||
}
|
}
|
||||||
|
|
@ -394,9 +439,10 @@ object HiscoresView {
|
||||||
minimumSize = preferredSize
|
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 {
|
val totalLevelIcon = ImageCanvas(bufferedImageSprite).apply {
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
preferredSize = Constants.ICON_DIMENSION_LARGE
|
preferredSize = Constants.ICON_DIMENSION_LARGE
|
||||||
size = Constants.ICON_DIMENSION_LARGE
|
size = Constants.ICON_DIMENSION_LARGE
|
||||||
}
|
}
|
||||||
|
|
@ -418,6 +464,7 @@ object HiscoresView {
|
||||||
val bufferedImageSprite2 = getBufferedImageFromSprite(API.GetSprite(Constants.COMBAT_LVL_SPRITE))
|
val bufferedImageSprite2 = getBufferedImageFromSprite(API.GetSprite(Constants.COMBAT_LVL_SPRITE))
|
||||||
|
|
||||||
val combatLevelIcon = ImageCanvas(bufferedImageSprite2).apply {
|
val combatLevelIcon = ImageCanvas(bufferedImageSprite2).apply {
|
||||||
|
fillColor = COLOR_BACKGROUND_DARK
|
||||||
preferredSize = Constants.ICON_DIMENSION_LARGE
|
preferredSize = Constants.ICON_DIMENSION_LARGE
|
||||||
size = Constants.ICON_DIMENSION_LARGE
|
size = Constants.ICON_DIMENSION_LARGE
|
||||||
}
|
}
|
||||||
|
|
@ -431,7 +478,7 @@ object HiscoresView {
|
||||||
}
|
}
|
||||||
|
|
||||||
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply {
|
||||||
background = Constants.COLOR_BACKGROUND_DARK
|
background = COLOR_BACKGROUND_DARK
|
||||||
add(combatLevelIcon)
|
add(combatLevelIcon)
|
||||||
add(combatLevelLabel)
|
add(combatLevelLabel)
|
||||||
}
|
}
|
||||||
|
|
@ -439,8 +486,9 @@ object HiscoresView {
|
||||||
totalCombatPanel.add(totalLevelPanel)
|
totalCombatPanel.add(totalLevelPanel)
|
||||||
totalCombatPanel.add(combatLevelPanel)
|
totalCombatPanel.add(combatLevelPanel)
|
||||||
hiscorePanel.add(totalCombatPanel)
|
hiscorePanel.add(totalCombatPanel)
|
||||||
|
hiscorePanel.add(Box.createVerticalStrut(10))
|
||||||
|
|
||||||
return hiscorePanel
|
hiScoreView = hiscorePanel
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HiscoresResponse(
|
data class HiscoresResponse(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import java.awt.Canvas
|
import java.awt.Canvas
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.Dimension
|
import java.awt.Dimension
|
||||||
|
|
@ -8,31 +9,25 @@ import java.awt.image.BufferedImage
|
||||||
|
|
||||||
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
class ImageCanvas(private val image: BufferedImage) : Canvas() {
|
||||||
|
|
||||||
|
var fillColor: Color = WIDGET_COLOR
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Manually set the alpha value to 255 (fully opaque) only for pixels that are not fully transparent
|
|
||||||
val width = image.width
|
val width = image.width
|
||||||
val height = image.height
|
val height = image.height
|
||||||
for (y in 0 until height) {
|
for (y in 0 until height) {
|
||||||
for (x in 0 until width) {
|
for (x in 0 until width) {
|
||||||
// Retrieve the current pixel color
|
|
||||||
val color = image.getRGB(x, y)
|
val color = image.getRGB(x, y)
|
||||||
|
|
||||||
// Check if the pixel is not fully transparent (i.e., color is not 0)
|
|
||||||
if (color != 0) {
|
if (color != 0) {
|
||||||
// Ensure the alpha is set to 255 (fully opaque)
|
|
||||||
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
val newColor = (color and 0x00FFFFFF) or (0xFF shl 24)
|
||||||
|
|
||||||
// Set the pixel with the updated color
|
|
||||||
image.setRGB(x, y, newColor)
|
image.setRGB(x, y, newColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun paint(g: Graphics) {
|
override fun paint(g: Graphics) {
|
||||||
super.paint(g)
|
super.paint(g)
|
||||||
g.color = Color(27, 27, 27)
|
g.color = fillColor
|
||||||
g.fillRect(0, 0, width, height)
|
g.fillRect(0, 0, width, height)
|
||||||
g.drawImage(image, 0, 0, width, height, this)
|
g.drawImage(image, 0, 0, width, height, this)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
package KondoKit
|
|
||||||
|
|
||||||
import java.awt.Color
|
|
||||||
import java.lang.reflect.Field
|
|
||||||
import java.lang.reflect.ParameterizedType
|
|
||||||
import java.lang.reflect.Type
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is used for the runtime editing of plugin variables.
|
|
||||||
To expose fields name them starting with `kondoExposed_`
|
|
||||||
When they are applied this will trigger an invoke of OnKondoValueUpdated()
|
|
||||||
if it is implemented. Check GroundItems plugin for an example.
|
|
||||||
*/
|
|
||||||
|
|
||||||
object KondoKitUtils {
|
|
||||||
const val KONDO_PREFIX = "kondoExposed_"
|
|
||||||
|
|
||||||
fun isKondoExposed(field: Field): Boolean {
|
|
||||||
return field.name.startsWith(KONDO_PREFIX)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getKondoExposedFields(instance: Any): List<Field> {
|
|
||||||
val exposedFields: MutableList<Field> = ArrayList()
|
|
||||||
for (field in instance.javaClass.declaredFields) {
|
|
||||||
if (isKondoExposed(field)) {
|
|
||||||
exposedFields.add(field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exposedFields
|
|
||||||
}
|
|
||||||
|
|
||||||
fun convertValue(type: Class<*>, genericType: Type?, value: String): Any {
|
|
||||||
return when {
|
|
||||||
type == Int::class.java -> value.toInt()
|
|
||||||
type == Double::class.java -> value.toDouble()
|
|
||||||
type == Boolean::class.java -> value.toBoolean()
|
|
||||||
type == Color::class.java -> convertToColor(value)
|
|
||||||
type == List::class.java && genericType is ParameterizedType -> {
|
|
||||||
val actualTypeArgument = genericType.actualTypeArguments.firstOrNull()
|
|
||||||
when {
|
|
||||||
value.isBlank() -> emptyList<Any>() // Handle empty string by returning an empty list
|
|
||||||
actualTypeArgument == Int::class.javaObjectType -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim().toInt() }
|
|
||||||
actualTypeArgument == String::class.java -> value.trim('[', ']').split(",").filter { it.isNotBlank() }.map { it.trim() }
|
|
||||||
else -> throw IllegalArgumentException("Unsupported List type: $actualTypeArgument")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> value // Default to String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun convertToColor(value: String): Color {
|
|
||||||
val color = Color.decode(value) // Assumes value is in format "#RRGGBB" or "0xRRGGBB"
|
|
||||||
return color
|
|
||||||
}
|
|
||||||
|
|
||||||
fun colorToHex(color: Color): String {
|
|
||||||
return "#%02x%02x%02x".format(color.red, color.green, color.blue)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun colorToIntArray(color: Color): IntArray {
|
|
||||||
return intArrayOf(color.red, color.green, color.blue)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FieldObserver {
|
|
||||||
fun onFieldChange(field: Field, newValue: Any?)
|
|
||||||
}
|
|
||||||
|
|
||||||
class FieldNotifier(private val plugin: Any) {
|
|
||||||
private val observers = mutableListOf<FieldObserver>()
|
|
||||||
|
|
||||||
fun addObserver(observer: FieldObserver) {
|
|
||||||
observers.add(observer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun notifyFieldChange(field: Field, newValue: Any?) {
|
|
||||||
for (observer in observers) {
|
|
||||||
observer.onFieldChange(field, newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setFieldValue(field: Field, value: Any?) {
|
|
||||||
field.isAccessible = true
|
|
||||||
field.set(plugin, value)
|
|
||||||
notifyFieldChange(field, value)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val onUpdateMethod = plugin::class.java.getMethod("OnKondoValueUpdated")
|
|
||||||
onUpdateMethod.invoke(plugin)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.Helpers.addMouseListenerToAll
|
||||||
import KondoKit.Helpers.formatHtmlLabelText
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.XPTrackerView.wrappedWidget
|
import KondoKit.XPTrackerView.wrappedWidget
|
||||||
|
import KondoKit.plugin.Companion.POPUP_BACKGROUND
|
||||||
|
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||||
|
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
|
||||||
|
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import rt4.NpcTypeList
|
import rt4.*
|
||||||
import rt4.ObjStackNode
|
|
||||||
import rt4.Player
|
|
||||||
import rt4.SceneGraph
|
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
|
import java.awt.Font
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
|
@ -22,19 +28,23 @@ import java.net.URL
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
object LootTrackerView {
|
object LootTrackerView {
|
||||||
private const val SNAPSHOT_LIFESPAN = 10
|
private const val SNAPSHOT_LIFESPAN = 10
|
||||||
const val BAG_ICON = 900;
|
const val BAG_ICON = 900
|
||||||
val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>()
|
val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>()
|
||||||
var gePriceMap = loadGEPrices()
|
var gePriceMap = loadGEPrices()
|
||||||
|
const val VIEW_NAME = "LOOT_TRACKER_VIEW"
|
||||||
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
|
||||||
private val npcKillCounts = mutableMapOf<String, Int>()
|
private val npcKillCounts = mutableMapOf<String, Int>()
|
||||||
private var totalTrackerWidget: XPWidget? = null
|
private var totalTrackerWidget: XPWidget? = null
|
||||||
var lastConfirmedKillNpcId = -1;
|
var lastConfirmedKillNpcId = -1
|
||||||
|
private var customToolTipWindow: JWindow? = null
|
||||||
|
var lootTrackerView: JPanel? = null
|
||||||
|
|
||||||
fun loadGEPrices(): Map<String, String> {
|
fun loadGEPrices(): Map<String, String> {
|
||||||
return if (plugin.kondoExposed_useLiveGEPrices) {
|
return if (plugin.useLiveGEPrices) {
|
||||||
try {
|
try {
|
||||||
println("LootTracker: Loading Remote GE Prices")
|
println("LootTracker: Loading Remote GE Prices")
|
||||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||||
|
|
@ -69,7 +79,7 @@ object LootTrackerView {
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
println("LootTracker: Loading Local GE Prices")
|
println("LootTracker: Loading Local GE Prices")
|
||||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8))
|
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8))
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
val json = lines.joinToString("\n")
|
val json = lines.joinToString("\n")
|
||||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||||
|
|
@ -94,19 +104,37 @@ object LootTrackerView {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun createLootTrackerView(): JPanel {
|
fun createLootTrackerView() {
|
||||||
return JPanel().apply {
|
lootTrackerView = JPanel().apply {
|
||||||
layout = FlowLayout(FlowLayout.CENTER, 0, 5)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS) // Use BoxLayout on Y axis to stack widgets vertically
|
||||||
background = VIEW_BACKGROUND_COLOR
|
background = VIEW_BACKGROUND_COLOR
|
||||||
preferredSize = Dimension(270, 700)
|
|
||||||
maximumSize = Dimension(270, 700)
|
|
||||||
minimumSize = Dimension(270, 700)
|
|
||||||
add(Box.createVerticalStrut(5))
|
add(Box.createVerticalStrut(5))
|
||||||
totalTrackerWidget = createTotalLootWidget()
|
totalTrackerWidget = createTotalLootWidget()
|
||||||
add(wrappedWidget(totalTrackerWidget!!.panel))
|
|
||||||
add(Helpers.Spacer(height = 15))
|
val wrapped = wrappedWidget(totalTrackerWidget!!.container)
|
||||||
|
val popupMenu = resetLootTrackerMenu()
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addMouseListenerToAll(wrapped,rightClickListener)
|
||||||
|
wrapped.addMouseListener(rightClickListener)
|
||||||
|
add(wrapped)
|
||||||
|
add(Box.createVerticalStrut(8))
|
||||||
revalidate()
|
revalidate()
|
||||||
repaint()
|
if(focusedView == VIEW_NAME)
|
||||||
|
repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +150,7 @@ object LootTrackerView {
|
||||||
totalTrackerWidget?.let {
|
totalTrackerWidget?.let {
|
||||||
it.previousXp += newVal
|
it.previousXp += newVal
|
||||||
it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor)
|
it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor)
|
||||||
it.panel.repaint()
|
it.container.repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,12 +160,12 @@ object LootTrackerView {
|
||||||
val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor))
|
val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor))
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = -1,
|
skillId = -1,
|
||||||
panel = createWidgetPanel(bufferedImageSprite,l2,l1),
|
container = createWidgetPanel(bufferedImageSprite,l2,l1),
|
||||||
xpGainedLabel = l2,
|
xpGainedLabel = l2,
|
||||||
xpLeftLabel = JLabel(),
|
xpLeftLabel = JLabel(),
|
||||||
actionsRemainingLabel = JLabel(),
|
actionsRemainingLabel = JLabel(),
|
||||||
xpPerHourLabel = l1,
|
xpPerHourLabel = l1,
|
||||||
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
|
progressBar = ProgressBar(0.0, Color(0,0,0)), // unused.
|
||||||
totalXpGained = 0,
|
totalXpGained = 0,
|
||||||
startTime = System.currentTimeMillis(),
|
startTime = System.currentTimeMillis(),
|
||||||
previousXp = 0
|
previousXp = 0
|
||||||
|
|
@ -146,16 +174,19 @@ object LootTrackerView {
|
||||||
|
|
||||||
private fun createWidgetPanel(bufferedImageSprite: BufferedImage, l1 : JLabel, l2 : JLabel): Panel {
|
private fun createWidgetPanel(bufferedImageSprite: BufferedImage, l1 : JLabel, l2 : JLabel): Panel {
|
||||||
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||||
size = Dimension(width, height)
|
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
|
minimumSize = preferredSize
|
||||||
|
maximumSize = preferredSize
|
||||||
|
size = preferredSize
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
val imageContainer = Panel(FlowLayout()).apply {
|
val imageContainer = Panel(BorderLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
add(imageCanvas)
|
add(imageCanvas, BorderLayout.NORTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Panel(BorderLayout(5, 5)).apply {
|
return Panel(BorderLayout(5, 0)).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = TOTAL_XP_WIDGET_SIZE
|
preferredSize = TOTAL_XP_WIDGET_SIZE
|
||||||
add(imageContainer, BorderLayout.WEST)
|
add(imageContainer, BorderLayout.WEST)
|
||||||
|
|
@ -164,7 +195,7 @@ object LootTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTextPanel(l1 : JLabel, l2: JLabel): Panel {
|
private fun createTextPanel(l1 : JLabel, l2: JLabel): Panel {
|
||||||
return Panel(GridLayout(2, 1, 5, 5)).apply {
|
return Panel(GridLayout(2, 1, 5, 0)).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
add(l1)
|
add(l1)
|
||||||
add(l2)
|
add(l2)
|
||||||
|
|
@ -173,7 +204,7 @@ object LootTrackerView {
|
||||||
|
|
||||||
private fun createLabel(text: String): JLabel {
|
private fun createLabel(text: String): JLabel {
|
||||||
return JLabel(text).apply {
|
return JLabel(text).apply {
|
||||||
font = Font("Arial", Font.PLAIN, 11)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.LEFT
|
horizontalAlignment = JLabel.LEFT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,12 +220,19 @@ object LootTrackerView {
|
||||||
|
|
||||||
// Recalculate lootPanel size based on the number of unique items.
|
// Recalculate lootPanel size based on the number of unique items.
|
||||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
||||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
val rowsNeeded = ceil(totalItems / 6.0).toInt()
|
||||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1)
|
val lootPanelHeight = rowsNeeded * (40)
|
||||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
|
||||||
|
|
||||||
|
val size = Dimension(lootPanel.width,lootPanelHeight+32)
|
||||||
|
lootPanel.parent.preferredSize = size
|
||||||
|
lootPanel.parent.minimumSize = size
|
||||||
|
lootPanel.parent.maximumSize = size
|
||||||
|
lootPanel.parent.revalidate()
|
||||||
|
lootPanel.parent.repaint()
|
||||||
lootPanel.revalidate()
|
lootPanel.revalidate()
|
||||||
lootPanel.repaint()
|
|
||||||
|
if(focusedView == VIEW_NAME)
|
||||||
|
lootPanel.repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,27 +242,93 @@ object LootTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 0, 0))
|
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 1, 3153952))
|
||||||
return FixedSizePanel(Dimension(36, 32)).apply {
|
|
||||||
|
// Create the panel for the item
|
||||||
|
val itemPanel = FixedSizePanel(Dimension(36, 32)).apply {
|
||||||
preferredSize = Dimension(36, 32)
|
preferredSize = Dimension(36, 32)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
add(ImageCanvas(bufferedImageSprite).apply {
|
|
||||||
|
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||||
preferredSize = Dimension(36, 32)
|
preferredSize = Dimension(36, 32)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = preferredSize
|
minimumSize = preferredSize
|
||||||
maximumSize = preferredSize
|
maximumSize = preferredSize
|
||||||
}, BorderLayout.CENTER)
|
}
|
||||||
|
|
||||||
|
// Add the imageCanvas to the panel
|
||||||
|
add(imageCanvas, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
// Put the itemId as a property for reference
|
||||||
putClientProperty("itemId", itemId)
|
putClientProperty("itemId", itemId)
|
||||||
|
|
||||||
|
// Add mouse listener for custom hover text
|
||||||
|
imageCanvas.addMouseListener(object : MouseAdapter() {
|
||||||
|
override fun mouseEntered(e: MouseEvent) {
|
||||||
|
// Show custom tooltip when the mouse enters the component
|
||||||
|
showCustomToolTip(e.point, itemId,quantity,imageCanvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseExited(e: MouseEvent) {
|
||||||
|
// Hide tooltip when mouse exits
|
||||||
|
hideCustomToolTip()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return itemPanel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to show the custom tooltip
|
||||||
|
fun showCustomToolTip(location: Point, itemId: Int, quantity: Int, parentComponent: ImageCanvas) {
|
||||||
|
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) {
|
private fun updateItemPanelIcon(panel: JPanel, itemId: Int, quantity: Int) {
|
||||||
panel.removeAll()
|
panel.removeAll()
|
||||||
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
|
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
|
||||||
panel.revalidate()
|
panel.revalidate()
|
||||||
panel.repaint()
|
if(focusedView == VIEW_NAME)
|
||||||
|
panel.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateKillCountLabel(lootTrackerPanel: JPanel, npcName: String) {
|
private fun updateKillCountLabel(lootTrackerPanel: JPanel, npcName: String) {
|
||||||
|
|
@ -246,9 +350,11 @@ object LootTrackerView {
|
||||||
?.apply {
|
?.apply {
|
||||||
val newValue = (getClientProperty("val") as? Int ?: 0) + valueOfNewDrops.toInt()
|
val newValue = (getClientProperty("val") as? Int ?: 0) + valueOfNewDrops.toInt()
|
||||||
text = "${formatValue(newValue)} gp"
|
text = "${formatValue(newValue)} gp"
|
||||||
|
foreground = primaryColor
|
||||||
putClientProperty("val", newValue)
|
putClientProperty("val", newValue)
|
||||||
revalidate()
|
revalidate()
|
||||||
repaint()
|
if(focusedView == VIEW_NAME)
|
||||||
|
repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -276,7 +382,7 @@ object LootTrackerView {
|
||||||
|
|
||||||
if (newDrops.isNotEmpty()) {
|
if (newDrops.isNotEmpty()) {
|
||||||
val npcName = NpcTypeList.get(npcId).name
|
val npcName = NpcTypeList.get(npcId).name
|
||||||
lastConfirmedKillNpcId = npcId;
|
lastConfirmedKillNpcId = npcId
|
||||||
handleNewDrops(npcName.toString(), newDrops, lootTrackerView)
|
handleNewDrops(npcName.toString(), newDrops, lootTrackerView)
|
||||||
toRemove.add(npcId)
|
toRemove.add(npcId)
|
||||||
} else if (snapshot.age >= SNAPSHOT_LIFESPAN) {
|
} else if (snapshot.age >= SNAPSHOT_LIFESPAN) {
|
||||||
|
|
@ -308,11 +414,11 @@ object LootTrackerView {
|
||||||
private fun handleNewDrops(npcName: String, newDrops: Set<Item>, lootTrackerView: JPanel) {
|
private fun handleNewDrops(npcName: String, newDrops: Set<Item>, lootTrackerView: JPanel) {
|
||||||
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
findLootItemsPanel(lootTrackerView, npcName)?.let {
|
||||||
} ?: run {
|
} ?: run {
|
||||||
// Panel doesn't exist, so create and add it
|
|
||||||
lootTrackerView.add(createLootFrame(npcName))
|
lootTrackerView.add(createLootFrame(npcName))
|
||||||
lootTrackerView.add(Helpers.Spacer(height = 15))
|
lootTrackerView.add(Box.createVerticalStrut(8))
|
||||||
lootTrackerView.revalidate()
|
lootTrackerView.revalidate()
|
||||||
lootTrackerView.repaint()
|
if(focusedView == VIEW_NAME)
|
||||||
|
lootTrackerView.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
npcKillCounts[npcName] = npcKillCounts.getOrDefault(npcName, 0) + 1
|
npcKillCounts[npcName] = npcKillCounts.getOrDefault(npcName, 0) + 1
|
||||||
|
|
@ -321,7 +427,7 @@ object LootTrackerView {
|
||||||
newDrops.forEach { drop ->
|
newDrops.forEach { drop ->
|
||||||
val geValue = (gePriceMap[drop.id.toString()]?.toInt() ?: 0) * drop.quantity
|
val geValue = (gePriceMap[drop.id.toString()]?.toInt() ?: 0) * drop.quantity
|
||||||
updateValueLabel(lootTrackerView, geValue.toString(), npcName)
|
updateValueLabel(lootTrackerView, geValue.toString(), npcName)
|
||||||
addItemToLootPanel(lootTrackerView, drop, npcName)
|
plugin.registerDrawAction { addItemToLootPanel(lootTrackerView, drop, npcName) }
|
||||||
updateTotalValue(geValue)
|
updateTotalValue(geValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -330,29 +436,30 @@ object LootTrackerView {
|
||||||
val childFramePanel = JPanel().apply {
|
val childFramePanel = JPanel().apply {
|
||||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
minimumSize = Dimension(270, 0)
|
minimumSize = Dimension(230, 0)
|
||||||
maximumSize = Dimension(270, 700)
|
maximumSize = Dimension(230, 700)
|
||||||
|
name = "HELLO_WORLD"
|
||||||
}
|
}
|
||||||
|
|
||||||
val labelPanel = JPanel(BorderLayout()).apply {
|
val labelPanel = JPanel(BorderLayout()).apply {
|
||||||
background = Color(21, 21, 21)
|
background = TITLE_BAR_COLOR
|
||||||
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
|
||||||
maximumSize = Dimension(270, 24)
|
maximumSize = Dimension(230, 24)
|
||||||
minimumSize = maximumSize
|
minimumSize = maximumSize
|
||||||
preferredSize = maximumSize
|
preferredSize = maximumSize
|
||||||
}
|
}
|
||||||
|
|
||||||
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
|
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
|
||||||
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
|
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
|
||||||
foreground = Color(200, 200, 200)
|
foreground = secondaryColor
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.LEFT
|
horizontalAlignment = JLabel.LEFT
|
||||||
name = "killCountLabel_$npcName"
|
name = "killCountLabel_$npcName"
|
||||||
}
|
}
|
||||||
|
|
||||||
val valueLabel = JLabel("0 gp").apply {
|
val valueLabel = JLabel("0 gp").apply {
|
||||||
foreground = Color(200, 200, 200)
|
foreground = secondaryColor
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
horizontalAlignment = JLabel.RIGHT
|
horizontalAlignment = JLabel.RIGHT
|
||||||
name = "valueLabel_$npcName"
|
name = "valueLabel_$npcName"
|
||||||
}
|
}
|
||||||
|
|
@ -370,19 +477,120 @@ object LootTrackerView {
|
||||||
|
|
||||||
lootItemPanels[npcName] = mutableMapOf()
|
lootItemPanels[npcName] = mutableMapOf()
|
||||||
|
|
||||||
// Determine number of items and adjust size of lootPanel
|
|
||||||
val totalItems = lootItemPanels[npcName]?.size ?: 0
|
|
||||||
val rowsNeeded = Math.ceil(totalItems / 6.0).toInt()
|
|
||||||
val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1) // Height per row = 36 + spacing
|
|
||||||
lootPanel.preferredSize = Dimension(270, lootPanelHeight+10)
|
|
||||||
|
|
||||||
childFramePanel.add(labelPanel)
|
childFramePanel.add(labelPanel)
|
||||||
childFramePanel.add(lootPanel)
|
childFramePanel.add(lootPanel)
|
||||||
childFramePanel.add(lootPanel)
|
|
||||||
|
|
||||||
|
val popupMenu = removeLootFrameMenu(childFramePanel, npcName)
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labelPanel.addMouseListener(rightClickListener)
|
||||||
return childFramePanel
|
return childFramePanel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
class FixedSizePanel(private val fixedSize: Dimension) : JPanel() {
|
||||||
override fun getPreferredSize(): Dimension {
|
override fun getPreferredSize(): Dimension {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,22 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.plugin.Companion.PROGRESS_BAR_FILL
|
||||||
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
import java.awt.Canvas
|
import java.awt.Canvas
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
|
import java.awt.Dimension
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
import java.awt.Graphics
|
import java.awt.Graphics
|
||||||
|
|
||||||
class ProgressBar(
|
class ProgressBar(
|
||||||
private var progress: Double,
|
private var progress: Double,
|
||||||
private val barColor: Color,
|
private val barColor: Color,
|
||||||
private var currentLevel: Int = 0,
|
private var currentLevel: Int = 0,
|
||||||
private var nextLevel: Int = 1
|
private var nextLevel: Int = 1
|
||||||
) : Canvas() {
|
) : Canvas() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
font = Font("Arial", Font.PLAIN, 12)
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun paint(g: Graphics) {
|
override fun paint(g: Graphics) {
|
||||||
|
|
@ -25,22 +28,32 @@ class ProgressBar(
|
||||||
g.fillRect(0, 0, width, this.height)
|
g.fillRect(0, 0, width, this.height)
|
||||||
|
|
||||||
// Draw the unfilled part of the progress bar
|
// Draw the unfilled part of the progress bar
|
||||||
g.color = Color(100, 100, 100)
|
g.color = PROGRESS_BAR_FILL
|
||||||
g.fillRect(width, 0, this.width - width, this.height)
|
g.fillRect(width, 0, this.width - width, this.height)
|
||||||
|
|
||||||
|
// Variables for text position
|
||||||
|
val textY = this.height / 2 + 6
|
||||||
|
|
||||||
// Draw the current level on the far left
|
// Draw the current level on the far left
|
||||||
g.color = Color(255, 255, 255)
|
drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, secondaryColor)
|
||||||
g.drawString("Lvl. $currentLevel", 5, this.height / 2 + 4)
|
|
||||||
|
|
||||||
// Draw the percentage in the middle
|
// Draw the percentage in the middle
|
||||||
val percentageText = String.format("%.2f%%", progress)
|
val percentageText = String.format("%.2f%%", progress)
|
||||||
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
|
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
|
||||||
g.drawString(percentageText, (this.width - percentageWidth) / 2, this.height / 2 + 4)
|
drawTextWithShadow(g, percentageText, (this.width - percentageWidth) / 2, textY, secondaryColor)
|
||||||
|
|
||||||
// Draw the next level on the far right
|
// Draw the next level on the far right
|
||||||
val nextLevelText = "Lvl. $nextLevel"
|
val nextLevelText = "Lvl. $nextLevel"
|
||||||
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
|
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
|
||||||
g.drawString(nextLevelText, this.width - nextLevelWidth - 5, this.height / 2 + 4)
|
drawTextWithShadow(g, nextLevelText, this.width - nextLevelWidth - 5, textY, secondaryColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPreferredSize(): Dimension {
|
||||||
|
return Dimension(220, 16) // Force the height to 16px, width can be anything appropriate
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMinimumSize(): Dimension {
|
||||||
|
return Dimension(220, 16) // Force the minimum height to 16px, width can be smaller
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
|
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
|
||||||
|
|
@ -50,4 +63,15 @@ class ProgressBar(
|
||||||
if(isVisible)
|
if(isVisible)
|
||||||
repaint()
|
repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to draw text with a shadow effect
|
||||||
|
private fun drawTextWithShadow(g: Graphics, text: String, x: Int, y: Int, textColor: Color) {
|
||||||
|
// Draw shadow (black text with -1 x and -1 y offset)
|
||||||
|
g.color = Color(0, 0, 0)
|
||||||
|
g.drawString(text, x + 1, y + 1)
|
||||||
|
|
||||||
|
// Draw actual text on top
|
||||||
|
g.color = textColor
|
||||||
|
g.drawString(text, x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,149 +1,324 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
import KondoKit.KondoKitUtils.convertValue
|
import KondoKit.Helpers.convertValue
|
||||||
|
import KondoKit.Helpers.showToast
|
||||||
|
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
|
||||||
|
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
|
import plugin.PluginInfo
|
||||||
import plugin.PluginRepository
|
import plugin.PluginRepository
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
import java.lang.reflect.Field
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is used for the runtime editing of plugin variables.
|
||||||
|
To expose fields add the @Exposed annotation.
|
||||||
|
When they are applied this will trigger an invoke of OnKondoValueUpdated()
|
||||||
|
if it is implemented. Check GroundItems plugin for an example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
object ReflectiveEditorView {
|
object ReflectiveEditorView {
|
||||||
fun createReflectiveEditorView(): JPanel {
|
var reflectiveEditorView: JPanel? = null
|
||||||
|
private val loadedPlugins: MutableList<String> = mutableListOf()
|
||||||
|
const val VIEW_NAME = "REFLECTIVE_EDITOR_VIEW"
|
||||||
|
fun createReflectiveEditorView() {
|
||||||
val reflectiveEditorPanel = JPanel(BorderLayout())
|
val reflectiveEditorPanel = JPanel(BorderLayout())
|
||||||
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
|
||||||
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
reflectiveEditorPanel.add(Box.createVerticalStrut(5))
|
||||||
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
|
||||||
return reflectiveEditorPanel
|
reflectiveEditorView = reflectiveEditorPanel
|
||||||
|
addPlugins(reflectiveEditorView!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addPlugins(reflectiveEditorView: JPanel) {
|
fun addPlugins(reflectiveEditorView: JPanel) {
|
||||||
|
reflectiveEditorView.removeAll() // clear previous
|
||||||
|
loadedPlugins.clear()
|
||||||
try {
|
try {
|
||||||
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
|
||||||
loadedPluginsField.isAccessible = true
|
loadedPluginsField.isAccessible = true
|
||||||
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
|
||||||
|
|
||||||
for ((_, plugin) in loadedPlugins) {
|
for ((pluginInfo, plugin) in loadedPlugins) {
|
||||||
addPluginToEditor(reflectiveEditorView, plugin as Plugin)
|
addPluginToEditor(reflectiveEditorView, pluginInfo as PluginInfo, plugin as Plugin)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
reflectiveEditorView.revalidate()
|
|
||||||
reflectiveEditorView.repaint()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addPluginToEditor(reflectiveEditorView: JPanel, plugin: Any) {
|
// Add a centered box for plugins that have no exposed fields
|
||||||
reflectiveEditorView.layout = BoxLayout(reflectiveEditorView, BoxLayout.Y_AXIS)
|
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 label = JLabel("Loaded Plugins without Exposed Fields", SwingConstants.CENTER)
|
||||||
val exposedFields = KondoKitUtils.getKondoExposedFields(plugin)
|
label.font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||||
|
|
||||||
if (exposedFields.isNotEmpty()) {
|
|
||||||
val packageName = plugin.javaClass.`package`.name
|
|
||||||
val labelPanel = JPanel(BorderLayout())
|
|
||||||
labelPanel.maximumSize = Dimension(Int.MAX_VALUE, 30) // Adjust height to be minimal
|
|
||||||
labelPanel.background = VIEW_BACKGROUND_COLOR // Ensure it matches the overall background
|
|
||||||
labelPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
|
||||||
|
|
||||||
val label = JLabel("$packageName", SwingConstants.CENTER)
|
|
||||||
label.foreground = primaryColor
|
label.foreground = primaryColor
|
||||||
label.font = Font("Arial", Font.BOLD, 14)
|
noExposedPanel.add(label, BorderLayout.NORTH)
|
||||||
labelPanel.add(label, BorderLayout.CENTER)
|
|
||||||
reflectiveEditorView.add(labelPanel)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (field in exposedFields) {
|
val pluginsList = JList(loadedPlugins.toTypedArray())
|
||||||
field.isAccessible = true
|
pluginsList.background = WIDGET_COLOR
|
||||||
|
pluginsList.foreground = secondaryColor
|
||||||
|
pluginsList.font = Font("RuneScape Small", Font.PLAIN, 16)
|
||||||
|
|
||||||
val fieldPanel = JPanel()
|
// Wrap the JList in a JScrollPane with a fixed height
|
||||||
fieldPanel.layout = GridBagLayout()
|
val maxScrollPaneHeight = 200
|
||||||
fieldPanel.background = WIDGET_COLOR // Match the background for minimal borders
|
val scrollPane = JScrollPane(pluginsList).apply {
|
||||||
fieldPanel.foreground = secondaryColor
|
verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
|
||||||
fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0) // No visible border, just spacing
|
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||||
fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40)
|
|
||||||
|
|
||||||
val gbc = GridBagConstraints()
|
|
||||||
gbc.insets = Insets(0, 5, 0, 5) // Less padding, more minimal spacing
|
|
||||||
|
|
||||||
val label = JLabel(field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).capitalize())
|
|
||||||
label.foreground = secondaryColor
|
|
||||||
gbc.gridx = 0
|
|
||||||
gbc.gridy = 0
|
|
||||||
gbc.weightx = 0.0
|
|
||||||
gbc.anchor = GridBagConstraints.WEST
|
|
||||||
fieldPanel.add(label, gbc)
|
|
||||||
|
|
||||||
val textField = JTextField(field.get(plugin)?.toString() ?: "")
|
|
||||||
textField.background = VIEW_BACKGROUND_COLOR
|
|
||||||
textField.foreground = secondaryColor
|
|
||||||
textField.border = BorderFactory.createLineBorder(WIDGET_COLOR, 1) // Subtle border
|
|
||||||
gbc.gridx = 1
|
|
||||||
gbc.gridy = 0
|
|
||||||
gbc.weightx = 1.0
|
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL
|
|
||||||
fieldPanel.add(textField, gbc)
|
|
||||||
|
|
||||||
val applyButton = JButton("Apply")
|
|
||||||
applyButton.background = primaryColor
|
|
||||||
applyButton.foreground = VIEW_BACKGROUND_COLOR
|
|
||||||
applyButton.border = BorderFactory.createLineBorder(primaryColor, 1)
|
|
||||||
gbc.gridx = 2
|
|
||||||
gbc.gridy = 0
|
|
||||||
gbc.weightx = 0.0
|
|
||||||
gbc.fill = GridBagConstraints.NONE
|
|
||||||
applyButton.addActionListener {
|
|
||||||
try {
|
|
||||||
val newValue = convertValue(field.type, field.genericType, textField.text)
|
|
||||||
fieldNotifier.setFieldValue(field, newValue)
|
|
||||||
JOptionPane.showMessageDialog(
|
|
||||||
null,
|
|
||||||
"${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)} updated successfully!"
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
JOptionPane.showMessageDialog(
|
|
||||||
null,
|
|
||||||
"Failed to update ${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)}: ${e.message}",
|
|
||||||
"Error",
|
|
||||||
JOptionPane.ERROR_MESSAGE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldPanel.add(applyButton, gbc)
|
// Create a wrapper panel with BoxLayout to constrain the scroll pane
|
||||||
reflectiveEditorView.add(fieldPanel)
|
val scrollPaneWrapper = JPanel().apply {
|
||||||
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
|
add(scrollPane)
|
||||||
|
}
|
||||||
|
|
||||||
var previousValue = field.get(plugin)?.toString()
|
noExposedPanel.add(scrollPaneWrapper, BorderLayout.CENTER)
|
||||||
val timer = Timer()
|
|
||||||
timer.schedule(object : TimerTask() {
|
// Center the panel within the reflectiveEditorView
|
||||||
override fun run() {
|
val centeredPanel = JPanel().apply {
|
||||||
val currentValue = field.get(plugin)?.toString()
|
preferredSize = Dimension(240, maxScrollPaneHeight)
|
||||||
if (currentValue != previousValue) {
|
maximumSize = preferredSize
|
||||||
previousValue = currentValue
|
minimumSize = preferredSize
|
||||||
SwingUtilities.invokeLater {
|
}
|
||||||
fieldNotifier.notifyFieldChange(field, currentValue)
|
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) // Poll every 1000 milliseconds (1 second)
|
||||||
}, 0, 1000)
|
}
|
||||||
|
|
||||||
fieldNotifier.addObserver(object : KondoKitUtils.FieldObserver {
|
if (exposedFields.isNotEmpty()) {
|
||||||
override fun onFieldChange(field: Field, newValue: Any?) {
|
reflectiveEditorView.add(Box.createVerticalStrut(5))
|
||||||
if (field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).equals(label.text, ignoreCase = true)) {
|
}
|
||||||
textField.text = newValue?.toString() ?: ""
|
|
||||||
textField.revalidate()
|
|
||||||
textField.repaint()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if (exposedFields.isNotEmpty()) {
|
else {
|
||||||
reflectiveEditorView.add(Box.createVerticalStrut(10))
|
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.GlSprite
|
||||||
import rt4.SoftwareIndexedSprite
|
import rt4.SoftwareIndexedSprite
|
||||||
import rt4.SoftwareSprite
|
import rt4.SoftwareSprite
|
||||||
|
import java.awt.Color
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
|
||||||
|
// Define interfaces for common sprite types
|
||||||
|
interface BaseSprite {
|
||||||
|
val width: Int
|
||||||
|
val height: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IndexedSprite : BaseSprite {
|
||||||
|
val pixels: ByteArray
|
||||||
|
val palette: IntArray
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NonIndexedSprite : BaseSprite {
|
||||||
|
val pixels: IntArray?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapter functions for existing sprite types
|
||||||
|
fun adaptSoftwareSprite(sprite: SoftwareSprite): NonIndexedSprite {
|
||||||
|
return object : NonIndexedSprite {
|
||||||
|
override val width: Int = sprite.width
|
||||||
|
override val height: Int = sprite.height
|
||||||
|
override val pixels: IntArray? = sprite.pixels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun adaptSoftwareIndexedSprite(sprite: SoftwareIndexedSprite): IndexedSprite {
|
||||||
|
return object : IndexedSprite {
|
||||||
|
override val width: Int = sprite.width
|
||||||
|
override val height: Int = sprite.height
|
||||||
|
override val pixels: ByteArray = sprite.pixels
|
||||||
|
override val palette: IntArray = sprite.pallet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun adaptGlSprite(sprite: GlSprite): NonIndexedSprite {
|
||||||
|
return object : NonIndexedSprite {
|
||||||
|
override val width: Int = sprite.width
|
||||||
|
override val height: Int = sprite.height
|
||||||
|
override val pixels: IntArray? = sprite.pixels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun adaptGlIndexedSprite(sprite: GlIndexedSprite): IndexedSprite {
|
||||||
|
return object : IndexedSprite {
|
||||||
|
override val width: Int = sprite.width
|
||||||
|
override val height: Int = sprite.height
|
||||||
|
override val pixels: ByteArray = sprite.pixels
|
||||||
|
override val palette: IntArray = sprite.pallet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object SpriteToBufferedImage {
|
object SpriteToBufferedImage {
|
||||||
/**
|
/**
|
||||||
* Converts a SoftwareSprite back into a BufferedImage.
|
* Converts a BaseSprite into a BufferedImage.
|
||||||
|
*
|
||||||
|
* Handles both indexed and non-indexed sprites, with optional tinting and grayscale.
|
||||||
*
|
*
|
||||||
* @param sprite The sprite to be converted.
|
* @param sprite The sprite to be converted.
|
||||||
|
* @param tint An optional Color to tint the image.
|
||||||
|
* @param grayscale If true, converts the image to grayscale.
|
||||||
|
* @param brightnessBoost A multiplier to boost the brightness of the image.
|
||||||
* @return The BufferedImage created from the sprite.
|
* @return The BufferedImage created from the sprite.
|
||||||
*/
|
*/
|
||||||
fun convertToBufferedImage(sprite: SoftwareSprite): BufferedImage {
|
private fun convertToBufferedImage(
|
||||||
|
sprite: BaseSprite,
|
||||||
|
tint: Color? = null,
|
||||||
|
grayscale: Boolean = false,
|
||||||
|
brightnessBoost: Float = 1.0f
|
||||||
|
): BufferedImage {
|
||||||
val width = sprite.width
|
val width = sprite.width
|
||||||
val height = sprite.height
|
val height = sprite.height
|
||||||
val pixels = sprite.pixels
|
|
||||||
|
|
||||||
// Create a BufferedImage with ARGB color model
|
|
||||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
||||||
|
|
||||||
// Manually set pixels and print the pixel data
|
when (sprite) {
|
||||||
for (y in 0 until height) {
|
is IndexedSprite -> {
|
||||||
for (x in 0 until width) {
|
val pixels = sprite.pixels
|
||||||
val pixel = pixels[y * width + x]
|
val palette = sprite.palette
|
||||||
image.setRGB(x, y, pixel)
|
|
||||||
|
// 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
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertToBufferedImage(sprite: SoftwareIndexedSprite): BufferedImage {
|
/**
|
||||||
val width = sprite.width
|
* Applies a tint to a given color using the tint's alpha value to control the intensity.
|
||||||
val height = sprite.height
|
*
|
||||||
val pixels = sprite.pixels // byte[]
|
* @param original The original color.
|
||||||
val palette = sprite.pallet
|
* @param tint The tint color to be applied.
|
||||||
|
* @param brightnessBoost A multiplier to boost the brightness of the image.
|
||||||
// Create a BufferedImage with ARGB color model
|
* @return The tinted color.
|
||||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
*/
|
||||||
|
private fun applyTint(original: Color, tint: Color, brightnessBoost: Float): Color {
|
||||||
// Manually set pixels using the palette
|
val boostedColor = applyBrightness(original, brightnessBoost)
|
||||||
for (y in 0 until height) {
|
val r = (boostedColor.red * tint.red / 255).coerceIn(0, 255)
|
||||||
for (x in 0 until width) {
|
val g = (boostedColor.green * tint.green / 255).coerceIn(0, 255)
|
||||||
// Get the index from the sprite's pixel array
|
val b = (boostedColor.blue * tint.blue / 255).coerceIn(0, 255)
|
||||||
val index = pixels[y * width + x].toInt() and 0xFF
|
return Color(r, g, b, boostedColor.alpha)
|
||||||
// Map the index to a color in the palette
|
|
||||||
val color = palette[index]
|
|
||||||
// Set the ARGB color in the BufferedImage
|
|
||||||
image.setRGB(x, y, color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return image
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertToBufferedImage(sprite: GlIndexedSprite): BufferedImage {
|
/**
|
||||||
val width = sprite.width
|
* Boosts the brightness of a given color.
|
||||||
val height = sprite.height
|
*
|
||||||
val pixels = sprite.pixels // byte[]
|
* @param original The original color.
|
||||||
val palette = sprite.pallet
|
* @param factor The multiplier to boost the brightness.
|
||||||
|
* @return The color with boosted brightness.
|
||||||
// Create a BufferedImage with ARGB color model
|
*/
|
||||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
private fun applyBrightness(original: Color, factor: Float): Color {
|
||||||
|
val r = (original.red * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||||
// Manually set pixels using the palette
|
val g = (original.green * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||||
for (y in 0 until height) {
|
val b = (original.blue * factor).coerceIn(0.0f, 255.0f).toInt()
|
||||||
for (x in 0 until width) {
|
return Color(r, g, b, original.alpha)
|
||||||
// Get the index from the sprite's pixel array
|
|
||||||
val index = pixels[y * width + x].toInt() and 0xFF
|
|
||||||
// Map the index to a color in the palette
|
|
||||||
val color = palette[index]
|
|
||||||
// Set the ARGB color in the BufferedImage
|
|
||||||
image.setRGB(x, y, color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return image
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
fun convertToBufferedImage(sprite: GlSprite): BufferedImage {
|
* Converts a color to grayscale and applies a brightness boost.
|
||||||
val width = sprite.width
|
*
|
||||||
val height = sprite.height
|
* @param original The original color.
|
||||||
val pixels = sprite.pixels
|
* @param brightnessBoost A multiplier to boost the brightness.
|
||||||
|
* @return The grayscale version of the color with boosted brightness.
|
||||||
// Create a BufferedImage with ARGB color model
|
*/
|
||||||
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
|
private fun applyGrayscale(original: Color, brightnessBoost: Float): Color {
|
||||||
|
// Calculate the grayscale value using the luminosity method
|
||||||
if(pixels == null) {
|
val grayValue = (0.3 * original.red + 0.59 * original.green + 0.11 * original.blue).toInt()
|
||||||
return image
|
val boostedGray = (grayValue * brightnessBoost).coerceIn(0.0f, 255.0f).toInt()
|
||||||
}
|
return Color(boostedGray, boostedGray, boostedGray, original.alpha)
|
||||||
|
|
||||||
// Manually set pixels and print the pixel data
|
|
||||||
for (y in 0 until height) {
|
|
||||||
for (x in 0 until width) {
|
|
||||||
val pixel = pixels[y * width + x]
|
|
||||||
image.setRGB(x, y, pixel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return image
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBufferedImageFromSprite(sprite: Any?) : BufferedImage {
|
/**
|
||||||
return when(sprite){
|
* Converts an unknown sprite object into a BufferedImage if it matches a known sprite type.
|
||||||
is GlSprite -> convertToBufferedImage(sprite)
|
*
|
||||||
is SoftwareSprite -> convertToBufferedImage(sprite)
|
* @param sprite The sprite object to be converted.
|
||||||
is SoftwareIndexedSprite -> convertToBufferedImage(sprite)
|
* @param tint An optional Color to tint the image.
|
||||||
is GlIndexedSprite -> convertToBufferedImage(sprite)
|
* @param grayscale If true, converts the image to grayscale.
|
||||||
else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
|
* @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 {
|
object XPTable {
|
||||||
|
|
||||||
const val MAX_LEVEL = 99
|
private const val MAX_LEVEL = 99
|
||||||
const val INVALID_LEVEL = -1
|
private const val INVALID_LEVEL = -1
|
||||||
const val SKILLS_XP_TABLE = 716
|
private const val SKILLS_XP_TABLE = 716
|
||||||
|
|
||||||
private var xpTable: MutableList<Int> = mutableListOf()
|
private var xpTable: MutableList<Int> = mutableListOf()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,43 @@
|
||||||
package KondoKit
|
package KondoKit
|
||||||
|
|
||||||
|
import KondoKit.Helpers.addMouseListenerToAll
|
||||||
import KondoKit.Helpers.formatHtmlLabelText
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.formatNumber
|
import KondoKit.Helpers.formatNumber
|
||||||
import KondoKit.Helpers.getProgressBarColor
|
import KondoKit.Helpers.getProgressBarColor
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.plugin.Companion.IMAGE_SIZE
|
import KondoKit.plugin.Companion.IMAGE_SIZE
|
||||||
|
import KondoKit.plugin.Companion.LVL_ICON
|
||||||
|
import KondoKit.plugin.Companion.POPUP_BACKGROUND
|
||||||
|
import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
||||||
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
|
||||||
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_COLOR
|
import KondoKit.plugin.Companion.WIDGET_COLOR
|
||||||
import KondoKit.plugin.Companion.WIDGET_SIZE
|
import KondoKit.plugin.Companion.WIDGET_SIZE
|
||||||
import KondoKit.plugin.Companion.kondoExposed_playerXPMultiplier
|
import KondoKit.plugin.Companion.playerXPMultiplier
|
||||||
import KondoKit.plugin.Companion.primaryColor
|
import KondoKit.plugin.Companion.primaryColor
|
||||||
import KondoKit.plugin.Companion.secondaryColor
|
import KondoKit.plugin.Companion.secondaryColor
|
||||||
import KondoKit.plugin.StateManager.totalXPWidget
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import javax.swing.Box
|
import javax.swing.*
|
||||||
import javax.swing.BoxLayout
|
|
||||||
import javax.swing.JLabel
|
|
||||||
import javax.swing.JPanel
|
|
||||||
|
|
||||||
object XPTrackerView {
|
object XPTrackerView {
|
||||||
|
|
||||||
private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4)
|
private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4)
|
||||||
|
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
||||||
|
var totalXPWidget: XPWidget? = null
|
||||||
|
val initialXP: MutableMap<Int, Int> = HashMap()
|
||||||
|
var xpTrackerView: JPanel? = null
|
||||||
|
const val VIEW_NAME = "XP_TRACKER_VIEW"
|
||||||
|
|
||||||
|
|
||||||
val npcHitpointsMap: Map<Int, Int> = try {
|
val npcHitpointsMap: Map<Int, Int> = try {
|
||||||
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/npc_hitpoints_map.json"), StandardCharsets.UTF_8))
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
val json = lines.joinToString("\n")
|
val json = lines.joinToString("\n")
|
||||||
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
||||||
|
|
@ -74,8 +82,8 @@ object XPTrackerView {
|
||||||
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) {
|
||||||
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId]
|
||||||
val xpPerKill = when (xpWidget.skillId) {
|
val xpPerKill = when (xpWidget.skillId) {
|
||||||
3 -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
3 -> playerXPMultiplier * (npcHP ?: 1) // Hitpoints
|
||||||
else -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
else -> playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills
|
||||||
}
|
}
|
||||||
val remainingKills = xpLeft / xpPerKill
|
val remainingKills = xpLeft / xpPerKill
|
||||||
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor)
|
||||||
|
|
@ -92,24 +100,61 @@ object XPTrackerView {
|
||||||
xpWidget.xpGainedLabel.text = formatHtmlLabelText("XP Gained: ", primaryColor, formattedXp, secondaryColor)
|
xpWidget.xpGainedLabel.text = formatHtmlLabelText("XP Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||||
|
|
||||||
// Update the progress bar with current level, progress, and next level
|
// Update the progress bar with current level, progress, and next level
|
||||||
xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, focusedView == VIEW_NAME)
|
||||||
|
|
||||||
xpWidget.previousXp = xp
|
xpWidget.previousXp = xp
|
||||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if (focusedView == VIEW_NAME)
|
||||||
xpWidget.panel.repaint()
|
xpWidget.container.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) {
|
||||||
val totalXPWidget = plugin.StateManager.totalXPWidget ?: return
|
val totalXPWidget = totalXPWidget ?: return
|
||||||
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
|
||||||
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
|
||||||
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
|
||||||
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if (focusedView == VIEW_NAME)
|
||||||
totalXPWidget.panel.repaint()
|
totalXPWidget.container.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun resetXPTracker(xpTrackerView : JPanel){
|
||||||
|
|
||||||
|
// Redo logic here
|
||||||
|
xpTrackerView.removeAll()
|
||||||
|
val popupMenu = createResetMenu()
|
||||||
|
|
||||||
|
// Create a custom MouseListener
|
||||||
|
val rightClickListener = object : MouseAdapter() {
|
||||||
|
override fun mousePressed(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(e: MouseEvent) {
|
||||||
|
if (e.isPopupTrigger) {
|
||||||
|
popupMenu.show(e.component, e.x, e.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the XP widget
|
||||||
|
totalXPWidget = createTotalXPWidget()
|
||||||
|
val wrapped = wrappedWidget(totalXPWidget!!.container)
|
||||||
|
addMouseListenerToAll(wrapped,rightClickListener)
|
||||||
|
wrapped.addMouseListener(rightClickListener)
|
||||||
|
xpTrackerView.add(Box.createVerticalStrut(5))
|
||||||
|
xpTrackerView.add(wrapped)
|
||||||
|
xpTrackerView.add(Box.createVerticalStrut(5))
|
||||||
|
|
||||||
|
initialXP.clear()
|
||||||
|
xpWidgets.clear()
|
||||||
|
|
||||||
|
xpTrackerView.revalidate()
|
||||||
|
if (focusedView == VIEW_NAME)
|
||||||
|
xpTrackerView.repaint()
|
||||||
|
}
|
||||||
|
|
||||||
fun createTotalXPWidget(): XPWidget {
|
fun createTotalXPWidget(): XPWidget {
|
||||||
val widgetPanel = Panel().apply {
|
val widgetPanel = Panel().apply {
|
||||||
|
|
@ -120,43 +165,39 @@ object XPTrackerView {
|
||||||
minimumSize = TOTAL_XP_WIDGET_SIZE
|
minimumSize = TOTAL_XP_WIDGET_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(898))
|
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(LVL_ICON))
|
||||||
|
|
||||||
|
|
||||||
val imageContainer = Panel(FlowLayout()).apply {
|
val imageContainer = Panel(FlowLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
preferredSize = IMAGE_SIZE
|
maximumSize = preferredSize
|
||||||
maximumSize = IMAGE_SIZE
|
minimumSize = preferredSize
|
||||||
minimumSize = IMAGE_SIZE
|
size = preferredSize
|
||||||
size = IMAGE_SIZE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferedImageSprite.let { image ->
|
bufferedImageSprite.let { image ->
|
||||||
val imageCanvas = ImageCanvas(image).apply {
|
val imageCanvas = ImageCanvas(image).apply {
|
||||||
background = WIDGET_COLOR
|
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
preferredSize = Dimension(image.width, image.height)
|
maximumSize = preferredSize
|
||||||
maximumSize = Dimension(image.width, image.height)
|
minimumSize = preferredSize
|
||||||
minimumSize = Dimension(image.width, image.height)
|
size = preferredSize
|
||||||
size = Dimension(image.width, image.height)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imageContainer.add(imageCanvas)
|
imageContainer.add(imageCanvas)
|
||||||
imageContainer.size = Dimension(image.width, image.height)
|
imageContainer.size = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
||||||
|
|
||||||
imageContainer.revalidate()
|
imageContainer.revalidate()
|
||||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(focusedView == VIEW_NAME)
|
||||||
imageContainer.repaint()
|
imageContainer.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
val textPanel = Panel().apply {
|
val textPanel = Panel().apply {
|
||||||
layout = GridLayout(2, 1, 5, 5)
|
layout = GridLayout(2, 1, 5, 0)
|
||||||
background = WIDGET_COLOR
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val font = Font("Arial", Font.PLAIN, 11)
|
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
val xpGainedLabel = JLabel(
|
val xpGainedLabel = JLabel(
|
||||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
formatHtmlLabelText("Gained: ", primaryColor, "0", secondaryColor)
|
||||||
).apply {
|
).apply {
|
||||||
this.horizontalAlignment = JLabel.LEFT
|
this.horizontalAlignment = JLabel.LEFT
|
||||||
this.font = font
|
this.font = font
|
||||||
|
|
@ -177,14 +218,14 @@ object XPTrackerView {
|
||||||
|
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = -1,
|
skillId = -1,
|
||||||
panel = widgetPanel,
|
container = widgetPanel,
|
||||||
xpGainedLabel = xpGainedLabel,
|
xpGainedLabel = xpGainedLabel,
|
||||||
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply {
|
||||||
this.horizontalAlignment = JLabel.LEFT
|
this.horizontalAlignment = JLabel.LEFT
|
||||||
this.font = font
|
this.font = font
|
||||||
},
|
},
|
||||||
xpPerHourLabel = xpPerHourLabel,
|
xpPerHourLabel = xpPerHourLabel,
|
||||||
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
|
progressBar = ProgressBar(0.0, Color.BLACK), // Unused
|
||||||
totalXpGained = 0,
|
totalXpGained = 0,
|
||||||
startTime = System.currentTimeMillis(),
|
startTime = System.currentTimeMillis(),
|
||||||
previousXp = 0,
|
previousXp = 0,
|
||||||
|
|
@ -193,19 +234,66 @@ object XPTrackerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createXPTrackerView(): JPanel? {
|
fun createXPTrackerView(){
|
||||||
val widgetViewPanel = JPanel()
|
val widgetViewPanel = JPanel().apply {
|
||||||
widgetViewPanel.layout = BoxLayout(widgetViewPanel, BoxLayout.Y_AXIS)
|
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||||
widgetViewPanel.background = VIEW_BACKGROUND_COLOR
|
background = VIEW_BACKGROUND_COLOR
|
||||||
widgetViewPanel.add(Box.createVerticalStrut(5))
|
}
|
||||||
|
|
||||||
|
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()
|
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))
|
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 {
|
fun createXPWidget(skillId: Int, previousXp: Int): XPWidget {
|
||||||
val widgetPanel = Panel().apply {
|
val widgetPanel = Panel().apply {
|
||||||
layout = BorderLayout(5, 5)
|
layout = BorderLayout(5, 5)
|
||||||
|
|
@ -237,19 +325,19 @@ object XPTrackerView {
|
||||||
imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size
|
imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size
|
||||||
|
|
||||||
imageContainer.revalidate()
|
imageContainer.revalidate()
|
||||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(focusedView == VIEW_NAME)
|
||||||
imageContainer.repaint()
|
imageContainer.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
val textPanel = Panel().apply {
|
val textPanel = Panel().apply {
|
||||||
layout = GridLayout(2, 2, 5, 5)
|
layout = GridLayout(2, 2, 5, 0)
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
val font = Font("Arial", Font.PLAIN, 11)
|
val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
|
||||||
val xpGainedLabel = JLabel(
|
val xpGainedLabel = JLabel(
|
||||||
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor)
|
||||||
).apply {
|
).apply {
|
||||||
this.horizontalAlignment = JLabel.LEFT
|
this.horizontalAlignment = JLabel.LEFT
|
||||||
this.font = font
|
this.font = font
|
||||||
|
|
@ -278,7 +366,7 @@ object XPTrackerView {
|
||||||
|
|
||||||
val levelPanel = Panel().apply {
|
val levelPanel = Panel().apply {
|
||||||
layout = BorderLayout(5, 0)
|
layout = BorderLayout(5, 0)
|
||||||
background = Color(43, 43, 43)
|
background = WIDGET_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply {
|
val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply {
|
||||||
|
|
@ -297,12 +385,12 @@ object XPTrackerView {
|
||||||
widgetPanel.add(levelPanel, BorderLayout.SOUTH)
|
widgetPanel.add(levelPanel, BorderLayout.SOUTH)
|
||||||
|
|
||||||
widgetPanel.revalidate()
|
widgetPanel.revalidate()
|
||||||
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
|
if(focusedView == VIEW_NAME)
|
||||||
widgetPanel.repaint()
|
widgetPanel.repaint()
|
||||||
|
|
||||||
return XPWidget(
|
return XPWidget(
|
||||||
skillId = skillId,
|
skillId = skillId,
|
||||||
panel = widgetPanel,
|
container = widgetPanel,
|
||||||
xpGainedLabel = xpGainedLabel,
|
xpGainedLabel = xpGainedLabel,
|
||||||
xpLeftLabel = xpLeftLabel,
|
xpLeftLabel = xpLeftLabel,
|
||||||
xpPerHourLabel = xpPerHourLabel,
|
xpPerHourLabel = xpPerHourLabel,
|
||||||
|
|
@ -314,18 +402,18 @@ object XPTrackerView {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wrappedWidget(component: Component, padding: Int = 7): Panel {
|
fun wrappedWidget(component: Component, padding: Int = 7): Container {
|
||||||
val outerPanelSize = Dimension(
|
val outerPanelSize = Dimension(
|
||||||
component.preferredSize.width + 2 * padding,
|
component.preferredSize.width + 2 * padding,
|
||||||
component.preferredSize.height + 2 * padding
|
component.preferredSize.height + 2 * padding
|
||||||
)
|
)
|
||||||
val outerPanel = Panel(GridBagLayout()).apply {
|
val outerPanel = JPanel(GridBagLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = outerPanelSize
|
preferredSize = outerPanelSize
|
||||||
maximumSize = outerPanelSize
|
maximumSize = outerPanelSize
|
||||||
minimumSize = outerPanelSize
|
minimumSize = outerPanelSize
|
||||||
}
|
}
|
||||||
val innerPanel = Panel(BorderLayout()).apply {
|
val innerPanel = JPanel(BorderLayout()).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = component.preferredSize
|
preferredSize = component.preferredSize
|
||||||
maximumSize = component.preferredSize
|
maximumSize = component.preferredSize
|
||||||
|
|
@ -343,14 +431,14 @@ object XPTrackerView {
|
||||||
|
|
||||||
|
|
||||||
data class XPWidget(
|
data class XPWidget(
|
||||||
val panel: Panel,
|
val container: Container,
|
||||||
val skillId: Int,
|
val skillId: Int,
|
||||||
val xpGainedLabel: JLabel,
|
val xpGainedLabel: JLabel,
|
||||||
val xpLeftLabel: JLabel,
|
val xpLeftLabel: JLabel,
|
||||||
val xpPerHourLabel: JLabel,
|
val xpPerHourLabel: JLabel,
|
||||||
val actionsRemainingLabel: JLabel,
|
val actionsRemainingLabel: JLabel,
|
||||||
val progressBar: ProgressBar,
|
val progressBar: ProgressBar,
|
||||||
var totalXpGained: Int = 0,
|
var totalXpGained: Int = 0,
|
||||||
var startTime: Long = System.currentTimeMillis(),
|
var startTime: Long = System.currentTimeMillis(),
|
||||||
var previousXp: Int = 0
|
var previousXp: Int = 0
|
||||||
)
|
)
|
||||||
|
|
@ -4,214 +4,201 @@ import KondoKit.Constants.COMBAT_LVL_SPRITE
|
||||||
import KondoKit.Helpers.formatHtmlLabelText
|
import KondoKit.Helpers.formatHtmlLabelText
|
||||||
import KondoKit.Helpers.formatNumber
|
import KondoKit.Helpers.formatNumber
|
||||||
import KondoKit.Helpers.getSpriteId
|
import KondoKit.Helpers.getSpriteId
|
||||||
|
import KondoKit.Helpers.showAlert
|
||||||
import KondoKit.HiscoresView.createHiscoreSearchView
|
import KondoKit.HiscoresView.createHiscoreSearchView
|
||||||
|
import KondoKit.HiscoresView.hiScoreView
|
||||||
import KondoKit.LootTrackerView.BAG_ICON
|
import KondoKit.LootTrackerView.BAG_ICON
|
||||||
import KondoKit.LootTrackerView.createLootTrackerView
|
import KondoKit.LootTrackerView.createLootTrackerView
|
||||||
|
import KondoKit.LootTrackerView.lootTrackerView
|
||||||
import KondoKit.LootTrackerView.npcDeathSnapshots
|
import KondoKit.LootTrackerView.npcDeathSnapshots
|
||||||
import KondoKit.LootTrackerView.onPostClientTick
|
import KondoKit.LootTrackerView.onPostClientTick
|
||||||
import KondoKit.LootTrackerView.takeGroundSnapshot
|
import KondoKit.LootTrackerView.takeGroundSnapshot
|
||||||
import KondoKit.ReflectiveEditorView.addPlugins
|
import KondoKit.ReflectiveEditorView.addPlugins
|
||||||
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
import KondoKit.ReflectiveEditorView.createReflectiveEditorView
|
||||||
|
import KondoKit.ReflectiveEditorView.reflectiveEditorView
|
||||||
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
|
||||||
import KondoKit.XPTrackerView.createTotalXPWidget
|
import KondoKit.Themes.Theme
|
||||||
|
import KondoKit.Themes.ThemeType
|
||||||
|
import KondoKit.Themes.getTheme
|
||||||
import KondoKit.XPTrackerView.createXPTrackerView
|
import KondoKit.XPTrackerView.createXPTrackerView
|
||||||
import KondoKit.XPTrackerView.createXPWidget
|
import KondoKit.XPTrackerView.createXPWidget
|
||||||
|
import KondoKit.XPTrackerView.initialXP
|
||||||
|
import KondoKit.XPTrackerView.resetXPTracker
|
||||||
|
import KondoKit.XPTrackerView.totalXPWidget
|
||||||
import KondoKit.XPTrackerView.updateWidget
|
import KondoKit.XPTrackerView.updateWidget
|
||||||
import KondoKit.XPTrackerView.wrappedWidget
|
import KondoKit.XPTrackerView.wrappedWidget
|
||||||
import KondoKit.plugin.StateManager.initialXP
|
import KondoKit.XPTrackerView.xpTrackerView
|
||||||
import KondoKit.plugin.StateManager.totalXPWidget
|
import KondoKit.XPTrackerView.xpWidgets
|
||||||
import KondoKit.plugin.StateManager.xpWidgets
|
import KondoKit.plugin.StateManager.focusedView
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.api.*
|
import plugin.api.*
|
||||||
import plugin.api.API.*
|
import plugin.api.API.*
|
||||||
import plugin.api.FontColor.fromColor
|
import plugin.api.FontColor.fromColor
|
||||||
import rt4.GameShell
|
import rt4.*
|
||||||
|
import rt4.DisplayMode
|
||||||
|
import rt4.GameShell.canvas
|
||||||
import rt4.GameShell.frame
|
import rt4.GameShell.frame
|
||||||
import rt4.GlRenderer
|
|
||||||
import rt4.InterfaceList
|
|
||||||
import rt4.Player
|
|
||||||
import rt4.client.js5Archive8
|
import rt4.client.js5Archive8
|
||||||
import rt4.client.mainLoadState
|
import rt4.client.mainLoadState
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
|
import java.awt.Font
|
||||||
import java.awt.event.ActionListener
|
import java.awt.event.ActionListener
|
||||||
import java.awt.event.MouseAdapter
|
import java.awt.event.MouseAdapter
|
||||||
import java.awt.event.MouseEvent
|
import java.awt.event.MouseEvent
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FIELD)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class Exposed(val description: String = "")
|
||||||
|
|
||||||
class plugin : Plugin() {
|
class plugin : Plugin() {
|
||||||
companion object {
|
companion object {
|
||||||
val WIDGET_SIZE = Dimension(270, 55)
|
val WIDGET_SIZE = Dimension(220, 50)
|
||||||
val TOTAL_XP_WIDGET_SIZE = Dimension(270, 30)
|
val TOTAL_XP_WIDGET_SIZE = Dimension(220, 30)
|
||||||
val IMAGE_SIZE = Dimension(20, 20)
|
val IMAGE_SIZE = Dimension(25, 23)
|
||||||
val WIDGET_COLOR = Color(27, 27, 27)
|
|
||||||
val VIEW_BACKGROUND_COLOR = Color(37, 37, 37)
|
// Default Theme Colors
|
||||||
val primaryColor = Color(129, 129, 129) // Color for "XP Gained:"
|
var WIDGET_COLOR = Color(30, 30, 30)
|
||||||
val secondaryColor = Color(226, 226, 226) // Color for "0"
|
var TITLE_BAR_COLOR = Color(21, 21, 21)
|
||||||
var kondoExposed_useLiveGEPrices = true
|
var VIEW_BACKGROUND_COLOR = Color(40, 40, 40)
|
||||||
var kondoExposed_playerXPMultiplier = 5
|
var primaryColor = Color(165, 165, 165) // Color for "XP Gained:"
|
||||||
const val FIXED_WIDTH = 782
|
var secondaryColor = Color(255, 255, 255) // Color for "0"
|
||||||
const val SCROLLPANE_WIDTH = 340
|
var POPUP_BACKGROUND = Color(45, 45, 45)
|
||||||
|
var POPUP_FOREGROUND = Color(220, 220, 220)
|
||||||
|
var TOOLTIP_BACKGROUND = Color(50,50,50)
|
||||||
|
var SCROLL_BAR_COLOR = Color(64, 64, 64)
|
||||||
|
var PROGRESS_BAR_FILL = Color(61, 56, 49)
|
||||||
|
var NAV_TINT: Color? = null
|
||||||
|
var NAV_GREYSCALE = false
|
||||||
|
var BOOST = 1f
|
||||||
|
|
||||||
|
var appliedTheme = ThemeType.RUNELITE
|
||||||
|
|
||||||
|
@Exposed("Theme colors for KondoKit, requires a relaunch to apply.")
|
||||||
|
var theme = ThemeType.RUNELITE
|
||||||
|
|
||||||
|
@Exposed("Default: true, Use Local JSON or the prices from the Live/Stable server API")
|
||||||
|
var useLiveGEPrices = true
|
||||||
|
|
||||||
|
@Exposed("Used to calculate Combat Actions until next level.")
|
||||||
|
var playerXPMultiplier = 5
|
||||||
|
|
||||||
|
@Exposed("Start minimized/collapsed by default")
|
||||||
|
var launchMinimized = false
|
||||||
|
|
||||||
|
@Exposed("Default 16 on Windows, 0 Linux/macOS. If Kondo is not " +
|
||||||
|
"perfectly snapped to the edge of the game due to window chrome you can update this to fix it")
|
||||||
|
var uiOffset = 0
|
||||||
|
|
||||||
|
@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 WRENCH_ICON = 907
|
||||||
private const val LVL_ICON = 898
|
|
||||||
private const val LOOT_ICON = 777
|
private const val LOOT_ICON = 777
|
||||||
private const val MAG_SPRITE = 1423
|
private const val MAG_SPRITE = 1423
|
||||||
|
const val LVL_ICON = 898
|
||||||
private lateinit var cardLayout: CardLayout
|
private lateinit var cardLayout: CardLayout
|
||||||
private lateinit var mainContentPanel: Panel
|
private lateinit var mainContentPanel: JPanel
|
||||||
private var scrollPane: JScrollPane? = null
|
private var rightPanelWrapper: JScrollPane? = null
|
||||||
private var hiScoreView: JPanel? = null
|
|
||||||
private var reflectiveEditorView: JPanel? = null
|
|
||||||
private var lootTrackerView: JPanel? = null
|
|
||||||
private var xpTrackerView: JPanel? = null
|
|
||||||
private var accumulatedTime = 0L
|
private var accumulatedTime = 0L
|
||||||
private const val tickInterval = 600L
|
private var reloadInterfaces = false
|
||||||
|
private const val TICK_INTERVAL = 600L
|
||||||
private var pluginsReloaded = false
|
private var pluginsReloaded = false
|
||||||
private var loginScreen = 160;
|
private var loginScreen = 160
|
||||||
private var lastLogin = ""
|
private var lastLogin = ""
|
||||||
private var initialized = false;
|
private var initialized = false
|
||||||
|
private var lastClickTime = 0L
|
||||||
|
private var lastUIOffset = 0
|
||||||
|
private 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 {
|
override fun Init() {
|
||||||
// Check all skill sprites
|
// Disable Font AA
|
||||||
try{
|
System.setProperty("sun.java2d.opengl", "false")
|
||||||
for (i in 0 until 24) {
|
System.setProperty("awt.useSystemAAFontSettings", "off")
|
||||||
if(!js5Archive8.isFileReady(getSpriteId(i))){
|
System.setProperty("swing.aatext", "false")
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val otherIcons = arrayOf(LVL_ICON, MAG_SPRITE, LOOT_ICON, WRENCH_ICON, COMBAT_LVL_SPRITE, BAG_ICON);
|
|
||||||
for (icon in otherIcons) {
|
|
||||||
if(!js5Archive8.isFileReady(icon)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e : Exception){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun OnLogin() {
|
override fun OnLogin() {
|
||||||
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
|
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
|
||||||
// if we logged in with a new character
|
// if we logged in with a new character
|
||||||
// we need to reset the trackers
|
// we need to reset the trackers
|
||||||
xpTrackerView?.removeAll()
|
xpTrackerView?.let { resetXPTracker(it) }
|
||||||
totalXPWidget = createTotalXPWidget()
|
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
|
||||||
xpTrackerView?.add(wrappedWidget(totalXPWidget!!.panel))
|
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
|
||||||
initialXP.clear()
|
|
||||||
xpWidgets.clear()
|
|
||||||
|
|
||||||
xpTrackerView?.revalidate()
|
|
||||||
if (StateManager.focusedView == "XP_TRACKER_VIEW")
|
|
||||||
xpTrackerView?.repaint()
|
|
||||||
}
|
}
|
||||||
lastLogin = Player.usernameInput.toString()
|
lastLogin = Player.usernameInput.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
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>?) {
|
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
|
||||||
if (currentEntries != null) {
|
if (currentEntries != null) {
|
||||||
for ((index, entry) in currentEntries.withIndex()) {
|
for ((index, entry) in currentEntries.withIndex()) {
|
||||||
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
|
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
|
||||||
val input = entry.subject
|
val input = entry.subject
|
||||||
val username = input
|
// Trim spaces, clean up tags, and remove the level info
|
||||||
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "")
|
val cleanedInput = input
|
||||||
.replace(Regex("<img=\\d+>"), "")
|
.trim() // Remove any leading/trailing spaces
|
||||||
.split(" ") // Split by spaces
|
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "") // Remove color tags
|
||||||
.first() // Take the first part, which is the username
|
.replace(Regex("<img=\\d+>"), "") // Remove image tags
|
||||||
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(username.replace(" ","_")))
|
.replace(Regex("\\(level: \\d+\\)"), "") // Remove level text e.g. (level: 44)
|
||||||
|
.trim() // Trim again to remove extra spaces after removing level text
|
||||||
|
|
||||||
|
// Proceed with the full cleaned username
|
||||||
|
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(cleanedInput))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
override fun OnPluginsReloaded(): Boolean {
|
||||||
if (!initialized) return true
|
if (!initialized) return true
|
||||||
|
updateDisplaySettings()
|
||||||
UpdateDisplaySettings()
|
frame.remove(rightPanelWrapper)
|
||||||
|
|
||||||
frame.remove(scrollPane)
|
|
||||||
frame.layout = BorderLayout()
|
frame.layout = BorderLayout()
|
||||||
frame.add(scrollPane, BorderLayout.EAST)
|
rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) }
|
||||||
|
|
||||||
// Clear or regenerate the reflectiveEditorView
|
|
||||||
reflectiveEditorView?.removeAll()
|
|
||||||
reflectiveEditorView?.revalidate()
|
|
||||||
if(StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW")
|
|
||||||
reflectiveEditorView?.repaint()
|
|
||||||
|
|
||||||
frame.revalidate()
|
frame.revalidate()
|
||||||
frame.repaint()
|
|
||||||
pluginsReloaded = true
|
pluginsReloaded = true
|
||||||
|
reloadInterfaces = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
override fun OnXPUpdate(skillId: Int, xp: Int) {
|
||||||
if (!initialXP.containsKey(skillId)) {
|
if (!initialXP.containsKey(skillId)) {
|
||||||
initialXP[skillId] = xp
|
initialXP[skillId] = xp
|
||||||
return
|
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) {
|
xpTrackerView?.add(wrappedWidget(xpWidget.container))
|
||||||
updateWidget(xpWidget, xp)
|
xpTrackerView?.add(Box.createVerticalStrut(5))
|
||||||
} else {
|
|
||||||
val previousXp = initialXP[skillId] ?: xp
|
|
||||||
if (xp == initialXP[skillId]) return
|
|
||||||
|
|
||||||
xpWidget = createXPWidget(skillId, previousXp)
|
if(focusedView == XPTrackerView.VIEW_NAME) {
|
||||||
xpWidgets[skillId] = xpWidget
|
xpTrackerView?.revalidate()
|
||||||
|
xpTrackerView?.repaint()
|
||||||
|
}
|
||||||
|
|
||||||
xpTrackerView?.add(wrappedWidget(xpWidget.panel))
|
updateWidget(xpWidget, xp)
|
||||||
xpTrackerView?.add(Box.createVerticalStrut(5))
|
}
|
||||||
|
|
||||||
xpTrackerView?.revalidate()
|
|
||||||
if(StateManager.focusedView == "XP_TRACKER_VIEW")
|
|
||||||
xpTrackerView?.repaint()
|
|
||||||
|
|
||||||
updateWidget(xpWidget, xp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) {
|
override fun Draw(timeDelta: Long) {
|
||||||
|
|
@ -221,155 +208,498 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginsReloaded) {
|
if (pluginsReloaded) {
|
||||||
InterfaceList.method3712(true) // Gets the resize working correctly
|
|
||||||
reflectiveEditorView?.let { addPlugins(it) }
|
reflectiveEditorView?.let { addPlugins(it) }
|
||||||
pluginsReloaded = false
|
pluginsReloaded = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reloadInterfaces){
|
||||||
|
InterfaceList.method3712(true) // Gets the resize working correctly
|
||||||
|
reloadInterfaces = false
|
||||||
|
}
|
||||||
|
|
||||||
accumulatedTime += timeDelta
|
accumulatedTime += timeDelta
|
||||||
if (accumulatedTime >= tickInterval) {
|
if (accumulatedTime >= TICK_INTERVAL) {
|
||||||
lootTrackerView?.let { onPostClientTick(it) }
|
lootTrackerView?.let { onPostClientTick(it) }
|
||||||
accumulatedTime = 0L
|
accumulatedTime = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw synced actions (that require to be done between glBegin and glEnd)
|
||||||
|
if (drawActions.isNotEmpty()) {
|
||||||
|
synchronized(drawActions) {
|
||||||
|
val actionsCopy = drawActions.toList()
|
||||||
|
drawActions.clear()
|
||||||
|
for (action in actionsCopy) {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
// Init in the draw call so we know we are between glBegin and glEnd for HD
|
||||||
if(!initialized && mainLoadState >= loginScreen) {
|
if(!initialized && mainLoadState >= loginScreen) {
|
||||||
initKondoUI()
|
initKondoUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initKondoUI(){
|
override fun LateDraw(timeDelta: Long) {
|
||||||
DrawText(FontType.LARGE, fromColor(Color(16777215)), TextModifier.CENTER, "KondoKit Loading Sprites...", GameShell.canvasWidth/2, GameShell.canvasHeight/2)
|
if (!initialized) return
|
||||||
if(!allSpritesLoaded()) return;
|
if(GameShell.fullScreenFrame != null) {
|
||||||
val frame: Frame? = GameShell.frame
|
DisplayMode.setWindowMode(true, 0, FIXED_WIDTH, FIXED_HEIGHT)
|
||||||
if (frame != null) {
|
showAlert("Fullscreen is not supported by KondoKit. Disable the plugin first.",
|
||||||
kondoExposed_useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true
|
"Error",
|
||||||
kondoExposed_playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5
|
JOptionPane.INFORMATION_MESSAGE
|
||||||
cardLayout = CardLayout()
|
)
|
||||||
mainContentPanel = Panel(cardLayout)
|
return
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
if(!useScaledFixed) return
|
||||||
|
if(GetWindowMode() == WindowMode.FIXED){
|
||||||
|
moveAltCanvasToFront()
|
||||||
|
} else {
|
||||||
|
moveCanvasToFront()
|
||||||
|
}
|
||||||
|
altCanvas?.updateGameImage() // Update the game image as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Update() {
|
override fun Update() {
|
||||||
xpWidgets.values.forEach { xpWidget ->
|
|
||||||
|
val widgets = xpWidgets.values
|
||||||
|
val totalXP = totalXPWidget
|
||||||
|
|
||||||
|
widgets.forEach { xpWidget ->
|
||||||
val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0
|
val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||||
val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||||
val formattedXpPerHour = formatNumber(xpPerHour)
|
val formattedXpPerHour = formatNumber(xpPerHour)
|
||||||
xpWidget.xpPerHourLabel.text =
|
xpWidget.xpPerHourLabel.text =
|
||||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor)
|
||||||
xpWidget.panel.repaint()
|
xpWidget.container.repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
totalXPWidget?.let { totalXPWidget ->
|
totalXP?.let { totalXPWidget ->
|
||||||
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0
|
||||||
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0
|
||||||
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
val formattedTotalXpPerHour = formatNumber(totalXPPerHour)
|
||||||
totalXPWidget.xpPerHourLabel.text =
|
totalXPWidget.xpPerHourLabel.text =
|
||||||
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor)
|
||||||
totalXPWidget.panel.repaint()
|
totalXPWidget.container.repaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
|
||||||
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
val preDeathSnapshot = takeGroundSnapshot(Pair(x,z))
|
||||||
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun 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 {
|
private fun updateDisplaySettings() {
|
||||||
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId))
|
val mode = GetWindowMode()
|
||||||
val buttonSize = Dimension(42, 42)
|
val currentScrollPaneWidth = if (mainContentPanel.isVisible) NAVBAR_WIDTH + MAIN_CONTENT_WIDTH else NAVBAR_WIDTH
|
||||||
val imageSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
|
lastUIOffset = uiOffset
|
||||||
|
|
||||||
val actionListener = ActionListener {
|
if(mode != WindowMode.FIXED) {
|
||||||
cardLayout.show(mainContentPanel, viewName)
|
destroyAltCanvas()
|
||||||
StateManager.focusedView = viewName
|
} 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 {
|
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
preferredSize = imageSize
|
preferredSize = imageSize
|
||||||
maximumSize = imageSize
|
maximumSize = imageSize
|
||||||
minimumSize = imageSize
|
minimumSize = imageSize
|
||||||
addMouseListener(object : MouseAdapter() {
|
|
||||||
override fun mouseClicked(e: MouseEvent?) {
|
|
||||||
actionListener.actionPerformed(null)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val button = JButton().apply {
|
// Wrapping the ImageCanvas in another JPanel to prevent stretching
|
||||||
|
val imageCanvasWrapper = JPanel().apply {
|
||||||
|
layout = GridBagLayout() // Keeps the layout of the wrapped panel minimal
|
||||||
|
preferredSize = imageSize
|
||||||
|
maximumSize = imageSize
|
||||||
|
minimumSize = imageSize
|
||||||
|
isOpaque = false // No background for the wrapper
|
||||||
|
add(imageCanvas) // Adding ImageCanvas directly, layout won't stretch it
|
||||||
|
}
|
||||||
|
|
||||||
|
val panelButton = JPanel().apply {
|
||||||
layout = GridBagLayout()
|
layout = GridBagLayout()
|
||||||
preferredSize = buttonSize
|
preferredSize = buttonSize
|
||||||
maximumSize = buttonSize
|
maximumSize = buttonSize
|
||||||
minimumSize = buttonSize
|
minimumSize = buttonSize
|
||||||
background = WIDGET_COLOR
|
background = WIDGET_COLOR
|
||||||
isFocusPainted = false
|
isOpaque = true
|
||||||
isBorderPainted = false
|
|
||||||
|
|
||||||
val gbc = GridBagConstraints().apply {
|
val gbc = GridBagConstraints().apply {
|
||||||
anchor = GridBagConstraints.CENTER
|
anchor = GridBagConstraints.CENTER
|
||||||
|
fill = GridBagConstraints.NONE // Prevents stretching
|
||||||
}
|
}
|
||||||
|
|
||||||
add(imageCanvas, gbc)
|
add(imageCanvasWrapper, gbc)
|
||||||
addActionListener(actionListener)
|
|
||||||
|
// Hover and click behavior
|
||||||
|
val hoverListener = object : MouseAdapter() {
|
||||||
|
override fun mouseEntered(e: MouseEvent?) {
|
||||||
|
background = WIDGET_COLOR.darker()
|
||||||
|
imageCanvas.fillColor = WIDGET_COLOR.darker()
|
||||||
|
imageCanvas.repaint()
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
object StateManager {
|
||||||
val initialXP: MutableMap<Int, Int> = HashMap()
|
|
||||||
val xpWidgets: MutableMap<Int, XPWidget> = HashMap()
|
|
||||||
var totalXPWidget: XPWidget? = null
|
|
||||||
var focusedView: String = ""
|
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'
|
AUTHOR='downthecrop'
|
||||||
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.'
|
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.'
|
||||||
VERSION=1.1
|
VERSION=2.0
|
||||||
Binary file not shown.
|
|
@ -25,6 +25,7 @@ class plugin : Plugin() {
|
||||||
|
|
||||||
private var timeMode = TIME_MODE_INITIALIZATION
|
private var timeMode = TIME_MODE_INITIALIZATION
|
||||||
private var initTime: Long = 0
|
private var initTime: Long = 0
|
||||||
|
private var logoutFlag = true
|
||||||
private var displayMessageCounter = 0
|
private var displayMessageCounter = 0
|
||||||
|
|
||||||
private var component: Component? = null
|
private var component: Component? = null
|
||||||
|
|
@ -35,6 +36,20 @@ class plugin : Plugin() {
|
||||||
displayMessageCounter = 0
|
displayMessageCounter = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun OnPluginsReloaded(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun OnLogin() {
|
||||||
|
if(logoutFlag)
|
||||||
|
initTime = System.currentTimeMillis()
|
||||||
|
logoutFlag = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun OnLogout() {
|
||||||
|
logoutFlag = true
|
||||||
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) {
|
override fun Draw(timeDelta: Long) {
|
||||||
if (component == null)
|
if (component == null)
|
||||||
return
|
return
|
||||||
|
|
@ -134,6 +149,9 @@ class plugin : Plugin() {
|
||||||
API.InsertMiniMenuEntry("Disable Timer", "") {
|
API.InsertMiniMenuEntry("Disable Timer", "") {
|
||||||
timeMode = DEFAULT_TIME_MODE
|
timeMode = DEFAULT_TIME_MODE
|
||||||
}
|
}
|
||||||
|
API.InsertMiniMenuEntry("Reset Play Time", "") {
|
||||||
|
initTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='Woahscam, Ceikry'
|
AUTHOR='Woahscam, Ceikry'
|
||||||
DESCRIPTION='Displays the session time played, system time, or no time over the "Report Abuse" button.'
|
DESCRIPTION='Displays the session time played, system time, or no time over the "Report Abuse" button.'
|
||||||
VERSION=1.2
|
VERSION=1.1
|
||||||
|
|
@ -1,81 +1,89 @@
|
||||||
package ToggleResizableSD
|
package ToggleResizableSD
|
||||||
|
|
||||||
|
import KondoKit.Exposed
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.annotations.PluginMeta
|
|
||||||
import plugin.api.API
|
import plugin.api.API
|
||||||
import rt4.*
|
import plugin.api.API.StoreData
|
||||||
|
import rt4.DisplayMode
|
||||||
|
import rt4.GameShell
|
||||||
|
import rt4.InterfaceList
|
||||||
|
import rt4.client
|
||||||
import java.awt.event.KeyAdapter
|
import java.awt.event.KeyAdapter
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
|
|
||||||
class plugin : Plugin() {
|
class plugin : Plugin() {
|
||||||
var toggleResize = false
|
|
||||||
var wantHd = false //Setting wantHd to true hides the black screen on logout (when resize SD is enabled), by enabling HD on logout
|
@Exposed("Use Resizable SD")
|
||||||
|
var useResizable = false
|
||||||
|
|
||||||
|
@Exposed("Setting wantHd to true hides the black screen on logout (when resize SD is enabled), by enabling HD on logout ")
|
||||||
|
var wantHd = false
|
||||||
|
|
||||||
override fun Init() {
|
override fun Init() {
|
||||||
API.AddKeyboardListener(object : KeyAdapter() {
|
API.AddKeyboardListener(object : KeyAdapter() {
|
||||||
override fun keyPressed(e: KeyEvent) {
|
override fun keyPressed(e: KeyEvent) {
|
||||||
if (e.keyCode == KeyEvent.VK_F12) {
|
if (e.keyCode == KeyEvent.VK_F12) {
|
||||||
toggleResize = true
|
toggleResizableSd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!DisplayMode.resizableSD && API.GetData("use-resizable-sd") == true) {
|
|
||||||
toggleResize = true
|
useResizable = DisplayMode.resizableSD
|
||||||
|
if (API.GetData("use-resizable-sd") == true) {
|
||||||
|
useResizable = true
|
||||||
}
|
}
|
||||||
var osNameLowerCase: String = ""
|
|
||||||
var osName: String
|
var osNameLowerCase: String = System.getProperty("os.name").toLowerCase()
|
||||||
|
|
||||||
try {
|
|
||||||
osName = System.getProperty("os.name")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
osName = "Unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
osNameLowerCase = osName.toLowerCase()
|
|
||||||
if (!osNameLowerCase.startsWith("mac")) {
|
if (!osNameLowerCase.startsWith("mac")) {
|
||||||
wantHd = true
|
wantHd = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (API.GetData("want-hd") == false) {
|
if (API.GetData("want-hd") == false) {
|
||||||
wantHd = false
|
wantHd = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ProcessCommand(commandStr: String, args: Array<out String>?) {
|
override fun ProcessCommand(commandStr: String, args: Array<out String>?) {
|
||||||
when(commandStr.toLowerCase()) {
|
when (commandStr.toLowerCase()) {
|
||||||
"::toggleresizablesd", "::resizablesd", "::togglersd", "::rsd" -> {
|
"::toggleresizablesd", "::resizablesd", "::togglersd", "::rsd" -> {
|
||||||
toggleResize = true //We could call toggleResizableSd() directly here, but it's not necessary.
|
toggleResizableSd()
|
||||||
}
|
}
|
||||||
"::toggleresizablesdhd", "::resizablesdhd", "::togglersdhd", "::rsdhd", -> {
|
"::toggleresizablesdhd", "::resizablesdhd", "::togglersdhd", "::rsdhd" -> {
|
||||||
wantHd = !wantHd
|
wantHd = !wantHd
|
||||||
API.StoreData("want-hd", wantHd)
|
StoreData("want-hd", wantHd)
|
||||||
API.SendMessage("You have turned login screen HD " + (if (wantHd) "on" else "off"))
|
API.SendMessage("You have turned login screen HD " + (if (wantHd) "on" else "off"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleResizableSd() {
|
fun toggleResizableSd() {
|
||||||
//We only want to toggle resizable SD when we are logged in and the lobby/welcome interface is not open.
|
//We only want to toggle resizable SD when we are logged in and the lobby/welcome interface is not open.
|
||||||
if (InterfaceList.aClass13_26 == null || client.gameState != 30) {
|
if (InterfaceList.aClass13_26 == null || client.gameState != 30) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
toggleResize = false
|
|
||||||
DisplayMode.resizableSD = !DisplayMode.resizableSD;
|
DisplayMode.resizableSD = !DisplayMode.resizableSD
|
||||||
if(!DisplayMode.resizableSD){
|
useResizable = DisplayMode.resizableSD
|
||||||
//Revert to fixed
|
StoreData("use-resizable-sd", useResizable)
|
||||||
API.StoreData("use-resizable-sd", false) //Note: It is important to call StoreData before setWindowMode because setWindowMode causes all plugins to reload.
|
|
||||||
|
if (!DisplayMode.resizableSD) {
|
||||||
DisplayMode.setWindowMode(true, 0, -1, -1)
|
DisplayMode.setWindowMode(true, 0, -1, -1)
|
||||||
} else {
|
} else {
|
||||||
//Use resizable
|
|
||||||
API.StoreData("use-resizable-sd", true) //Note: It is important to call StoreData before setWindowMode because setWindowMode causes all plugins to reload.
|
|
||||||
DisplayMode.setWindowMode(true, 0, GameShell.frameWidth, GameShell.frameHeight)
|
DisplayMode.setWindowMode(true, 0, GameShell.frameWidth, GameShell.frameHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) {
|
override fun Draw(timeDelta: Long) {
|
||||||
if (toggleResize) {
|
if (useResizable != DisplayMode.resizableSD) {
|
||||||
toggleResizableSd()
|
toggleResizableSd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun OnKondoValueUpdated() {
|
||||||
|
StoreData("want-hd", wantHd)
|
||||||
|
StoreData("use-resizable-sd", useResizable)
|
||||||
|
}
|
||||||
|
|
||||||
override fun OnLogout() {
|
override fun OnLogout() {
|
||||||
if (DisplayMode.resizableSD && wantHd) {
|
if (DisplayMode.resizableSD && wantHd) {
|
||||||
//Because resizable SD always uses the "HD" size canvas/window mode (check the in-game Graphics Options with resizeable SD enabled if you don't believe me!), useHD becomes true when logging out, so logging out with resizeSD enabled means "HD" will always be enabled on the login screen after logging out, so we might as well fix the HD flyover by setting resizableSD to false first, and then calling setWindowMode to replace the canvas and set newMode to 2.
|
//Because resizable SD always uses the "HD" size canvas/window mode (check the in-game Graphics Options with resizeable SD enabled if you don't believe me!), useHD becomes true when logging out, so logging out with resizeSD enabled means "HD" will always be enabled on the login screen after logging out, so we might as well fix the HD flyover by setting resizableSD to false first, and then calling setWindowMode to replace the canvas and set newMode to 2.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='ipkpjersi'
|
AUTHOR='ipkpjersi'
|
||||||
DESCRIPTION='Allows you to use F12 to toggle resizable SD.'
|
DESCRIPTION='Allows you to use F12 to toggle resizable SD.'
|
||||||
VERSION=1.0
|
VERSION=1.1
|
||||||
|
|
@ -1,12 +1,34 @@
|
||||||
package XPDropPlugin;
|
package XPDropPlugin
|
||||||
|
|
||||||
|
import KondoKit.Exposed
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
import plugin.annotations.PluginMeta
|
import plugin.api.API
|
||||||
import plugin.api.*
|
import plugin.api.API.*
|
||||||
|
import plugin.api.FontColor.fromColor
|
||||||
|
import plugin.api.FontType
|
||||||
|
import plugin.api.TextModifier
|
||||||
|
import plugin.api.WindowMode
|
||||||
|
import rt4.Sprite
|
||||||
|
import rt4.client
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.InputStream
|
||||||
|
import javax.imageio.ImageIO
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
|
||||||
class plugin : Plugin() {
|
class plugin : Plugin() {
|
||||||
|
|
||||||
|
enum class Theme {
|
||||||
|
DEFAULT, RUNELITE
|
||||||
|
}
|
||||||
|
|
||||||
|
@Exposed
|
||||||
|
private var theme = Theme.DEFAULT
|
||||||
|
|
||||||
|
@Exposed
|
||||||
|
private var alwaysShow = false
|
||||||
|
|
||||||
private val displayTimeout = 10000L // 10 seconds
|
private val displayTimeout = 10000L // 10 seconds
|
||||||
private val drawStart = 175
|
private val drawStart = 175
|
||||||
private val drawPadding = 25
|
private val drawPadding = 25
|
||||||
|
|
@ -15,41 +37,50 @@ class plugin : Plugin() {
|
||||||
private var totalXp = 0
|
private var totalXp = 0
|
||||||
private val activeGains = ArrayList<XPGain>()
|
private val activeGains = ArrayList<XPGain>()
|
||||||
private var lastGain = 0L
|
private var lastGain = 0L
|
||||||
|
private val IN_GAME = 30
|
||||||
|
|
||||||
|
private val spriteCache = HashMap<Int, Sprite?>()
|
||||||
|
|
||||||
|
override fun Init() {
|
||||||
|
val themeIndex = (GetData("xp-drop-theme") as? String) ?: "DEFAULT"
|
||||||
|
theme = Theme.valueOf(themeIndex)
|
||||||
|
alwaysShow = (GetData("xp-drop-alwaysShow") as? Boolean) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
override fun Draw(deltaTime: Long) {
|
override fun Draw(deltaTime: Long) {
|
||||||
if (System.currentTimeMillis() - lastGain >= displayTimeout && activeGains.isEmpty())
|
if (shouldSkipDrawing()) return
|
||||||
return
|
|
||||||
|
|
||||||
drawTotalXPBox()
|
drawTotalXPBox()
|
||||||
|
|
||||||
val removeList = ArrayList<XPGain>()
|
val removeList = ArrayList<XPGain>()
|
||||||
var posX = API.GetWindowDimensions().width / 2
|
|
||||||
|
|
||||||
if (API.GetWindowMode() == WindowMode.FIXED)
|
val movementSpeedFactor = deltaTime / 16.666 // 60 FPS
|
||||||
posX += 60
|
|
||||||
|
|
||||||
for(gain in activeGains) {
|
for (gain in activeGains) {
|
||||||
gain.currentPos -= ceil(deltaTime / 20.0).toInt()
|
gain.currentPos -= ceil(movementSpeedFactor).toInt() // Adjust movement based on deltaTime
|
||||||
if (gain.currentPos <= drawClear) {
|
if (gain.currentPos <= drawClear) {
|
||||||
removeList.add(gain)
|
removeList.add(gain)
|
||||||
totalXp += gain.xp
|
totalXp += gain.xp
|
||||||
} else if (gain.currentPos <= drawStart){
|
} else if (gain.currentPos <= drawStart) {
|
||||||
val sprite = XPSprites.getSpriteForSkill(skillId = gain.skill)
|
drawXPDrops(gain)
|
||||||
sprite?.render(posX - 25, gain.currentPos - 20)
|
|
||||||
API.DrawText(
|
|
||||||
FontType.SMALL,
|
|
||||||
FontColor.fromColor(Color.WHITE),
|
|
||||||
TextModifier.LEFT,
|
|
||||||
addCommas(gain.xp.toString()),
|
|
||||||
posX,
|
|
||||||
gain.currentPos
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activeGains.removeAll(removeList.toSet())
|
activeGains.removeAll(removeList.toSet())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldSkipDrawing(): Boolean {
|
||||||
|
return client.gameState < IN_GAME || (!alwaysShow && isDisplayTimeoutExpired() && activeGains.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OnKondoValueUpdated() {
|
||||||
|
StoreData("xp-drop-theme",theme.toString())
|
||||||
|
StoreData("xp-drop-alwaysShow",alwaysShow)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isDisplayTimeoutExpired(): Boolean {
|
||||||
|
return System.currentTimeMillis() - lastGain >= displayTimeout
|
||||||
|
}
|
||||||
|
|
||||||
override fun OnXPUpdate(skill: Int, xp: Int) {
|
override fun OnXPUpdate(skill: Int, xp: Int) {
|
||||||
if (xp == lastXp[skill]) return
|
if (xp == lastXp[skill]) return
|
||||||
|
|
||||||
|
|
@ -81,6 +112,54 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawTotalXPBox() {
|
private fun drawTotalXPBox() {
|
||||||
|
when (theme) {
|
||||||
|
Theme.DEFAULT -> drawDefaultXPBox()
|
||||||
|
Theme.RUNELITE -> drawRuneliteXPBox()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawXPDrops(gain : XPGain) {
|
||||||
|
when (theme) {
|
||||||
|
Theme.DEFAULT -> drawDefaultXPDrop(gain)
|
||||||
|
Theme.RUNELITE -> drawRuneliteXPDrops(gain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawDefaultXPDrop(gain: XPGain) {
|
||||||
|
var posX = API.GetWindowDimensions().width / 2
|
||||||
|
if (API.GetWindowMode() == WindowMode.FIXED)
|
||||||
|
posX += 60
|
||||||
|
val sprite = spriteCache.getOrPut(gain.skill) { XPSprites.getSpriteForSkill(skillId = gain.skill) }
|
||||||
|
sprite?.render(posX - 25, gain.currentPos - 20)
|
||||||
|
DrawText(
|
||||||
|
FontType.SMALL,
|
||||||
|
fromColor(Color.WHITE),
|
||||||
|
TextModifier.LEFT,
|
||||||
|
addCommas(gain.xp.toString()),
|
||||||
|
posX,
|
||||||
|
gain.currentPos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawRuneliteXPDrops(gain: XPGain) {
|
||||||
|
val w = API.GetWindowDimensions().width
|
||||||
|
val offset = if(API.GetWindowMode() == WindowMode.FIXED) 251 else 225
|
||||||
|
val extra = 2;
|
||||||
|
val posX = w - (offset + extra)
|
||||||
|
|
||||||
|
val str = addCommas(gain.xp.toString())
|
||||||
|
val fontCharWidth = 4
|
||||||
|
val displace = str.length*fontCharWidth + 30
|
||||||
|
|
||||||
|
// should be scaled https://github.com/runelite/runelite/blob/0500906f8de9cd20875c168a7a59e5e066ed5058/runelite-client/src/main/java/net/runelite/client/game/SkillIconManager.java#L50
|
||||||
|
// but for now this is good enough
|
||||||
|
|
||||||
|
val sprite = spriteCache.getOrPut(gain.skill) { XPSprites.getSpriteForSkill(skillId = gain.skill) }
|
||||||
|
sprite?.render(posX - displace, gain.currentPos - 20)
|
||||||
|
drawTextWithDropShadow(posX, gain.currentPos, Color.WHITE, addCommas(gain.xp.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawDefaultXPBox() {
|
||||||
var posX = API.GetWindowDimensions().width / 2
|
var posX = API.GetWindowDimensions().width / 2
|
||||||
val posY = API.GetWindowDimensions().height / 4
|
val posY = API.GetWindowDimensions().height / 4
|
||||||
|
|
||||||
|
|
@ -89,13 +168,13 @@ class plugin : Plugin() {
|
||||||
|
|
||||||
API.ClipRect(0, 0, posX * 2, posY * 4)
|
API.ClipRect(0, 0, posX * 2, posY * 4)
|
||||||
|
|
||||||
val horizontal = API.GetSprite(822)
|
val horizontal = spriteCache.getOrPut(822) { API.GetSprite(822) }
|
||||||
val horizontalTop = API.GetSprite(820)
|
val horizontalTop = spriteCache.getOrPut(820) { API.GetSprite(820) }
|
||||||
val tlCorner = API.GetSprite(824)
|
val tlCorner = spriteCache.getOrPut(824) { API.GetSprite(824) }
|
||||||
val blCorner = API.GetSprite(826)
|
val blCorner = spriteCache.getOrPut(826) { API.GetSprite(826) }
|
||||||
val trCorner = API.GetSprite(825)
|
val trCorner = spriteCache.getOrPut(825) { API.GetSprite(825) }
|
||||||
val brCorner = API.GetSprite(827)
|
val brCorner = spriteCache.getOrPut(827) { API.GetSprite(827) }
|
||||||
val bg = API.GetSprite(657)
|
val bg = spriteCache.getOrPut(657) { API.GetSprite(657) }
|
||||||
|
|
||||||
bg?.render(posX - 77, 10)
|
bg?.render(posX - 77, 10)
|
||||||
API.FillRect(posX - 75, 5, 140, 30, 0, 64)
|
API.FillRect(posX - 75, 5, 140, 30, 0, 64)
|
||||||
|
|
@ -112,26 +191,58 @@ class plugin : Plugin() {
|
||||||
horizontalTop?.render(posX + 9, -8)
|
horizontalTop?.render(posX + 9, -8)
|
||||||
horizontal?.render(posX + 9, 22)
|
horizontal?.render(posX + 9, 22)
|
||||||
|
|
||||||
API.DrawText(
|
DrawText(
|
||||||
FontType.SMALL,
|
FontType.SMALL,
|
||||||
FontColor.fromColor(Color.WHITE),
|
fromColor(Color.WHITE),
|
||||||
TextModifier.LEFT,
|
TextModifier.LEFT,
|
||||||
"Total Xp: ${addCommas(totalXp.toString())}",
|
"Total Xp: ${addCommas(totalXp.toString())}",
|
||||||
posX - 65,
|
posX - 65,
|
||||||
28
|
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)
|
data class XPGain(val skill: Int, val xp: Int, var currentPos: Int)
|
||||||
|
|
||||||
fun addCommas(num: String): String{
|
fun addCommas(num: String): String {
|
||||||
var newString = ""
|
var newString = ""
|
||||||
if(num.length > 9){
|
if (num.length > 9) {
|
||||||
return "Lots!"
|
return "Lots!"
|
||||||
}
|
}
|
||||||
var counter = 1
|
var counter = 1
|
||||||
num.reversed().forEach {
|
num.reversed().forEach {
|
||||||
if(counter % 3 == 0 && counter != num.length){
|
if (counter % 3 == 0 && counter != num.length) {
|
||||||
newString += "$it,"
|
newString += "$it,"
|
||||||
} else {
|
} else {
|
||||||
newString += it
|
newString += it
|
||||||
|
|
@ -140,4 +251,10 @@ class plugin : Plugin() {
|
||||||
}
|
}
|
||||||
return newString.reversed()
|
return newString.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun drawTextWithDropShadow(x: Int, y: Int, color: Color, text: String, mod : TextModifier = TextModifier.RIGHT) {
|
||||||
|
DrawText(FontType.SMALL, fromColor(Color(0)), mod, text, x + 1, y + 1)
|
||||||
|
DrawText(FontType.SMALL, fromColor(color), mod, text, x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='Ceikry'
|
AUTHOR='Ceikry'
|
||||||
DESCRIPTION='Draws nice and clean experience drops onto the screen.'
|
DESCRIPTION='Draws nice and clean experience drops onto the screen.'
|
||||||
VERSION=1.2
|
VERSION=1.3
|
||||||
|
|
|
||||||
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png
Normal file
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 348 B |
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png
Normal file
BIN
plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 329 B |
Loading…
Add table
Add a link
Reference in a new issue