centralize all colors, begin themeing support

This commit is contained in:
downthecrop 2024-10-22 18:39:30 -07:00
parent 72bf9a8297
commit 2a059d1c02
9 changed files with 405 additions and 186 deletions

View file

@ -190,6 +190,10 @@ object Helpers {
}
}
fun showAlert(message: String, title: String, type: Int){
JOptionPane.showMessageDialog(null, message, title, type)
}
fun formatHtmlLabelText(text1: String, color1: Color, text2: String, color2: Color): String {
return "<html><div style='white-space:nowrap;'>" +
"<span style='color:rgb(${color1.red},${color1.green},${color1.blue});'>$text1</span>" +

View file

@ -7,10 +7,15 @@ import KondoKit.Helpers.formatHtmlLabelText
import KondoKit.Helpers.getSpriteId
import KondoKit.Helpers.showToast
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
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.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
@ -27,6 +32,7 @@ import java.net.SocketTimeoutException
import java.net.URL
import javax.swing.*
import javax.swing.border.MatteBorder
import kotlin.math.floor
object Constants {
// Sprite IDs
@ -38,7 +44,6 @@ object Constants {
// Dimensions
val SEARCH_FIELD_DIMENSION = Dimension(230, 30)
val ICON_DIMENSION_SMALL = Dimension(12, 12)
val ICON_DIMENSION_MEDIUM = Dimension(18, 20)
val ICON_DIMENSION_LARGE = Dimension(30, 30)
val HISCORE_PANEL_DIMENSION = Dimension(230, 500)
val FILTER_PANEL_DIMENSION = Dimension(230, 30)
@ -50,11 +55,9 @@ object Constants {
val NUMBER_LABEL_DIMENSION = Dimension(20, 20)
// Colors
val COLOR_BACKGROUND_DARK = Color(27, 27, 27)
val COLOR_BACKGROUND_DARK = WIDGET_COLOR
val COLOR_BACKGROUND_MEDIUM = VIEW_BACKGROUND_COLOR
val COLOR_FOREGROUND_LIGHT = Color(200, 200, 200)
val COLOR_RED = Color.RED
val COLOR_SKILL_PANEL = Color(60, 60, 60)
val COLOR_FOREGROUND_LIGHT = POPUP_FOREGROUND
// Fonts
val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14)
@ -67,6 +70,7 @@ var text: String = ""
object HiscoresView {
const val VIEW_NAME = "HISCORE_SEARCH_VIEW"
var hiScoreView: JPanel? = null
class CustomSearchField(private val hiscoresPanel: JPanel) : Canvas() {
@ -137,9 +141,9 @@ object HiscoresView {
}
})
Timer(1000) {
Timer(500) {
cursorVisible = !cursorVisible
if(plugin.StateManager.focusedView == "HISCORE_SEARCH_VIEW")
if(focusedView == VIEW_NAME)
repaint()
}.start()
}
@ -165,7 +169,7 @@ object HiscoresView {
}
if (text.isNotEmpty()) {
g.color = Constants.COLOR_RED
g.color = Color.RED
g.drawString("x", width - 20, 20)
}
}
@ -305,13 +309,13 @@ object HiscoresView {
summoning: Int,
isMemberWorld: Boolean
): Double {
val base = (defence + hitpoints + Math.floor(prayer.toDouble() / 2)) * 0.25
val base = (defence + hitpoints + floor(prayer.toDouble() / 2)) * 0.25
val melee = (attack + strength) * 0.325
val range = Math.floor(ranged * 1.5) * 0.325
val mage = Math.floor(magic * 1.5) * 0.325
val range = floor(ranged * 1.5) * 0.325
val mage = floor(magic * 1.5) * 0.325
val maxCombatType = maxOf(melee, range, mage)
val summoningFactor = if (isMemberWorld) Math.floor(summoning.toDouble() / 8) else 0.0
val summoningFactor = if (isMemberWorld) floor(summoning.toDouble() / 8) else 0.0
return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0
}
@ -338,7 +342,7 @@ object HiscoresView {
fun createHiscoreSearchView() {
val hiscorePanel = JPanel().apply {
layout = BoxLayout(this, BoxLayout.Y_AXIS)
name = "HISCORE_SEARCH_VIEW"
name = VIEW_NAME
background = Constants.COLOR_BACKGROUND_MEDIUM
preferredSize = Constants.HISCORE_PANEL_DIMENSION
maximumSize = preferredSize
@ -369,7 +373,7 @@ object HiscoresView {
val playerNamePanel = JPanel().apply {
layout = GridBagLayout() // This will center the JLabel both vertically and horizontally
background = WIDGET_COLOR
background = TOOLTIP_BACKGROUND.darker()
preferredSize = Constants.FILTER_PANEL_DIMENSION
maximumSize = preferredSize
minimumSize = preferredSize
@ -436,6 +440,7 @@ object HiscoresView {
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
}
@ -457,6 +462,7 @@ object HiscoresView {
val bufferedImageSprite2 = getBufferedImageFromSprite(API.GetSprite(Constants.COMBAT_LVL_SPRITE))
val combatLevelIcon = ImageCanvas(bufferedImageSprite2).apply {
fillColor = COLOR_BACKGROUND_DARK
preferredSize = Constants.ICON_DIMENSION_LARGE
size = Constants.ICON_DIMENSION_LARGE
}

View file

@ -4,11 +4,17 @@ import KondoKit.Helpers.addMouseListenerToAll
import KondoKit.Helpers.formatHtmlLabelText
import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite
import KondoKit.XPTrackerView.wrappedWidget
import KondoKit.plugin.Companion.IMAGE_SIZE
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 java.awt.*
@ -30,6 +36,7 @@ object LootTrackerView {
const val BAG_ICON = 900;
val npcDeathSnapshots = mutableMapOf<Int, GroundSnapshot>()
var gePriceMap = loadGEPrices()
const val VIEW_NAME = "LOOT_TRACKER_VIEW";
private val lootItemPanels = mutableMapOf<String, MutableMap<Int, Int>>()
private val npcKillCounts = mutableMapOf<String, Int>()
private var totalTrackerWidget: XPWidget? = null
@ -125,8 +132,9 @@ object LootTrackerView {
addMouseListenerToAll(wrapped,rightClickListener)
wrapped.addMouseListener(rightClickListener)
add(wrapped)
add(Box.createVerticalStrut(10))
add(Box.createVerticalStrut(8))
revalidate()
if(focusedView == VIEW_NAME)
repaint()
}
}
@ -158,7 +166,7 @@ object LootTrackerView {
xpLeftLabel = JLabel(),
actionsRemainingLabel = JLabel(),
xpPerHourLabel = l1,
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
progressBar = ProgressBar(0.0, Color(0,0,0)), // unused.
totalXpGained = 0,
startTime = System.currentTimeMillis(),
previousXp = 0
@ -167,16 +175,19 @@ object LootTrackerView {
private fun createWidgetPanel(bufferedImageSprite: BufferedImage, l1 : JLabel, l2 : JLabel): Panel {
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
size = Dimension(width, height)
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
minimumSize = preferredSize
maximumSize = preferredSize
size = preferredSize
background = WIDGET_COLOR
}
val imageContainer = Panel(FlowLayout()).apply {
val imageContainer = Panel(BorderLayout()).apply {
background = WIDGET_COLOR
add(imageCanvas)
add(imageCanvas, BorderLayout.NORTH)
}
return Panel(BorderLayout(5, 5)).apply {
return Panel(BorderLayout(5, 0)).apply {
background = WIDGET_COLOR
preferredSize = TOTAL_XP_WIDGET_SIZE
add(imageContainer, BorderLayout.WEST)
@ -185,7 +196,7 @@ object LootTrackerView {
}
private fun createTextPanel(l1 : JLabel, l2: JLabel): Panel {
return Panel(GridLayout(2, 1, 5, 5)).apply {
return Panel(GridLayout(2, 1, 5, 0)).apply {
background = WIDGET_COLOR
add(l1)
add(l2)
@ -219,8 +230,9 @@ object LootTrackerView {
lootPanel.parent.maximumSize = size
lootPanel.parent.revalidate()
lootPanel.parent.repaint()
lootPanel.revalidate()
if(focusedView == VIEW_NAME)
lootPanel.repaint()
}
}
@ -231,7 +243,7 @@ object LootTrackerView {
}
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 0, 0))
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 1, 3153952))
// Create the panel for the item
val itemPanel = FixedSizePanel(Dimension(36, 32)).apply {
@ -276,22 +288,22 @@ object LootTrackerView {
val gePricePerItem = gePriceMap[itemDef.id.toString()]?.toInt() ?: 0
val totalGePrice = gePricePerItem * quantity
val totalHaPrice = itemDef.cost * quantity
val geText = if (quantity > 1) " (${gePricePerItem} ea)" else ""
val haText = if (quantity > 1) " (${itemDef.cost} ea)" else ""
val text = "<html><div style='color: white; background-color: #323232; padding: 3px;'>" +
val geText = if (quantity > 1) " (${formatValue(gePricePerItem)} ea)" else ""
val haText = if (quantity > 1) " (${formatValue(itemDef.cost)} ea)" else ""
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
val textColor = Helpers.colorToHex(secondaryColor)
val text = "<html><div style='color: "+textColor+"; background-color: "+bgColor+"; padding: 3px;'>" +
"${itemDef.name} x $quantity<br>" +
"GE: $totalGePrice ${geText}<br>" +
"HA: $totalHaPrice ${haText}</div></html>"
"GE: ${formatValue(totalGePrice)} ${geText}<br>" +
"HA: ${formatValue(totalHaPrice)} ${haText}</div></html>"
val _font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
val c = Color(50,50,50)
if (customToolTipWindow == null) {
customToolTipWindow = JWindow().apply {
contentPane = JLabel(text).apply {
border = BorderFactory.createLineBorder(Color.BLACK)
isOpaque = true
background = c
background = TOOLTIP_BACKGROUND
foreground = Color.WHITE
font = _font
}
@ -316,6 +328,7 @@ object LootTrackerView {
panel.removeAll()
panel.add(createItemPanel(itemId, quantity).components[0], BorderLayout.CENTER)
panel.revalidate()
if(focusedView == VIEW_NAME)
panel.repaint()
}
@ -340,6 +353,7 @@ object LootTrackerView {
text = "${formatValue(newValue)} gp"
putClientProperty("val", newValue)
revalidate()
if(focusedView == VIEW_NAME)
repaint()
}
}
@ -401,8 +415,9 @@ object LootTrackerView {
findLootItemsPanel(lootTrackerView, npcName)?.let {
} ?: run {
lootTrackerView.add(createLootFrame(npcName))
lootTrackerView.add(Box.createVerticalStrut(10))
lootTrackerView.add(Box.createVerticalStrut(8))
lootTrackerView.revalidate()
if(focusedView == VIEW_NAME)
lootTrackerView.repaint()
}
@ -427,7 +442,7 @@ object LootTrackerView {
}
val labelPanel = JPanel(BorderLayout()).apply {
background = Color(21, 21, 21)
background = TITLE_BAR_COLOR
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
maximumSize = Dimension(230, 24)
minimumSize = maximumSize
@ -436,14 +451,14 @@ object LootTrackerView {
val killCount = npcKillCounts.getOrPut(npcName) { 0 }
val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply {
foreground = Color(200, 200, 200)
foreground = secondaryColor
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
horizontalAlignment = JLabel.LEFT
name = "killCountLabel_$npcName"
}
val valueLabel = JLabel("0 gp").apply {
foreground = Color(200, 200, 200)
foreground = secondaryColor
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
horizontalAlignment = JLabel.RIGHT
name = "valueLabel_$npcName"
@ -491,13 +506,13 @@ object LootTrackerView {
val popupMenu = JPopupMenu()
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
popupMenu.background = Color(45, 45, 45)
popupMenu.background = POPUP_BACKGROUND
// Create menu items with custom font and colors
val menuItem1 = JMenuItem("Remove").apply {
font = rFont // Set custom font
background = Color(45, 45, 45) // Dark background for item
foreground = Color(220, 220, 220) // Light text color for item
background = POPUP_BACKGROUND // Dark background for item
foreground = POPUP_FOREGROUND // Light text color for item
}
popupMenu.add(menuItem1)
menuItem1.addActionListener {
@ -509,7 +524,7 @@ object LootTrackerView {
if (toRemoveIndex >= 0 && toRemoveIndex < components.size - 1) {
val nextComponent = components[toRemoveIndex + 1]
if (nextComponent is Box.Filler) {
// Nasty way to remove the Box.createVerticalStrut(10) after
// Nasty way to remove the Box.createVerticalStrut(8) after
// the lootpanel.
parent.remove(nextComponent)
}
@ -528,13 +543,13 @@ object LootTrackerView {
val popupMenu = JPopupMenu()
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
popupMenu.background = Color(45, 45, 45)
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 = Color(45, 45, 45) // Dark background for item
foreground = Color(220, 220, 220) // Light text color for item
background = POPUP_BACKGROUND // Dark background for item
foreground = POPUP_FOREGROUND // Light text color for item
}
popupMenu.add(menuItem1)
menuItem1.addActionListener {
@ -572,7 +587,7 @@ object LootTrackerView {
wrapped.addMouseListener(rightClickListener)
lootTrackerView?.add(Box.createVerticalStrut(5))
lootTrackerView?.add(wrapped)
lootTrackerView?.add(Box.createVerticalStrut(10))
lootTrackerView?.add(Box.createVerticalStrut(8))
lootTrackerView?.revalidate()
lootTrackerView?.repaint()
}

View file

@ -1,5 +1,7 @@
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
@ -26,24 +28,24 @@ class ProgressBar(
g.fillRect(0, 0, width, this.height)
// Draw the unfilled part of the progress bar
g.color = Color(61, 56, 49) // from Runelite
g.color = PROGRESS_BAR_FILL
g.fillRect(width, 0, this.width - width, this.height)
// Variables for text position
val textY = this.height / 2 + 6
// Draw the current level on the far left
drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, Color(255, 255, 255))
drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, secondaryColor)
// Draw the percentage in the middle
val percentageText = String.format("%.2f%%", progress)
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
drawTextWithShadow(g, percentageText, (this.width - percentageWidth) / 2, textY, Color(255, 255, 255))
drawTextWithShadow(g, percentageText, (this.width - percentageWidth) / 2, textY, secondaryColor)
// Draw the next level on the far right
val nextLevelText = "Lvl. $nextLevel"
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
drawTextWithShadow(g, nextLevelText, this.width - nextLevelWidth - 5, textY, Color(255, 255, 255))
drawTextWithShadow(g, nextLevelText, this.width - nextLevelWidth - 5, textY, secondaryColor)
}
override fun getPreferredSize(): Dimension {

View file

@ -2,10 +2,13 @@ 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.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
@ -28,6 +31,7 @@ import kotlin.math.ceil
object ReflectiveEditorView {
var reflectiveEditorView: JPanel? = null
val loadedPlugins: MutableList<String> = mutableListOf()
const val VIEW_NAME = "REFLECTIVE_EDITOR_VIEW"
fun createReflectiveEditorView() {
val reflectiveEditorPanel = JPanel(BorderLayout())
reflectiveEditorPanel.background = VIEW_BACKGROUND_COLOR
@ -100,7 +104,7 @@ object ReflectiveEditorView {
reflectiveEditorView.revalidate()
if(plugin.StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW")
if(focusedView == VIEW_NAME)
reflectiveEditorView.repaint()
}
@ -128,7 +132,7 @@ object ReflectiveEditorView {
label.font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
labelPanel.add(label, BorderLayout.CENTER)
label.isOpaque = true
label.background = Color(21, 21, 21)
label.background = TITLE_BAR_COLOR
reflectiveEditorView.add(labelPanel)
for (field in exposedFields) {
@ -270,7 +274,6 @@ object ReflectiveEditorView {
fun showCustomToolTip(text: String, component: JComponent) {
val _font = Font("RuneScape Small", Font.PLAIN, 16)
val backgroundColor = Color(50, 50, 50)
val maxWidth = 150
val lineHeight = 16
@ -290,10 +293,12 @@ object ReflectiveEditorView {
if (customToolTipWindow == null) {
customToolTipWindow = JWindow().apply {
contentPane = JLabel("<html><div style='color: white; background-color: #323232; padding: 3px; word-break: break-all;'>$text</div></html>").apply {
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
val textColor = Helpers.colorToHex(secondaryColor)
contentPane = JLabel("<html><div style='color: $textColor; background-color: $bgColor; padding: 3px; word-break: break-all;'>$text</div></html>").apply {
border = BorderFactory.createLineBorder(Color.BLACK)
isOpaque = true
background = backgroundColor
background = TOOLTIP_BACKGROUND
foreground = Color.WHITE
font = _font
maximumSize = Dimension(maxWidth, Int.MAX_VALUE)
@ -304,7 +309,9 @@ object ReflectiveEditorView {
} else {
// Update the tooltip text
val label = customToolTipWindow!!.contentPane as JLabel
label.text = "<html><div style='color: white; background-color: #323232; padding: 3px; word-break: break-all;'>$text</div></html>"
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
val textColor = Helpers.colorToHex(secondaryColor)
label.text = "<html><div style='color: $textColor; background-color: $bgColor; padding: 3px; word-break: break-all;'>$text</div></html>"
label.preferredSize = Dimension(maxWidth, requiredHeight)
customToolTipWindow!!.pack()
}

View file

@ -1,8 +1,8 @@
package KondoKit
import KondoKit.plugin.Companion.SCROLL_BAR_COLOR
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
import rt4.GameShell.frame
import java.awt.Color
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.Rectangle
@ -147,7 +147,7 @@ class ScrollablePanel(private val content: JPanel) : JPanel() {
if (showScrollbar) {
val g2 = g as Graphics2D
val scrollbarX = 238
g2.color = Color(64, 64, 64)
g2.color = SCROLL_BAR_COLOR
g2.fillRect(scrollbarX, scrollbarY, 2, scrollbarHeight)
}
}

View file

@ -4,109 +4,172 @@ import rt4.GlIndexedSprite
import rt4.GlSprite
import rt4.SoftwareIndexedSprite
import rt4.SoftwareSprite
import java.awt.Color
import java.awt.image.BufferedImage
// Define interfaces for common sprite types
interface BaseSprite {
val width: Int
val height: Int
}
interface IndexedSprite : BaseSprite {
val pixels: ByteArray
val palette: IntArray
}
interface NonIndexedSprite : BaseSprite {
val pixels: IntArray?
}
// Adapter functions for existing sprite types
fun adaptSoftwareSprite(sprite: SoftwareSprite): NonIndexedSprite {
return object : NonIndexedSprite {
override val width: Int = sprite.width
override val height: Int = sprite.height
override val pixels: IntArray? = sprite.pixels
}
}
fun adaptSoftwareIndexedSprite(sprite: SoftwareIndexedSprite): IndexedSprite {
return object : IndexedSprite {
override val width: Int = sprite.width
override val height: Int = sprite.height
override val pixels: ByteArray = sprite.pixels
override val palette: IntArray = sprite.pallet
}
}
fun adaptGlSprite(sprite: GlSprite): NonIndexedSprite {
return object : NonIndexedSprite {
override val width: Int = sprite.width
override val height: Int = sprite.height
override val pixels: IntArray? = sprite.pixels
}
}
fun adaptGlIndexedSprite(sprite: GlIndexedSprite): IndexedSprite {
return object : IndexedSprite {
override val width: Int = sprite.width
override val height: Int = sprite.height
override val pixels: ByteArray = sprite.pixels
override val palette: IntArray = sprite.pallet
}
}
object SpriteToBufferedImage {
/**
* Converts a SoftwareSprite back into a BufferedImage.
* Converts a BaseSprite into a BufferedImage.
*
* Handles both indexed and non-indexed sprites, with optional tinting and grayscale.
*
* @param sprite The sprite to be converted.
* @param tint An optional Color to tint the image.
* @param grayscale If true, converts the image to grayscale.
* @return The BufferedImage created from the sprite.
*/
fun convertToBufferedImage(sprite: SoftwareSprite): BufferedImage {
fun convertToBufferedImage(sprite: BaseSprite, tint: Color? = null, grayscale: Boolean = false): BufferedImage {
val width = sprite.width
val height = sprite.height
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
when (sprite) {
is IndexedSprite -> {
val pixels = sprite.pixels
// Create a BufferedImage with ARGB color model
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
// Manually set pixels and print the pixel data
for (y in 0 until height) {
for (x in 0 until width) {
val pixel = pixels[y * width + x]
image.setRGB(x, y, pixel)
}
}
return image
}
fun convertToBufferedImage(sprite: SoftwareIndexedSprite): BufferedImage {
val width = sprite.width
val height = sprite.height
val pixels = sprite.pixels // byte[]
val palette = sprite.pallet
// Create a BufferedImage with ARGB color model
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
val palette = sprite.palette
// 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
var color = palette[index]
// Apply grayscale or tint if provided
val finalColor = if (grayscale) {
applyGrayscale(Color(color, true))
} else if (tint != null) {
applyTint(Color(color, true), tint)
} else {
Color(color, true)
}
fun convertToBufferedImage(sprite: GlIndexedSprite): BufferedImage {
val width = sprite.width
val height = sprite.height
val pixels = sprite.pixels // byte[]
val palette = sprite.pallet
image.setRGB(x, y, finalColor.rgb)
}
}
}
is NonIndexedSprite -> {
val pixels = sprite.pixels ?: return image // Handle null case for GlSprite
// Create a BufferedImage with ARGB color model
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
// Manually set pixels using the palette
// Manually set pixels directly
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)
var color = pixels[y * width + x]
// Apply grayscale or tint if provided
val finalColor = if (grayscale) {
applyGrayscale(Color(color, true))
} else if (tint != null) {
applyTint(Color(color, true), tint)
} else {
Color(color, true)
}
image.setRGB(x, y, finalColor.rgb)
}
}
}
}
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, with alpha controlling the intensity.
* @return The tinted color.
*/
fun applyTint(original: Color, tint: Color): Color {
val alpha = tint.alpha / 255.0 // Normalize alpha to range [0, 1]
val invAlpha = 1.0 - alpha
fun convertToBufferedImage(sprite: GlSprite): BufferedImage {
val width = sprite.width
val height = sprite.height
val pixels = sprite.pixels
// Blend the tint with the original color based on the alpha
val r = (original.red * invAlpha + tint.red * alpha).toInt().coerceIn(0, 255)
val g = (original.green * invAlpha + tint.green * alpha).toInt().coerceIn(0, 255)
val b = (original.blue * invAlpha + tint.blue * alpha).toInt().coerceIn(0, 255)
// Create a BufferedImage with ARGB color model
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
if(pixels == null) {
return image
return Color(r, g, b, original.alpha)
}
// Manually set pixels and print the pixel data
for (y in 0 until height) {
for (x in 0 until width) {
val pixel = pixels[y * width + x]
image.setRGB(x, y, pixel)
}
}
return image
/**
* Converts a color to grayscale.
*
* @param original The original color.
* @return The grayscale version of the color.
*/
fun applyGrayscale(original: Color): Color {
// Calculate the grayscale value using the luminosity method
val grayValue = (0.3 * original.red + 0.59 * original.green + 0.11 * original.blue).toInt()
return Color(grayValue, grayValue, grayValue, original.alpha)
}
fun getBufferedImageFromSprite(sprite: Any?) : BufferedImage {
return when(sprite){
is GlSprite -> convertToBufferedImage(sprite)
is SoftwareSprite -> convertToBufferedImage(sprite)
is SoftwareIndexedSprite -> convertToBufferedImage(sprite)
is GlIndexedSprite -> convertToBufferedImage(sprite)
else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
/**
* Converts an unknown sprite object into a BufferedImage if it matches a known sprite type.
*
* @param sprite The sprite object to be converted.
* @param tint An optional Color to tint the image.
* @param grayscale If true, converts the image to grayscale.
* @return The BufferedImage created from the sprite or a default image if unsupported.
*/
fun getBufferedImageFromSprite(sprite: Any?, tint: Color? = null, grayscale: Boolean = false): BufferedImage {
return when (sprite) {
is SoftwareSprite -> convertToBufferedImage(adaptSoftwareSprite(sprite), tint, grayscale)
is SoftwareIndexedSprite -> convertToBufferedImage(adaptSoftwareIndexedSprite(sprite), tint, grayscale)
is GlSprite -> convertToBufferedImage(adaptGlSprite(sprite), tint, grayscale)
is GlIndexedSprite -> convertToBufferedImage(adaptGlIndexedSprite(sprite), tint, grayscale)
else -> BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) // Default empty image for unsupported types
}
}
}

View file

@ -8,6 +8,8 @@ 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
@ -15,6 +17,7 @@ import KondoKit.plugin.Companion.WIDGET_SIZE
import KondoKit.plugin.Companion.playerXPMultiplier
import KondoKit.plugin.Companion.primaryColor
import KondoKit.plugin.Companion.secondaryColor
import KondoKit.plugin.StateManager.focusedView
import plugin.api.API
import java.awt.*
import java.awt.event.MouseAdapter
@ -30,6 +33,7 @@ object XPTrackerView {
var totalXPWidget: XPWidget? = null
val initialXP: MutableMap<Int, Int> = HashMap()
var xpTrackerView: JPanel? = null
const val VIEW_NAME = "XP_TRACKER_VIEW"
val npcHitpointsMap: Map<Int, Int> = try {
@ -96,10 +100,10 @@ object XPTrackerView {
xpWidget.xpGainedLabel.text = formatHtmlLabelText("XP Gained: ", primaryColor, formattedXp, secondaryColor)
// Update the progress bar with current level, progress, and next level
xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, focusedView == VIEW_NAME)
xpWidget.previousXp = xp
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
if (focusedView == VIEW_NAME)
xpWidget.container.repaint()
}
@ -109,7 +113,7 @@ object XPTrackerView {
totalXPWidget.totalXpGained += xpGainedSinceLastUpdate
val formattedXp = formatNumber(totalXPWidget.totalXpGained)
totalXPWidget.xpGainedLabel.text = formatHtmlLabelText("Gained: ", primaryColor, formattedXp, secondaryColor)
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
if (focusedView == VIEW_NAME)
totalXPWidget.container.repaint()
}
@ -148,7 +152,7 @@ object XPTrackerView {
xpWidgets.clear()
xpTrackerView.revalidate()
if (plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
if (focusedView == VIEW_NAME)
xpTrackerView.repaint()
}
@ -164,10 +168,10 @@ object XPTrackerView {
val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(LVL_ICON))
val imageContainer = Panel(FlowLayout()).apply {
preferredSize = IMAGE_SIZE
maximumSize = IMAGE_SIZE
minimumSize = IMAGE_SIZE
size = IMAGE_SIZE
preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
maximumSize = preferredSize
minimumSize = preferredSize
size = preferredSize
}
bufferedImageSprite.let { image ->
@ -182,7 +186,7 @@ object XPTrackerView {
imageContainer.size = Dimension(bufferedImageSprite.width, bufferedImageSprite.height)
imageContainer.revalidate()
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
if(focusedView == VIEW_NAME)
imageContainer.repaint()
}
@ -221,7 +225,7 @@ object XPTrackerView {
this.font = font
},
xpPerHourLabel = xpPerHourLabel,
progressBar = ProgressBar(0.0, Color(150, 50, 50)),
progressBar = ProgressBar(0.0, Color.BLACK), // Unused
totalXpGained = 0,
startTime = System.currentTimeMillis(),
previousXp = 0,
@ -272,25 +276,13 @@ object XPTrackerView {
val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
popupMenu.background = Color(45, 45, 45)
popupMenu.background = POPUP_BACKGROUND
// Create menu items with custom font and colors
val menuItem1 = JMenuItem("Reset Tracker").apply {
font = rFont // Set custom font
background = Color(45, 45, 45) // Dark background for item
foreground = Color(220, 220, 220) // Light text color for item
}
val menuItem2 = JMenuItem("Option 2").apply {
font = rFont
background = Color(45, 45, 45)
foreground = Color(220, 220, 220)
}
val menuItem3 = JMenuItem("Option 3").apply {
font = rFont
background = Color(45, 45, 45)
foreground = Color(220, 220, 220)
background = POPUP_BACKGROUND // Dark background for item
foreground = POPUP_FOREGROUND // Light text color for item
}
// Add menu items to the popup menu
@ -333,7 +325,7 @@ object XPTrackerView {
imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size
imageContainer.revalidate()
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
if(focusedView == VIEW_NAME)
imageContainer.repaint()
}
@ -374,7 +366,7 @@ object XPTrackerView {
val levelPanel = Panel().apply {
layout = BorderLayout(5, 0)
background = Color(43, 43, 43)
background = WIDGET_COLOR
}
val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply {
@ -393,7 +385,7 @@ object XPTrackerView {
widgetPanel.add(levelPanel, BorderLayout.SOUTH)
widgetPanel.revalidate()
if(plugin.StateManager.focusedView == "XP_TRACKER_VIEW")
if(focusedView == VIEW_NAME)
widgetPanel.repaint()
return XPWidget(

View file

@ -4,6 +4,8 @@ import KondoKit.Constants.COMBAT_LVL_SPRITE
import KondoKit.Helpers.formatHtmlLabelText
import KondoKit.Helpers.formatNumber
import KondoKit.Helpers.getSpriteId
import KondoKit.Helpers.showAlert
import KondoKit.Helpers.showToast
import KondoKit.HiscoresView.createHiscoreSearchView
import KondoKit.HiscoresView.hiScoreView
import KondoKit.LootTrackerView.BAG_ICON
@ -26,6 +28,7 @@ import KondoKit.XPTrackerView.wrappedWidget
import KondoKit.XPTrackerView.xpTrackerView
import KondoKit.XPTrackerView.xpWidgets
import KondoKit.plugin.StateManager.focusedView
import com.sun.org.apache.xpath.internal.operations.Bool
import plugin.Plugin
import plugin.api.*
import plugin.api.API.*
@ -52,11 +55,26 @@ class plugin : Plugin() {
companion object {
val WIDGET_SIZE = Dimension(220, 50)
val TOTAL_XP_WIDGET_SIZE = Dimension(220, 30)
val IMAGE_SIZE = Dimension(20, 20)
val WIDGET_COLOR = Color(30, 30, 30)
val VIEW_BACKGROUND_COLOR = Color(40, 40, 40)
val primaryColor = Color(165, 165, 165) // Color for "XP Gained:"
val secondaryColor = Color(255, 255, 255) // Color for "0"
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 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
@ -90,6 +108,7 @@ class plugin : Plugin() {
private var initialized = false;
private var lastClickTime = 0L
private var lastUIOffset = 0
private const val HIDDEN_VIEW = "HIDDEN";
private val drawActions = mutableListOf<() -> Unit>()
fun registerDrawAction(action: () -> Unit) {
@ -159,6 +178,14 @@ class plugin : Plugin() {
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)
@ -191,7 +218,7 @@ class plugin : Plugin() {
private fun searchHiscore(username: String): Runnable {
return Runnable {
setActiveView("HISCORE_SEARCH_VIEW")
setActiveView(HiscoresView.VIEW_NAME)
val customSearchField = hiScoreView?.let { HiscoresView.CustomSearchField(it) }
customSearchField?.searchPlayer(username) ?: run {
@ -237,7 +264,7 @@ class plugin : Plugin() {
xpTrackerView?.add(Box.createVerticalStrut(5))
xpTrackerView?.revalidate()
if(focusedView == "XP_TRACKER_VIEW")
if(focusedView == XPTrackerView.VIEW_NAME)
xpTrackerView?.repaint()
updateWidget(xpWidget, xp)
@ -290,6 +317,10 @@ class plugin : Plugin() {
if (frame != null) {
loadFont()
val themeIndex = (GetData("kondoTheme") as? String) ?: "RUNELITE"
theme = ThemeType.valueOf(themeIndex)
applyTheme(getTheme(theme))
appliedTheme = theme
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel")
@ -337,10 +368,10 @@ class plugin : Plugin() {
createLootTrackerView()
createReflectiveEditorView()
mainContentPanel.add(ScrollablePanel(xpTrackerView!!), "XP_TRACKER_VIEW")
mainContentPanel.add(ScrollablePanel(hiScoreView!!), "HISCORE_SEARCH_VIEW")
mainContentPanel.add(ScrollablePanel(lootTrackerView!!), "LOOT_TRACKER_VIEW")
mainContentPanel.add(ScrollablePanel(reflectiveEditorView!!), "REFLECTIVE_EDITOR_VIEW")
mainContentPanel.add(ScrollablePanel(xpTrackerView!!), XPTrackerView.VIEW_NAME)
mainContentPanel.add(ScrollablePanel(hiScoreView!!), HiscoresView.VIEW_NAME)
mainContentPanel.add(ScrollablePanel(lootTrackerView!!), LootTrackerView.VIEW_NAME)
mainContentPanel.add(ScrollablePanel(reflectiveEditorView!!), ReflectiveEditorView.VIEW_NAME)
val navPanel = Panel().apply {
layout = BoxLayout(this, BoxLayout.Y_AXIS)
@ -348,10 +379,10 @@ class plugin : Plugin() {
preferredSize = Dimension(NAVBAR_WIDTH, frame.height)
}
navPanel.add(createNavButton(LVL_ICON, "XP_TRACKER_VIEW"))
navPanel.add(createNavButton(MAG_SPRITE, "HISCORE_SEARCH_VIEW"))
navPanel.add(createNavButton(LOOT_ICON, "LOOT_TRACKER_VIEW"))
navPanel.add(createNavButton(WRENCH_ICON, "REFLECTIVE_EDITOR_VIEW"))
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))
var rightPanel = Panel(BorderLayout()).apply {
add(mainContentPanel, BorderLayout.CENTER)
@ -370,9 +401,9 @@ class plugin : Plugin() {
rightPanelWrapper?.let { frame.add(it, BorderLayout.EAST) }
if(!launchMinimized){
setActiveView("XP_TRACKER_VIEW")
setActiveView(XPTrackerView.VIEW_NAME)
} else {
setActiveView("HIDDEN")
setActiveView(HIDDEN_VIEW)
}
initialized = true
pluginsReloaded = true
@ -410,7 +441,7 @@ class plugin : Plugin() {
private fun setActiveView(viewName: String) {
// Handle the visibility of the main content panel
if (viewName == "HIDDEN") {
if (viewName == HIDDEN_VIEW) {
mainContentPanel.isVisible = false
} else {
if (!mainContentPanel.isVisible) {
@ -434,7 +465,7 @@ class plugin : Plugin() {
}
private fun createNavButton(spriteId: Int, viewName: String): JPanel {
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId))
val bufferedImageSprite = getBufferedImageFromSprite(GetSprite(spriteId), NAV_TINT, NAV_GREYSCALE)
val buttonSize = Dimension(NAVBAR_WIDTH, 32)
val imageSize = Dimension((bufferedImageSprite.width / 1.2f).toInt(), (bufferedImageSprite.height / 1.2f).toInt())
val cooldownDuration = 100L
@ -536,4 +567,103 @@ class plugin : Plugin() {
object StateManager {
var focusedView: String = ""
}
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
}
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
)
ThemeType.DARKER_RUNELITE -> Theme(
widgetColor = Color(15, 15, 15), // Darker widget backgrounds
titleBarColor = Color(10, 10, 10), // Even darker title bar
viewBackgroundColor = Color(20, 20, 20), // Very dark background for the view
primaryColor = Color(140, 140, 140), // Darker shade for primary text
secondaryColor = Color(210, 210, 210), // Slightly muted white for secondary text
popupBackground = Color(25, 25, 25), // Very dark popup backgrounds
popupForeground = Color(200, 200, 200), // Slightly muted light gray for popup text
tooltipBackground = Color(30, 30, 30), // Darker tooltips background
scrollBarColor = Color(40, 40, 40), // Darker scroll bar
progressBarFill = Color(45, 40, 35), // Darker progress bar fill
navTint = null, //Color(20, 20, 20, 60), // No specific tint applied
navGreyScale = false // Navigation retains color (if applicable)
)
ThemeType.ORION -> Theme(
widgetColor = Color(50, 50, 50), // Darker gray for widget backgrounds
titleBarColor = Color(35, 35, 35), // Very dark gray for the title bar
viewBackgroundColor = Color(60, 60, 60), // Medium-dark gray for view background
primaryColor = Color(180, 180, 180), // Lighter gray for primary text or highlights
secondaryColor = Color(210, 210, 210), // Light gray for secondary text
popupBackground = Color(45, 45, 45), // Dark gray for popup backgrounds
popupForeground = Color(230, 230, 230), // Light gray for popup text
tooltipBackground = Color(55, 55, 55), // Slightly darker gray for tooltips
scrollBarColor = Color(75, 75, 75), // Dark gray for scroll bars
progressBarFill = Color(100, 100, 100), // Medium gray for progress bar fill
navTint = null,
navGreyScale = true
)
ThemeType.SARADOMIN -> Theme(
widgetColor = Color(75, 60, 45),
titleBarColor = Color(60, 48, 36),
viewBackgroundColor = Color(95, 76, 58),
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
)
}
}
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
)
}