Compare commits

...

28 commits

Author SHA1 Message Date
downthecrop
7594e98f51 Merge branch 'KondoKit-2.0' into 'master'
KondoKit 2.0

See merge request 2009scape/rt4-client!28
2024-10-28 23:43:44 +00:00
downthecrop
1de6638a12 Merge branch 'FIXED-RESIZE' into 'KondoKit-2.0'
Fixed Streched

See merge request downthecrop/rt4-client!4
2024-10-28 23:41:55 +00:00
downthecrop
4ebba194dc Merge branch 'KondoKit-2.0' into 'FIXED-RESIZE'
# Conflicts:
#   plugin-playground/src/main/kotlin/KondoKit/ScrollablePanel.kt
2024-10-28 23:41:42 +00:00
downthecrop
e05f5829d7 Restore original Kondo Fixed mode handling 2024-10-28 16:38:33 -07:00
downthecrop
cf5ccbf1ac undo some testing changes 2024-10-28 16:32:51 -07:00
downthecrop
cb9758f7d5 Organize overrides to be together in the source 2024-10-28 16:22:40 -07:00
downthecrop
1cca6611ee general cleanup 2024-10-28 12:43:29 -07:00
downthecrop
52d07d5795 add mousewheel relay 2024-10-28 11:42:37 -07:00
downthecrop
f2d4c17569 Correct offsets 2024-10-28 10:53:23 -07:00
downthecrop
c24df5ab46 cleanup files 2024-10-28 07:18:08 -07:00
downthecrop
07f4dc9349 mostly working alt canvas 2024-10-28 00:03:43 -07:00
downthecrop
c307db1e11 double buffering 2024-10-27 21:38:12 -07:00
downthecrop
8b89a2bb8c Faster transforms 2024-10-27 20:22:16 -07:00
downthecrop
c5ba4ecc94 true nn upscale 2024-10-27 20:11:25 -07:00
downthecrop
341d6758c1 Simplify altcanvas 2024-10-27 19:40:24 -07:00
downthecrop
1f1718d917 Keep the canvas in the frame but change the Z order 2024-10-27 08:10:38 -07:00
downthecrop
8180e20281 Removing debug from client + add print for unparented altcanvas 2024-10-26 22:33:54 -07:00
downthecrop
e7f46f1006 Constant for FIXED_HEIGHT + transfercomponent helper 2024-10-26 22:31:43 -07:00
downthecrop
83fe4805ac GC issues + some late invokes 2024-10-26 09:59:25 -07:00
downthecrop
71afb2b385 greatly reduce flickering 2024-10-26 09:06:23 -07:00
downthecrop
15deb6216f skip unrequired movement 2024-10-25 21:26:43 -07:00
downthecrop
4f8bf464c5 HD Resize redraw 2024-10-25 21:22:16 -07:00
downthecrop
b3c2adc51f support scaled fixed HD 2024-10-25 15:54:39 -07:00
downthecrop
838acc57ff handing for altcanvas in alerts 2024-10-24 23:21:55 -07:00
downthecrop
1eb7483d35 Regain focus on click 2024-10-24 20:58:45 -07:00
downthecrop
af4dafee89 Convert to volatile and centralize logic to the altcanvas. 2024-10-24 20:50:56 -07:00
downthecrop
8e554b573c gather functions together for easier debugging 2024-10-24 20:24:40 -07:00
downthecrop
668345b81e FIXED streched mode 2024-10-24 12:03:16 -07:00
14 changed files with 602 additions and 299 deletions

View file

@ -13,6 +13,7 @@ import rt4.Tile;
*/ */
public abstract class Plugin { public abstract class Plugin {
long timeOfLastDraw; long timeOfLastDraw;
long timeOfLastLateDraw;
void _init() { void _init() {
Init(); Init();
@ -24,6 +25,12 @@ public abstract class Plugin {
timeOfLastDraw = nowTime; timeOfLastDraw = nowTime;
} }
void _lateDraw() {
long nowTime = System.currentTimeMillis();
LateDraw(nowTime - timeOfLastLateDraw);
timeOfLastLateDraw = nowTime;
}
/** /**
* Draw() is called by the client rendering loop so that plugins can draw information onto the screen. * Draw() is called by the client rendering loop so that plugins can draw information onto the screen.
* This will be called once per frame, meaning it is framerate bound. * This will be called once per frame, meaning it is framerate bound.
@ -31,6 +38,14 @@ public abstract class Plugin {
*/ */
public void Draw(long timeDelta) {} public void Draw(long timeDelta) {}
/**
* LateDraw() is called at the end of a finalized frame
* This will be called once per frame, meaning it is framerate bound.
* @param timeDelta the time (ms) elapsed since the last draw call.
*/
public void LateDraw(long timeDelta) {}
/** /**
* Init() is called when the plugin is first loaded * Init() is called when the plugin is first loaded
*/ */

View file

@ -160,6 +160,11 @@ public class PluginRepository {
pluginsSnapshot.forEach(Plugin::_draw); pluginsSnapshot.forEach(Plugin::_draw);
} }
public static void LateDraw() {
List<Plugin> pluginsSnapshot = new ArrayList<>(loadedPlugins.values());
pluginsSnapshot.forEach(Plugin::_lateDraw);
}
public static void NPCOverheadDraw(Npc npc, int screenX, int screenY) { public static void NPCOverheadDraw(Npc npc, int screenX, int screenY) {
loadedPlugins.values().forEach((plugin) -> plugin.NPCOverheadDraw(npc, screenX, screenY)); loadedPlugins.values().forEach((plugin) -> plugin.NPCOverheadDraw(npc, screenX, screenY));
} }

View file

@ -12,7 +12,7 @@ import java.awt.*;
public abstract class FrameBuffer { public abstract class FrameBuffer {
@OriginalMember(owner = "client!vk", name = "e", descriptor = "[I") @OriginalMember(owner = "client!vk", name = "e", descriptor = "[I")
protected int[] pixels; public int[] pixels;
@OriginalMember(owner = "client!vk", name = "g", descriptor = "Ljava/awt/Image;") @OriginalMember(owner = "client!vk", name = "g", descriptor = "Ljava/awt/Image;")
protected Image image; protected Image image;

View file

@ -9,6 +9,7 @@ import org.openrs2.deob.annotation.OriginalMember;
import org.openrs2.deob.annotation.Pc; import org.openrs2.deob.annotation.Pc;
import java.awt.*; import java.awt.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -24,6 +25,11 @@ public final class GlRenderer {
public static float hFOV = 0; public static float hFOV = 0;
private static ByteBuffer pixelByteBuffer;
private static IntBuffer pixelIntBuffer;
public static int[] pixelData;
@OriginalMember(owner = "client!tf", name = "c", descriptor = "F") @OriginalMember(owner = "client!tf", name = "c", descriptor = "F")
private static float aFloat30; private static float aFloat30;
@ -200,10 +206,34 @@ public final class GlRenderer {
public static void swapBuffers() { public static void swapBuffers() {
try { try {
drawable.swapBuffers(); drawable.swapBuffers();
readPixels();
} catch (@Pc(3) Exception local3) { } catch (@Pc(3) Exception local3) {
} }
} }
public static void initializePixelBuffer(int width, int height) {
// Allocate ByteBuffer for BGRA pixels (4 bytes per pixel)
pixelByteBuffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder());
pixelIntBuffer = pixelByteBuffer.asIntBuffer();
pixelData = new int[width * height];
}
public static void readPixels() {
// Ensure the pixel buffer is initialized with the correct size
if (pixelByteBuffer == null || pixelIntBuffer.capacity() != canvasWidth * canvasHeight) {
initializePixelBuffer(canvasWidth, canvasHeight);
}
// Read pixels into the direct ByteBuffer
gl.glReadPixels(0, 0, canvasWidth, canvasHeight, GL2.GL_BGRA,
GlRenderer.bigEndian ? GL2.GL_UNSIGNED_INT_8_8_8_8_REV : GL2.GL_UNSIGNED_BYTE,
pixelByteBuffer);
// Convert to int array if needed
pixelIntBuffer.rewind(); // Prepare the IntBuffer for reading
pixelIntBuffer.get(pixelData, 0, pixelData.length); // Transfer to pixelData array if necessary
}
@OriginalMember(owner = "client!tf", name = "a", descriptor = "(Z)V") @OriginalMember(owner = "client!tf", name = "a", descriptor = "(Z)V")
public static void setFogEnabled(@OriginalArg(0) boolean enabled) { public static void setFogEnabled(@OriginalArg(0) boolean enabled) {
if (enabled == fogEnabled) { if (enabled == fogEnabled) {

View file

@ -921,6 +921,7 @@ public final class client extends GameShell {
Preferences.safeMode = false; Preferences.safeMode = false;
Preferences.write(GameShell.signLink); Preferences.write(GameShell.signLink);
} }
PluginRepository.LateDraw();
} }
@OriginalMember(owner = "client!client", name = "c", descriptor = "(B)V") @OriginalMember(owner = "client!client", name = "c", descriptor = "(B)V")

View file

@ -0,0 +1,166 @@
package KondoKit
import KondoKit.plugin.Companion.FIXED_HEIGHT
import KondoKit.plugin.Companion.FIXED_WIDTH
import plugin.api.API.IsHD
import rt4.GameShell.canvas
import rt4.GlRenderer
import rt4.SoftwareRaster
import java.awt.*
import java.awt.event.*
import java.awt.geom.AffineTransform
import java.awt.image.AffineTransformOp
import java.awt.image.BufferedImage
import java.awt.image.VolatileImage
class AltCanvas : Canvas() {
private var gameImage: VolatileImage? = null
private var op: AffineTransformOp? = null
private var transform: AffineTransform? = null
private var flippedImage: BufferedImage? = null
private var bufferImage = BufferedImage(FIXED_WIDTH, FIXED_HEIGHT, BufferedImage.TYPE_INT_BGR)
private var lastImageWidth = -1
private var lastImageHeight = -1
init {
isFocusable = true
requestFocusInWindow()
addMouseListener(object : MouseAdapter() {
override fun mousePressed(e: MouseEvent) = relayMouseEvent(e)
override fun mouseReleased(e: MouseEvent) = relayMouseEvent(e)
override fun mouseClicked(e: MouseEvent) = relayMouseEvent(e)
})
addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseMoved(e: MouseEvent) = relayMouseEvent(e)
override fun mouseDragged(e: MouseEvent) = relayMouseEvent(e)
})
addKeyListener(object : KeyAdapter() {
override fun keyPressed(e: KeyEvent) = relayKeyEvent { it.keyPressed(e) }
override fun keyReleased(e: KeyEvent) = relayKeyEvent { it.keyReleased(e) }
override fun keyTyped(e: KeyEvent) = relayKeyEvent { it.keyTyped(e) }
})
addMouseWheelListener(MouseWheelListener { e -> relayMouseWheelEvent(e) })
}
override fun update(g: Graphics) = paint(g)
override fun addNotify() {
super.addNotify()
createBufferStrategy(2) // Double-buffering
validateGameImage()
}
private fun validateGameImage() {
val gc = GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice.defaultConfiguration
gameImage?.let {
when (it.validate(gc)) {
VolatileImage.IMAGE_INCOMPATIBLE -> createGameImage(gc)
VolatileImage.IMAGE_RESTORED -> renderGameImage()
}
} ?: createGameImage(gc)
}
private fun createGameImage(gc: GraphicsConfiguration) {
gameImage = gc.createCompatibleVolatileImage(FIXED_WIDTH, FIXED_HEIGHT, Transparency.OPAQUE)
renderGameImage()
}
private fun renderGameImage() {
gameImage?.createGraphics()?.apply {
color = Color.DARK_GRAY
fillRect(0, 0, gameImage!!.width, gameImage!!.height)
color = Color.BLACK
drawString("KondoKit Scaled Fixed Canvas", 20, 20)
dispose()
}
}
override fun paint(g: Graphics) {
bufferStrategy?.let { strategy ->
val g2d = strategy.drawGraphics as? Graphics2D ?: return
g2d.color = Color.BLACK
g2d.fillRect(0, 0, width, height)
gameImage?.let { image ->
val scale = minOf(width.toDouble() / image.width, height.toDouble() / image.height)
val x = ((width - image.width * scale) / 2).toInt()
val y = ((height - image.height * scale) / 2).toInt()
g2d.drawImage(image, x, y, (image.width * scale).toInt(), (image.height * scale).toInt(), null)
}
g2d.dispose() // Release the graphics context
strategy.show() // Display the buffer
}
}
private fun relayMouseEvent(e: MouseEvent) {
requestFocusInWindow()
val scale = minOf(width.toDouble() / gameImage!!.width, height.toDouble() / gameImage!!.height)
val xOffset = ((width - gameImage!!.width * scale) / 2)
val yOffset = ((height - gameImage!!.height * scale) / 2)
val adjustedX = ((e.x - xOffset) / scale).toInt().coerceIn(0, gameImage!!.width - 1)
val adjustedY = ((e.y - yOffset) / scale).toInt().coerceIn(0, gameImage!!.height - 1)
canvas.dispatchEvent(MouseEvent(this, e.id, e.`when`, e.modifiersEx, adjustedX, adjustedY, e.clickCount, e.isPopupTrigger, e.button))
}
private fun relayKeyEvent(action: (KeyListener) -> Unit) {
for (listener in canvas.keyListeners) action(listener)
}
private fun relayMouseWheelEvent(e: MouseWheelEvent) {
val scale = minOf(width.toDouble() / gameImage!!.width, height.toDouble() / gameImage!!.height)
val xOffset = ((width - gameImage!!.width * scale) / 2)
val yOffset = ((height - gameImage!!.height * scale) / 2)
val adjustedX = ((e.x - xOffset) / scale).toInt().coerceIn(0, gameImage!!.width - 1)
val adjustedY = ((e.y - yOffset) / scale).toInt().coerceIn(0, gameImage!!.height - 1)
canvas.dispatchEvent(MouseWheelEvent(this, e.id, e.`when`, e.modifiersEx, adjustedX, adjustedY, e.clickCount, e.isPopupTrigger, e.scrollType, e.scrollAmount, e.wheelRotation))
}
fun updateGameImage() {
if (IsHD()) renderGlRaster() else renderSoftwareRaster()
repaint()
}
private fun renderGlRaster() {
val width = gameImage!!.width
val height = gameImage!!.height
bufferImage.setRGB(0, 0, width, height, GlRenderer.pixelData, 0, width)
if (width != lastImageWidth || height != lastImageHeight) {
transform = AffineTransform.getScaleInstance(1.0, -1.0).apply {
translate(0.0, -height.toDouble())
}
op = AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR)
flippedImage = BufferedImage(width, height, bufferImage.type)
lastImageWidth = width
lastImageHeight = height
}
op!!.filter(bufferImage, flippedImage)
gameImage?.createGraphics()?.apply {
drawImage(flippedImage, 0, 0, null)
dispose()
}
}
private fun renderSoftwareRaster() {
gameImage?.createGraphics()?.apply {
SoftwareRaster.frameBuffer.draw(this)
dispose()
}
}
}

View file

@ -68,7 +68,7 @@ object Helpers {
// Adjust for parent component location if it exists // Adjust for parent component location if it exists
if (parentComponent != null) { if (parentComponent != null && GameShell.canvas.isShowing) {
val parentLocation = parentComponent.locationOnScreen val parentLocation = parentComponent.locationOnScreen
val x = parentLocation.x val x = parentLocation.x
val y = GameShell.canvas.locationOnScreen.y val y = GameShell.canvas.locationOnScreen.y
@ -95,10 +95,8 @@ object Helpers {
} }
private fun convertToColor(value: String): Color {
fun convertToColor(value: String): Color { return Color.decode(value)
val color = Color.decode(value) // Assumes value is in format "#RRGGBB" or "0xRRGGBB"
return color
} }
fun colorToHex(color: Color): String { fun colorToHex(color: Color): String {
@ -134,11 +132,7 @@ object Helpers {
class FieldNotifier(private val plugin: Any) { class FieldNotifier(private val plugin: Any) {
private val observers = mutableListOf<FieldObserver>() private val observers = mutableListOf<FieldObserver>()
fun addObserver(observer: FieldObserver) { private fun notifyFieldChange(field: Field, newValue: Any?) {
observers.add(observer)
}
fun notifyFieldChange(field: Field, newValue: Any?) {
for (observer in observers) { for (observer in observers) {
observer.onFieldChange(field, newValue) observer.onFieldChange(field, newValue)
} }

View file

@ -7,9 +7,7 @@ import KondoKit.Helpers.formatHtmlLabelText
import KondoKit.Helpers.getSpriteId import KondoKit.Helpers.getSpriteId
import KondoKit.Helpers.showToast import KondoKit.Helpers.showToast
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
import KondoKit.plugin.Companion.POPUP_BACKGROUND
import KondoKit.plugin.Companion.POPUP_FOREGROUND import KondoKit.plugin.Companion.POPUP_FOREGROUND
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
import KondoKit.plugin.Companion.WIDGET_COLOR import KondoKit.plugin.Companion.WIDGET_COLOR
@ -77,8 +75,8 @@ object HiscoresView {
private var cursorVisible: Boolean = true private var cursorVisible: Boolean = true
private val gson = Gson() private val gson = Gson()
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.MAG_SPRITE)) private val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.MAG_SPRITE))
val imageCanvas = bufferedImageSprite.let { private val imageCanvas = bufferedImageSprite.let {
ImageCanvas(it).apply { ImageCanvas(it).apply {
preferredSize = Constants.ICON_DIMENSION_SMALL preferredSize = Constants.ICON_DIMENSION_SMALL
size = preferredSize size = preferredSize
@ -112,7 +110,9 @@ object HiscoresView {
} else { } else {
text += e.keyChar text += e.keyChar
} }
repaint() SwingUtilities.invokeLater {
repaint()
}
} }
override fun keyPressed(e: KeyEvent) { override fun keyPressed(e: KeyEvent) {
if (e.isControlDown) { if (e.isControlDown) {
@ -125,7 +125,9 @@ object HiscoresView {
val clipboard = Toolkit.getDefaultToolkit().systemClipboard val clipboard = Toolkit.getDefaultToolkit().systemClipboard
val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String
text += pasteText text += pasteText
repaint() SwingUtilities.invokeLater {
repaint()
}
} }
} }
} }
@ -136,7 +138,9 @@ object HiscoresView {
override fun mouseClicked(e: MouseEvent) { override fun mouseClicked(e: MouseEvent) {
if (e.x > width - 20 && e.y < 20) { if (e.x > width - 20 && e.y < 20) {
text = "" text = ""
repaint() SwingUtilities.invokeLater {
repaint()
}
} }
} }
}) })
@ -144,7 +148,9 @@ object HiscoresView {
Timer(500) { Timer(500) {
cursorVisible = !cursorVisible cursorVisible = !cursorVisible
if(focusedView == VIEW_NAME) if(focusedView == VIEW_NAME)
repaint() SwingUtilities.invokeLater {
repaint()
}
}.start() }.start()
} }
@ -235,7 +241,7 @@ object HiscoresView {
playerNameLabel?.revalidate() playerNameLabel?.revalidate()
playerNameLabel?.repaint() playerNameLabel?.repaint()
if(data == null) return; if(data == null) return
playerNameLabel?.removeAll() playerNameLabel?.removeAll()
@ -319,10 +325,6 @@ object HiscoresView {
return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0 return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0
} }
private fun showError(message: String) {
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE)
}
private fun findComponentByName(container: Container, name: String): Component? { private fun findComponentByName(container: Container, name: String): Component? {
for (component in container.components) { for (component in container.components) {
if (name == component.name) { if (name == component.name) {
@ -437,7 +439,7 @@ object HiscoresView {
minimumSize = preferredSize minimumSize = preferredSize
} }
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE)); val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(Constants.LVL_BAR_SPRITE))
val totalLevelIcon = ImageCanvas(bufferedImageSprite).apply { val totalLevelIcon = ImageCanvas(bufferedImageSprite).apply {
fillColor = COLOR_BACKGROUND_DARK fillColor = COLOR_BACKGROUND_DARK
@ -486,7 +488,7 @@ object HiscoresView {
hiscorePanel.add(totalCombatPanel) hiscorePanel.add(totalCombatPanel)
hiscorePanel.add(Box.createVerticalStrut(10)) hiscorePanel.add(Box.createVerticalStrut(10))
hiScoreView = hiscorePanel; hiScoreView = hiscorePanel
} }
data class HiscoresResponse( data class HiscoresResponse(

View file

@ -4,7 +4,6 @@ import KondoKit.Helpers.addMouseListenerToAll
import KondoKit.Helpers.formatHtmlLabelText import KondoKit.Helpers.formatHtmlLabelText
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
import KondoKit.XPTrackerView.wrappedWidget import KondoKit.XPTrackerView.wrappedWidget
import KondoKit.plugin.Companion.IMAGE_SIZE
import KondoKit.plugin.Companion.POPUP_BACKGROUND import KondoKit.plugin.Companion.POPUP_BACKGROUND
import KondoKit.plugin.Companion.POPUP_FOREGROUND import KondoKit.plugin.Companion.POPUP_FOREGROUND
import KondoKit.plugin.Companion.TITLE_BAR_COLOR import KondoKit.plugin.Companion.TITLE_BAR_COLOR
@ -33,15 +32,15 @@ import kotlin.math.ceil
object LootTrackerView { object LootTrackerView {
private const val SNAPSHOT_LIFESPAN = 10 private const val SNAPSHOT_LIFESPAN = 10
const val BAG_ICON = 900; const val BAG_ICON = 900
val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>() val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>()
var gePriceMap = loadGEPrices() var gePriceMap = loadGEPrices()
const val VIEW_NAME = "LOOT_TRACKER_VIEW"; const val VIEW_NAME = "LOOT_TRACKER_VIEW"
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>() private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
private val npcKillCounts = mutableMapOf<String, Int>() private val npcKillCounts = mutableMapOf<String, Int>()
private var totalTrackerWidget: XPWidget? = null private var totalTrackerWidget: XPWidget? = null
var lastConfirmedKillNpcId = -1 var lastConfirmedKillNpcId = -1
var customToolTipWindow: JWindow? = null private var customToolTipWindow: JWindow? = null
var lootTrackerView: JPanel? = null var lootTrackerView: JPanel? = null
fun loadGEPrices(): Map<String, String> { fun loadGEPrices(): Map<String, String> {
@ -284,7 +283,7 @@ object LootTrackerView {
// Function to show the custom tooltip // Function to show the custom tooltip
fun showCustomToolTip(location: Point, itemId: Int, quantity: Int, parentComponent: ImageCanvas) { fun showCustomToolTip(location: Point, itemId: Int, quantity: Int, parentComponent: ImageCanvas) {
var itemDef = ObjTypeList.get(itemId) val itemDef = ObjTypeList.get(itemId)
val gePricePerItem = gePriceMap[itemDef.id.toString()]?.toInt() ?: 0 val gePricePerItem = gePriceMap[itemDef.id.toString()]?.toInt() ?: 0
val totalGePrice = gePricePerItem * quantity val totalGePrice = gePricePerItem * quantity
val totalHaPrice = itemDef.cost * quantity val totalHaPrice = itemDef.cost * quantity
@ -351,6 +350,7 @@ object LootTrackerView {
?.apply { ?.apply {
val newValue = (getClientProperty("val") as? Int ?: 0) + valueOfNewDrops.toInt() val newValue = (getClientProperty("val") as? Int ?: 0) + valueOfNewDrops.toInt()
text = "${formatValue(newValue)} gp" text = "${formatValue(newValue)} gp"
foreground = primaryColor
putClientProperty("val", newValue) putClientProperty("val", newValue)
revalidate() revalidate()
if(focusedView == VIEW_NAME) if(focusedView == VIEW_NAME)
@ -382,7 +382,7 @@ object LootTrackerView {
if (newDrops.isNotEmpty()) { if (newDrops.isNotEmpty()) {
val npcName = NpcTypeList.get(npcId).name val npcName = NpcTypeList.get(npcId).name
lastConfirmedKillNpcId = npcId; lastConfirmedKillNpcId = npcId
handleNewDrops(npcName.toString(), newDrops, lootTrackerView) handleNewDrops(npcName.toString(), newDrops, lootTrackerView)
toRemove.add(npcId) toRemove.add(npcId)
} else if (snapshot.age >= SNAPSHOT_LIFESPAN) { } else if (snapshot.age >= SNAPSHOT_LIFESPAN) {
@ -501,7 +501,7 @@ object LootTrackerView {
return childFramePanel return childFramePanel
} }
fun removeLootFrameMenu(toRemove: JPanel, npcName: String): JPopupMenu { private fun removeLootFrameMenu(toRemove: JPanel, npcName: String): JPopupMenu {
// Create a popup menu // Create a popup menu
val popupMenu = JPopupMenu() val popupMenu = JPopupMenu()
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
@ -538,7 +538,7 @@ object LootTrackerView {
} }
fun resetLootTrackerMenu(): JPopupMenu { private fun resetLootTrackerMenu(): JPopupMenu {
// Create a popup menu // Create a popup menu
val popupMenu = JPopupMenu() val popupMenu = JPopupMenu()
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)

View file

@ -30,7 +30,7 @@ import kotlin.math.ceil
object ReflectiveEditorView { object ReflectiveEditorView {
var reflectiveEditorView: JPanel? = null var reflectiveEditorView: JPanel? = null
val loadedPlugins: MutableList<String> = mutableListOf() private val loadedPlugins: MutableList<String> = mutableListOf()
const val VIEW_NAME = "REFLECTIVE_EDITOR_VIEW" const val VIEW_NAME = "REFLECTIVE_EDITOR_VIEW"
fun createReflectiveEditorView() { fun createReflectiveEditorView() {
val reflectiveEditorPanel = JPanel(BorderLayout()) val reflectiveEditorPanel = JPanel(BorderLayout())
@ -266,7 +266,7 @@ object ReflectiveEditorView {
} }
} }
else { else {
loadedPlugins.add(plugin.javaClass.`package`.name); loadedPlugins.add(plugin.javaClass.`package`.name)
} }
} }

View file

@ -9,6 +9,7 @@ import java.awt.Rectangle
import java.awt.event.* import java.awt.event.*
import java.util.* import java.util.*
import javax.swing.JPanel import javax.swing.JPanel
import javax.swing.SwingUtilities
class ScrollablePanel(private val content: JPanel) : JPanel() { class ScrollablePanel(private val content: JPanel) : JPanel() {
private var lastMouseY = 0 private var lastMouseY = 0
@ -83,21 +84,23 @@ class ScrollablePanel(private val content: JPanel) : JPanel() {
} }
private fun handleResize() { private fun handleResize() {
// Ensure the ScrollablePanel resizes with the frame SwingUtilities.invokeLater{
bounds = Rectangle(0, 0, 242, frame.height) // 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 // Dynamically update content bounds and scrollbar on frame resize with buffer
lastSize = content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer) lastSize = content.preferredSize.height.coerceAtLeast(frame.height + viewBuffer)
content.bounds = Rectangle(0, 0, 242, lastSize) content.bounds = Rectangle(0, 0, 242, lastSize)
showScrollbar = content.height > frame.height showScrollbar = content.height > frame.height
currentOffsetY = 0 currentOffsetY = 0
content.setLocation(0, currentOffsetY) content.setLocation(0, currentOffsetY)
updateScrollbar() updateScrollbar()
revalidate() revalidate()
repaint() repaint()
}
} }
private fun scrollContent(deltaY: Int) { private fun scrollContent(deltaY: Int) {
@ -106,40 +109,41 @@ class ScrollablePanel(private val content: JPanel) : JPanel() {
content.setLocation(0, currentOffsetY) content.setLocation(0, currentOffsetY)
return return
} }
SwingUtilities.invokeLater {
currentOffsetY += deltaY
currentOffsetY += deltaY // Apply buffer to maxOffset
val maxOffset = (frame.height - content.height + viewBuffer).coerceAtMost(0)
currentOffsetY = currentOffsetY.coerceAtMost(0).coerceAtLeast(maxOffset)
// Apply buffer to maxOffset content.setLocation(0, currentOffsetY)
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 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() {
showScrollbar = content.height > frame.height
val contentHeight = content.height
val viewHeight = frame.height + viewBuffer
if (showScrollbar) {
val scrollableRatio = viewHeight.toDouble() / contentHeight val scrollableRatio = viewHeight.toDouble() / contentHeight
scrollbarY = ((-currentOffsetY / contentHeight.toDouble()) * viewHeight).toInt() scrollbarY = ((-currentOffsetY / contentHeight.toDouble()) * viewHeight).toInt()
scrollbarHeight = (viewHeight * scrollableRatio).toInt().coerceAtLeast(20) scrollbarHeight = (viewHeight * scrollableRatio).toInt().coerceAtLeast(20)
} else { repaint()
scrollbarY = 0
scrollbarHeight = 0
} }
}
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) { override fun paintComponent(g: Graphics) {

View file

@ -69,7 +69,7 @@ object SpriteToBufferedImage {
* @param brightnessBoost A multiplier to boost the brightness of the image. * @param brightnessBoost A multiplier to boost the brightness of the image.
* @return The BufferedImage created from the sprite. * @return The BufferedImage created from the sprite.
*/ */
fun convertToBufferedImage( private fun convertToBufferedImage(
sprite: BaseSprite, sprite: BaseSprite,
tint: Color? = null, tint: Color? = null,
grayscale: Boolean = false, grayscale: Boolean = false,
@ -88,7 +88,7 @@ object SpriteToBufferedImage {
for (y in 0 until height) { for (y in 0 until height) {
for (x in 0 until width) { for (x in 0 until width) {
val index = pixels[y * width + x].toInt() and 0xFF val index = pixels[y * width + x].toInt() and 0xFF
var color = palette[index] val color = palette[index]
// Apply grayscale or tint if provided // Apply grayscale or tint if provided
val finalColor = if (grayscale) { val finalColor = if (grayscale) {
@ -109,7 +109,7 @@ object SpriteToBufferedImage {
// Manually set pixels directly // Manually set pixels directly
for (y in 0 until height) { for (y in 0 until height) {
for (x in 0 until width) { for (x in 0 until width) {
var color = pixels[y * width + x] val color = pixels[y * width + x]
// Apply grayscale or tint if provided // Apply grayscale or tint if provided
val finalColor = if (grayscale) { val finalColor = if (grayscale) {
@ -137,7 +137,7 @@ object SpriteToBufferedImage {
* @param brightnessBoost A multiplier to boost the brightness of the image. * @param brightnessBoost A multiplier to boost the brightness of the image.
* @return The tinted color. * @return The tinted color.
*/ */
fun applyTint(original: Color, tint: Color, brightnessBoost: Float): Color { private fun applyTint(original: Color, tint: Color, brightnessBoost: Float): Color {
val boostedColor = applyBrightness(original, brightnessBoost) val boostedColor = applyBrightness(original, brightnessBoost)
val r = (boostedColor.red * tint.red / 255).coerceIn(0, 255) val r = (boostedColor.red * tint.red / 255).coerceIn(0, 255)
val g = (boostedColor.green * tint.green / 255).coerceIn(0, 255) val g = (boostedColor.green * tint.green / 255).coerceIn(0, 255)
@ -152,7 +152,7 @@ object SpriteToBufferedImage {
* @param factor The multiplier to boost the brightness. * @param factor The multiplier to boost the brightness.
* @return The color with boosted brightness. * @return The color with boosted brightness.
*/ */
fun applyBrightness(original: Color, factor: Float): Color { private fun applyBrightness(original: Color, factor: Float): Color {
val r = (original.red * factor).coerceIn(0.0f, 255.0f).toInt() val r = (original.red * factor).coerceIn(0.0f, 255.0f).toInt()
val g = (original.green * 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() val b = (original.blue * factor).coerceIn(0.0f, 255.0f).toInt()
@ -166,7 +166,7 @@ object SpriteToBufferedImage {
* @param brightnessBoost A multiplier to boost the brightness. * @param brightnessBoost A multiplier to boost the brightness.
* @return The grayscale version of the color with boosted brightness. * @return The grayscale version of the color with boosted brightness.
*/ */
fun applyGrayscale(original: Color, brightnessBoost: Float): Color { private fun applyGrayscale(original: Color, brightnessBoost: Float): Color {
// Calculate the grayscale value using the luminosity method // Calculate the grayscale value using the luminosity method
val grayValue = (0.3 * original.red + 0.59 * original.green + 0.11 * original.blue).toInt() 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() val boostedGray = (grayValue * brightnessBoost).coerceIn(0.0f, 255.0f).toInt()

View file

@ -6,9 +6,9 @@ import rt4.Node
object XPTable { object XPTable {
const val MAX_LEVEL = 99 private const val MAX_LEVEL = 99
const val INVALID_LEVEL = -1 private const val INVALID_LEVEL = -1
const val SKILLS_XP_TABLE = 716 private const val SKILLS_XP_TABLE = 716
private var xpTable: MutableList<Int> = mutableListOf() private var xpTable: MutableList<Int> = mutableListOf()

View file

@ -34,14 +34,14 @@ import plugin.Plugin
import plugin.api.* import plugin.api.*
import plugin.api.API.* import plugin.api.API.*
import plugin.api.FontColor.fromColor import plugin.api.FontColor.fromColor
import rt4.GameShell import rt4.*
import rt4.DisplayMode
import rt4.GameShell.canvas
import rt4.GameShell.frame import rt4.GameShell.frame
import rt4.GlRenderer
import rt4.InterfaceList
import rt4.Player
import rt4.client.js5Archive8 import rt4.client.js5Archive8
import rt4.client.mainLoadState import rt4.client.mainLoadState
import java.awt.* import java.awt.*
import java.awt.Font
import java.awt.event.ActionListener import java.awt.event.ActionListener
import java.awt.event.MouseAdapter import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent import java.awt.event.MouseEvent
@ -91,7 +91,11 @@ class plugin : Plugin() {
"perfectly snapped to the edge of the game due to window chrome you can update this to fix it") "perfectly snapped to the edge of the game due to window chrome you can update this to fix it")
var uiOffset = 0 var uiOffset = 0
private const val FIXED_WIDTH = 765 @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 NAVBAR_WIDTH = 30
private const val MAIN_CONTENT_WIDTH = 242 private const val MAIN_CONTENT_WIDTH = 242
private const val WRENCH_ICON = 907 private const val WRENCH_ICON = 907
@ -103,14 +107,16 @@ class plugin : Plugin() {
private var rightPanelWrapper: JScrollPane? = null private var rightPanelWrapper: JScrollPane? = null
private var accumulatedTime = 0L private var accumulatedTime = 0L
private var reloadInterfaces = false private var reloadInterfaces = false
private const val tickInterval = 600L private const val TICK_INTERVAL = 600L
private var pluginsReloaded = false private var pluginsReloaded = false
private var loginScreen = 160 private var loginScreen = 160
private var lastLogin = "" private var lastLogin = ""
private var initialized = false private var initialized = false
private var lastClickTime = 0L private var lastClickTime = 0L
private var lastUIOffset = 0 private var lastUIOffset = 0
private var themeName = "RUNELITE"
private const val HIDDEN_VIEW = "HIDDEN" private const val HIDDEN_VIEW = "HIDDEN"
private var altCanvas: AltCanvas? = null
private val drawActions = mutableListOf<() -> Unit>() private val drawActions = mutableListOf<() -> Unit>()
fun registerDrawAction(action: () -> Unit) { fun registerDrawAction(action: () -> Unit) {
@ -120,24 +126,11 @@ class plugin : Plugin() {
} }
} }
fun allSpritesLoaded() : Boolean { override fun Init() {
// Check all skill sprites // Disable Font AA
try{ System.setProperty("sun.java2d.opengl", "false")
for (i in 0 until 24) { System.setProperty("awt.useSystemAAFontSettings", "off")
if(!js5Archive8.isFileReady(getSpriteId(i))){ System.setProperty("swing.aatext", "false")
return false
}
}
val otherIcons = arrayOf(LVL_ICON, MAG_SPRITE, LOOT_ICON, WRENCH_ICON, COMBAT_LVL_SPRITE, BAG_ICON)
for (icon in otherIcons) {
if(!js5Archive8.isFileReady(icon)){
return false
}
}
} catch (e : Exception){
return false
}
return true
} }
override fun OnLogin() { override fun OnLogin() {
@ -149,55 +142,6 @@ class plugin : Plugin() {
lastLogin = Player.usernameInput.toString() lastLogin = Player.usernameInput.toString()
} }
override fun Init() {
// Disable Font AA
System.setProperty("sun.java2d.opengl", "false")
System.setProperty("awt.useSystemAAFontSettings", "off")
System.setProperty("swing.aatext", "false")
}
private fun UpdateDisplaySettings() {
val mode = GetWindowMode()
val currentScrollPaneWidth = if (mainContentPanel.isVisible) NAVBAR_WIDTH + MAIN_CONTENT_WIDTH else NAVBAR_WIDTH
lastUIOffset = uiOffset
when (mode) {
WindowMode.FIXED -> {
if (frame.width < FIXED_WIDTH + currentScrollPaneWidth + uiOffset) {
frame.setSize(FIXED_WIDTH + currentScrollPaneWidth + uiOffset, frame.height)
}
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?.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)
if(lastUIOffset != uiOffset){
UpdateDisplaySettings()
reloadInterfaces = true
}
}
override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) { override fun OnMiniMenuCreate(currentEntries: Array<out MiniMenuEntry>?) {
if (currentEntries != null) { if (currentEntries != null) {
for ((index, entry) in currentEntries.withIndex()) { for ((index, entry) in currentEntries.withIndex()) {
@ -218,59 +162,43 @@ class plugin : Plugin() {
} }
} }
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.")
}
}
}
override fun OnPluginsReloaded(): Boolean { override fun OnPluginsReloaded(): Boolean {
if (!initialized) return true if (!initialized) return true
updateDisplaySettings()
UpdateDisplaySettings()
frame.remove(rightPanelWrapper) frame.remove(rightPanelWrapper)
frame.layout = BorderLayout() frame.layout = BorderLayout()
frame.add(rightPanelWrapper, BorderLayout.EAST) rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) }
frame.revalidate() frame.revalidate()
frame.repaint()
pluginsReloaded = true pluginsReloaded = true
reloadInterfaces = true reloadInterfaces = true
return true return true
} }
override fun OnXPUpdate(skillId: Int, xp: Int) { override fun OnXPUpdate(skillId: Int, xp: Int) {
if (!initialXP.containsKey(skillId)) { if (!initialXP.containsKey(skillId)) {
initialXP[skillId] = xp initialXP[skillId] = xp
return return
} }
var xpWidget = xpWidgets[skillId]
if (xpWidget != null) {
updateWidget(xpWidget, xp)
} else {
val previousXp = initialXP[skillId] ?: xp
if (xp == initialXP[skillId]) return
var xpWidget = xpWidgets[skillId] xpWidget = createXPWidget(skillId, previousXp)
xpWidgets[skillId] = xpWidget
if (xpWidget != null) { xpTrackerView?.add(wrappedWidget(xpWidget.container))
updateWidget(xpWidget, xp) xpTrackerView?.add(Box.createVerticalStrut(5))
} else {
val previousXp = initialXP[skillId] ?: xp
if (xp == initialXP[skillId]) return
xpWidget = createXPWidget(skillId, previousXp) if(focusedView == XPTrackerView.VIEW_NAME) {
xpWidgets[skillId] = xpWidget xpTrackerView?.revalidate()
xpTrackerView?.repaint()
}
xpTrackerView?.add(wrappedWidget(xpWidget.container)) updateWidget(xpWidget, xp)
xpTrackerView?.add(Box.createVerticalStrut(5)) }
xpTrackerView?.revalidate()
if(focusedView == XPTrackerView.VIEW_NAME)
xpTrackerView?.repaint()
updateWidget(xpWidget, xp)
}
} }
override fun Draw(timeDelta: Long) { override fun Draw(timeDelta: Long) {
@ -290,7 +218,7 @@ class plugin : Plugin() {
} }
accumulatedTime += timeDelta accumulatedTime += timeDelta
if (accumulatedTime >= tickInterval) { if (accumulatedTime >= TICK_INTERVAL) {
lootTrackerView?.let { onPostClientTick(it) } lootTrackerView?.let { onPostClientTick(it) }
accumulatedTime = 0L accumulatedTime = 0L
} }
@ -312,82 +240,202 @@ class plugin : Plugin() {
} }
} }
private fun initKondoUI(){ override fun LateDraw(timeDelta: Long) {
DrawText(FontType.LARGE, fromColor(Color(16777215)), TextModifier.CENTER, "KondoKit Loading Sprites...", GameShell.canvasWidth/2, GameShell.canvasHeight/2) if (!initialized) return
if(!allSpritesLoaded()) return; if(GameShell.fullScreenFrame != null) {
val frame: Frame? = GameShell.frame DisplayMode.setWindowMode(true, 0, FIXED_WIDTH, FIXED_HEIGHT)
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 initAltCanvas(){
if (frame != null) { if (frame != null) {
altCanvas = AltCanvas().apply {
preferredSize = Dimension(FIXED_WIDTH, FIXED_HEIGHT)
}
altCanvas?.let { frame.add(it) }
moveAltCanvasToFront()
frame.setComponentZOrder(rightPanelWrapper, 2)
}
updateDisplaySettings()
}
loadFont() private fun updateDisplaySettings() {
val themeIndex = (GetData("kondoTheme") as? String) ?: "RUNELITE" val mode = GetWindowMode()
theme = ThemeType.valueOf(themeIndex) val currentScrollPaneWidth = if (mainContentPanel.isVisible) NAVBAR_WIDTH + MAIN_CONTENT_WIDTH else NAVBAR_WIDTH
applyTheme(getTheme(theme)) lastUIOffset = uiOffset
appliedTheme = theme
try { when (mode) {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel") WindowMode.FIXED -> {
if (frame.width < FIXED_WIDTH + currentScrollPaneWidth + uiOffset) {
frame.setSize(FIXED_WIDTH + currentScrollPaneWidth + uiOffset, frame.height)
}
// Modify the UI properties to match theme val difference = frame.width - (uiOffset + currentScrollPaneWidth)
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) if (useScaledFixed) {
UIManager.put("nimbusSelectedText", secondaryColor) GameShell.leftMargin = 0
UIManager.put("text", secondaryColor) val canvasWidth = difference + uiOffset / 2
val canvasHeight = frame.height - canvas.y // Restricting height to frame height
UIManager.put("nimbusFocus", TITLE_BAR_COLOR) altCanvas?.size = Dimension(canvasWidth, canvasHeight)
UIManager.put("nimbusInfoBlue", POPUP_BACKGROUND) altCanvas?.setLocation(0, canvas.y)
UIManager.put("nimbusLightBackground", WIDGET_COLOR) canvas.setLocation(0, canvas.y)
UIManager.put("nimbusSelectionBackground", PROGRESS_BAR_FILL) } else {
val difference = frame.width - (FIXED_WIDTH + uiOffset + currentScrollPaneWidth)
UIManager.put("Button.background", WIDGET_COLOR) GameShell.leftMargin = difference / 2
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(GameShell.frame)
} catch (e : Exception) {
e.printStackTrace()
} }
// Restore saved values WindowMode.RESIZABLE -> {
useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true GameShell.canvasWidth = frame.width - (currentScrollPaneWidth + uiOffset)
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 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(altCanvas == null && useScaledFixed){
initAltCanvas()
} else if(altCanvas != null && !useScaledFixed){
destroyAltCanvas()
}
if(lastUIOffset != uiOffset){
updateDisplaySettings()
reloadInterfaces = true
}
}
private fun destroyAltCanvas(){
moveCanvasToFront()
frame.remove(altCanvas)
altCanvas = null
updateDisplaySettings()
}
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 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 restoreSettings(){
themeName = (GetData("kondoTheme") as? String) ?: "RUNELITE"
useLiveGEPrices = (GetData("kondoUseRemoteGE") as? Boolean) ?: true
playerXPMultiplier = (GetData("kondoPlayerXPMultiplier") as? Int) ?: 5
val osName = System.getProperty("os.name").toLowerCase()
uiOffset = (GetData("kondoUIOffset") as? Int) ?: if (osName.contains("win")) 16 else 0
launchMinimized = (GetData("kondoLaunchMinimized") as? Boolean) ?: false
useScaledFixed = (GetData("kondoScaledFixed") as? Boolean) ?: false
}
private fun initKondoUI(){
DrawText(FontType.LARGE, fromColor(Color(16777215)), TextModifier.CENTER, "KondoKit Loading Sprites...", GameShell.canvasWidth/2, GameShell.canvasHeight/2)
if(!allSpritesLoaded()) return
val frame: Frame? = GameShell.frame
if (frame != null) {
restoreSettings()
theme = ThemeType.valueOf(themeName)
applyTheme(getTheme(theme))
appliedTheme = theme
configureLookAndFeel()
cardLayout = CardLayout() cardLayout = CardLayout()
mainContentPanel = JPanel(cardLayout).apply { mainContentPanel = JPanel(cardLayout).apply {
@ -419,7 +467,7 @@ class plugin : Plugin() {
navPanel.add(createNavButton(LOOT_ICON, LootTrackerView.VIEW_NAME)) navPanel.add(createNavButton(LOOT_ICON, LootTrackerView.VIEW_NAME))
navPanel.add(createNavButton(WRENCH_ICON, ReflectiveEditorView.VIEW_NAME)) navPanel.add(createNavButton(WRENCH_ICON, ReflectiveEditorView.VIEW_NAME))
var rightPanel = Panel(BorderLayout()).apply { val rightPanel = Panel(BorderLayout()).apply {
add(mainContentPanel, BorderLayout.CENTER) add(mainContentPanel, BorderLayout.CENTER)
add(navPanel, BorderLayout.EAST) add(navPanel, BorderLayout.EAST)
} }
@ -427,53 +475,29 @@ class plugin : Plugin() {
rightPanelWrapper = JScrollPane(rightPanel).apply { rightPanelWrapper = JScrollPane(rightPanel).apply {
preferredSize = Dimension(NAVBAR_WIDTH + MAIN_CONTENT_WIDTH, frame.height) preferredSize = Dimension(NAVBAR_WIDTH + MAIN_CONTENT_WIDTH, frame.height)
background = VIEW_BACKGROUND_COLOR background = VIEW_BACKGROUND_COLOR
border = BorderFactory.createEmptyBorder() // Removes the border completely border = BorderFactory.createEmptyBorder()
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_NEVER verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_NEVER
} }
frame.layout = BorderLayout() frame.layout = BorderLayout()
rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) } rightPanelWrapper?.let {
frame.add(it, BorderLayout.EAST)
}
if(!launchMinimized){ if(launchMinimized){
setActiveView(XPTrackerView.VIEW_NAME)
} else {
setActiveView(HIDDEN_VIEW) setActiveView(HIDDEN_VIEW)
} else {
setActiveView(XPTrackerView.VIEW_NAME)
}
if(useScaledFixed) {
initAltCanvas()
} }
initialized = true initialized = true
pluginsReloaded = true pluginsReloaded = true
} }
} }
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 setActiveView(viewName: String) { private fun setActiveView(viewName: String) {
// Handle the visibility of the main content panel // Handle the visibility of the main content panel
if (viewName == HIDDEN_VIEW) { if (viewName == HIDDEN_VIEW) {
@ -486,14 +510,15 @@ class plugin : Plugin() {
} }
reloadInterfaces = true reloadInterfaces = true
UpdateDisplaySettings() updateDisplaySettings()
// Revalidate and repaint necessary panels // Revalidate and repaint necessary panels
mainContentPanel.revalidate() mainContentPanel.revalidate()
mainContentPanel.repaint()
rightPanelWrapper?.revalidate() rightPanelWrapper?.revalidate()
rightPanelWrapper?.repaint()
frame?.revalidate() frame?.revalidate()
mainContentPanel.repaint()
rightPanelWrapper?.repaint()
frame?.repaint() frame?.repaint()
focusedView = viewName focusedView = viewName
@ -543,7 +568,7 @@ class plugin : Plugin() {
maximumSize = buttonSize maximumSize = buttonSize
minimumSize = buttonSize minimumSize = buttonSize
background = WIDGET_COLOR background = WIDGET_COLOR
isOpaque = true // Ensure background is painted isOpaque = true
val gbc = GridBagConstraints().apply { val gbc = GridBagConstraints().apply {
anchor = GridBagConstraints.CENTER anchor = GridBagConstraints.CENTER
@ -580,8 +605,69 @@ class plugin : Plugin() {
return panelButton return panelButton
} }
private fun configureLookAndFeel(){
loadFont()
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel")
fun loadFont(): Font? { // 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") val fontStream = plugin::class.java.getResourceAsStream("res/runescape_small.ttf")
return if (fontStream != null) { return if (fontStream != null) {
try { try {
@ -603,7 +689,7 @@ class plugin : Plugin() {
var focusedView: String = "" var focusedView: String = ""
} }
fun applyTheme(theme: Theme) { private fun applyTheme(theme: Theme) {
WIDGET_COLOR = theme.widgetColor WIDGET_COLOR = theme.widgetColor
TITLE_BAR_COLOR = theme.titleBarColor TITLE_BAR_COLOR = theme.titleBarColor
VIEW_BACKGROUND_COLOR = theme.viewBackgroundColor VIEW_BACKGROUND_COLOR = theme.viewBackgroundColor