diff --git a/client/src/main/java/plugin/Plugin.java b/client/src/main/java/plugin/Plugin.java index e8e3641..7a4a039 100644 --- a/client/src/main/java/plugin/Plugin.java +++ b/client/src/main/java/plugin/Plugin.java @@ -13,7 +13,6 @@ import rt4.Tile; */ public abstract class Plugin { long timeOfLastDraw; - long timeOfLastLateDraw; void _init() { Init(); @@ -25,12 +24,6 @@ public abstract class Plugin { timeOfLastDraw = nowTime; } - void _lateDraw() { - long nowTime = System.currentTimeMillis(); - LateDraw(nowTime - timeOfLastLateDraw); - timeOfLastLateDraw = nowTime; - } - /** * Draw() is called by the client rendering loop so that plugins can draw information onto the screen. * This will be called once per frame, meaning it is framerate bound. @@ -38,14 +31,6 @@ public abstract class Plugin { */ public void Draw(long timeDelta) {} - - /** - * LateDraw() is called at the end of a finalized frame - * This will be called once per frame, meaning it is framerate bound. - * @param timeDelta the time (ms) elapsed since the last draw call. - */ - public void LateDraw(long timeDelta) {} - /** * Init() is called when the plugin is first loaded */ diff --git a/client/src/main/java/plugin/PluginInfo.java b/client/src/main/java/plugin/PluginInfo.java index 1550394..01fddc6 100644 --- a/client/src/main/java/plugin/PluginInfo.java +++ b/client/src/main/java/plugin/PluginInfo.java @@ -13,10 +13,10 @@ import java.util.Properties; * A data class for storing information about plugins. * @author ceikry */ -public class PluginInfo { - public double version; - public String author; - public String description; +class PluginInfo { + double version; + String author; + String description; public PluginInfo(String author, String description, double version) { this.version = version; diff --git a/client/src/main/java/plugin/PluginRepository.java b/client/src/main/java/plugin/PluginRepository.java index 55faf80..2af27f6 100644 --- a/client/src/main/java/plugin/PluginRepository.java +++ b/client/src/main/java/plugin/PluginRepository.java @@ -160,11 +160,6 @@ public class PluginRepository { pluginsSnapshot.forEach(Plugin::_draw); } - public static void LateDraw() { - List pluginsSnapshot = new ArrayList<>(loadedPlugins.values()); - pluginsSnapshot.forEach(Plugin::_lateDraw); - } - public static void NPCOverheadDraw(Npc npc, int screenX, int screenY) { loadedPlugins.values().forEach((plugin) -> plugin.NPCOverheadDraw(npc, screenX, screenY)); } diff --git a/client/src/main/java/plugin/api/API.java b/client/src/main/java/plugin/api/API.java index 78ec7b3..aabc7a3 100644 --- a/client/src/main/java/plugin/api/API.java +++ b/client/src/main/java/plugin/api/API.java @@ -307,7 +307,6 @@ public class API { public static int[] CalculateSceneGraphScreenPosition(int entityX, int entityZ, int yOffset) { final int HALF_FIXED_WIDTH = 256; final int HALF_FIXED_HEIGHT = 167; - final int RESIZABLE_SD_OFFSET = 500; int elevation = SceneGraph.getTileHeight(plane, entityX, entityZ) - yOffset; entityX -= SceneGraph.cameraX; @@ -330,27 +329,15 @@ public class API { int[] screenPos = new int[2]; // X,Y if (entityZ >= 50) { - if (GetWindowMode() == WindowMode.FIXED) { + if(GetWindowMode() == WindowMode.FIXED) { screenPos[0] = HALF_FIXED_WIDTH + ((entityX << 9) / entityZ); screenPos[1] = HALF_FIXED_HEIGHT + ((elevation << 9) / entityZ); } else { Dimension canvas = GetWindowDimensions(); - double newViewDistH; - double newViewDistV; - - if (API.IsHD()) { - newViewDistH = (canvas.width / 2) / Math.tan(Math.toRadians(GlRenderer.hFOV) / 2); - newViewDistV = (canvas.height / 2) / Math.tan(Math.toRadians(GlRenderer.vFOV) / 2); - } else { - double aspectRatio = (double) canvas.width / canvas.height; - double vFOV = 2 * Math.toDegrees(Math.atan((double) canvas.height / (2 * RESIZABLE_SD_OFFSET))); - double hFOV = 2 * Math.toDegrees(Math.atan(Math.tan(Math.toRadians(vFOV / 2)) * aspectRatio)); - - newViewDistH = (canvas.width / 2) / Math.tan(Math.toRadians(hFOV) / 2); - newViewDistV = (canvas.height / 2) / Math.tan(Math.toRadians(vFOV) / 2); - } - screenPos[0] = canvas.width / 2 + (int) ((entityX * newViewDistH) / entityZ); - screenPos[1] = canvas.height / 2 + (int) ((elevation * newViewDistV) / entityZ); + double newViewDistH = (canvas.width / 2) / Math.tan(Math.toRadians(GlRenderer.hFOV) / 2); + double newViewDistV = (canvas.height / 2) / Math.tan(Math.toRadians(GlRenderer.vFOV) / 2); + screenPos[0] = canvas.width / 2 + (int)((entityX * newViewDistH) / entityZ); + screenPos[1] = canvas.height / 2 + (int)((elevation * newViewDistV) / entityZ); } } else { screenPos[0] = -1; diff --git a/client/src/main/java/rt4/FrameBuffer.java b/client/src/main/java/rt4/FrameBuffer.java index f641434..5999c44 100644 --- a/client/src/main/java/rt4/FrameBuffer.java +++ b/client/src/main/java/rt4/FrameBuffer.java @@ -12,7 +12,7 @@ import java.awt.*; public abstract class FrameBuffer { @OriginalMember(owner = "client!vk", name = "e", descriptor = "[I") - public int[] pixels; + protected int[] pixels; @OriginalMember(owner = "client!vk", name = "g", descriptor = "Ljava/awt/Image;") protected Image image; diff --git a/client/src/main/java/rt4/GlRenderer.java b/client/src/main/java/rt4/GlRenderer.java index 92e4564..b42e0de 100644 --- a/client/src/main/java/rt4/GlRenderer.java +++ b/client/src/main/java/rt4/GlRenderer.java @@ -9,7 +9,6 @@ import org.openrs2.deob.annotation.OriginalMember; import org.openrs2.deob.annotation.Pc; import java.awt.*; -import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.charset.StandardCharsets; @@ -25,11 +24,6 @@ public final class GlRenderer { public static float hFOV = 0; - - private static ByteBuffer pixelByteBuffer; - private static IntBuffer pixelIntBuffer; - public static int[] pixelData; - @OriginalMember(owner = "client!tf", name = "c", descriptor = "F") private static float aFloat30; @@ -206,34 +200,10 @@ public final class GlRenderer { public static void swapBuffers() { try { drawable.swapBuffers(); - readPixels(); } catch (@Pc(3) Exception local3) { } } - public static void initializePixelBuffer(int width, int height) { - // Allocate ByteBuffer for BGRA pixels (4 bytes per pixel) - pixelByteBuffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder()); - pixelIntBuffer = pixelByteBuffer.asIntBuffer(); - pixelData = new int[width * height]; - } - - public static void readPixels() { - // Ensure the pixel buffer is initialized with the correct size - if (pixelByteBuffer == null || pixelIntBuffer.capacity() != canvasWidth * canvasHeight) { - initializePixelBuffer(canvasWidth, canvasHeight); - } - - // Read pixels into the direct ByteBuffer - gl.glReadPixels(0, 0, canvasWidth, canvasHeight, GL2.GL_BGRA, - GlRenderer.bigEndian ? GL2.GL_UNSIGNED_INT_8_8_8_8_REV : GL2.GL_UNSIGNED_BYTE, - pixelByteBuffer); - - // Convert to int array if needed - pixelIntBuffer.rewind(); // Prepare the IntBuffer for reading - pixelIntBuffer.get(pixelData, 0, pixelData.length); // Transfer to pixelData array if necessary - } - @OriginalMember(owner = "client!tf", name = "a", descriptor = "(Z)V") public static void setFogEnabled(@OriginalArg(0) boolean enabled) { if (enabled == fogEnabled) { diff --git a/client/src/main/java/rt4/client.java b/client/src/main/java/rt4/client.java index 6aab3b8..26e9ca2 100644 --- a/client/src/main/java/rt4/client.java +++ b/client/src/main/java/rt4/client.java @@ -921,7 +921,6 @@ public final class client extends GameShell { Preferences.safeMode = false; Preferences.write(GameShell.signLink); } - PluginRepository.LateDraw(); } @OriginalMember(owner = "client!client", name = "c", descriptor = "(B)V") diff --git a/plugin-playground/build.gradle b/plugin-playground/build.gradle index a1644a7..c5f4a9f 100644 --- a/plugin-playground/build.gradle +++ b/plugin-playground/build.gradle @@ -110,10 +110,4 @@ task buildPlugins(type: Copy, dependsOn: classes) { from "build/classes/kotlin/main" into pluginsPath } - - // Find and copy any 'res' directories from 'src/main/kotlin/**/res/' - copy { - from fileTree(dir: "src/main/kotlin", include: "**/res/**") - into pluginsPath - } } \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/GroundItems/res/item_configs.json b/plugin-playground/src/main/kotlin/GroundItems/item_configs.json similarity index 100% rename from plugin-playground/src/main/kotlin/GroundItems/res/item_configs.json rename to plugin-playground/src/main/kotlin/GroundItems/item_configs.json diff --git a/plugin-playground/src/main/kotlin/GroundItems/plugin.kt b/plugin-playground/src/main/kotlin/GroundItems/plugin.kt index 8fe5b18..ecd5ba8 100644 --- a/plugin-playground/src/main/kotlin/GroundItems/plugin.kt +++ b/plugin-playground/src/main/kotlin/GroundItems/plugin.kt @@ -1,7 +1,7 @@ package GroundItems -import KondoKit.Exposed import plugin.Plugin +import plugin.annotations.PluginMeta import plugin.api.API.* import plugin.api.FontColor.fromColor import plugin.api.FontType @@ -18,26 +18,27 @@ import java.nio.charset.StandardCharsets import java.text.DecimalFormat import kotlin.math.roundToInt -class plugin : Plugin() { - - @Exposed(description = "Default: true, Use Local JSON or the prices from the Live/Stable server API") - private var useLiveGEPrices = true - @Exposed( "Default: 5,000 (blue)") - private var lowValue = 5000 - @Exposed( "Default: 20,000 (green)") - private var mediumValue = 20000 - @Exposed( "Default: 50,000 (orange)") - private var highValue = 50000 - @Exposed( "Default: 100,000 (pink)") - private var insaneValue = 100000 - @Exposed("Default: 0, No labels will be drawn for items below this value (unless tagged)") - private var hideBelowValue = 0 - @Exposed("Tag Items (purple) add/remove with Ctrl+RightClick Tag/Ignore") - private lateinit var taggedItems: List - @Exposed("Ignore items add/remove with Ctrl+RightClick Tag/Ignore") - private lateinit var ignoredItems: List +@PluginMeta( + author = "downthecrop", + description = + """ + Ground Items Overlay. Just like Runelite! + cmds ::set(low,med,high,insane,hide), ::(tag,ignore)item ID, ::(reset)groundconfig + Special thanks to Chisato for the original skeleton. + """, + version = 1.2 +) +open class plugin : Plugin() { + private var kondoExposed_lowValue = 5000 + private var kondoExposed_mediumValue = 20000 + private var kondoExposed_highValue = 50000 + private var kondoExposed_insaneValue = 100000 + private var kondoExposed_hideBelowValue = 0 + private var kondoExposed_useLiveGEPrices = true private val coindId = 995 + private lateinit var kondoExposed_taggedItems: List + private lateinit var kondoExposed_ignoredItems: List private var gePriceMap = loadGEPrices() @@ -59,23 +60,23 @@ class plugin : Plugin() { ) override fun Init() { - lowValue = GetData("low-value") as? Int ?: 5000 - mediumValue = GetData("medium-value") as? Int ?: 20000 - highValue = GetData("high-value") as? Int ?: 50000 - insaneValue = GetData("insane-value") as? Int ?: 100000 - hideBelowValue = GetData("hide-below-value") as? Int ?: 0 - useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true - taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList() - ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList() - if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $useLiveGEPrices") + kondoExposed_lowValue = GetData("low-value") as? Int ?: 5000 + kondoExposed_mediumValue = GetData("medium-value") as? Int ?: 20000 + kondoExposed_highValue = GetData("high-value") as? Int ?: 50000 + kondoExposed_insaneValue = GetData("insane-value") as? Int ?: 100000 + kondoExposed_hideBelowValue = GetData("hide-below-value") as? Int ?: 0 + kondoExposed_useLiveGEPrices = GetData("ground-item-use-remote") as? Boolean ?: true + kondoExposed_taggedItems = GetData("ground-item-tags")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList() + kondoExposed_ignoredItems = GetData("ground-item-ignore")?.let { it.toString().split(",").mapNotNull { it.toIntOrNull() } } ?: emptyList() + if (gePriceMap.isEmpty()) SendMessage("Ground Items unable to load GE Prices, Remote: $kondoExposed_useLiveGEPrices") } private fun isTagged(itemId: Int): Boolean { - return taggedItems.contains(itemId) + return kondoExposed_taggedItems.contains(itemId) } private fun isHidden(itemId: Int): Boolean { - return ignoredItems.contains(itemId) + return kondoExposed_ignoredItems.contains(itemId) } override fun Draw(timeDelta: Long) = renderGroundItemNames() @@ -140,10 +141,10 @@ class plugin : Plugin() { val screenY = screenPos[1] val color = when { isTagged(itemDef.id) -> colorMap["tagged"] - highestValue < lowValue -> "#FFFFFF" - highestValue < mediumValue -> colorMap["lowValue"] - highestValue < highValue -> colorMap["mediumValue"] - highestValue < insaneValue -> colorMap["highValue"] + highestValue < kondoExposed_lowValue -> "#FFFFFF" + highestValue < kondoExposed_mediumValue -> colorMap["lowValue"] + highestValue < kondoExposed_highValue -> colorMap["mediumValue"] + highestValue < kondoExposed_insaneValue -> colorMap["highValue"] else -> colorMap["insaneValue"] } ?: "#FFFFFF" val colorInt = color.drop(1).toInt(16) @@ -182,7 +183,7 @@ class plugin : Plugin() { val haValue = if (itemDef.id == coindId) item.value.amount else (itemDef.cost * 0.6 * item.value.amount).roundToInt() val geValue = (gePriceMap[itemDef.id.toString()]?.toInt() ?: 0) * item.value.amount val highestValue = maxOf(haValue, geValue) - return !((highestValue < hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id)) + return !((highestValue < kondoExposed_hideBelowValue || isHidden(itemDef.id)) && !isTagged(itemDef.id)) } override fun OnMiniMenuCreate(currentEntries: Array?) { @@ -199,7 +200,7 @@ class plugin : Plugin() { private fun ignoreItem(itemId: Int): Runnable { return Runnable { - val existingIgnores = ignoredItems.toMutableList() + val existingIgnores = kondoExposed_ignoredItems.toMutableList() if (existingIgnores.contains(itemId)) { existingIgnores.remove(itemId) @@ -214,7 +215,7 @@ class plugin : Plugin() { private fun tagItem(itemId: Int): Runnable { return Runnable { - val existingTags = taggedItems.toMutableList() + val existingTags = kondoExposed_taggedItems.toMutableList() if (existingTags.contains(itemId)) { existingTags.remove(itemId) @@ -229,28 +230,28 @@ class plugin : Plugin() { private fun resetConfig() { - lowValue = 5000 - mediumValue = 20000 - highValue = 50000 - insaneValue = 100000 - hideBelowValue = 0 - useLiveGEPrices = true + kondoExposed_lowValue = 5000 + kondoExposed_mediumValue = 20000 + kondoExposed_highValue = 50000 + kondoExposed_insaneValue = 100000 + kondoExposed_hideBelowValue = 0 + kondoExposed_useLiveGEPrices = true StoreData("ground-item-tags",""); StoreData("ground-item-ignore",""); - StoreData("low-value", lowValue) - StoreData("ground-item-use-remote", useLiveGEPrices) - StoreData("medium-value", mediumValue) - StoreData("high-value", highValue) - StoreData("insane-value", insaneValue) - StoreData("hide-below-value", hideBelowValue) + StoreData("low-value", kondoExposed_lowValue) + StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices) + StoreData("medium-value", kondoExposed_mediumValue) + StoreData("high-value", kondoExposed_highValue) + StoreData("insane-value", kondoExposed_insaneValue) + StoreData("hide-below-value", kondoExposed_hideBelowValue) } private fun displayRanges() { - val low = lowValue - val medium = mediumValue - val high = highValue - val insane = insaneValue - val hide = hideBelowValue + val low = kondoExposed_lowValue + val medium = kondoExposed_mediumValue + val high = kondoExposed_highValue + val insane = kondoExposed_insaneValue + val hide = kondoExposed_hideBelowValue SendMessage("== Ground Item Config ==") SendMessage("Low: $low") @@ -259,12 +260,12 @@ class plugin : Plugin() { SendMessage("Insane: $insane") SendMessage("Hide Below: $hide") SendMessage("-- Ignored Items --") - for(item in ignoredItems){ + for(item in kondoExposed_ignoredItems){ val itemDef = ObjTypeList.get(item) SendMessage("Ignored: ${itemDef.name} ${itemDef.id}") } SendMessage("-- Tagged Items --") - for(item in taggedItems){ + for(item in kondoExposed_taggedItems){ val itemDef = ObjTypeList.get(item) SendMessage("Tagged: ${itemDef.name} ${itemDef.id}") } @@ -277,19 +278,19 @@ class plugin : Plugin() { } fun OnKondoValueUpdated() { - StoreData("ground-item-tags",taggedItems); - StoreData("ground-item-ignore",ignoredItems); - StoreData("low-value", lowValue) - StoreData("medium-value", mediumValue) - StoreData("high-value", highValue) - StoreData("insane-value", insaneValue) - StoreData("ground-item-use-remote", useLiveGEPrices) - StoreData("hide-below-value", hideBelowValue) + StoreData("ground-item-tags",kondoExposed_taggedItems); + StoreData("ground-item-ignore",kondoExposed_ignoredItems); + StoreData("low-value", kondoExposed_lowValue) + StoreData("medium-value", kondoExposed_mediumValue) + StoreData("high-value", kondoExposed_highValue) + StoreData("insane-value", kondoExposed_insaneValue) + StoreData("ground-item-use-remote", kondoExposed_useLiveGEPrices) + StoreData("hide-below-value", kondoExposed_hideBelowValue) gePriceMap = loadGEPrices(); } fun loadGEPrices(): Map { - return if (useLiveGEPrices) { + return if (kondoExposed_useLiveGEPrices) { try { println("GroundItems: Loading Remote GE Prices") val url = URL("https://cdn.2009scape.org/gedata/latest.json") @@ -324,7 +325,7 @@ class plugin : Plugin() { } else { try { println("GroundItems: Loading Local GE Prices") - BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8)) + BufferedReader(InputStreamReader(GroundItems.plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8)) .useLines { lines -> val json = lines.joinToString("\n") val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" } diff --git a/plugin-playground/src/main/kotlin/GroundItems/plugin.properties b/plugin-playground/src/main/kotlin/GroundItems/plugin.properties index c77a215..e14f44c 100644 --- a/plugin-playground/src/main/kotlin/GroundItems/plugin.properties +++ b/plugin-playground/src/main/kotlin/GroundItems/plugin.properties @@ -5,4 +5,4 @@ Commands:\ ::(tag,ignore)item ID\ ::(reset)groundconfig\ Special thanks to Chisato for the original skeleton. -VERSION=1.3 \ No newline at end of file +VERSION=1.2 \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/AltCanvas.kt b/plugin-playground/src/main/kotlin/KondoKit/AltCanvas.kt deleted file mode 100644 index af5c01d..0000000 --- a/plugin-playground/src/main/kotlin/KondoKit/AltCanvas.kt +++ /dev/null @@ -1,166 +0,0 @@ -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() - } - } -} \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/Helpers.kt b/plugin-playground/src/main/kotlin/KondoKit/Helpers.kt index 4f6e3c4..a606805 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/Helpers.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/Helpers.kt @@ -1,159 +1,10 @@ package KondoKit -import rt4.GameShell -import java.awt.* -import java.awt.event.MouseListener -import java.lang.reflect.Field -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import java.util.* -import java.util.Timer -import javax.swing.* +import java.awt.Color +import java.awt.Dimension +import javax.swing.JPanel 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() // 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() - - private fun notifyFieldChange(field: Field, newValue: Any?) { - for (observer in observers) { - observer.onFieldChange(field, newValue) - } - } - - fun setFieldValue(field: Field, value: Any?) { - field.isAccessible = true - field.set(plugin, value) - notifyFieldChange(field, value) - - try { - val onUpdateMethod = plugin::class.java.getMethod("OnKondoValueUpdated") - onUpdateMethod.invoke(plugin) - } catch (e: NoSuchMethodException) { - // The method doesn't exist - } catch (e: Exception) { - e.printStackTrace() - } - } - } - fun getSpriteId(skillId: Int) : Int { return when (skillId) { 0 -> 197 @@ -184,15 +35,9 @@ object Helpers { } } - fun showAlert(message: String, title: String, type: Int){ - JOptionPane.showMessageDialog(null, message, title, type) - } - fun formatHtmlLabelText(text1: String, color1: Color, text2: String, color2: Color): String { - return "
" + - "$text1" + - "$text2" + - "
" + return "$text1" + + "$text2" } fun formatNumber(value: Int): String { @@ -233,4 +78,13 @@ object Helpers { else -> Color(128, 128, 128) // Default grey for unhandled skill IDs } } + + class Spacer(width: Int = 0, height: Int = 0) : JPanel() { + init { + preferredSize = Dimension(width, height) + maximumSize = preferredSize + minimumSize = preferredSize + isOpaque = false + } + } } \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/HiscoresView.kt b/plugin-playground/src/main/kotlin/KondoKit/HiscoresView.kt index 0b48ab3..e1e0f77 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/HiscoresView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/HiscoresView.kt @@ -1,19 +1,9 @@ package KondoKit import KondoKit.Constants.COLOR_BACKGROUND_DARK -import KondoKit.Constants.SKILL_DISPLAY_ORDER -import KondoKit.Constants.SKILL_SPRITE_DIMENSION -import KondoKit.Helpers.formatHtmlLabelText import KondoKit.Helpers.getSpriteId -import KondoKit.Helpers.showToast import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite -import KondoKit.plugin.Companion.POPUP_FOREGROUND -import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR -import KondoKit.plugin.Companion.WIDGET_COLOR -import KondoKit.plugin.Companion.primaryColor -import KondoKit.plugin.Companion.secondaryColor -import KondoKit.plugin.StateManager.focusedView import com.google.gson.Gson import plugin.api.API import rt4.Sprites @@ -26,11 +16,9 @@ import java.awt.event.MouseEvent import java.io.BufferedReader import java.io.InputStreamReader import java.net.HttpURLConnection -import java.net.SocketTimeoutException import java.net.URL import javax.swing.* import javax.swing.border.MatteBorder -import kotlin.math.floor object Constants { // Sprite IDs @@ -40,49 +28,47 @@ object Constants { const val LVL_BAR_SPRITE = 898 // Dimensions - val SEARCH_FIELD_DIMENSION = Dimension(230, 30) + val SEARCH_FIELD_DIMENSION = Dimension(270, 30) val ICON_DIMENSION_SMALL = Dimension(12, 12) + val ICON_DIMENSION_MEDIUM = Dimension(18, 20) val ICON_DIMENSION_LARGE = Dimension(30, 30) - val HISCORE_PANEL_DIMENSION = Dimension(230, 500) - val FILTER_PANEL_DIMENSION = Dimension(230, 30) - val SKILLS_PANEL_DIMENSION = Dimension(230, 290) - val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(230, 30) - val SKILL_PANEL_DIMENSION = Dimension(76, 35) + val HISCORE_PANEL_DIMENSION = Dimension(270, 400) + val FILTER_PANEL_DIMENSION = Dimension(270, 30) + val SKILLS_PANEL_DIMENSION = Dimension(300, 300) + val TOTAL_COMBAT_PANEL_DIMENSION = Dimension(270, 30) + val SKILL_PANEL_DIMENSION = Dimension(90, 35) val IMAGE_CANVAS_DIMENSION = Dimension(20, 20) - val SKILL_SPRITE_DIMENSION = Dimension(14, 14) - val NUMBER_LABEL_DIMENSION = Dimension(20, 20) + val NUMBER_LABEL_DIMENSION = Dimension(30, 20) // Colors - val COLOR_BACKGROUND_DARK = WIDGET_COLOR - val COLOR_BACKGROUND_MEDIUM = VIEW_BACKGROUND_COLOR - val COLOR_FOREGROUND_LIGHT = POPUP_FOREGROUND + val COLOR_BACKGROUND_DARK = Color(27, 27, 27) + val COLOR_BACKGROUND_MEDIUM = Color(37, 37, 37) + val COLOR_BACKGROUND_LIGHT = Color(43, 43, 43) + val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200) + val COLOR_RED = Color.RED + val COLOR_SKILL_PANEL = Color(60, 60, 60) // Fonts val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14) - val FONT_ARIAL_PLAIN_12 = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + val FONT_ARIAL_PLAIN_12 = Font("Arial", Font.PLAIN, 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 = "" object HiscoresView { - - const val VIEW_NAME = "HISCORE_SEARCH_VIEW" - var hiScoreView: JPanel? = null class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() { private var cursorVisible: Boolean = true private val gson = Gson() - private val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.MAG_SPRITE)) - private val imageCanvas = bufferedImageSprite.let { + val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.MAG_SPRITE)) + val imageCanvas = bufferedImageSprite.let { ImageCanvas(it).apply { preferredSize = Constants.ICON_DIMENSION_SMALL size = preferredSize minimumSize = preferredSize maximumSize = preferredSize - fillColor = COLOR_BACKGROUND_DARK } } @@ -110,9 +96,7 @@ object HiscoresView { } else { text += e.keyChar } - SwingUtilities.invokeLater { - repaint() - } + repaint() } override fun keyPressed(e: KeyEvent) { if (e.isControlDown) { @@ -125,9 +109,7 @@ object HiscoresView { val clipboard = Toolkit.getDefaultToolkit().systemClipboard val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String text += pasteText - SwingUtilities.invokeLater { - repaint() - } + repaint() } } } @@ -138,19 +120,15 @@ object HiscoresView { override fun mouseClicked(e: MouseEvent) { if (e.x > width - 20 && e.y < 20) { text = "" - SwingUtilities.invokeLater { - repaint() - } + repaint() } } }) Timer(500) { cursorVisible = !cursorVisible - if(focusedView == VIEW_NAME) - SwingUtilities.invokeLater { - repaint() - } + if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW") + repaint() }.start() } @@ -162,7 +140,7 @@ object HiscoresView { val fm = g.fontMetrics val cursorX = fm.stringWidth(text) + 30 - imageCanvas.let { canvas -> + imageCanvas?.let { canvas -> val imgG = g.create(5, 5, canvas.width, canvas.height) canvas.paint(imgG) imgG.dispose() @@ -175,16 +153,14 @@ object HiscoresView { } if (text.isNotEmpty()) { - g.color = Color.RED + g.color = Constants.COLOR_RED g.drawString("x", width - 20, 20) } } fun searchPlayer(username: String) { - text = username.replace(" ", "_") - val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${text.toLowerCase()}" - - updateHiscoresView(null, "Searching...") + text = username + val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${username.toLowerCase()}" Thread { try { @@ -192,10 +168,6 @@ object HiscoresView { val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" - // If a request take longer than 5 seconds timeout. - connection.connectTimeout = 5000 - connection.readTimeout = 5000 - val responseCode = connection.responseCode if (responseCode == HttpURLConnection.HTTP_OK) { val reader = BufferedReader(InputStreamReader(connection.inputStream)) @@ -207,46 +179,28 @@ object HiscoresView { } } else { SwingUtilities.invokeLater { - showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE) + showError("Player not found!") } } - } catch (e: SocketTimeoutException) { - SwingUtilities.invokeLater { - showToast(hiscoresPanel, "Request timed out", JOptionPane.ERROR_MESSAGE) - } } catch (e: Exception) { - // Handle other errors SwingUtilities.invokeLater { - showToast(hiscoresPanel, "Error fetching data!", JOptionPane.ERROR_MESSAGE) + showError("Error fetching data!") } } }.start() } - private fun updatePlayerData(jsonResponse: String, username: String) { val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java) updateHiscoresView(hiscoresResponse, username) } - private fun updateHiscoresView(data: HiscoresResponse?, username: String) { + private fun updateHiscoresView(data: HiscoresResponse, username: String) { val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel - 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 + playerNameLabel?.removeAll() // Clear previous components + if (ironMode != "0") { val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1]) val imageCanvas = ironmanBufferedImage.let { @@ -259,14 +213,11 @@ object HiscoresView { playerNameLabel?.add(imageCanvas) } - val exp_multiplier = data.info.exp_multiplier - nameLabel = JLabel(formatHtmlLabelText(username, secondaryColor, " (${exp_multiplier}x)", primaryColor), JLabel.CENTER).apply { + val nameLabel = JLabel(username, 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() @@ -315,16 +266,20 @@ object HiscoresView { summoning: Int, isMemberWorld: Boolean ): Double { - val base = (defence + hitpoints + floor(prayer.toDouble() / 2)) * 0.25 + val base = (defence + hitpoints + Math.floor(prayer.toDouble() / 2)) * 0.25 val melee = (attack + strength) * 0.325 - val range = floor(ranged * 1.5) * 0.325 - val mage = floor(magic * 1.5) * 0.325 + val range = Math.floor(ranged * 1.5) * 0.325 + val mage = Math.floor(magic * 1.5) * 0.325 val maxCombatType = maxOf(melee, range, mage) - val summoningFactor = if (isMemberWorld) floor(summoning.toDouble() / 8) else 0.0 + val summoningFactor = if (isMemberWorld) Math.floor(summoning.toDouble() / 8) else 0.0 return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0 } + private fun showError(message: String) { + JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE) + } + private fun findComponentByName(container: Container, name: String): Component? { for (component in container.components) { if (name == component.name) { @@ -341,10 +296,10 @@ object HiscoresView { } } - fun createHiscoreSearchView() { + fun createHiscoreSearchView(): JPanel { val hiscorePanel = JPanel().apply { layout = BoxLayout(this, BoxLayout.Y_AXIS) - name = VIEW_NAME + name = "HISCORE_SEARCH_VIEW" background = Constants.COLOR_BACKGROUND_MEDIUM preferredSize = Constants.HISCORE_PANEL_DIMENSION maximumSize = preferredSize @@ -369,13 +324,14 @@ object HiscoresView { add(searchFieldWrapper) } - hiscorePanel.add(Box.createVerticalStrut(10)) + hiscorePanel.add(Helpers.Spacer(height = 10)) hiscorePanel.add(searchPanel) - hiscorePanel.add(Box.createVerticalStrut(10)) + hiscorePanel.add(Helpers.Spacer(height = 10)) + // Adding the player name panel in place of the filterPanel val playerNamePanel = JPanel().apply { - layout = GridBagLayout() // This will center the JLabel both vertically and horizontally - background = TOOLTIP_BACKGROUND.darker() + layout = FlowLayout(FlowLayout.CENTER) + background = VIEW_BACKGROUND_COLOR preferredSize = Constants.FILTER_PANEL_DIMENSION maximumSize = preferredSize minimumSize = preferredSize @@ -383,7 +339,7 @@ object HiscoresView { } hiscorePanel.add(playerNamePanel) - hiscorePanel.add(Box.createVerticalStrut(10)) + hiscorePanel.add(Helpers.Spacer(height = 10)) val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply { background = Constants.COLOR_BACKGROUND_MEDIUM @@ -392,10 +348,10 @@ object HiscoresView { minimumSize = preferredSize } - for (i in SKILL_DISPLAY_ORDER) { + for (i in 0 until 24) { val skillPanel = JPanel().apply { layout = BorderLayout() - background = COLOR_BACKGROUND_DARK + background = Constants.COLOR_SKILL_PANEL preferredSize = Constants.SKILL_PANEL_DIMENSION maximumSize = preferredSize minimumSize = preferredSize @@ -406,9 +362,8 @@ object HiscoresView { val imageCanvas = bufferedImageSprite.let { ImageCanvas(it).apply { - preferredSize = SKILL_SPRITE_DIMENSION - size = SKILL_SPRITE_DIMENSION - fillColor = COLOR_BACKGROUND_DARK + preferredSize = Constants.IMAGE_CANVAS_DIMENSION + size = Constants.IMAGE_CANVAS_DIMENSION } } @@ -421,7 +376,7 @@ object HiscoresView { } val imageContainer = JPanel(FlowLayout(FlowLayout.CENTER, 5, 0)).apply { - background = COLOR_BACKGROUND_DARK + background = Constants.COLOR_BACKGROUND_DARK add(imageCanvas) add(numberLabel) } @@ -439,10 +394,9 @@ object HiscoresView { minimumSize = preferredSize } - val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE)) + val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE)); val totalLevelIcon = ImageCanvas(bufferedImageSprite).apply { - fillColor = COLOR_BACKGROUND_DARK preferredSize = Constants.ICON_DIMENSION_LARGE size = Constants.ICON_DIMENSION_LARGE } @@ -464,7 +418,6 @@ object HiscoresView { val bufferedImageSprite2 = getBufferedImageFromSprite(API.GetSprite(Constants.COMBAT_LVL_SPRITE)) val combatLevelIcon = ImageCanvas(bufferedImageSprite2).apply { - fillColor = COLOR_BACKGROUND_DARK preferredSize = Constants.ICON_DIMENSION_LARGE size = Constants.ICON_DIMENSION_LARGE } @@ -478,7 +431,7 @@ object HiscoresView { } val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply { - background = COLOR_BACKGROUND_DARK + background = Constants.COLOR_BACKGROUND_DARK add(combatLevelIcon) add(combatLevelLabel) } @@ -486,9 +439,8 @@ object HiscoresView { totalCombatPanel.add(totalLevelPanel) totalCombatPanel.add(combatLevelPanel) hiscorePanel.add(totalCombatPanel) - hiscorePanel.add(Box.createVerticalStrut(10)) - hiScoreView = hiscorePanel + return hiscorePanel } data class HiscoresResponse( diff --git a/plugin-playground/src/main/kotlin/KondoKit/ImageCanvas.kt b/plugin-playground/src/main/kotlin/KondoKit/ImageCanvas.kt index 3a9a9f3..8923f08 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/ImageCanvas.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/ImageCanvas.kt @@ -1,6 +1,5 @@ package KondoKit -import KondoKit.plugin.Companion.WIDGET_COLOR import java.awt.Canvas import java.awt.Color import java.awt.Dimension @@ -9,25 +8,31 @@ import java.awt.image.BufferedImage class ImageCanvas(private val image: BufferedImage) : Canvas() { - var fillColor: Color = WIDGET_COLOR - init { + // Manually set the alpha value to 255 (fully opaque) only for pixels that are not fully transparent val width = image.width val height = image.height for (y in 0 until height) { for (x in 0 until width) { + // Retrieve the current pixel color val color = image.getRGB(x, y) + + // Check if the pixel is not fully transparent (i.e., color is not 0) if (color != 0) { + // Ensure the alpha is set to 255 (fully opaque) val newColor = (color and 0x00FFFFFF) or (0xFF shl 24) + + // Set the pixel with the updated color image.setRGB(x, y, newColor) } } } } + override fun paint(g: Graphics) { super.paint(g) - g.color = fillColor + g.color = Color(27, 27, 27) g.fillRect(0, 0, width, height) g.drawImage(image, 0, 0, width, height, this) } diff --git a/plugin-playground/src/main/kotlin/KondoKit/KondoKitUtils.kt b/plugin-playground/src/main/kotlin/KondoKit/KondoKitUtils.kt new file mode 100644 index 0000000..3ef3c39 --- /dev/null +++ b/plugin-playground/src/main/kotlin/KondoKit/KondoKitUtils.kt @@ -0,0 +1,100 @@ +package KondoKit + +import java.awt.Color +import java.lang.reflect.Field +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type + + +/* + This is used for the runtime editing of plugin variables. + To expose fields name them starting with `kondoExposed_` + When they are applied this will trigger an invoke of OnKondoValueUpdated() + if it is implemented. Check GroundItems plugin for an example. + */ + +object KondoKitUtils { + const val KONDO_PREFIX = "kondoExposed_" + + fun isKondoExposed(field: Field): Boolean { + return field.name.startsWith(KONDO_PREFIX) + } + + fun getKondoExposedFields(instance: Any): List { + val exposedFields: MutableList = 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() // 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() + + 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() + } + } + + + } + +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/LootTrackerView.kt b/plugin-playground/src/main/kotlin/KondoKit/LootTrackerView.kt index cfdb4a2..9da6c6b 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/LootTrackerView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/LootTrackerView.kt @@ -1,25 +1,19 @@ package KondoKit -import KondoKit.Helpers.addMouseListenerToAll import KondoKit.Helpers.formatHtmlLabelText import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite import KondoKit.XPTrackerView.wrappedWidget -import KondoKit.plugin.Companion.POPUP_BACKGROUND -import KondoKit.plugin.Companion.POPUP_FOREGROUND -import KondoKit.plugin.Companion.TITLE_BAR_COLOR -import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR import KondoKit.plugin.Companion.WIDGET_COLOR import KondoKit.plugin.Companion.primaryColor import KondoKit.plugin.Companion.secondaryColor -import KondoKit.plugin.StateManager.focusedView import plugin.api.API -import rt4.* +import rt4.NpcTypeList +import rt4.ObjStackNode +import rt4.Player +import rt4.SceneGraph import java.awt.* -import java.awt.Font -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent import java.awt.image.BufferedImage import java.io.BufferedReader import java.io.InputStreamReader @@ -28,23 +22,19 @@ import java.net.URL import java.nio.charset.StandardCharsets import java.text.DecimalFormat import javax.swing.* -import kotlin.math.ceil object LootTrackerView { private const val SNAPSHOT_LIFESPAN = 10 - const val BAG_ICON = 900 + const val BAG_ICON = 900; val npcDeathSnapshots = mutableMapOf() var gePriceMap = loadGEPrices() - const val VIEW_NAME = "LOOT_TRACKER_VIEW" private val lootItemPanels = mutableMapOf>() private val npcKillCounts = mutableMapOf() private var totalTrackerWidget: XPWidget? = null - var lastConfirmedKillNpcId = -1 - private var customToolTipWindow: JWindow? = null - var lootTrackerView: JPanel? = null + var lastConfirmedKillNpcId = -1; fun loadGEPrices(): Map { - return if (plugin.useLiveGEPrices) { + return if (plugin.kondoExposed_useLiveGEPrices) { try { println("LootTracker: Loading Remote GE Prices") val url = URL("https://cdn.2009scape.org/gedata/latest.json") @@ -79,7 +69,7 @@ object LootTrackerView { } else { try { println("LootTracker: Loading Local GE Prices") - BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/item_configs.json"), StandardCharsets.UTF_8)) + BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("item_configs.json"), StandardCharsets.UTF_8)) .useLines { lines -> val json = lines.joinToString("\n") val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" } @@ -104,37 +94,19 @@ object LootTrackerView { - fun createLootTrackerView() { - lootTrackerView = JPanel().apply { - layout = BoxLayout(this, BoxLayout.Y_AXIS) // Use BoxLayout on Y axis to stack widgets vertically + fun createLootTrackerView(): JPanel { + return JPanel().apply { + layout = FlowLayout(FlowLayout.CENTER, 0, 5) background = VIEW_BACKGROUND_COLOR + preferredSize = Dimension(270, 700) + maximumSize = Dimension(270, 700) + minimumSize = Dimension(270, 700) add(Box.createVerticalStrut(5)) totalTrackerWidget = createTotalLootWidget() - - 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)) + add(wrappedWidget(totalTrackerWidget!!.panel)) + add(Helpers.Spacer(height = 15)) revalidate() - if(focusedView == VIEW_NAME) - repaint() + repaint() } } @@ -150,7 +122,7 @@ object LootTrackerView { totalTrackerWidget?.let { it.previousXp += newVal it.xpPerHourLabel.text = formatHtmlLabelText("Total Value: ", primaryColor, formatValue(it.previousXp) + " gp", secondaryColor) - it.container.repaint() + it.panel.repaint() } } @@ -160,12 +132,12 @@ object LootTrackerView { val l2 = createLabel(formatHtmlLabelText("Total Count: ", primaryColor, "0", secondaryColor)) return XPWidget( skillId = -1, - container = createWidgetPanel(bufferedImageSprite,l2,l1), + panel = createWidgetPanel(bufferedImageSprite,l2,l1), xpGainedLabel = l2, xpLeftLabel = JLabel(), actionsRemainingLabel = JLabel(), xpPerHourLabel = l1, - progressBar = ProgressBar(0.0, Color(0,0,0)), // unused. + progressBar = ProgressBar(0.0, Color(150, 50, 50)), totalXpGained = 0, startTime = System.currentTimeMillis(), previousXp = 0 @@ -174,19 +146,16 @@ object LootTrackerView { private fun createWidgetPanel(bufferedImageSprite: BufferedImage, l1 : JLabel, l2 : JLabel): Panel { val imageCanvas = ImageCanvas(bufferedImageSprite).apply { - preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height) - minimumSize = preferredSize - maximumSize = preferredSize - size = preferredSize + size = Dimension(width, height) background = WIDGET_COLOR } - val imageContainer = Panel(BorderLayout()).apply { + val imageContainer = Panel(FlowLayout()).apply { background = WIDGET_COLOR - add(imageCanvas, BorderLayout.NORTH) + add(imageCanvas) } - return Panel(BorderLayout(5, 0)).apply { + return Panel(BorderLayout(5, 5)).apply { background = WIDGET_COLOR preferredSize = TOTAL_XP_WIDGET_SIZE add(imageContainer, BorderLayout.WEST) @@ -195,7 +164,7 @@ object LootTrackerView { } private fun createTextPanel(l1 : JLabel, l2: JLabel): Panel { - return Panel(GridLayout(2, 1, 5, 0)).apply { + return Panel(GridLayout(2, 1, 5, 5)).apply { background = WIDGET_COLOR add(l1) add(l2) @@ -204,7 +173,7 @@ object LootTrackerView { private fun createLabel(text: String): JLabel { return JLabel(text).apply { - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + font = Font("Arial", Font.PLAIN, 11) horizontalAlignment = JLabel.LEFT } } @@ -220,19 +189,12 @@ object LootTrackerView { // Recalculate lootPanel size based on the number of unique items. val totalItems = lootItemPanels[npcName]?.size ?: 0 - val rowsNeeded = ceil(totalItems / 6.0).toInt() - val lootPanelHeight = rowsNeeded * (40) + val rowsNeeded = Math.ceil(totalItems / 6.0).toInt() + val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1) + 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() - - if(focusedView == VIEW_NAME) - lootPanel.repaint() + lootPanel.repaint() } } @@ -242,93 +204,27 @@ object LootTrackerView { } private fun createItemPanel(itemId: Int, quantity: Int): JPanel { - val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 1, 3153952)) - - // Create the panel for the item - val itemPanel = FixedSizePanel(Dimension(36, 32)).apply { + val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 0, 0)) + return FixedSizePanel(Dimension(36, 32)).apply { preferredSize = Dimension(36, 32) background = WIDGET_COLOR minimumSize = preferredSize maximumSize = preferredSize - - val imageCanvas = ImageCanvas(bufferedImageSprite).apply { + add(ImageCanvas(bufferedImageSprite).apply { preferredSize = Dimension(36, 32) background = WIDGET_COLOR minimumSize = preferredSize maximumSize = preferredSize - } - - // Add the imageCanvas to the panel - add(imageCanvas, BorderLayout.CENTER) - - // Put the itemId as a property for reference + }, BorderLayout.CENTER) 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 = "
" + - "${itemDef.name} x $quantity
" + - "GE: ${formatValue(totalGePrice)} ${geText}
" + - "HA: ${formatValue(totalHaPrice)} ${haText}
" - - val _font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) - if (customToolTipWindow == null) { - customToolTipWindow = JWindow().apply { - contentPane = JLabel(text).apply { - border = BorderFactory.createLineBorder(Color.BLACK) - isOpaque = true - background = TOOLTIP_BACKGROUND - foreground = Color.WHITE - font = _font - } - pack() - } - } - - // Calculate the tooltip location relative to the parent component - val screenLocation = parentComponent.locationOnScreen - customToolTipWindow!!.setLocation(screenLocation.x + location.x, screenLocation.y + location.y + 20) - customToolTipWindow!!.isVisible = true - } - - // Function to hide the custom tooltip - fun hideCustomToolTip() { - customToolTipWindow?.isVisible = false - customToolTipWindow = null // Nullify the global instance - } - - private fun updateItemPanelIcon(panel: JPanel, itemId: Int, quantity: Int) { panel.removeAll() panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER) panel.revalidate() - if(focusedView == VIEW_NAME) - panel.repaint() + panel.repaint() } private fun updateKillCountLabel(lootTrackerPanel: JPanel, npcName: String) { @@ -350,11 +246,9 @@ object LootTrackerView { ?.apply { val newValue = (getClientProperty("val") as? Int ?: 0) + valueOfNewDrops.toInt() text = "${formatValue(newValue)} gp" - foreground = primaryColor putClientProperty("val", newValue) revalidate() - if(focusedView == VIEW_NAME) - repaint() + repaint() } } } @@ -382,7 +276,7 @@ object LootTrackerView { if (newDrops.isNotEmpty()) { val npcName = NpcTypeList.get(npcId).name - lastConfirmedKillNpcId = npcId + lastConfirmedKillNpcId = npcId; handleNewDrops(npcName.toString(), newDrops, lootTrackerView) toRemove.add(npcId) } else if (snapshot.age >= SNAPSHOT_LIFESPAN) { @@ -414,11 +308,11 @@ object LootTrackerView { private fun handleNewDrops(npcName: String, newDrops: Set, lootTrackerView: JPanel) { findLootItemsPanel(lootTrackerView, npcName)?.let { } ?: run { + // Panel doesn't exist, so create and add it lootTrackerView.add(createLootFrame(npcName)) - lootTrackerView.add(Box.createVerticalStrut(8)) + lootTrackerView.add(Helpers.Spacer(height = 15)) lootTrackerView.revalidate() - if(focusedView == VIEW_NAME) - lootTrackerView.repaint() + lootTrackerView.repaint() } npcKillCounts[npcName] = npcKillCounts.getOrDefault(npcName, 0) + 1 @@ -427,7 +321,7 @@ object LootTrackerView { newDrops.forEach { drop -> val geValue = (gePriceMap[drop.id.toString()]?.toInt() ?: 0) * drop.quantity updateValueLabel(lootTrackerView, geValue.toString(), npcName) - plugin.registerDrawAction { addItemToLootPanel(lootTrackerView, drop, npcName) } + addItemToLootPanel(lootTrackerView, drop, npcName) updateTotalValue(geValue) } } @@ -436,30 +330,29 @@ object LootTrackerView { val childFramePanel = JPanel().apply { layout = BoxLayout(this, BoxLayout.Y_AXIS) background = WIDGET_COLOR - minimumSize = Dimension(230, 0) - maximumSize = Dimension(230, 700) - name = "HELLO_WORLD" + minimumSize = Dimension(270, 0) + maximumSize = Dimension(270, 700) } val labelPanel = JPanel(BorderLayout()).apply { - background = TITLE_BAR_COLOR + background = Color(21, 21, 21) border = BorderFactory.createEmptyBorder(5, 5, 5, 5) - maximumSize = Dimension(230, 24) + maximumSize = Dimension(270, 24) minimumSize = maximumSize preferredSize = maximumSize } val killCount = npcKillCounts.getOrPut(npcName) { 0 } val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply { - foreground = secondaryColor - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + foreground = Color(200, 200, 200) + font = Font("Arial", Font.PLAIN, 12) horizontalAlignment = JLabel.LEFT name = "killCountLabel_$npcName" } val valueLabel = JLabel("0 gp").apply { - foreground = secondaryColor - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + foreground = Color(200, 200, 200) + font = Font("Arial", Font.PLAIN, 12) horizontalAlignment = JLabel.RIGHT name = "valueLabel_$npcName" } @@ -477,120 +370,19 @@ object LootTrackerView { lootItemPanels[npcName] = mutableMapOf() + // Determine number of items and adjust size of lootPanel + val totalItems = lootItemPanels[npcName]?.size ?: 0 + val rowsNeeded = Math.ceil(totalItems / 6.0).toInt() + val lootPanelHeight = rowsNeeded * 36 + (rowsNeeded - 1) // Height per row = 36 + spacing + lootPanel.preferredSize = Dimension(270, lootPanelHeight+10) + childFramePanel.add(labelPanel) childFramePanel.add(lootPanel) + childFramePanel.add(lootPanel) - val popupMenu = removeLootFrameMenu(childFramePanel, npcName) - - // Create a custom MouseListener - val rightClickListener = object : MouseAdapter() { - override fun mousePressed(e: MouseEvent) { - if (e.isPopupTrigger) { - popupMenu.show(e.component, e.x, e.y) - } - } - - override fun mouseReleased(e: MouseEvent) { - if (e.isPopupTrigger) { - popupMenu.show(e.component, e.x, e.y) - } - } - } - - labelPanel.addMouseListener(rightClickListener) return childFramePanel } - private fun removeLootFrameMenu(toRemove: JPanel, npcName: String): JPopupMenu { - // Create a popup menu - val popupMenu = JPopupMenu() - val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) - - popupMenu.background = POPUP_BACKGROUND - - // Create menu items with custom font and colors - val menuItem1 = JMenuItem("Remove").apply { - font = rFont // Set custom font - background = POPUP_BACKGROUND // Dark background for item - foreground = POPUP_FOREGROUND // Light text color for item - } - popupMenu.add(menuItem1) - menuItem1.addActionListener { - lootItemPanels[npcName]?.clear() - npcKillCounts[npcName] = 0 - lootTrackerView?.let { parent -> - val components = parent.components - val toRemoveIndex = components.indexOf(toRemove) - if (toRemoveIndex >= 0 && toRemoveIndex < components.size - 1) { - val nextComponent = components[toRemoveIndex + 1] - if (nextComponent is Box.Filler) { - // Nasty way to remove the Box.createVerticalStrut(8) after - // the lootpanel. - parent.remove(nextComponent) - } - } - parent.remove(toRemove) - parent.revalidate() - parent.repaint() - } - } - return popupMenu - } - - - private fun resetLootTrackerMenu(): JPopupMenu { - // Create a popup menu - val popupMenu = JPopupMenu() - val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) - - popupMenu.background = POPUP_BACKGROUND - - // Create menu items with custom font and colors - val menuItem1 = JMenuItem("Reset Loot Tracker").apply { - font = rFont // Set custom font - background = POPUP_BACKGROUND // Dark background for item - foreground = POPUP_FOREGROUND // Light text color for item - } - popupMenu.add(menuItem1) - menuItem1.addActionListener { - plugin.registerDrawAction { - resetLootTracker() - } - } - return popupMenu - } - - private fun resetLootTracker(){ - lootTrackerView?.removeAll() - npcKillCounts.clear() - lootItemPanels.clear() - totalTrackerWidget = createTotalLootWidget() - - val wrapped = wrappedWidget(totalTrackerWidget!!.container) - val _popupMenu = resetLootTrackerMenu() - - // Create a custom MouseListener - val rightClickListener = object : MouseAdapter() { - override fun mousePressed(e: MouseEvent) { - if (e.isPopupTrigger) { - _popupMenu.show(e.component, e.x, e.y) - } - } - - override fun mouseReleased(e: MouseEvent) { - if (e.isPopupTrigger) { - _popupMenu.show(e.component, e.x, e.y) - } - } - } - addMouseListenerToAll(wrapped,rightClickListener) - wrapped.addMouseListener(rightClickListener) - lootTrackerView?.add(Box.createVerticalStrut(5)) - lootTrackerView?.add(wrapped) - lootTrackerView?.add(Box.createVerticalStrut(8)) - lootTrackerView?.revalidate() - lootTrackerView?.repaint() - } class FixedSizePanel(private val fixedSize: Dimension) : JPanel() { override fun getPreferredSize(): Dimension { diff --git a/plugin-playground/src/main/kotlin/KondoKit/ProgressBar.kt b/plugin-playground/src/main/kotlin/KondoKit/ProgressBar.kt index 2c5546f..0554641 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/ProgressBar.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/ProgressBar.kt @@ -1,22 +1,19 @@ package KondoKit -import KondoKit.plugin.Companion.PROGRESS_BAR_FILL -import KondoKit.plugin.Companion.secondaryColor import java.awt.Canvas import java.awt.Color -import java.awt.Dimension import java.awt.Font import java.awt.Graphics class ProgressBar( - private var progress: Double, - private val barColor: Color, - private var currentLevel: Int = 0, - private var nextLevel: Int = 1 + private var progress: Double, + private val barColor: Color, + private var currentLevel: Int = 0, + private var nextLevel: Int = 1 ) : Canvas() { init { - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + font = Font("Arial", Font.PLAIN, 12) } override fun paint(g: Graphics) { @@ -28,32 +25,22 @@ class ProgressBar( g.fillRect(0, 0, width, this.height) // Draw the unfilled part of the progress bar - g.color = PROGRESS_BAR_FILL + g.color = Color(100, 100, 100) 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 - drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, secondaryColor) + g.color = Color(255, 255, 255) + g.drawString("Lvl. $currentLevel", 5, this.height / 2 + 4) // Draw the percentage in the middle val percentageText = String.format("%.2f%%", progress) val percentageWidth = g.fontMetrics.stringWidth(percentageText) - drawTextWithShadow(g, percentageText, (this.width - percentageWidth) / 2, textY, secondaryColor) + g.drawString(percentageText, (this.width - percentageWidth) / 2, this.height / 2 + 4) // Draw the next level on the far right val nextLevelText = "Lvl. $nextLevel" val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText) - 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 + g.drawString(nextLevelText, this.width - nextLevelWidth - 5, this.height / 2 + 4) } fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) { @@ -63,15 +50,4 @@ class ProgressBar( if(isVisible) repaint() } - - // Helper function to draw text with a shadow effect - private fun drawTextWithShadow(g: Graphics, text: String, x: Int, y: Int, textColor: Color) { - // Draw shadow (black text with -1 x and -1 y offset) - g.color = Color(0, 0, 0) - g.drawString(text, x + 1, y + 1) - - // Draw actual text on top - g.color = textColor - g.drawString(text, x, y) - } } diff --git a/plugin-playground/src/main/kotlin/KondoKit/ReflectiveEditorView.kt b/plugin-playground/src/main/kotlin/KondoKit/ReflectiveEditorView.kt index 5b6cfd2..a9227c9 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/ReflectiveEditorView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/ReflectiveEditorView.kt @@ -1,324 +1,149 @@ package KondoKit -import KondoKit.Helpers.convertValue -import KondoKit.Helpers.showToast -import KondoKit.plugin.Companion.TITLE_BAR_COLOR -import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND +import KondoKit.KondoKitUtils.convertValue import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR import KondoKit.plugin.Companion.WIDGET_COLOR import KondoKit.plugin.Companion.primaryColor import KondoKit.plugin.Companion.secondaryColor -import KondoKit.plugin.StateManager.focusedView import plugin.Plugin -import plugin.PluginInfo import plugin.PluginRepository import java.awt.* -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent +import java.lang.reflect.Field import java.util.* import java.util.Timer import javax.swing.* -import kotlin.math.ceil - -/* - This is used for the runtime editing of plugin variables. - To expose fields add the @Exposed annotation. - When they are applied this will trigger an invoke of OnKondoValueUpdated() - if it is implemented. Check GroundItems plugin for an example. - */ - object ReflectiveEditorView { - var reflectiveEditorView: JPanel? = null - private val loadedPlugins: MutableList = mutableListOf() - const val VIEW_NAME = "REFLECTIVE_EDITOR_VIEW" - fun createReflectiveEditorView() { + fun createReflectiveEditorView(): JPanel { val reflectiveEditorPanel = JPanel(BorderLayout()) reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR reflectiveEditorPanel.add(Box.createVerticalStrut(5)) reflectiveEditorPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) - reflectiveEditorView = reflectiveEditorPanel - addPlugins(reflectiveEditorView!!) + return reflectiveEditorPanel } fun addPlugins(reflectiveEditorView: JPanel) { - reflectiveEditorView.removeAll() // clear previous - loadedPlugins.clear() try { val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins") loadedPluginsField.isAccessible = true val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *> - for ((pluginInfo, plugin) in loadedPlugins) { - addPluginToEditor(reflectiveEditorView, pluginInfo as PluginInfo, plugin as Plugin) + for ((_, plugin) in loadedPlugins) { + addPluginToEditor(reflectiveEditorView, plugin as Plugin) } } catch (e: Exception) { e.printStackTrace() } - - // Add a centered box for plugins that have no exposed fields - if (loadedPlugins.isNotEmpty()) { - val noExposedPanel = JPanel(BorderLayout()) - noExposedPanel.background = VIEW_BACKGROUND_COLOR - noExposedPanel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) - - val label = JLabel("Loaded Plugins without Exposed Fields", SwingConstants.CENTER) - label.font = Font("RuneScape Small", Font.PLAIN, 16) - label.foreground = primaryColor - noExposedPanel.add(label, BorderLayout.NORTH) - - val pluginsList = JList(loadedPlugins.toTypedArray()) - pluginsList.background = WIDGET_COLOR - pluginsList.foreground = secondaryColor - pluginsList.font = Font("RuneScape Small", Font.PLAIN, 16) - - // Wrap the JList in a JScrollPane with a fixed height - val maxScrollPaneHeight = 200 - val scrollPane = JScrollPane(pluginsList).apply { - verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED - horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER - } - - // Create a wrapper panel with BoxLayout to constrain the scroll pane - val scrollPaneWrapper = JPanel().apply { - layout = BoxLayout(this, BoxLayout.Y_AXIS) - add(scrollPane) - } - - noExposedPanel.add(scrollPaneWrapper, BorderLayout.CENTER) - - // Center the panel within the reflectiveEditorView - val centeredPanel = JPanel().apply { - preferredSize = Dimension(240, maxScrollPaneHeight) - maximumSize = preferredSize - minimumSize = preferredSize - } - centeredPanel.layout = BoxLayout(centeredPanel, BoxLayout.Y_AXIS) - centeredPanel.add(Box.createVerticalGlue()) - centeredPanel.add(noExposedPanel) - centeredPanel.add(Box.createVerticalGlue()) - - reflectiveEditorView.add(Box.createVerticalStrut(10)) - reflectiveEditorView.add(centeredPanel) - } - - reflectiveEditorView.revalidate() - if(focusedView == VIEW_NAME) - reflectiveEditorView.repaint() + reflectiveEditorView.repaint() } - private fun addPluginToEditor(reflectiveEditorView: JPanel, pluginInfo : PluginInfo, plugin: Plugin) { + private fun addPluginToEditor(reflectiveEditorView: JPanel, plugin: Any) { 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" - } - } + val fieldNotifier = KondoKitUtils.FieldNotifier(plugin) + val exposedFields = KondoKitUtils.getKondoExposedFields(plugin) 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) + 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 v$version", SwingConstants.CENTER) + val label = JLabel("$packageName", SwingConstants.CENTER) label.foreground = primaryColor - label.font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + label.font = Font("Arial", Font.BOLD, 14) 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>)).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) - } - - if (exposedFields.isNotEmpty()) { - reflectiveEditorView.add(Box.createVerticalStrut(5)) - } } - else { - loadedPlugins.add(plugin.javaClass.`package`.name) + + for (field in exposedFields) { + field.isAccessible = true + + val fieldPanel = JPanel() + fieldPanel.layout = GridBagLayout() + fieldPanel.background = WIDGET_COLOR // Match the background for minimal borders + fieldPanel.foreground = secondaryColor + fieldPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0) // No visible border, just spacing + fieldPanel.maximumSize = Dimension(Int.MAX_VALUE, 40) + + val gbc = GridBagConstraints() + gbc.insets = Insets(0, 5, 0, 5) // Less padding, more minimal spacing + + val label = JLabel(field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).capitalize()) + label.foreground = secondaryColor + gbc.gridx = 0 + gbc.gridy = 0 + gbc.weightx = 0.0 + gbc.anchor = GridBagConstraints.WEST + fieldPanel.add(label, gbc) + + val textField = JTextField(field.get(plugin)?.toString() ?: "") + textField.background = VIEW_BACKGROUND_COLOR + textField.foreground = secondaryColor + textField.border = BorderFactory.createLineBorder(WIDGET_COLOR, 1) // Subtle border + gbc.gridx = 1 + gbc.gridy = 0 + gbc.weightx = 1.0 + gbc.fill = GridBagConstraints.HORIZONTAL + fieldPanel.add(textField, gbc) + + val applyButton = JButton("Apply") + applyButton.background = primaryColor + applyButton.foreground = VIEW_BACKGROUND_COLOR + applyButton.border = BorderFactory.createLineBorder(primaryColor, 1) + gbc.gridx = 2 + gbc.gridy = 0 + gbc.weightx = 0.0 + gbc.fill = GridBagConstraints.NONE + applyButton.addActionListener { + try { + val newValue = convertValue(field.type, field.genericType, textField.text) + fieldNotifier.setFieldValue(field, newValue) + JOptionPane.showMessageDialog( + null, + "${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)} updated successfully!" + ) + } catch (e: Exception) { + JOptionPane.showMessageDialog( + null, + "Failed to update ${field.name.removePrefix(KondoKitUtils.KONDO_PREFIX)}: ${e.message}", + "Error", + JOptionPane.ERROR_MESSAGE + ) + } + } + + fieldPanel.add(applyButton, gbc) + reflectiveEditorView.add(fieldPanel) + + var previousValue = field.get(plugin)?.toString() + val timer = Timer() + timer.schedule(object : TimerTask() { + override fun run() { + val currentValue = field.get(plugin)?.toString() + if (currentValue != previousValue) { + previousValue = currentValue + SwingUtilities.invokeLater { + fieldNotifier.notifyFieldChange(field, currentValue) + } + } + } + }, 0, 1000) + + fieldNotifier.addObserver(object : KondoKitUtils.FieldObserver { + override fun onFieldChange(field: Field, newValue: Any?) { + if (field.name.removePrefix(KondoKitUtils.KONDO_PREFIX).equals(label.text, ignoreCase = true)) { + textField.text = newValue?.toString() ?: "" + textField.revalidate() + textField.repaint() + } + } + }) + } + if (exposedFields.isNotEmpty()) { + reflectiveEditorView.add(Box.createVerticalStrut(10)) } } - - 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("
$text
").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 = "
$text
" - 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 - } -} \ No newline at end of file +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt b/plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt deleted file mode 100644 index 0e772b9..0000000 --- a/plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt +++ /dev/null @@ -1,162 +0,0 @@ -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) - } - } -} diff --git a/plugin-playground/src/main/kotlin/KondoKit/SpriteToBufferedImage.kt b/plugin-playground/src/main/kotlin/KondoKit/SpriteToBufferedImage.kt index 0258838..e273c98 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/SpriteToBufferedImage.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/SpriteToBufferedImage.kt @@ -4,196 +4,109 @@ import rt4.GlIndexedSprite import rt4.GlSprite import rt4.SoftwareIndexedSprite import rt4.SoftwareSprite -import java.awt.Color import java.awt.image.BufferedImage -// Define interfaces for common sprite types -interface BaseSprite { - val width: Int - val height: Int -} - -interface IndexedSprite : BaseSprite { - val pixels: ByteArray - val palette: IntArray -} - -interface NonIndexedSprite : BaseSprite { - val pixels: IntArray? -} - -// Adapter functions for existing sprite types -fun adaptSoftwareSprite(sprite: SoftwareSprite): NonIndexedSprite { - return object : NonIndexedSprite { - override val width: Int = sprite.width - override val height: Int = sprite.height - override val pixels: IntArray? = sprite.pixels - } -} - -fun adaptSoftwareIndexedSprite(sprite: SoftwareIndexedSprite): IndexedSprite { - return object : IndexedSprite { - override val width: Int = sprite.width - override val height: Int = sprite.height - override val pixels: ByteArray = sprite.pixels - override val palette: IntArray = sprite.pallet - } -} - -fun adaptGlSprite(sprite: GlSprite): NonIndexedSprite { - return object : NonIndexedSprite { - override val width: Int = sprite.width - override val height: Int = sprite.height - override val pixels: IntArray? = sprite.pixels - } -} - -fun adaptGlIndexedSprite(sprite: GlIndexedSprite): IndexedSprite { - return object : IndexedSprite { - override val width: Int = sprite.width - override val height: Int = sprite.height - override val pixels: ByteArray = sprite.pixels - override val palette: IntArray = sprite.pallet - } -} - object SpriteToBufferedImage { /** - * Converts a BaseSprite into a BufferedImage. - * - * Handles both indexed and non-indexed sprites, with optional tinting and grayscale. + * Converts a SoftwareSprite back into a BufferedImage. * * @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. */ - private fun convertToBufferedImage( - sprite: BaseSprite, - tint: Color? = null, - grayscale: Boolean = false, - brightnessBoost: Float = 1.0f - ): BufferedImage { + fun convertToBufferedImage(sprite: SoftwareSprite): BufferedImage { val width = sprite.width val height = sprite.height + val pixels = sprite.pixels + + // Create a BufferedImage with ARGB color model val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) - when (sprite) { - is IndexedSprite -> { - val pixels = sprite.pixels - val palette = sprite.palette - - // Manually set pixels using the palette - for (y in 0 until height) { - for (x in 0 until width) { - val index = pixels[y * width + x].toInt() and 0xFF - val color = palette[index] - - // Apply grayscale or tint if provided - val finalColor = if (grayscale) { - applyGrayscale(Color(color, true), brightnessBoost) - } else if (tint != null) { - applyTint(Color(color, true), tint, brightnessBoost) - } else { - applyBrightness(Color(color, true), brightnessBoost) - } - - image.setRGB(x, y, finalColor.rgb) - } - } - } - is NonIndexedSprite -> { - val pixels = sprite.pixels ?: return image // Handle null case for GlSprite - - // Manually set pixels directly - for (y in 0 until height) { - for (x in 0 until width) { - val color = pixels[y * width + x] - - // Apply grayscale or tint if provided - val finalColor = if (grayscale) { - applyGrayscale(Color(color, true), brightnessBoost) - } else if (tint != null) { - applyTint(Color(color, true), tint, brightnessBoost) - } else { - applyBrightness(Color(color, true), brightnessBoost) - } - - image.setRGB(x, y, finalColor.rgb) - } - } + // 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 } - /** - * Applies a tint to a given color using the tint's alpha value to control the intensity. - * - * @param original The original color. - * @param tint The tint color to be applied. - * @param brightnessBoost A multiplier to boost the brightness of the image. - * @return The tinted color. - */ - private fun applyTint(original: Color, tint: Color, brightnessBoost: Float): Color { - val boostedColor = applyBrightness(original, brightnessBoost) - val r = (boostedColor.red * tint.red / 255).coerceIn(0, 255) - val g = (boostedColor.green * tint.green / 255).coerceIn(0, 255) - val b = (boostedColor.blue * tint.blue / 255).coerceIn(0, 255) - return Color(r, g, b, boostedColor.alpha) + fun convertToBufferedImage(sprite: SoftwareIndexedSprite): BufferedImage { + val width = sprite.width + val height = sprite.height + val pixels = sprite.pixels // byte[] + val palette = sprite.pallet + + // Create a BufferedImage with ARGB color model + val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) + + // Manually set pixels using the palette + for (y in 0 until height) { + for (x in 0 until width) { + // Get the index from the sprite's pixel array + val index = pixels[y * width + x].toInt() and 0xFF + // Map the index to a color in the palette + val color = palette[index] + // Set the ARGB color in the BufferedImage + image.setRGB(x, y, color) + } + } + return image } - /** - * Boosts the brightness of a given color. - * - * @param original The original color. - * @param factor The multiplier to boost the brightness. - * @return The color with boosted brightness. - */ - private fun applyBrightness(original: Color, factor: Float): Color { - val r = (original.red * factor).coerceIn(0.0f, 255.0f).toInt() - val g = (original.green * factor).coerceIn(0.0f, 255.0f).toInt() - val b = (original.blue * factor).coerceIn(0.0f, 255.0f).toInt() - return Color(r, g, b, original.alpha) + fun convertToBufferedImage(sprite: GlIndexedSprite): BufferedImage { + val width = sprite.width + val height = sprite.height + val pixels = sprite.pixels // byte[] + val palette = sprite.pallet + + // Create a BufferedImage with ARGB color model + val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) + + // Manually set pixels using the palette + for (y in 0 until height) { + for (x in 0 until width) { + // Get the index from the sprite's pixel array + val index = pixels[y * width + x].toInt() and 0xFF + // Map the index to a color in the palette + val color = palette[index] + // Set the ARGB color in the BufferedImage + image.setRGB(x, y, color) + } + } + return image } - /** - * Converts a color to grayscale and applies a brightness boost. - * - * @param original The original color. - * @param brightnessBoost A multiplier to boost the brightness. - * @return The grayscale version of the color with boosted brightness. - */ - private fun applyGrayscale(original: Color, brightnessBoost: Float): Color { - // Calculate the grayscale value using the luminosity method - val grayValue = (0.3 * original.red + 0.59 * original.green + 0.11 * original.blue).toInt() - val boostedGray = (grayValue * brightnessBoost).coerceIn(0.0f, 255.0f).toInt() - return Color(boostedGray, boostedGray, boostedGray, original.alpha) + + fun convertToBufferedImage(sprite: GlSprite): BufferedImage { + val width = sprite.width + val height = sprite.height + val pixels = sprite.pixels + + // Create a BufferedImage with ARGB color model + val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) + + if(pixels == null) { + return image + } + + // Manually set pixels and print the pixel data + for (y in 0 until height) { + for (x in 0 until width) { + val pixel = pixels[y * width + x] + image.setRGB(x, y, pixel) + } + } + return image } - /** - * Converts an unknown sprite object into a BufferedImage if it matches a known sprite type. - * - * @param sprite The sprite object to be converted. - * @param tint An optional Color to tint the image. - * @param grayscale If true, converts the image to grayscale. - * @param brightnessBoost A multiplier to boost the brightness of the image. - * @return The BufferedImage created from the sprite or a default image if unsupported. - */ - fun getBufferedImageFromSprite( - sprite: Any?, - tint: Color? = null, - grayscale: Boolean = false, - brightnessBoost: Float = 1.0f - ): BufferedImage { - return when (sprite) { - is SoftwareSprite -> convertToBufferedImage(adaptSoftwareSprite(sprite), tint, grayscale, brightnessBoost) - is SoftwareIndexedSprite -> convertToBufferedImage(adaptSoftwareIndexedSprite(sprite), tint, grayscale, brightnessBoost) - is GlSprite -> convertToBufferedImage(adaptGlSprite(sprite), tint, grayscale, brightnessBoost) - is GlIndexedSprite -> convertToBufferedImage(adaptGlIndexedSprite(sprite), tint, grayscale, brightnessBoost) - else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) // Default empty image for unsupported types + fun getBufferedImageFromSprite(sprite: Any?) : BufferedImage { + return when(sprite){ + is GlSprite -> convertToBufferedImage(sprite) + is SoftwareSprite -> convertToBufferedImage(sprite) + is SoftwareIndexedSprite -> convertToBufferedImage(sprite) + is GlIndexedSprite -> convertToBufferedImage(sprite) + else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) } } } \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/Themes.kt b/plugin-playground/src/main/kotlin/KondoKit/Themes.kt deleted file mode 100644 index 1b7988d..0000000 --- a/plugin-playground/src/main/kotlin/KondoKit/Themes.kt +++ /dev/null @@ -1,94 +0,0 @@ -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 - ) -} diff --git a/plugin-playground/src/main/kotlin/KondoKit/XPTable.kt b/plugin-playground/src/main/kotlin/KondoKit/XPTable.kt index 78138fe..38df208 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/XPTable.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/XPTable.kt @@ -6,9 +6,9 @@ import rt4.Node object XPTable { - private const val MAX_LEVEL = 99 - private const val INVALID_LEVEL = -1 - private const val SKILLS_XP_TABLE = 716 + const val MAX_LEVEL = 99 + const val INVALID_LEVEL = -1 + const val SKILLS_XP_TABLE = 716 private var xpTable: MutableList = mutableListOf() diff --git a/plugin-playground/src/main/kotlin/KondoKit/XPTrackerView.kt b/plugin-playground/src/main/kotlin/KondoKit/XPTrackerView.kt index b975194..0472ce9 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/XPTrackerView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/XPTrackerView.kt @@ -1,43 +1,35 @@ package KondoKit -import KondoKit.Helpers.addMouseListenerToAll import KondoKit.Helpers.formatHtmlLabelText import KondoKit.Helpers.formatNumber import KondoKit.Helpers.getProgressBarColor import KondoKit.Helpers.getSpriteId import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite import KondoKit.plugin.Companion.IMAGE_SIZE -import KondoKit.plugin.Companion.LVL_ICON -import KondoKit.plugin.Companion.POPUP_BACKGROUND -import KondoKit.plugin.Companion.POPUP_FOREGROUND import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR import KondoKit.plugin.Companion.WIDGET_COLOR import KondoKit.plugin.Companion.WIDGET_SIZE -import KondoKit.plugin.Companion.playerXPMultiplier +import KondoKit.plugin.Companion.kondoExposed_playerXPMultiplier import KondoKit.plugin.Companion.primaryColor import KondoKit.plugin.Companion.secondaryColor -import KondoKit.plugin.StateManager.focusedView +import KondoKit.plugin.StateManager.totalXPWidget import plugin.api.API import java.awt.* -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent import java.io.BufferedReader import java.io.InputStreamReader import java.nio.charset.StandardCharsets -import javax.swing.* +import javax.swing.Box +import javax.swing.BoxLayout +import javax.swing.JLabel +import javax.swing.JPanel object XPTrackerView { - private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4) - val xpWidgets: MutableMap = HashMap() - var totalXPWidget: XPWidget? = null - val initialXP: MutableMap = HashMap() - var xpTrackerView: JPanel? = null - const val VIEW_NAME = "XP_TRACKER_VIEW" + private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4) val npcHitpointsMap: Map = try { - BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("res/npc_hitpoints_map.json"), StandardCharsets.UTF_8)) + BufferedReader(InputStreamReader(plugin::class.java.getResourceAsStream("npc_hitpoints_map.json"), StandardCharsets.UTF_8)) .useLines { lines -> val json = lines.joinToString("\n") val pairs = json.trim().removeSurrounding("{", "}").split(",") @@ -82,8 +74,8 @@ object XPTrackerView { if(LootTrackerView.lastConfirmedKillNpcId != -1 && npcHitpointsMap.isNotEmpty()) { val npcHP = npcHitpointsMap[LootTrackerView.lastConfirmedKillNpcId] val xpPerKill = when (xpWidget.skillId) { - 3 -> playerXPMultiplier * (npcHP ?: 1) // Hitpoints - else -> playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills + 3 -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) // Hitpoints + else -> kondoExposed_playerXPMultiplier * (npcHP ?: 1) * 4 // Combat XP for other skills } val remainingKills = xpLeft / xpPerKill xpWidget.actionsRemainingLabel.text = formatHtmlLabelText("Kills: ", primaryColor, remainingKills.toString(), secondaryColor) @@ -100,61 +92,24 @@ object XPTrackerView { xpWidget.xpGainedLabel.text = formatHtmlLabelText("XP Gained: ", primaryColor, formattedXp, secondaryColor) // Update the progress bar with current level, progress, and next level - xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, focusedView == VIEW_NAME) + xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, plugin.StateManager.focusedView == "XP_TRACKER_VIEW") xpWidget.previousXp = xp - if (focusedView == VIEW_NAME) - xpWidget.container.repaint() + if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW") + xpWidget.panel.repaint() } private fun updateTotalXPWidget(xpGainedSinceLastUpdate: Int) { - val totalXPWidget = totalXPWidget ?: return + val totalXPWidget = plugin.StateManager.totalXPWidget ?: return totalXPWidget.totalXpGained += xpGainedSinceLastUpdate val formattedXp = formatNumber(totalXPWidget.totalXpGained) totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor) - if (focusedView == VIEW_NAME) - totalXPWidget.container.repaint() + if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW") + totalXPWidget.panel.repaint() } - fun resetXPTracker(xpTrackerView : JPanel){ - - // Redo logic here - xpTrackerView.removeAll() - val popupMenu = createResetMenu() - - // Create a custom MouseListener - val rightClickListener = object : MouseAdapter() { - override fun mousePressed(e: MouseEvent) { - if (e.isPopupTrigger) { - popupMenu.show(e.component, e.x, e.y) - } - } - - override fun mouseReleased(e: MouseEvent) { - if (e.isPopupTrigger) { - popupMenu.show(e.component, e.x, e.y) - } - } - } - - // Create the XP widget - totalXPWidget = createTotalXPWidget() - val wrapped = wrappedWidget(totalXPWidget!!.container) - addMouseListenerToAll(wrapped,rightClickListener) - wrapped.addMouseListener(rightClickListener) - xpTrackerView.add(Box.createVerticalStrut(5)) - xpTrackerView.add(wrapped) - xpTrackerView.add(Box.createVerticalStrut(5)) - - initialXP.clear() - xpWidgets.clear() - - xpTrackerView.revalidate() - if (focusedView == VIEW_NAME) - xpTrackerView.repaint() - } fun createTotalXPWidget(): XPWidget { val widgetPanel = Panel().apply { @@ -165,39 +120,43 @@ object XPTrackerView { minimumSize = TOTAL_XP_WIDGET_SIZE } - val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(LVL_ICON)) + val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(898)) + val imageContainer = Panel(FlowLayout()).apply { - preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height) - maximumSize = preferredSize - minimumSize = preferredSize - size = preferredSize + background = WIDGET_COLOR + preferredSize = IMAGE_SIZE + maximumSize = IMAGE_SIZE + minimumSize = IMAGE_SIZE + size = IMAGE_SIZE } bufferedImageSprite.let { image -> val imageCanvas = ImageCanvas(image).apply { - preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height) - maximumSize = preferredSize - minimumSize = preferredSize - size = preferredSize + background = WIDGET_COLOR + preferredSize = Dimension(image.width, image.height) + maximumSize = Dimension(image.width, image.height) + minimumSize = Dimension(image.width, image.height) + size = Dimension(image.width, image.height) } imageContainer.add(imageCanvas) - imageContainer.size = Dimension(bufferedImageSprite.width, bufferedImageSprite.height) + imageContainer.size = Dimension(image.width, image.height) imageContainer.revalidate() - if(focusedView == VIEW_NAME) + if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW") imageContainer.repaint() } val textPanel = Panel().apply { - layout = GridLayout(2, 1, 5, 0) + layout = GridLayout(2, 1, 5, 5) + background = WIDGET_COLOR } - val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + val font = Font("Arial", Font.PLAIN, 11) val xpGainedLabel = JLabel( - formatHtmlLabelText("Gained: ", primaryColor, "0", secondaryColor) + formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor) ).apply { this.horizontalAlignment = JLabel.LEFT this.font = font @@ -218,14 +177,14 @@ object XPTrackerView { return XPWidget( skillId = -1, - container = widgetPanel, + panel = widgetPanel, xpGainedLabel = xpGainedLabel, xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply { this.horizontalAlignment = JLabel.LEFT this.font = font }, xpPerHourLabel = xpPerHourLabel, - progressBar = ProgressBar(0.0, Color.BLACK), // Unused + progressBar = ProgressBar(0.0, Color(150, 50, 50)), totalXpGained = 0, startTime = System.currentTimeMillis(), previousXp = 0, @@ -234,66 +193,19 @@ object XPTrackerView { } - fun createXPTrackerView(){ - val widgetViewPanel = JPanel().apply { - layout = BoxLayout(this, BoxLayout.Y_AXIS) - background = VIEW_BACKGROUND_COLOR - } + fun createXPTrackerView(): JPanel? { + val widgetViewPanel = JPanel() + widgetViewPanel.layout = BoxLayout(widgetViewPanel, BoxLayout.Y_AXIS) + widgetViewPanel.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() - val wrapped = wrappedWidget(totalXPWidget!!.container) - addMouseListenerToAll(wrapped,rightClickListener) - wrapped.addMouseListener(rightClickListener) - widgetViewPanel.add(Box.createVerticalStrut(5)) - widgetViewPanel.add(wrapped) + widgetViewPanel.add(wrappedWidget(totalXPWidget!!.panel)) widgetViewPanel.add(Box.createVerticalStrut(5)) - xpTrackerView = widgetViewPanel + return widgetViewPanel } - - fun createResetMenu(): JPopupMenu { - // Create a popup menu - val popupMenu = JPopupMenu() - - val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) - - popupMenu.background = POPUP_BACKGROUND - - // Create menu items with custom font and colors - val menuItem1 = JMenuItem("Reset Tracker").apply { - font = rFont // Set custom font - background = POPUP_BACKGROUND // Dark background for item - foreground = POPUP_FOREGROUND // Light text color for item - } - - // Add menu items to the popup menu - popupMenu.add(menuItem1) - - // Add action listeners to each menu item (optional) - menuItem1.addActionListener { plugin.registerDrawAction { resetXPTracker(xpTrackerView!!) } } - return popupMenu - } - - fun createXPWidget(skillId: Int, previousXp: Int): XPWidget { val widgetPanel = Panel().apply { layout = BorderLayout(5, 5) @@ -325,19 +237,19 @@ object XPTrackerView { imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size imageContainer.revalidate() - if(focusedView == VIEW_NAME) + if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW") imageContainer.repaint() } val textPanel = Panel().apply { - layout = GridLayout(2, 2, 5, 0) + layout = GridLayout(2, 2, 5, 5) background = WIDGET_COLOR } - val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + val font = Font("Arial", Font.PLAIN, 11) val xpGainedLabel = JLabel( - formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor) + formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor) ).apply { this.horizontalAlignment = JLabel.LEFT this.font = font @@ -366,7 +278,7 @@ object XPTrackerView { val levelPanel = Panel().apply { layout = BorderLayout(5, 0) - background = WIDGET_COLOR + background = Color(43, 43, 43) } val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply { @@ -385,12 +297,12 @@ object XPTrackerView { widgetPanel.add(levelPanel, BorderLayout.SOUTH) widgetPanel.revalidate() - if(focusedView == VIEW_NAME) + if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW") widgetPanel.repaint() return XPWidget( skillId = skillId, - container = widgetPanel, + panel = widgetPanel, xpGainedLabel = xpGainedLabel, xpLeftLabel = xpLeftLabel, xpPerHourLabel = xpPerHourLabel, @@ -402,18 +314,18 @@ object XPTrackerView { ) } - fun wrappedWidget(component: Component, padding: Int = 7): Container { + fun wrappedWidget(component: Component, padding: Int = 7): Panel { val outerPanelSize = Dimension( - component.preferredSize.width + 2 * padding, - component.preferredSize.height + 2 * padding + component.preferredSize.width + 2 * padding, + component.preferredSize.height + 2 * padding ) - val outerPanel = JPanel(GridBagLayout()).apply { + val outerPanel = Panel(GridBagLayout()).apply { background = WIDGET_COLOR preferredSize = outerPanelSize maximumSize = outerPanelSize minimumSize = outerPanelSize } - val innerPanel = JPanel(BorderLayout()).apply { + val innerPanel = Panel(BorderLayout()).apply { background = WIDGET_COLOR preferredSize = component.preferredSize maximumSize = component.preferredSize @@ -431,14 +343,14 @@ object XPTrackerView { data class XPWidget( - val container: Container, - val skillId: Int, - val xpGainedLabel: JLabel, - val xpLeftLabel: JLabel, - val xpPerHourLabel: JLabel, - val actionsRemainingLabel: JLabel, - val progressBar: ProgressBar, - var totalXpGained: Int = 0, - var startTime: Long = System.currentTimeMillis(), - var previousXp: Int = 0 + val panel: Panel, + val skillId: Int, + val xpGainedLabel: JLabel, + val xpLeftLabel: JLabel, + val xpPerHourLabel: JLabel, + val actionsRemainingLabel: JLabel, + val progressBar: ProgressBar, + var totalXpGained: Int = 0, + var startTime: Long = System.currentTimeMillis(), + var previousXp: Int = 0 ) \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/res/item_configs.json b/plugin-playground/src/main/kotlin/KondoKit/item_configs.json similarity index 100% rename from plugin-playground/src/main/kotlin/KondoKit/res/item_configs.json rename to plugin-playground/src/main/kotlin/KondoKit/item_configs.json diff --git a/plugin-playground/src/main/kotlin/KondoKit/res/npc_hitpoints_map.json b/plugin-playground/src/main/kotlin/KondoKit/npc_hitpoints_map.json similarity index 100% rename from plugin-playground/src/main/kotlin/KondoKit/res/npc_hitpoints_map.json rename to plugin-playground/src/main/kotlin/KondoKit/npc_hitpoints_map.json diff --git a/plugin-playground/src/main/kotlin/KondoKit/plugin.kt b/plugin-playground/src/main/kotlin/KondoKit/plugin.kt index fcf4666..65de95e 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/plugin.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/plugin.kt @@ -4,201 +4,214 @@ import KondoKit.Constants.COMBAT_LVL_SPRITE import KondoKit.Helpers.formatHtmlLabelText import KondoKit.Helpers.formatNumber import KondoKit.Helpers.getSpriteId -import KondoKit.Helpers.showAlert import KondoKit.HiscoresView.createHiscoreSearchView -import KondoKit.HiscoresView.hiScoreView import KondoKit.LootTrackerView.BAG_ICON import KondoKit.LootTrackerView.createLootTrackerView -import KondoKit.LootTrackerView.lootTrackerView import KondoKit.LootTrackerView.npcDeathSnapshots import KondoKit.LootTrackerView.onPostClientTick import KondoKit.LootTrackerView.takeGroundSnapshot import KondoKit.ReflectiveEditorView.addPlugins import KondoKit.ReflectiveEditorView.createReflectiveEditorView -import KondoKit.ReflectiveEditorView.reflectiveEditorView import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite -import KondoKit.Themes.Theme -import KondoKit.Themes.ThemeType -import KondoKit.Themes.getTheme +import KondoKit.XPTrackerView.createTotalXPWidget import KondoKit.XPTrackerView.createXPTrackerView import KondoKit.XPTrackerView.createXPWidget -import KondoKit.XPTrackerView.initialXP -import KondoKit.XPTrackerView.resetXPTracker -import KondoKit.XPTrackerView.totalXPWidget import KondoKit.XPTrackerView.updateWidget import KondoKit.XPTrackerView.wrappedWidget -import KondoKit.XPTrackerView.xpTrackerView -import KondoKit.XPTrackerView.xpWidgets -import KondoKit.plugin.StateManager.focusedView +import KondoKit.plugin.StateManager.initialXP +import KondoKit.plugin.StateManager.totalXPWidget +import KondoKit.plugin.StateManager.xpWidgets import plugin.Plugin import plugin.api.* import plugin.api.API.* import plugin.api.FontColor.fromColor -import rt4.* -import rt4.DisplayMode -import rt4.GameShell.canvas +import rt4.GameShell import rt4.GameShell.frame +import rt4.GlRenderer +import rt4.InterfaceList +import rt4.Player import rt4.client.js5Archive8 import rt4.client.mainLoadState import java.awt.* -import java.awt.Font import java.awt.event.ActionListener import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import javax.swing.* -@Target(AnnotationTarget.FIELD) -@Retention(AnnotationRetention.RUNTIME) -annotation class Exposed(val description: String = "") - class plugin : Plugin() { companion object { - val WIDGET_SIZE = Dimension(220, 50) - val TOTAL_XP_WIDGET_SIZE = Dimension(220, 30) - val IMAGE_SIZE = Dimension(25, 23) - - // Default Theme Colors - var WIDGET_COLOR = Color(30, 30, 30) - var TITLE_BAR_COLOR = Color(21, 21, 21) - var VIEW_BACKGROUND_COLOR = Color(40, 40, 40) - var primaryColor = Color(165, 165, 165) // Color for "XP Gained:" - var secondaryColor = Color(255, 255, 255) // Color for "0" - var POPUP_BACKGROUND = Color(45, 45, 45) - var POPUP_FOREGROUND = Color(220, 220, 220) - var TOOLTIP_BACKGROUND = Color(50,50,50) - var SCROLL_BAR_COLOR = Color(64, 64, 64) - var PROGRESS_BAR_FILL = Color(61, 56, 49) - var NAV_TINT: Color? = null - var NAV_GREYSCALE = false - var BOOST = 1f - - var appliedTheme = ThemeType.RUNELITE - - @Exposed("Theme colors for KondoKit, requires a relaunch to apply.") - var theme = ThemeType.RUNELITE - - @Exposed("Default: true, Use Local JSON or the prices from the Live/Stable server API") - var useLiveGEPrices = true - - @Exposed("Used to calculate Combat Actions until next level.") - var playerXPMultiplier = 5 - - @Exposed("Start minimized/collapsed by default") - var launchMinimized = false - - @Exposed("Default 16 on Windows, 0 Linux/macOS. If Kondo is not " + - "perfectly snapped to the edge of the game due to window chrome you can update this to fix it") - var uiOffset = 0 - - @Exposed("Stretched/Scaled Fixed Mode Support") - var useScaledFixed = false - - const val FIXED_WIDTH = 765 - const val FIXED_HEIGHT = 503 - private const val NAVBAR_WIDTH = 30 - private const val MAIN_CONTENT_WIDTH = 242 + val WIDGET_SIZE = Dimension(270, 55) + val TOTAL_XP_WIDGET_SIZE = Dimension(270, 30) + val IMAGE_SIZE = Dimension(20, 20) + val WIDGET_COLOR = Color(27, 27, 27) + val VIEW_BACKGROUND_COLOR = Color(37, 37, 37) + val primaryColor = Color(129, 129, 129) // Color for "XP Gained:" + val secondaryColor = Color(226, 226, 226) // Color for "0" + var kondoExposed_useLiveGEPrices = true + var kondoExposed_playerXPMultiplier = 5 + const val FIXED_WIDTH = 782 + const val SCROLLPANE_WIDTH = 340 private const val WRENCH_ICON = 907 + private const val LVL_ICON = 898 private const val LOOT_ICON = 777 private const val MAG_SPRITE = 1423 - const val LVL_ICON = 898 private lateinit var cardLayout: CardLayout - private lateinit var mainContentPanel: JPanel - private var rightPanelWrapper: JScrollPane? = null + private lateinit var mainContentPanel: Panel + private var scrollPane: JScrollPane? = null + private var hiScoreView: JPanel? = null + private var reflectiveEditorView: JPanel? = null + private var lootTrackerView: JPanel? = null + private var xpTrackerView: JPanel? = null private var accumulatedTime = 0L - private var reloadInterfaces = false - private const val TICK_INTERVAL = 600L + private const val tickInterval = 600L private var pluginsReloaded = false - private var loginScreen = 160 + private var loginScreen = 160; private var lastLogin = "" - 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) - } - } + private var initialized = false; } - override fun Init() { - // Disable Font AA - System.setProperty("sun.java2d.opengl", "false") - System.setProperty("awt.useSystemAAFontSettings", "off") - System.setProperty("swing.aatext", "false") + fun allSpritesLoaded() : Boolean { + // Check all skill sprites + try{ + for (i in 0 until 24) { + if(!js5Archive8.isFileReady(getSpriteId(i))){ + return false; + } + } + val otherIcons = arrayOf(LVL_ICON, MAG_SPRITE, LOOT_ICON, WRENCH_ICON, COMBAT_LVL_SPRITE, BAG_ICON); + for (icon in otherIcons) { + if(!js5Archive8.isFileReady(icon)){ + return false; + } + } + } catch (e : Exception){ + return false; + } + return true; } override fun OnLogin() { if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) { // if we logged in with a new character // we need to reset the trackers - xpTrackerView?.let { resetXPTracker(it) } + xpTrackerView?.removeAll() + totalXPWidget = createTotalXPWidget() + xpTrackerView?.add(Box.createVerticalStrut(5)) + xpTrackerView?.add(wrappedWidget(totalXPWidget!!.panel)) + xpTrackerView?.add(Box.createVerticalStrut(5)) + initialXP.clear() + xpWidgets.clear() + + xpTrackerView?.revalidate() + if (StateManager.focusedView == "XP_TRACKER_VIEW") + xpTrackerView?.repaint() } lastLogin = Player.usernameInput.toString() } + private fun UpdateDisplaySettings() { + val mode = GetWindowMode() + when (mode) { + WindowMode.FIXED -> { + if (frame.width < FIXED_WIDTH + SCROLLPANE_WIDTH) { + frame.setSize(FIXED_WIDTH + SCROLLPANE_WIDTH, frame.height) + } + val difference = frame.width - (FIXED_WIDTH + SCROLLPANE_WIDTH) + GameShell.leftMargin = difference / 2 + } + WindowMode.RESIZABLE -> { + GameShell.canvasWidth -= SCROLLPANE_WIDTH + } + } + scrollPane?.revalidate() + scrollPane?.repaint() + } + + fun OnKondoValueUpdated(){ + StoreData("kondoUseRemoteGE", kondoExposed_useLiveGEPrices) + StoreData("kondoPlayerXPMultiplier", kondoExposed_playerXPMultiplier) + LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices() + } + override fun OnMiniMenuCreate(currentEntries: Array?) { if (currentEntries != null) { for ((index, entry) in currentEntries.withIndex()) { if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) { val input = entry.subject - // Trim spaces, clean up tags, and remove the level info - val cleanedInput = input - .trim() // Remove any leading/trailing spaces - .replace(Regex(""), "") // Remove color tags - .replace(Regex(""), "") // Remove image tags - .replace(Regex("\\(level: \\d+\\)"), "") // Remove level text e.g. (level: 44) - .trim() // Trim again to remove extra spaces after removing level text - - // Proceed with the full cleaned username - InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(cleanedInput)) + val username = input + .replace(Regex(""), "") + .replace(Regex(""), "") + .split(" ") // Split by spaces + .first() // Take the first part, which is the username + InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(username.replace(" ","_"))) } } } } + private fun searchHiscore(username: String): Runnable { + return Runnable { + cardLayout.show(mainContentPanel, "HISCORE_SEARCH_VIEW") + StateManager.focusedView = "HISCORE_SEARCH_VIEW" + val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) } + + customSearchField?.searchPlayer(username) ?: run { + println("searchView is null or CustomSearchField creation failed.") + } + hiScoreView?.repaint() + } + } + + override fun OnPluginsReloaded(): Boolean { if (!initialized) return true - updateDisplaySettings() - frame.remove(rightPanelWrapper) + + UpdateDisplaySettings() + + frame.remove(scrollPane) frame.layout = BorderLayout() - rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) } + frame.add(scrollPane, BorderLayout.EAST) + + // Clear or regenerate the reflectiveEditorView + reflectiveEditorView?.removeAll() + reflectiveEditorView?.revalidate() + if(StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW") + reflectiveEditorView?.repaint() + frame.revalidate() + frame.repaint() pluginsReloaded = true - reloadInterfaces = true return true } + override fun OnXPUpdate(skillId: Int, xp: Int) { - if (!initialXP.containsKey(skillId)) { - initialXP[skillId] = xp - return - } - var xpWidget = xpWidgets[skillId] - if (xpWidget != null) { - updateWidget(xpWidget, xp) - } else { - val previousXp = initialXP[skillId] ?: xp - if (xp == initialXP[skillId]) return + if (!initialXP.containsKey(skillId)) { + initialXP[skillId] = xp + return + } - xpWidget = createXPWidget(skillId, previousXp) - xpWidgets[skillId] = xpWidget + var xpWidget = xpWidgets[skillId] - xpTrackerView?.add(wrappedWidget(xpWidget.container)) - xpTrackerView?.add(Box.createVerticalStrut(5)) + if (xpWidget != null) { + updateWidget(xpWidget, xp) + } else { + val previousXp = initialXP[skillId] ?: xp + if (xp == initialXP[skillId]) return - if(focusedView == XPTrackerView.VIEW_NAME) { - xpTrackerView?.revalidate() - xpTrackerView?.repaint() - } + xpWidget = createXPWidget(skillId, previousXp) + xpWidgets[skillId] = xpWidget - updateWidget(xpWidget, xp) - } + xpTrackerView?.add(wrappedWidget(xpWidget.panel)) + xpTrackerView?.add(Box.createVerticalStrut(5)) + + xpTrackerView?.revalidate() + if(StateManager.focusedView == "XP_TRACKER_VIEW") + xpTrackerView?.repaint() + + updateWidget(xpWidget, xp) + } } override fun Draw(timeDelta: Long) { @@ -208,498 +221,155 @@ class plugin : Plugin() { } if (pluginsReloaded) { + InterfaceList.method3712(true) // Gets the resize working correctly reflectiveEditorView?.let { addPlugins(it) } pluginsReloaded = false } - if (reloadInterfaces){ - InterfaceList.method3712(true) // Gets the resize working correctly - reloadInterfaces = false - } - accumulatedTime += timeDelta - if (accumulatedTime >= TICK_INTERVAL) { + if (accumulatedTime >= tickInterval) { lootTrackerView?.let { onPostClientTick(it) } accumulatedTime = 0L } - // Draw synced actions (that require to be done between glBegin and glEnd) - if (drawActions.isNotEmpty()) { - synchronized(drawActions) { - val actionsCopy = drawActions.toList() - drawActions.clear() - for (action in actionsCopy) { - action() - } - } - } // Init in the draw call so we know we are between glBegin and glEnd for HD if(!initialized && mainLoadState >= loginScreen) { initKondoUI() } - } - override fun LateDraw(timeDelta: Long) { - if (!initialized) return - if(GameShell.fullScreenFrame != null) { - DisplayMode.setWindowMode(true, 0, FIXED_WIDTH, FIXED_HEIGHT) - showAlert("Fullscreen is not supported by KondoKit. Disable the plugin first.", - "Error", - JOptionPane.INFORMATION_MESSAGE - ) - return - } - if(!useScaledFixed) return - if(GetWindowMode() == WindowMode.FIXED){ - moveAltCanvasToFront() - } else { - moveCanvasToFront() - } - altCanvas?.updateGameImage() // Update the game image as needed - } - - override fun Update() { - - val widgets = xpWidgets.values - val totalXP = totalXPWidget - - widgets.forEach { xpWidget -> - val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0 - val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0 - val formattedXpPerHour = formatNumber(xpPerHour) - xpWidget.xpPerHourLabel.text = - formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor) - xpWidget.container.repaint() - } - - totalXP?.let { totalXPWidget -> - val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0 - val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0 - val formattedTotalXpPerHour = formatNumber(totalXPPerHour) - totalXPWidget.xpPerHourLabel.text = - formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor) - totalXPWidget.container.repaint() - } - } - - override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) { - val preDeathSnapshot = takeGroundSnapshot(Pair(x,z)) - npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0) - } - - private fun allSpritesLoaded() : Boolean { - // Check all skill sprites - try{ - for (i in 0 until 24) { - if(!js5Archive8.isFileReady(getSpriteId(i))){ - return false - } - } - val otherIcons = arrayOf(LVL_ICON, MAG_SPRITE, LOOT_ICON, WRENCH_ICON, COMBAT_LVL_SPRITE, BAG_ICON) - for (icon in otherIcons) { - if(!js5Archive8.isFileReady(icon)){ - return false - } - } - } catch (e : Exception){ - return false - } - return true - } - - private fun updateDisplaySettings() { - val mode = GetWindowMode() - val currentScrollPaneWidth = if (mainContentPanel.isVisible) NAVBAR_WIDTH + MAIN_CONTENT_WIDTH else NAVBAR_WIDTH - lastUIOffset = uiOffset - - if(mode != WindowMode.FIXED) { - destroyAltCanvas() - } else if (useScaledFixed && altCanvas == null) { - initAltCanvas() - } - - when (mode) { - WindowMode.FIXED -> { - if (frame.width < FIXED_WIDTH + currentScrollPaneWidth + uiOffset) { - frame.setSize(FIXED_WIDTH + currentScrollPaneWidth + uiOffset, frame.height) - } - - val difference = frame.width - (uiOffset + currentScrollPaneWidth) - - if (useScaledFixed) { - GameShell.leftMargin = 0 - val canvasWidth = difference + uiOffset / 2 - val canvasHeight = frame.height - canvas.y // Restricting height to frame height - - altCanvas?.size = Dimension(canvasWidth, canvasHeight) - altCanvas?.setLocation(0, canvas.y) - canvas.setLocation(0, canvas.y) - } else { - val difference = frame.width - (FIXED_WIDTH + uiOffset + currentScrollPaneWidth) - GameShell.leftMargin = difference / 2 - } - } - - WindowMode.RESIZABLE -> { - GameShell.canvasWidth = frame.width - (currentScrollPaneWidth + uiOffset) - } - } - - rightPanelWrapper?.preferredSize = Dimension(currentScrollPaneWidth, frame.height) - rightPanelWrapper?.isDoubleBuffered = true - rightPanelWrapper?.revalidate() - rightPanelWrapper?.repaint() - } - - fun OnKondoValueUpdated(){ - StoreData("kondoUseRemoteGE", useLiveGEPrices) - StoreData("kondoTheme", theme.toString()) - if(appliedTheme != theme) { - showAlert( - "KondoKit Theme changes require a relaunch.", - "KondoKit", - JOptionPane.INFORMATION_MESSAGE - ) - } - StoreData("kondoPlayerXPMultiplier", playerXPMultiplier) - LootTrackerView.gePriceMap = LootTrackerView.loadGEPrices() - StoreData("kondoLaunchMinimized", launchMinimized) - StoreData("kondoUIOffset", uiOffset) - StoreData("kondoScaledFixed", useScaledFixed) - if(lastUIOffset != uiOffset){ - reloadInterfaces = true - } - updateDisplaySettings() - } - - private fun initAltCanvas(){ - if(GetWindowMode() != WindowMode.FIXED || altCanvas != null) return - if (frame != null) { - altCanvas = AltCanvas().apply { - preferredSize = Dimension(FIXED_WIDTH, FIXED_HEIGHT) - } - altCanvas?.let { frame.add(it) } - moveAltCanvasToFront() - frame.setComponentZOrder(rightPanelWrapper, 2) - } - } - - private fun destroyAltCanvas(){ - if (altCanvas == null) return - moveCanvasToFront() - frame.remove(altCanvas) - altCanvas = null - } - - private fun moveAltCanvasToFront(){ - if (altCanvas == null) return - frame.setComponentZOrder(canvas, 2) - frame.setComponentZOrder(altCanvas, 1) - frame.setComponentZOrder(rightPanelWrapper, 0) - } - - private fun moveCanvasToFront(){ - if (altCanvas == null) return - frame.setComponentZOrder(altCanvas, 2) - frame.setComponentZOrder(canvas, 1) - frame.setComponentZOrder(rightPanelWrapper, 0) - } - - private fun searchHiscore(username: String): Runnable { - return Runnable { - setActiveView(HiscoresView.VIEW_NAME) - val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) } - - customSearchField?.searchPlayer(username) ?: run { - println("searchView is null or CustomSearchField creation failed.") - } - } - } - - private fun restoreSettings(){ - themeName = (GetData("kondoTheme") as? String) ?: "RUNELITE" - useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true - playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5 - val osName = System.getProperty("os.name").toLowerCase() - uiOffset = (GetData("kondoUIOffset") as? Int) ?: if (osName.contains("win")) 16 else 0 - launchMinimized = (GetData("kondoLaunchMinimized") as? Boolean) ?: false - useScaledFixed = (GetData("kondoScaledFixed") as? Boolean) ?: false } private fun initKondoUI(){ DrawText(FontType.LARGE, fromColor(Color(16777215)), TextModifier.CENTER, "KondoKit Loading Sprites...", GameShell.canvasWidth/2, GameShell.canvasHeight/2) - if(!allSpritesLoaded()) return + if(!allSpritesLoaded()) return; val frame: Frame? = GameShell.frame if (frame != null) { - restoreSettings() - theme = ThemeType.valueOf(themeName) - applyTheme(getTheme(theme)) - appliedTheme = theme - configureLookAndFeel() - + kondoExposed_useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true + kondoExposed_playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5 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 - } + mainContentPanel = Panel(cardLayout) + mainContentPanel.background = VIEW_BACKGROUND_COLOR - // 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) + 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(NAVBAR_WIDTH, frame.height) + preferredSize = Dimension(42, 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)) + 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) } - rightPanelWrapper = JScrollPane(rightPanel).apply { - preferredSize = Dimension(NAVBAR_WIDTH + MAIN_CONTENT_WIDTH, frame.height) + scrollPane = JScrollPane(rightPanel).apply { + preferredSize = Dimension(SCROLLPANE_WIDTH, frame.height) background = VIEW_BACKGROUND_COLOR - border = BorderFactory.createEmptyBorder() + border = BorderFactory.createEmptyBorder() // Removes the border completely horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_NEVER } frame.layout = BorderLayout() - rightPanelWrapper?.let { - frame.add(it, BorderLayout.EAST) - } + scrollPane?.let { frame.add(it, BorderLayout.EAST) } - if(launchMinimized){ - setActiveView(HIDDEN_VIEW) - } else { - setActiveView(XPTrackerView.VIEW_NAME) - } + frame.revalidate() + frame.repaint() + + StateManager.focusedView = "XP_TRACKER_VIEW" initialized = true pluginsReloaded = true + UpdateDisplaySettings() } } - 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) + override fun Update() { + xpWidgets.values.forEach { xpWidget -> + val elapsedTime = (System.currentTimeMillis() - xpWidget.startTime) / 1000.0 / 60.0 / 60.0 + val xpPerHour = if (elapsedTime > 0) (xpWidget.totalXpGained / elapsedTime).toInt() else 0 + val formattedXpPerHour = formatNumber(xpPerHour) + xpWidget.xpPerHourLabel.text = + formatHtmlLabelText("XP /hr: ", primaryColor, formattedXpPerHour, secondaryColor) + xpWidget.panel.repaint() } - reloadInterfaces = true - updateDisplaySettings() - - // Revalidate and repaint necessary panels - mainContentPanel.revalidate() - rightPanelWrapper?.revalidate() - frame?.revalidate() - - mainContentPanel.repaint() - rightPanelWrapper?.repaint() - frame?.repaint() - - focusedView = viewName + totalXPWidget?.let { totalXPWidget -> + val elapsedTime = (System.currentTimeMillis() - totalXPWidget.startTime) / 1000.0 / 60.0 / 60.0 + val totalXPPerHour = if (elapsedTime > 0) (totalXPWidget.totalXpGained / elapsedTime).toInt() else 0 + val formattedTotalXpPerHour = formatNumber(totalXPPerHour) + totalXPWidget.xpPerHourLabel.text = + formatHtmlLabelText("XP /hr: ", primaryColor, formattedTotalXpPerHour, secondaryColor) + totalXPWidget.panel.repaint() + } } - 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 + + override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) { + val preDeathSnapshot = takeGroundSnapshot(Pair(x,z)) + npcDeathSnapshots[npcID] = LootTrackerView.GroundSnapshot(preDeathSnapshot, Pair(x, z), 0) + } + + + private fun createNavButton(spriteId: Int, viewName: String): JButton { + val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId)) + val buttonSize = Dimension(42, 42) + val imageSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height) val actionListener = ActionListener { - val currentTime = System.currentTimeMillis() - if (currentTime - lastClickTime < cooldownDuration) { - return@ActionListener - } - lastClickTime = currentTime - - if (focusedView == viewName) { - setActiveView("HIDDEN") - } else { - setActiveView(viewName) - } + cardLayout.show(mainContentPanel, viewName) + StateManager.focusedView = viewName } - // ImageCanvas with forced size val imageCanvas = ImageCanvas(bufferedImageSprite).apply { background = WIDGET_COLOR preferredSize = imageSize maximumSize = imageSize minimumSize = imageSize + addMouseListener(object : MouseAdapter() { + override fun mouseClicked(e: MouseEvent?) { + actionListener.actionPerformed(null) + } + }) } - // 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 { + val button = JButton().apply { layout = GridBagLayout() preferredSize = buttonSize maximumSize = buttonSize minimumSize = buttonSize background = WIDGET_COLOR - isOpaque = true + isFocusPainted = false + isBorderPainted = false val gbc = GridBagConstraints().apply { anchor = GridBagConstraints.CENTER - fill = GridBagConstraints.NONE // Prevents stretching } - add(imageCanvasWrapper, gbc) - - // Hover and click behavior - val hoverListener = object : MouseAdapter() { - override fun mouseEntered(e: MouseEvent?) { - background = WIDGET_COLOR.darker() - imageCanvas.fillColor = WIDGET_COLOR.darker() - imageCanvas.repaint() - repaint() - } - - override fun mouseExited(e: MouseEvent?) { - background = WIDGET_COLOR - imageCanvas.fillColor = WIDGET_COLOR - imageCanvas.repaint() - repaint() - } - - override fun mouseClicked(e: MouseEvent?) { - actionListener.actionPerformed(null) - } - } - - addMouseListener(hoverListener) - imageCanvas.addMouseListener(hoverListener) + add(imageCanvas, gbc) + addActionListener(actionListener) } - 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 - } + return button } object StateManager { + val initialXP: MutableMap = HashMap() + val xpWidgets: MutableMap = HashMap() + var totalXPWidget: XPWidget? = null var focusedView: String = "" } - - private fun applyTheme(theme: Theme) { - WIDGET_COLOR = theme.widgetColor - TITLE_BAR_COLOR = theme.titleBarColor - VIEW_BACKGROUND_COLOR = theme.viewBackgroundColor - primaryColor = theme.primaryColor - secondaryColor = theme.secondaryColor - POPUP_BACKGROUND = theme.popupBackground - POPUP_FOREGROUND = theme.popupForeground - TOOLTIP_BACKGROUND = theme.tooltipBackground - SCROLL_BAR_COLOR = theme.scrollBarColor - PROGRESS_BAR_FILL = theme.progressBarFill - NAV_TINT = theme.navTint - NAV_GREYSCALE = theme.navGreyScale - BOOST = theme.boost - } } diff --git a/plugin-playground/src/main/kotlin/KondoKit/plugin.properties b/plugin-playground/src/main/kotlin/KondoKit/plugin.properties index 629c117..51c6d2a 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/plugin.properties +++ b/plugin-playground/src/main/kotlin/KondoKit/plugin.properties @@ -1,3 +1,3 @@ AUTHOR='downthecrop' DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.' -VERSION=2.0 \ No newline at end of file +VERSION=1.1 \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/res/runescape_small.ttf b/plugin-playground/src/main/kotlin/KondoKit/res/runescape_small.ttf deleted file mode 100644 index 4f3d764..0000000 Binary files a/plugin-playground/src/main/kotlin/KondoKit/res/runescape_small.ttf and /dev/null differ diff --git a/plugin-playground/src/main/kotlin/LoginTimer/plugin.kt b/plugin-playground/src/main/kotlin/LoginTimer/plugin.kt index 1b8f60b..0ba67f7 100644 --- a/plugin-playground/src/main/kotlin/LoginTimer/plugin.kt +++ b/plugin-playground/src/main/kotlin/LoginTimer/plugin.kt @@ -25,7 +25,6 @@ class plugin : Plugin() { private var timeMode = TIME_MODE_INITIALIZATION private var initTime: Long = 0 - private var logoutFlag = true private var displayMessageCounter = 0 private var component: Component? = null @@ -36,20 +35,6 @@ class plugin : Plugin() { displayMessageCounter = 0 } - override fun OnPluginsReloaded(): Boolean { - return true - } - - override fun OnLogin() { - if(logoutFlag) - initTime = System.currentTimeMillis() - logoutFlag = false - } - - override fun OnLogout() { - logoutFlag = true - } - override fun Draw(timeDelta: Long) { if (component == null) return @@ -149,9 +134,6 @@ class plugin : Plugin() { API.InsertMiniMenuEntry("Disable Timer", "") { timeMode = DEFAULT_TIME_MODE } - API.InsertMiniMenuEntry("Reset Play Time", "") { - initTime = System.currentTimeMillis() - } } } } \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/LoginTimer/plugin.properties b/plugin-playground/src/main/kotlin/LoginTimer/plugin.properties index 1f9beaa..1ebf1f5 100644 --- a/plugin-playground/src/main/kotlin/LoginTimer/plugin.properties +++ b/plugin-playground/src/main/kotlin/LoginTimer/plugin.properties @@ -1,3 +1,3 @@ AUTHOR='Woahscam, Ceikry' DESCRIPTION='Displays the session time played, system time, or no time over the "Report Abuse" button.' -VERSION=1.1 \ No newline at end of file +VERSION=1.2 \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/ToggleResizableSD/plugin.kt b/plugin-playground/src/main/kotlin/ToggleResizableSD/plugin.kt index 58ed363..1eb5198 100644 --- a/plugin-playground/src/main/kotlin/ToggleResizableSD/plugin.kt +++ b/plugin-playground/src/main/kotlin/ToggleResizableSD/plugin.kt @@ -1,89 +1,81 @@ package ToggleResizableSD -import KondoKit.Exposed import plugin.Plugin +import plugin.annotations.PluginMeta import plugin.api.API -import plugin.api.API.StoreData -import rt4.DisplayMode -import rt4.GameShell -import rt4.InterfaceList -import rt4.client +import rt4.* import java.awt.event.KeyAdapter import java.awt.event.KeyEvent class plugin : Plugin() { - - @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 - + 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 override fun Init() { API.AddKeyboardListener(object : KeyAdapter() { override fun keyPressed(e: KeyEvent) { if (e.keyCode == KeyEvent.VK_F12) { - toggleResizableSd() + toggleResize = true } } }) - - useResizable = DisplayMode.resizableSD - if (API.GetData("use-resizable-sd") == true) { - useResizable = true + if (!DisplayMode.resizableSD && API.GetData("use-resizable-sd") == true) { + toggleResize = true } - - var osNameLowerCase: String = System.getProperty("os.name").toLowerCase() + var osNameLowerCase: String = "" + var osName: String + + try { + osName = System.getProperty("os.name") + } catch (e: Exception) { + osName = "Unknown" + } + + osNameLowerCase = osName.toLowerCase() if (!osNameLowerCase.startsWith("mac")) { wantHd = true } - if (API.GetData("want-hd") == false) { wantHd = false } } - + override fun ProcessCommand(commandStr: String, args: Array?) { - when (commandStr.toLowerCase()) { + when(commandStr.toLowerCase()) { "::toggleresizablesd", "::resizablesd", "::togglersd", "::rsd" -> { - toggleResizableSd() + toggleResize = true //We could call toggleResizableSd() directly here, but it's not necessary. } - "::toggleresizablesdhd", "::resizablesdhd", "::togglersdhd", "::rsdhd" -> { + "::toggleresizablesdhd", "::resizablesdhd", "::togglersdhd", "::rsdhd", -> { wantHd = !wantHd - StoreData("want-hd", wantHd) + API.StoreData("want-hd", wantHd) API.SendMessage("You have turned login screen HD " + (if (wantHd) "on" else "off")) } } } - + fun toggleResizableSd() { //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) { return } - - DisplayMode.resizableSD = !DisplayMode.resizableSD - useResizable = DisplayMode.resizableSD - StoreData("use-resizable-sd", useResizable) - - if (!DisplayMode.resizableSD) { + toggleResize = false + DisplayMode.resizableSD = !DisplayMode.resizableSD; + if(!DisplayMode.resizableSD){ + //Revert to fixed + API.StoreData("use-resizable-sd", false) //Note: It is important to call StoreData before setWindowMode because setWindowMode causes all plugins to reload. DisplayMode.setWindowMode(true, 0, -1, -1) } else { + //Use resizable + API.StoreData("use-resizable-sd", true) //Note: It is important to call StoreData before setWindowMode because setWindowMode causes all plugins to reload. DisplayMode.setWindowMode(true, 0, GameShell.frameWidth, GameShell.frameHeight) } } - + override fun Draw(timeDelta: Long) { - if (useResizable != DisplayMode.resizableSD) { + if (toggleResize) { toggleResizableSd() } } - - fun OnKondoValueUpdated() { - StoreData("want-hd", wantHd) - StoreData("use-resizable-sd", useResizable) - } - + override fun OnLogout() { if (DisplayMode.resizableSD && wantHd) { //Because resizable SD always uses the "HD" size canvas/window mode (check the in-game Graphics Options with resizeable SD enabled if you don't believe me!), useHD becomes true when logging out, so logging out with resizeSD enabled means "HD" will always be enabled on the login screen after logging out, so we might as well fix the HD flyover by setting resizableSD to false first, and then calling setWindowMode to replace the canvas and set newMode to 2. diff --git a/plugin-playground/src/main/kotlin/ToggleResizableSD/plugin.properties b/plugin-playground/src/main/kotlin/ToggleResizableSD/plugin.properties index 54fa4cc..08a1c2b 100644 --- a/plugin-playground/src/main/kotlin/ToggleResizableSD/plugin.properties +++ b/plugin-playground/src/main/kotlin/ToggleResizableSD/plugin.properties @@ -1,3 +1,3 @@ AUTHOR='ipkpjersi' DESCRIPTION='Allows you to use F12 to toggle resizable SD.' -VERSION=1.1 \ No newline at end of file +VERSION=1.0 \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/XPDropPlugin/plugin.kt b/plugin-playground/src/main/kotlin/XPDropPlugin/plugin.kt index e857ef1..24f1feb 100644 --- a/plugin-playground/src/main/kotlin/XPDropPlugin/plugin.kt +++ b/plugin-playground/src/main/kotlin/XPDropPlugin/plugin.kt @@ -1,34 +1,12 @@ -package XPDropPlugin - -import KondoKit.Exposed +package XPDropPlugin; + import plugin.Plugin -import plugin.api.API -import plugin.api.API.* -import plugin.api.FontColor.fromColor -import plugin.api.FontType -import plugin.api.TextModifier -import plugin.api.WindowMode -import rt4.Sprite -import rt4.client +import plugin.annotations.PluginMeta +import plugin.api.* import java.awt.Color -import java.awt.image.BufferedImage -import java.io.InputStream -import javax.imageio.ImageIO import kotlin.math.ceil - class plugin : Plugin() { - - enum class Theme { - DEFAULT, RUNELITE - } - - @Exposed - private var theme = Theme.DEFAULT - - @Exposed - private var alwaysShow = false - private val displayTimeout = 10000L // 10 seconds private val drawStart = 175 private val drawPadding = 25 @@ -37,50 +15,41 @@ class plugin : Plugin() { private var totalXp = 0 private val activeGains = ArrayList() private var lastGain = 0L - private val IN_GAME = 30 - - private val spriteCache = HashMap() - - override fun Init() { - val themeIndex = (GetData("xp-drop-theme") as? String) ?: "DEFAULT" - theme = Theme.valueOf(themeIndex) - alwaysShow = (GetData("xp-drop-alwaysShow") as? Boolean) ?: false - } override fun Draw(deltaTime: Long) { - if (shouldSkipDrawing()) return + if (System.currentTimeMillis() - lastGain >= displayTimeout && activeGains.isEmpty()) + return drawTotalXPBox() + val removeList = ArrayList() + var posX = API.GetWindowDimensions().width / 2 - val movementSpeedFactor = deltaTime / 16.666 // 60 FPS + if (API.GetWindowMode() == WindowMode.FIXED) + posX += 60 - for (gain in activeGains) { - gain.currentPos -= ceil(movementSpeedFactor).toInt() // Adjust movement based on deltaTime + for(gain in activeGains) { + gain.currentPos -= ceil(deltaTime / 20.0).toInt() if (gain.currentPos <= drawClear) { removeList.add(gain) totalXp += gain.xp - } else if (gain.currentPos <= drawStart) { - drawXPDrops(gain) + } else if (gain.currentPos <= drawStart){ + val sprite = XPSprites.getSpriteForSkill(skillId = gain.skill) + sprite?.render(posX - 25, gain.currentPos - 20) + API.DrawText( + FontType.SMALL, + FontColor.fromColor(Color.WHITE), + TextModifier.LEFT, + addCommas(gain.xp.toString()), + posX, + gain.currentPos + ) } } activeGains.removeAll(removeList.toSet()) } - private fun shouldSkipDrawing(): Boolean { - return client.gameState < IN_GAME || (!alwaysShow && isDisplayTimeoutExpired() && activeGains.isEmpty()) - } - - fun OnKondoValueUpdated() { - StoreData("xp-drop-theme",theme.toString()) - StoreData("xp-drop-alwaysShow",alwaysShow) - } - - private fun isDisplayTimeoutExpired(): Boolean { - return System.currentTimeMillis() - lastGain >= displayTimeout - } - override fun OnXPUpdate(skill: Int, xp: Int) { if (xp == lastXp[skill]) return @@ -112,54 +81,6 @@ class plugin : Plugin() { } private fun drawTotalXPBox() { - when (theme) { - Theme.DEFAULT -> drawDefaultXPBox() - Theme.RUNELITE -> drawRuneliteXPBox() - } - } - - private fun drawXPDrops(gain : XPGain) { - when (theme) { - Theme.DEFAULT -> drawDefaultXPDrop(gain) - Theme.RUNELITE -> drawRuneliteXPDrops(gain) - } - } - - private fun drawDefaultXPDrop(gain: XPGain) { - var posX = API.GetWindowDimensions().width / 2 - if (API.GetWindowMode() == WindowMode.FIXED) - posX += 60 - val sprite = spriteCache.getOrPut(gain.skill) { XPSprites.getSpriteForSkill(skillId = gain.skill) } - sprite?.render(posX - 25, gain.currentPos - 20) - DrawText( - FontType.SMALL, - fromColor(Color.WHITE), - TextModifier.LEFT, - addCommas(gain.xp.toString()), - posX, - gain.currentPos - ) - } - - private fun drawRuneliteXPDrops(gain: XPGain) { - val w = API.GetWindowDimensions().width - val offset = if(API.GetWindowMode() == WindowMode.FIXED) 251 else 225 - val extra = 2; - val posX = w - (offset + extra) - - val str = addCommas(gain.xp.toString()) - val fontCharWidth = 4 - val displace = str.length*fontCharWidth + 30 - - // should be scaled https://github.com/runelite/runelite/blob/0500906f8de9cd20875c168a7a59e5e066ed5058/runelite-client/src/main/java/net/runelite/client/game/SkillIconManager.java#L50 - // but for now this is good enough - - val sprite = spriteCache.getOrPut(gain.skill) { XPSprites.getSpriteForSkill(skillId = gain.skill) } - sprite?.render(posX - displace, gain.currentPos - 20) - drawTextWithDropShadow(posX, gain.currentPos, Color.WHITE, addCommas(gain.xp.toString())) - } - - private fun drawDefaultXPBox() { var posX = API.GetWindowDimensions().width / 2 val posY = API.GetWindowDimensions().height / 4 @@ -168,13 +89,13 @@ class plugin : Plugin() { API.ClipRect(0, 0, posX * 2, posY * 4) - val horizontal = spriteCache.getOrPut(822) { API.GetSprite(822) } - val horizontalTop = spriteCache.getOrPut(820) { API.GetSprite(820) } - val tlCorner = spriteCache.getOrPut(824) { API.GetSprite(824) } - val blCorner = spriteCache.getOrPut(826) { API.GetSprite(826) } - val trCorner = spriteCache.getOrPut(825) { API.GetSprite(825) } - val brCorner = spriteCache.getOrPut(827) { API.GetSprite(827) } - val bg = spriteCache.getOrPut(657) { API.GetSprite(657) } + val horizontal = API.GetSprite(822) + val horizontalTop = API.GetSprite(820) + val tlCorner = API.GetSprite(824) + val blCorner = API.GetSprite(826) + val trCorner = API.GetSprite(825) + val brCorner = API.GetSprite(827) + val bg = API.GetSprite(657) bg?.render(posX - 77, 10) API.FillRect(posX - 75, 5, 140, 30, 0, 64) @@ -191,58 +112,26 @@ class plugin : Plugin() { horizontalTop?.render(posX + 9, -8) horizontal?.render(posX + 9, 22) - DrawText( - FontType.SMALL, - fromColor(Color.WHITE), - TextModifier.LEFT, - "Total Xp: ${addCommas(totalXp.toString())}", - posX - 65, - 28 + API.DrawText( + FontType.SMALL, + FontColor.fromColor(Color.WHITE), + TextModifier.LEFT, + "Total Xp: ${addCommas(totalXp.toString())}", + posX - 65, + 28 ) } - private fun drawRuneliteXPBox() { - val boxHeight = 29 - val boxWidth = 119 - val posX = API.GetWindowDimensions().width - - val innerBorderColor = Color(90, 82, 69).rgb - val outerBorderColor = Color(56,48,35).rgb - - val offset = if(API.GetWindowMode() == WindowMode.FIXED) 251 else 225 - val boxStart = posX - (offset + boxWidth) - val yOffset = if(API.GetWindowMode() == WindowMode.FIXED) 4 else 0 - - val lvlIcon = 898; - val sprite = spriteCache.getOrPut(lvlIcon){ - val imageStream: InputStream = plugin::class.java.getResourceAsStream("res/rl-lvls.png") - imageStream.use { imageStream -> - val image: BufferedImage = ImageIO.read(imageStream) - API.GetSpriteFromPNG(image) - } - } - - // Draw a simple rectangle instead of the default box design - API.FillRect(boxStart, yOffset, boxWidth, boxHeight, innerBorderColor, 150) - drawTextWithDropShadow(boxStart+boxWidth-4, 18+yOffset, Color.WHITE, addCommas(totalXp.toString())) - - // Inner Border - API.DrawRect(boxStart+1, 1+yOffset, boxWidth-2, boxHeight-2, innerBorderColor) - // redraw around the border - API.DrawRect(boxStart, yOffset, boxWidth, boxHeight, outerBorderColor) - sprite?.render(boxStart + 3, 3+yOffset) - } - data class XPGain(val skill: Int, val xp: Int, var currentPos: Int) - fun addCommas(num: String): String { + fun addCommas(num: String): String{ var newString = "" - if (num.length > 9) { + if(num.length > 9){ return "Lots!" } var counter = 1 num.reversed().forEach { - if (counter % 3 == 0 && counter != num.length) { + if(counter % 3 == 0 && counter != num.length){ newString += "$it," } else { newString += it @@ -251,10 +140,4 @@ class plugin : Plugin() { } return newString.reversed() } - - private fun drawTextWithDropShadow(x: Int, y: Int, color: Color, text: String, mod : TextModifier = TextModifier.RIGHT) { - DrawText(FontType.SMALL, fromColor(Color(0)), mod, text, x + 1, y + 1) - DrawText(FontType.SMALL, fromColor(color), mod, text, x, y) - } } - diff --git a/plugin-playground/src/main/kotlin/XPDropPlugin/plugin.properties b/plugin-playground/src/main/kotlin/XPDropPlugin/plugin.properties index 53b820f..35027ea 100644 --- a/plugin-playground/src/main/kotlin/XPDropPlugin/plugin.properties +++ b/plugin-playground/src/main/kotlin/XPDropPlugin/plugin.properties @@ -1,3 +1,3 @@ AUTHOR='Ceikry' DESCRIPTION='Draws nice and clean experience drops onto the screen.' -VERSION=1.3 +VERSION=1.2 diff --git a/plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png b/plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png deleted file mode 100644 index c840a49..0000000 Binary files a/plugin-playground/src/main/kotlin/XPDropPlugin/res/rl-lvls.png and /dev/null differ diff --git a/plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png b/plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png deleted file mode 100644 index c68a872..0000000 Binary files a/plugin-playground/src/main/kotlin/XPDropPlugin/res/xpIco.png and /dev/null differ