aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/SackDisplayConfig.java6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/SackAPI.kt46
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt221
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt77
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt7
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt24
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt1
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)
}
-
}