diff options
author | hannibal2 <24389977+hannibal002@users.noreply.github.com> | 2024-04-15 18:53:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-15 18:53:53 +0200 |
commit | c78914d5e8bfca12eef8f6c9ff976aed26269345 (patch) | |
tree | 7aa494224edff67087b01570e866a817418e0caf | |
parent | 53f2cfd71a6e97fe59e2ced18045e8eed0434f4e (diff) | |
download | skyhanni-c78914d5e8bfca12eef8f6c9ff976aed26269345.tar.gz skyhanni-c78914d5e8bfca12eef8f6c9ff976aed26269345.tar.bz2 skyhanni-c78914d5e8bfca12eef8f6c9ff976aed26269345.zip |
Improvement: Sack Display Update (#1381)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Co-authored-by: Cal <cwolfson58@gmail.com>
8 files changed, 287 insertions, 98 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/SackDisplayConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/SackDisplayConfig.java index 64f2226a8..5fc8268dd 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/SackDisplayConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/SackDisplayConfig.java @@ -3,6 +3,7 @@ package at.hannibal2.skyhanni.config.features.inventory; import at.hannibal2.skyhanni.config.FeatureToggle; import at.hannibal2.skyhanni.config.HasLegacyId; import at.hannibal2.skyhanni.config.core.config.Position; +import at.hannibal2.skyhanni.utils.RenderUtils; import com.google.gson.annotations.Expose; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown; @@ -35,6 +36,11 @@ public class SackDisplayConfig { @ConfigEditorDropdown public NumberFormatEntry numberFormat = NumberFormatEntry.FORMATTED; + @Expose + @ConfigOption(name = "Alignment", desc = "Channge the alignment for numbers and money.") + @ConfigEditorDropdown + public RenderUtils.HorizontalAlignment alignment = RenderUtils.HorizontalAlignment.LEFT; + public enum NumberFormatEntry implements HasLegacyId { DEFAULT("Default", 0), FORMATTED("Formatted", 1), diff --git a/src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt index 328031478..cde612d82 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt @@ -24,6 +24,7 @@ import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull import at.hannibal2.skyhanni.utils.NEUItems.getPrice import at.hannibal2.skyhanni.utils.NumberUtil.formatInt import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimal +import at.hannibal2.skyhanni.utils.StringUtils.matchAll import at.hannibal2.skyhanni.utils.StringUtils.matchFirst import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher import at.hannibal2.skyhanni.utils.StringUtils.matches @@ -60,6 +61,15 @@ object SackAPI { var isTrophySack = false private var sackRarity: TrophyRarity? = null + /** + * TODO merge all 3 lists into one: + * + * move item name (currently key) into AbstractSackItem + * work with instance check + * add custom function for render behaviour. + * have only one render display function + */ + // val sackItem = mutableMapOf<String, SackOtherItem>() val runeItem = mutableMapOf<String, SackRune>() val gemstoneItem = mutableMapOf<String, SackGemstone>() @@ -119,13 +129,13 @@ object SackAPI { fun getSacksData(savingSacks: Boolean) { if (savingSacks) sackData = ProfileStorageData.sackProfiles?.sackContents ?: return - for ((_, stack) in stackList) { + for ((slot, stack) in stackList) { val name = stack.name val lore = stack.getLore() if (isGemstoneSack) { - lore.matchFirst(gemstonePattern) { - val gem = SackGemstone() + val gem = SackGemstone() + lore.matchAll(gemstonePattern) { val rarity = group("gemrarity") val stored = group("stored").formatInt() gem.internalName = gemstoneMap[name.removeColor()] ?: NEUInternalName.NONE @@ -134,26 +144,34 @@ object SackAPI { name.uppercase().split(" ")[0].removeColor() }_GEM".asInternalName() + gem.slot = slot + when (rarity) { "Rough" -> { gem.rough = stored + gem.stored += stored gem.roughPrice = internalName.sackPrice(stored) + gem.price += gem.roughPrice if (savingSacks) setSackItem(internalName, stored) } "Flawed" -> { gem.flawed = stored + gem.stored += stored gem.flawedPrice = internalName.sackPrice(stored) + gem.price += gem.flawedPrice if (savingSacks) setSackItem(internalName, stored) } "Fine" -> { gem.fine = stored + gem.stored += stored gem.finePrice = internalName.sackPrice(stored) + gem.price += gem.finePrice if (savingSacks) setSackItem(internalName, stored) + gemstoneItem[name] = gem } } - gemstoneItem[name] = gem } } } else if (isRuneSack) { @@ -163,11 +181,13 @@ object SackAPI { val level = group("level").romanToDecimal() val stored = group("stored").formatInt() rune.stack = stack + rune.stored += stored when (level) { 1 -> rune.lvl1 = stored 2 -> rune.lvl2 = stored 3 -> { + rune.slot = slot rune.lvl3 = stored runeItem[name] = rune } @@ -175,6 +195,7 @@ object SackAPI { } } } else { + // normal sack lore.matchFirst(numPattern) { val item = SackOtherItem() val stored = group("stored").formatInt() @@ -193,6 +214,7 @@ object SackAPI { } else { internalName.sackPrice(stored).coerceAtLeast(0) } + item.slot = slot sackItem[name] = item } } @@ -330,22 +352,26 @@ object SackAPI { var roughPrice: Long = 0, var flawedPrice: Long = 0, var finePrice: Long = 0, - ) + ): AbstractSackItem() data class SackRune( var stack: ItemStack? = null, var lvl1: Int = 0, var lvl2: Int = 0, var lvl3: Int = 0, - ) + ): AbstractSackItem() data class SackOtherItem( var internalName: NEUInternalName = NEUInternalName.NONE, var colorCode: String = "", - var stored: Int = 0, var total: Int = 0, - var price: Long = 0, var magmaFish: Int = 0, + ): AbstractSackItem() + + abstract class AbstractSackItem( + var stored: Int = 0, + var price: Long = 0, + var slot: Int = -1, ) fun NEUInternalName.getAmountInSacksOrNull(): Int? = @@ -373,6 +399,10 @@ private val gemstoneMap = mapOf( "Jasper Gemstones" to "ROUGH_JASPER_GEM".asInternalName(), "Ruby Gemstones" to "ROUGH_RUBY_GEM".asInternalName(), "Opal Gemstones" to "ROUGH_OPAL_GEM".asInternalName(), + "Onyx Gemstones" to "ROUGH_ONYX_GEM".asInternalName(), + "Aquamarine Gemstones" to "ROUGH_AQUAMARINE_GEM".asInternalName(), + "Citrine Gemstones" to "ROUGH_CITRINE_GEM".asInternalName(), + "Peridot Gemstones" to "ROUGH_PERIDOT_GEM".asInternalName(), ) // ideally should be correct but using alright should also be fine unless they sold their whole sacks diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt index 9d9c98115..7868d0440 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt @@ -10,35 +10,35 @@ import at.hannibal2.skyhanni.data.SackAPI import at.hannibal2.skyhanni.events.GuiContainerEvent import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarApi -import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList +import at.hannibal2.skyhanni.utils.CollectionUtils.addButton +import at.hannibal2.skyhanni.utils.CollectionUtils.addItemStack +import at.hannibal2.skyhanni.utils.CollectionUtils.addSelector +import at.hannibal2.skyhanni.utils.CollectionUtils.addString import at.hannibal2.skyhanni.utils.ConfigUtils import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.addButton -import at.hannibal2.skyhanni.utils.LorenzUtils.addSelector import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems -import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.NumberUtil import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.RenderUtils.highlight -import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.renderables.Renderable import net.minecraftforge.fml.common.eventhandler.SubscribeEvent object SackDisplay { - private var display = emptyList<List<Any>>() + private var display = emptyList<Renderable>() private val config get() = SkyHanniMod.feature.inventory.sackDisplay @SubscribeEvent fun onBackgroundDraw(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { if (SackAPI.inSackInventory) { if (!isEnabled()) return - config.position.renderStringsAndItems( - display, extraSpace = config.extraSpace, itemScale = 0.7, posLabel = "Sacks Items" + config.position.renderRenderables( + display, extraSpace = config.extraSpace, posLabel = "Sacks Items" ) } } @@ -59,8 +59,8 @@ object SackDisplay { display = drawDisplay(savingSacks) } - private fun drawDisplay(savingSacks: Boolean): List<List<Any>> { - val list = mutableListOf<List<Any>>() + private fun drawDisplay(savingSacks: Boolean): List<Renderable> { + val list = mutableListOf<Renderable>() var totalPrice = 0L totalPrice += drawNormalList(savingSacks, list) totalPrice += drawGemstoneDisplay(list) @@ -69,7 +69,7 @@ object SackDisplay { return list } - private fun drawNormalList(savingSacks: Boolean, newDisplay: MutableList<List<Any>>): Long { + private fun drawNormalList(savingSacks: Boolean, list: MutableList<Renderable>): Long { SackAPI.getSacksData(savingSacks) val sackItems = SackAPI.sackItem.toList() if (sackItems.isEmpty()) return 0L @@ -77,81 +77,114 @@ object SackDisplay { var totalPrice = 0L var rendered = 0 var totalMagmaFish = 0L - val sortedPairs: MutableMap<String, SackAPI.SackOtherItem> = when (config.sortingType) { - SortingTypeEntry.DESC_STORED -> sackItems.sortedByDescending { it.second.stored } - SortingTypeEntry.ASC_STORED -> sackItems.sortedBy { it.second.stored } - SortingTypeEntry.DESC_PRICE -> sackItems.sortedByDescending { it.second.price } - SortingTypeEntry.ASC_PRICE -> sackItems.sortedBy { it.second.price } - else -> sackItems.sortedByDescending { it.second.stored } - }.toMap().toMutableMap() - - sortedPairs.toList().forEach { (k, v) -> - if (v.stored == 0 && !config.showEmpty) { - sortedPairs.remove(k) - } - } + val sortedPairs = sort(sackItems) val amountShowing = if (config.itemToShow > sortedPairs.size) sortedPairs.size else config.itemToShow - newDisplay.addAsSingletonList("§7Items in Sacks: §o(Rendering $amountShowing of ${sortedPairs.size} items)") + list.addString("§7Items in Sacks: §o(Rendering $amountShowing of ${sortedPairs.size} items)") + val table = mutableListOf<List<Renderable>>() for ((itemName, item) in sortedPairs) { + val (internalName, colorCode, total, magmaFish) = item + val stored = item.stored + val price = item.price + val slot = item.slot - val (internalName, colorCode, stored, total, price, magmaFish) = item totalPrice += price if (rendered >= config.itemToShow) continue if (stored == 0 && !config.showEmpty) continue - val itemStack = internalName.getItemStack() - newDisplay.add(buildList { - add(" §7- ") - add(itemStack) - if (!SackAPI.isTrophySack) add(Renderable.optionalLink("${itemName.replace("§k", "")}: ", { - BazaarApi.searchForBazaarItem(itemName) - }) { !NEUItems.neuHasFocus() }) - else add("${itemName.replace("§k", "")}: ") - - add( - when (config.numberFormat) { - NumberFormatEntry.DEFAULT -> "$colorCode${stored}§7/§b${NumberUtil.format(total)}" - NumberFormatEntry.FORMATTED -> { - "$colorCode${NumberUtil.format(stored)}§7/§b${NumberUtil.format(total)}" + table.add(buildList { + addString(" §7- ") + addItemStack(internalName) + // TODO move replace into itemName + val nameText = Renderable.optionalLink( + itemName.replace("§k", ""), + onClick = { + if (!SackAPI.isTrophySack) { + BazaarApi.searchForBazaarItem(itemName) } + }, + highlightsOnHoverSlots = listOf(slot) + ) { !NEUItems.neuHasFocus() } + add(nameText) - NumberFormatEntry.UNFORMATTED -> "$colorCode${stored}§7/§b${ - total.addSeparators() - }" - else -> "$colorCode${stored}§7/§b${total}" + when (config.numberFormat) { + NumberFormatEntry.DEFAULT -> { + addAlignedNumber("$colorCode${stored.addSeparators()}") + addString("§7/") + addAlignedNumber("§b${NumberUtil.format(total)}") } - ) - if (colorCode == "§a") add(" §c§l(Full!)") + NumberFormatEntry.FORMATTED -> { + addAlignedNumber("$colorCode${NumberUtil.format(stored)}") + addString("§7/") + addAlignedNumber("§b${NumberUtil.format(total)}") + } + + NumberFormatEntry.UNFORMATTED -> { + addAlignedNumber("$colorCode${stored.addSeparators()}") + addString("§7/") + addAlignedNumber("§b${total.addSeparators()}") + } + + else -> { + addAlignedNumber("$colorCode${stored.addSeparators()}") + addString("§7/") + addAlignedNumber("§b${total.addSeparators()}") + } + } + + // TODO change color of amount if full +// if (colorCode == "§a") addString("§c§l(Full!)") + if (SackAPI.isTrophySack && magmaFish > 0) { totalMagmaFish += magmaFish add( Renderable.hoverTips( - " §7(§d${magmaFish} ", + Renderable.string( + "§d${magmaFish}", + horizontalAlign = config.alignment + ), listOf( "§6Magmafish: §b${magmaFish.addSeparators()}", "§6Magmafish value: §b${price / magmaFish}", "§6Magmafish per: §b${magmaFish / stored}" - ) + ), ) ) - add("MAGMA_FISH".asInternalName().getItemStack()) - add("§7)") + //TOOD add cache + addItemStack("MAGMA_FISH".asInternalName()) } - if (config.showPrice && price != 0L) add(" §7(§6${format(price)}§7)") + if (config.showPrice && price != 0L) addAlignedNumber("§6${format(price)}") }) rendered++ } + list.add(Renderable.table(table)) - if (SackAPI.isTrophySack) newDisplay.addAsSingletonList("§cTotal Magmafish: §6${totalMagmaFish.addSeparators()}") + if (SackAPI.isTrophySack) list.addString("§cTotal Magmafish: §6${totalMagmaFish.addSeparators()}") return totalPrice } - private fun drawOptions(newDisplay: MutableList<List<Any>>, totalPrice: Long) { + private fun <T : SackAPI.AbstractSackItem> sort(sackItems: List<Pair<String, T>>): MutableMap<String, T> { + val sortedPairs: MutableMap<String, T> = when (config.sortingType) { + SortingTypeEntry.DESC_STORED -> sackItems.sortedByDescending { it.second.stored } + SortingTypeEntry.ASC_STORED -> sackItems.sortedBy { it.second.stored } + SortingTypeEntry.DESC_PRICE -> sackItems.sortedByDescending { it.second.price } + SortingTypeEntry.ASC_PRICE -> sackItems.sortedBy { it.second.price } + else -> sackItems.sortedByDescending { it.second.stored } + }.toMap().toMutableMap() + + sortedPairs.toList().forEach { (k, v) -> + if (v.stored == 0 && !config.showEmpty) { + sortedPairs.remove(k) + } + } + return sortedPairs + } + + private fun drawOptions(list: MutableList<Renderable>, totalPrice: Long) { val name = SortType.entries[config.sortingType.ordinal].longName // todo avoid ordinal - newDisplay.addAsSingletonList("§7Sorted By: §c$name") + list.addString("§7Sorted By: §c$name") - newDisplay.addSelector<SortType>(" ", + list.addSelector<SortType>(" ", getName = { type -> type.shortName }, isCurrent = { it.ordinal == config.sortingType.ordinal }, // todo avoid ordinal onChange = { @@ -159,7 +192,7 @@ object SackDisplay { update(false) }) - newDisplay.addButton( + list.addButton( prefix = "§7Number format: ", getName = NumberFormat.entries[config.numberFormat.ordinal].displayName, // todo avoid ordinal onChange = { @@ -171,14 +204,14 @@ object SackDisplay { ) if (config.showPrice) { - newDisplay.addSelector<PriceFrom>(" ", + list.addSelector<PriceFrom>(" ", getName = { type -> type.displayName }, isCurrent = { it.ordinal == config.priceFrom.ordinal }, // todo avoid ordinal onChange = { config.priceFrom = SackDisplayConfig.PriceFrom.entries[it.ordinal] // todo avoid ordinal update(false) }) - newDisplay.addButton( + list.addButton( prefix = "§7Price Format: ", getName = PriceFormat.entries[config.priceFormat.ordinal].displayName, // todo avoid ordinal onChange = { @@ -188,48 +221,72 @@ object SackDisplay { update(false) } ) - newDisplay.addAsSingletonList("§eTotal price: §6${format(totalPrice)}") + list.addString("§eTotal price: §6${format(totalPrice)}") } } - private fun drawRunesDisplay(newDisplay: MutableList<List<Any>>) { + private fun drawRunesDisplay(list: MutableList<Renderable>) { if (SackAPI.runeItem.isEmpty()) return - newDisplay.addAsSingletonList("§7Runes:") - for ((name, rune) in SackAPI.runeItem) { - val list = mutableListOf<Any>() + list.addString("§7Runes:") + val table = mutableListOf<List<Renderable>>() + for ((name, rune) in sort(SackAPI.runeItem.toList())) { val (stack, lv1, lv2, lv3) = rune - list.add(" §7- ") - stack?.let { list.add(it) } - list.add(name) - list.add(" §f(§e$lv1§7-§e$lv2§7-§e$lv3§f)") - newDisplay.add(list) + table.add(buildList { + addString(" §7- ") + stack?.let { addItemStack(it) } + add( + Renderable.optionalLink( + name, + onClick = {}, + highlightsOnHoverSlots = listOf(rune.slot) + ) + ) + addAlignedNumber("§e$lv1") + addAlignedNumber("§e$lv2") + addAlignedNumber("§e$lv3") + }) } + list.add(Renderable.table(table)) } - private fun drawGemstoneDisplay(newDisplay: MutableList<List<Any>>): Long { + private fun drawGemstoneDisplay(list: MutableList<Renderable>): Long { if (SackAPI.gemstoneItem.isEmpty()) return 0L - newDisplay.addAsSingletonList("§7Gemstones:") + list.addString("§7Gemstones:") var totalPrice = 0L - for ((name, gem) in SackAPI.gemstoneItem) { + val table = mutableListOf<List<Renderable>>() + for ((name, gem) in sort(SackAPI.gemstoneItem.toList())) { val (internalName, rough, flawed, fine, roughprice, flawedprice, fineprice) = gem - newDisplay.add(buildList { - add(" §7- ") - add(internalName.getItemStack()) - add(Renderable.optionalLink("$name: ", { - BazaarApi.searchForBazaarItem(name.dropLast(1)) - }) { !NEUItems.neuHasFocus() }) - add(" (${rough.addSeparators()}-§a${flawed.addSeparators()}-§9${fine.addSeparators()})") + table.add(buildList { + addString(" §7- ") + addItemStack(internalName) + add(Renderable.optionalLink( + name, + onClick = { + BazaarApi.searchForBazaarItem(name.dropLast(1)) + }, + highlightsOnHoverSlots = listOf(gem.slot) + ) { !NEUItems.neuHasFocus() }) + addAlignedNumber(rough.addSeparators()) + addAlignedNumber("§a${flawed.addSeparators()}") + addAlignedNumber("§9${fine.addSeparators()}") val price = roughprice + flawedprice + fineprice totalPrice += price - if (config.showPrice && price != 0L) add(" §7(§6${format(price)}§7)") + if (config.showPrice && price != 0L) addAlignedNumber("§7(§6${format(price)}§7)") }) } - newDisplay.addAsSingletonList("§eTotal price: §6${format(totalPrice)}") + list.add(Renderable.table(table)) return totalPrice } - private fun format(price: Long) = - if (config.priceFormat == PriceFormatEntry.FORMATTED) NumberUtil.format(price) else price.addSeparators() + private fun MutableList<Renderable>.addAlignedNumber(string: String) { + addString(string, horizontalAlign = config.alignment) + } + + private fun format(price: Long) = if (config.priceFormat == PriceFormatEntry.FORMATTED) { + NumberUtil.format(price) + } else { + price.addSeparators() + } private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled diff --git a/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt index a9f5ec6c1..3082112ce 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt @@ -1,5 +1,8 @@ package at.hannibal2.skyhanni.utils +import at.hannibal2.skyhanni.utils.NEUItems.getItemStack +import at.hannibal2.skyhanni.utils.renderables.Renderable +import net.minecraft.item.ItemStack import java.util.Collections import java.util.WeakHashMap import java.util.concurrent.ConcurrentLinkedQueue @@ -171,4 +174,78 @@ object CollectionUtils { @Suppress("UNCHECKED_CAST") fun <T> List<T?>.takeIfAllNotNull(): List<T>? = takeIf { null !in this } as? List<T> + + // TODO add cache + fun MutableList<Renderable>.addString( + text: String, + horizontalAlign: RenderUtils.HorizontalAlignment = RenderUtils.HorizontalAlignment.LEFT, + verticalAlign: RenderUtils.VerticalAlignment = RenderUtils.VerticalAlignment.CENTER, + ) { + add(Renderable.string(text, horizontalAlign = horizontalAlign, verticalAlign = verticalAlign)) + } + + // TODO add internal name support, and caching + fun MutableList<Renderable>.addItemStack(itemStack: ItemStack) { + add(Renderable.itemStack(itemStack)) + } + + fun MutableList<Renderable>.addItemStack(internalName: NEUInternalName) { + addItemStack(internalName.getItemStack()) + } + + inline fun <reified T : Enum<T>> MutableList<Renderable>.addSelector( + prefix: String, + getName: (T) -> String, + isCurrent: (T) -> Boolean, + crossinline onChange: (T) -> Unit, + ) { + add(Renderable.horizontalContainer(buildSelector<T>(prefix, getName, isCurrent, onChange))) + } + + inline fun <reified T : Enum<T>> buildSelector( + prefix: String, + getName: (T) -> String, + isCurrent: (T) -> Boolean, + crossinline onChange: (T) -> Unit, + ) = buildList { + addString(prefix) + for (entry in enumValues<T>()) { + val display = getName(entry) + if (isCurrent(entry)) { + addString("§a[$display]") + } else { + addString("§e[") + add(Renderable.link("§e$display") { + onChange(entry) + }) + addString("§e]") + } + addString(" ") + } + } + + inline fun MutableList<Renderable>.addButton( + prefix: String, + getName: String, + crossinline onChange: () -> Unit, + tips: List<String> = emptyList(), + ) { + val onClick = { + if ((System.currentTimeMillis() - ChatUtils.lastButtonClicked) > 150) { // funny thing happen if I don't do that + onChange() + SoundUtils.playClickSound() + ChatUtils.lastButtonClicked = System.currentTimeMillis() + } + } + add(Renderable.horizontalContainer(buildList { + addString(prefix) + addString("§a[") + if (tips.isEmpty()) { + add(Renderable.link("§e$getName", false, onClick)) + } else { + add(Renderable.clickAndHover("§e$getName", tips, false, onClick)) + } + addString("§a]") + })) + } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt index 7e349aa9f..815bc13aa 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt @@ -217,6 +217,7 @@ object LorenzUtils { } } + @Deprecated("do not use List<Any>, use List<Renderable> instead", ReplaceWith("")) inline fun <reified T : Enum<T>> MutableList<List<Any>>.addSelector( prefix: String, getName: (T) -> String, @@ -226,6 +227,7 @@ object LorenzUtils { add(buildSelector<T>(prefix, getName, isCurrent, onChange)) } + @Deprecated("do not use List<Any>, use List<Renderable> instead", ReplaceWith("")) inline fun <reified T : Enum<T>> buildSelector( prefix: String, getName: (T) -> String, @@ -248,6 +250,7 @@ object LorenzUtils { } } + @Deprecated("do not use List<Any>, use List<Renderable> instead", ReplaceWith("")) inline fun MutableList<List<Any>>.addButton( prefix: String, getName: String, diff --git a/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt index 5079aa7fe..1057b5c0a 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt @@ -106,6 +106,13 @@ object StringUtils { return null } + inline fun <T> List<String>.matchAll(pattern: Pattern, consumer: Matcher.() -> T): T? { + for (line in this) { + pattern.matcher(line).let { if (it.find()) consumer(it) } + } + return null + } + private fun String.internalCleanPlayerName(): String { val split = trim().split(" ") return if (split.size > 1) { diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt index f3961660d..128cd4482 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt @@ -81,17 +81,23 @@ interface Renderable { text: String, onClick: () -> Unit, bypassChecks: Boolean = false, + highlightsOnHoverSlots: List<Int> = emptyList(), condition: () -> Boolean = { true }, - ): Renderable = link(string(text), onClick, bypassChecks, condition) + ): Renderable = link(string(text), onClick, bypassChecks, highlightsOnHoverSlots = highlightsOnHoverSlots, condition) fun link( renderable: Renderable, onClick: () -> Unit, bypassChecks: Boolean = false, + highlightsOnHoverSlots: List<Int> = emptyList(), condition: () -> Boolean = { true }, ): Renderable { return clickable( - hoverable(underlined(renderable), renderable, bypassChecks, condition = condition), + hoverable( + underlined(renderable), renderable, bypassChecks, + condition = condition, + highlightsOnHoverSlots = highlightsOnHoverSlots + ), onClick, 0, bypassChecks, @@ -245,6 +251,7 @@ interface Renderable { unhovered: Renderable, bypassChecks: Boolean = false, condition: () -> Boolean = { true }, + highlightsOnHoverSlots: List<Int> = emptyList(), ) = object : Renderable { override val width: Int get() = max(hovered.width, unhovered.width) @@ -255,11 +262,14 @@ interface Renderable { var isHovered = false override fun render(posX: Int, posY: Int) { - isHovered = if (isHovered(posX, posY) && condition() && shouldAllowLink(true, bypassChecks)) { - hovered.render(posX, posY) - true - } else { - unhovered.render(posX, posY) + val pair = Pair(posX, posY) + isHovered = if (isHovered(posX, posY) && condition() && shouldAllowLink(true, bypassChecks)) { + hovered.render(posX, posY) + HighlightOnHoverSlot.currentSlots[pair] = highlightsOnHoverSlots + true + } else { + unhovered.render(posX, posY) + HighlightOnHoverSlot.currentSlots.remove(pair) false } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt index 70969d8fd..4af54b09f 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt @@ -64,5 +64,4 @@ internal object RenderableUtils { this.render(posX, posY + yOffset) GlStateManager.translate(0f, -yOffset.toFloat(), 0f) } - } |