diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt
index 512a994d2c71..0583d3282b1b 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt
@@ -11,13 +11,12 @@ import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.InventoryCloseEvent
import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.CollectionUtils.addButton
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.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.NumberUtil.formatLong
import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary
@@ -27,7 +26,7 @@ import at.hannibal2.skyhanni.utils.NumberUtil.toRoman
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.RenderUtils.highlight
-import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.renderables.Renderable
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
@@ -78,7 +77,7 @@ object BestiaryData {
"^(?:\\(\\d+\\/\\d+\\) )?(?
Bestiary|.+) ➜ .+\$"
)
- private var display = emptyList>()
+ private var display: Renderable? = null
private val mobList = mutableListOf()
private val stackList = mutableMapOf()
private val catList = mutableListOf()
@@ -96,9 +95,7 @@ object BestiaryData {
fun onBackgroundDraw(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) {
if (!isEnabled()) return
if (inInventory) {
- config.position.renderStringsAndItems(
- display, extraSpace = -1, itemScale = 0.7, posLabel = "Bestiary Data"
- )
+ config.position.renderRenderable(display, posLabel = "Bestiary Data")
}
}
@@ -243,27 +240,23 @@ object BestiaryData {
}
}
- private fun drawDisplay(): List> {
- val newDisplay = mutableListOf>()
+ private fun drawDisplay(): Renderable {
+ val display = mutableListOf()
if (!overallProgressEnabled) {
- newDisplay.addAsSingletonList("§7Bestiary Data")
- newDisplay.addAsSingletonList(" §cPlease enable Overall Progress")
- newDisplay.addAsSingletonList(" §cUsing the Eye of Ender highlighted in red.")
- return newDisplay
+ display.add(Renderable.string("§7Bestiary Data"))
+ display.add(Renderable.string(" §cPlease enable Overall Progress"))
+ display.add(Renderable.string(" §cUsing the Eye of Ender highlighted in red."))
+ } else {
+ init()
+ addCategories(display)
+ if (mobList.isNotEmpty()) {
+ addList(display)
+ addButtons(display)
+ }
}
- init()
-
- addCategories(newDisplay)
-
- if (mobList.isEmpty()) return newDisplay
-
- addList(newDisplay)
-
- addButtons(newDisplay)
-
- return newDisplay
+ return Renderable.verticalContainer(display)
}
private fun sortMobList(): MutableList {
@@ -281,26 +274,21 @@ object BestiaryData {
return sortedMobList
}
- private fun addList(newDisplay: MutableList>) {
+ private fun addList(newDisplay: MutableList) {
val sortedMobList = sortMobList()
- newDisplay.addAsSingletonList("§7Bestiary Data")
+ newDisplay.add(Renderable.string("§7Bestiary Data"))
for (mob in sortedMobList) {
val isUnlocked = mob.actualRealTotalKill != 0.toLong()
val isMaxed = mob.percentToMax() >= 1
if (!isUnlocked) {
- newDisplay.add(
- buildList {
- add(" §7- ")
- add("${mob.name}: §cNot unlocked!")
- }
- )
+ newDisplay.add(Renderable.string(" §7- ${mob.name}: §cNot unlocked!"))
continue
}
if (isMaxed && config.hideMaxed) continue
val text = getMobLine(mob, isMaxed)
val tips = getMobHover(mob)
- newDisplay.addAsSingletonList(Renderable.hoverTips(text, tips) { true })
+ newDisplay.add(Renderable.hoverTips(text, tips) { true })
}
}
@@ -362,7 +350,7 @@ object BestiaryData {
return text
}
- private fun addButtons(newDisplay: MutableList>) {
+ private fun addButtons(newDisplay: MutableList) {
newDisplay.addButton(
prefix = "§7Number Format: ",
getName = FormatType.entries[config.numberFormat.ordinal].type, // todo: avoid ordinal
@@ -402,27 +390,27 @@ object BestiaryData {
)
}
- private fun addCategories(newDisplay: MutableList>) {
+ private fun addCategories(newDisplay: MutableList) {
if (catList.isNotEmpty()) {
- newDisplay.addAsSingletonList("§7Category")
- for (cat in catList) {
- newDisplay.add(
- buildList {
- add(" §7- ${cat.name}§7: ")
- val element = when {
- cat.familiesCompleted == cat.totalFamilies -> "§c§lCompleted!"
- cat.familiesFound == cat.totalFamilies -> "§b${cat.familiesCompleted}§7/§b${cat.totalFamilies} §7completed"
- cat.familiesFound < cat.totalFamilies ->
- "§b${cat.familiesFound}§7/§b${cat.totalFamilies} §7found, " +
- "§b${cat.familiesCompleted}§7/§b${cat.totalFamilies} §7completed"
-
- else -> continue
- }
- add(element)
- }
- )
+ newDisplay.add(Renderable.string("§7Category"))
+ newDisplay += catList.mapNotNull { buildCategoryLine(it) }
+ }
+ }
+
+ private fun buildCategoryLine(cat: Category): Renderable? {
+ val list = buildList {
+ add(Renderable.string(" §7- ${cat.name}§7: "))
+ val element = when {
+ cat.familiesCompleted == cat.totalFamilies -> "§c§lCompleted!"
+ cat.familiesFound == cat.totalFamilies -> "§b${cat.familiesCompleted}§7/§b${cat.totalFamilies} §7completed"
+ cat.familiesFound < cat.totalFamilies ->
+ "§b${cat.familiesFound}§7/§b${cat.totalFamilies} §7found, " +
+ "§b${cat.familiesCompleted}§7/§b${cat.totalFamilies} §7completed"
+ else -> return null
}
+ add(Renderable.string(element))
}
+ return Renderable.horizontalContainer(list)
}
private fun isOverallProgressEnabled(inventoryItems: Map): Boolean {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fame/CityProjectFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/fame/CityProjectFeatures.kt
index 01459cb2569d..40ba8f6cc8e2 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fame/CityProjectFeatures.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fame/CityProjectFeatures.kt
@@ -15,7 +15,6 @@ import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarApi
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.InventoryUtils.getUpperItems
import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice
@@ -34,7 +33,7 @@ import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat
import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher
import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.RenderUtils.highlight
-import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.TimeUtils
import at.hannibal2.skyhanni.utils.renderables.Renderable
@@ -52,7 +51,7 @@ object CityProjectFeatures {
private val config get() = SkyHanniMod.feature.event.cityProject
- private var display = emptyList>()
+ private var display: Renderable? = null
private var inInventory = false
private var lastReminderSend = SimpleTimeMark.farPast()
@@ -152,22 +151,23 @@ object CityProjectFeatures {
return true
}
- private fun buildList(materials: MutableMap) = buildList> {
- addAsSingletonList("§7City Project Materials")
+ private fun buildList(materials: MutableMap): Renderable? {
+ val lines = mutableListOf()
+ lines.add(Renderable.string("§7City Project Materials"))
if (materials.isEmpty()) {
- addAsSingletonList("§cNo Materials to contribute.")
- return@buildList
+ lines.add(Renderable.string("§cNo Materials to contribute."))
+ return Renderable.verticalContainer(lines)
}
for ((internalName, amount) in materials) {
val stack = internalName.getItemStack()
val name = internalName.itemName
- val list = mutableListOf()
- list.add(" §7- ")
- list.add(stack)
+ val line = mutableListOf()
+ line.add(Renderable.string(" §7- "))
+ line.add(Renderable.itemStack(stack))
- list.add(
+ line.add(
Renderable.optionalLink(
"$name §ex${amount.addSeparators()}",
{
@@ -182,9 +182,11 @@ object CityProjectFeatures {
val price = internalName.getPrice() * amount
val format = price.shortFormat()
- list.add(" §7(§6$format§7)")
- add(list)
+ line.add(Renderable.string(" §7(§6$format§7)"))
+ lines.add(Renderable.horizontalContainer(line))
}
+
+ return Renderable.verticalContainer(lines)
}
private fun fetchMaterials(item: ItemStack, materials: MutableMap) {
@@ -215,7 +217,7 @@ object CityProjectFeatures {
if (!config.showMaterials) return
if (!inInventory) return
- config.pos.renderStringsAndItems(display, posLabel = "City Project Materials")
+ config.pos.renderRenderable(display, posLabel = "City Project Materials")
}
@SubscribeEvent
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt
index daf26417b52f..9906436d6aad 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt
@@ -11,19 +11,19 @@ import at.hannibal2.skyhanni.features.fame.ReminderUtils
import at.hannibal2.skyhanni.features.garden.GardenAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.toInternalName
import at.hannibal2.skyhanni.utils.NEUItems.getItemStack
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
-import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.fromNow
import at.hannibal2.skyhanni.utils.TimeUtils.format
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import net.minecraft.item.ItemStack
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
-import java.util.Collections
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
@@ -33,7 +33,7 @@ object ComposterDisplay {
private val config get() = GardenAPI.config.composters
private val storage get() = GardenAPI.storage
- private var display = emptyList>()
+ private var display: Renderable? = null
private var composterEmptyTime: Duration? = null
private val bucket by lazy { "BUCKET".toInternalName().getItemStack() }
@@ -49,9 +49,8 @@ object ComposterDisplay {
val pattern by lazy { rawPattern.toPattern() }
- fun addToList(map: Map): List {
- return map[this]?.let { listOf(displayItem, it) }.orEmpty()
- }
+ fun renderableLine(map: Map): Renderable? =
+ map[this]?.let { labeledItemStack(it, displayItem) }
}
@HandleEvent
@@ -63,37 +62,42 @@ object ComposterDisplay {
if (tabListData.isNotEmpty()) {
composterEmptyTime = ComposterAPI.estimateEmptyTimeFromTab()
- updateDisplay()
+ display = updateDisplay()
sendNotify()
}
}
- private fun updateDisplay() {
- if (!config.displayEnabled) return
- val newDisplay = mutableListOf>()
- newDisplay.addAsSingletonList("§bComposter")
+ private fun updateDisplay(): Renderable? {
+ if (!config.displayEnabled) return null
+
+ val lines = mutableListOf()
+ lines.add(Renderable.string("§bComposter"))
+
+ DataType.TIME_LEFT.renderableLine(tabListData)?.let { lines.add(it) }
- newDisplay.add(DataType.TIME_LEFT.addToList(tabListData))
+ val ingredientLine = Renderable.horizontalContainer(
+ listOfNotNull(
+ DataType.ORGANIC_MATTER.renderableLine(tabListData),
+ Renderable.string(" "),
+ DataType.FUEL.renderableLine(tabListData),
+ )
+ )
- val list = mutableListOf()
- list.addAll(DataType.ORGANIC_MATTER.addToList(tabListData))
- list.add(" ")
- list.addAll(DataType.FUEL.addToList(tabListData))
- newDisplay.add(list)
+ lines.add(ingredientLine)
- newDisplay.add(DataType.STORED_COMPOST.addToList(tabListData))
- newDisplay.add(addComposterEmptyTime(composterEmptyTime))
+ DataType.STORED_COMPOST.renderableLine(tabListData)?.let { lines.add(it) }
+ lines.add(addComposterEmptyTime(composterEmptyTime))
- display = newDisplay
+ return Renderable.verticalContainer(lines, spacing = 1)
}
- private fun addComposterEmptyTime(emptyTime: Duration?): List {
+ private fun addComposterEmptyTime(emptyTime: Duration?): Renderable {
return if (emptyTime != null) {
GardenAPI.storage?.composterEmptyTime = emptyTime.fromNow()
val format = emptyTime.format()
- listOf(bucket, "§b$format")
+ labeledItemStack("§b$format", bucket)
} else {
- listOf("§cOpen Composter Upgrades!")
+ Renderable.string("§cOpen Composter Upgrades!")
}
}
@@ -154,7 +158,7 @@ object ComposterDisplay {
if (!LorenzUtils.inSkyBlock && !OutsideSbFeature.COMPOSTER_TIME.isSelected()) return
if (GardenAPI.inGarden() && config.displayEnabled) {
- config.displayPos.renderStringsAndItems(display, posLabel = "Composter Display")
+ config.displayPos.renderRenderable(display, posLabel = "Composter Display")
}
checkWarningsAndOutsideGarden()
@@ -179,11 +183,17 @@ object ComposterDisplay {
val inSb = LorenzUtils.inSkyBlock && config.displayOutsideGarden
val outsideSb = !LorenzUtils.inSkyBlock && OutsideSbFeature.COMPOSTER_TIME.isSelected()
if (!GardenAPI.inGarden() && (inSb || outsideSb)) {
- val list = Collections.singletonList(listOf(bucket, "§b$format"))
- config.outsideGardenPos.renderStringsAndItems(list, posLabel = "Composter Outside Garden")
+ val outsideGardenDisplay = labeledItemStack("§b$format", bucket)
+ config.outsideGardenPos.renderRenderable(outsideGardenDisplay, posLabel = "Composter Outside Garden")
}
}
+ private fun labeledItemStack(label: String, itemStack: ItemStack): Renderable {
+ return Renderable.horizontalContainer(
+ listOf(Renderable.itemStack(itemStack), Renderable.string(label))
+ )
+ }
+
private fun warn(warningMessage: String) {
if (!config.warnAlmostClose) return
val storage = GardenAPI.storage ?: return
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt
index d4bcc6db5ca7..7a0be62cd18f 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt
@@ -23,7 +23,7 @@ import at.hannibal2.skyhanni.features.misc.items.EstimatedItemValue
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.ChatUtils
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSelector
import at.hannibal2.skyhanni.utils.CollectionUtils.sortedDesc
import at.hannibal2.skyhanni.utils.ConfigUtils
import at.hannibal2.skyhanni.utils.HypixelCommands
@@ -32,7 +32,6 @@ import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.KeyboardManager
import at.hannibal2.skyhanni.utils.LorenzUtils
-import at.hannibal2.skyhanni.utils.LorenzUtils.addSelector
import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.NONE
import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.toInternalName
@@ -43,14 +42,13 @@ import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary
import at.hannibal2.skyhanni.utils.NumberUtil.roundTo
import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
-import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.SoundUtils
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.TimeUtils.format
import at.hannibal2.skyhanni.utils.renderables.Renderable
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
-import java.util.Collections
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.time.Duration
@@ -64,8 +62,8 @@ object ComposterOverlay {
private var organicMatter: Map = emptyMap()
private val config get() = GardenAPI.config.composters
- private var organicMatterDisplay = emptyList>()
- private var fuelExtraDisplay = emptyList>()
+ private var organicMatterDisplay: Renderable? = null
+ private var fuelExtraDisplay: Renderable? = null
private var currentTimeType = TimeType.HOUR
private var inComposter = false
@@ -168,27 +166,32 @@ object ComposterOverlay {
private fun update() {
val composterUpgrades = ComposterAPI.composterUpgrades ?: return
if (composterUpgrades.isEmpty()) {
- val list = Collections.singletonList(listOf("§cOpen Composter Upgrades!"))
- organicMatterDisplay = list
- fuelExtraDisplay = list
+ val openUpgradesHint = Renderable.string("§cOpen Composter Upgrades!")
+ organicMatterDisplay = openUpgradesHint
+ fuelExtraDisplay = openUpgradesHint
return
}
if (organicMatterFactors.isEmpty()) {
- organicMatterDisplay = listOf(
- Collections.singletonList("§cSkyHanni composter error:"),
- Collections.singletonList("§cRepo data not loaded!"),
- Collections.singletonList("§7(organicMatterFactors is empty)"),
+ organicMatterDisplay = Renderable.verticalContainer(
+ listOf(
+ Renderable.string("§cSkyHanni composter error:"),
+ Renderable.string("§cRepo data not loaded!"),
+ Renderable.string("§7(organicMatterFactors is empty)")
+ )
)
return
}
if (fuelFactors.isEmpty()) {
- organicMatterDisplay = listOf(
- Collections.singletonList("§cSkyHanni composter error:"),
- Collections.singletonList("§cRepo data not loaded!"),
- Collections.singletonList("§7(fuelFactors is empty)"),
+ organicMatterDisplay = Renderable.verticalContainer(
+ listOf(
+ Renderable.string("§cSkyHanni composter error:"),
+ Renderable.string("§cRepo data not loaded!"),
+ Renderable.string("§7(fuelFactors is empty)")
+ )
)
return
}
+
if (currentOrganicMatterItem.let { it !in organicMatterFactors.keys && it != NONE }) {
currentOrganicMatterItem = NONE
}
@@ -199,23 +202,23 @@ object ComposterOverlay {
fuelExtraDisplay = drawFuelExtraDisplay()
} else if (inComposterUpgrades) {
organicMatterDisplay = drawUpgradeStats()
- fuelExtraDisplay = emptyList()
+ fuelExtraDisplay = null
}
}
- private fun drawUpgradeStats(): List> {
- val newList = mutableListOf>()
+ private fun drawUpgradeStats(): Renderable {
+ val newList = mutableListOf()
var upgrade = extraComposterUpgrade
if (upgrade == null) {
- newList.addAsSingletonList("§7Preview: Nothing")
+ newList.add(Renderable.string("§7Preview: Nothing"))
} else {
val level = upgrade.getLevel(null)
val nextLevel = if (maxLevel) "§6§lMAX" else "§c➜ §a" + (level + 1)
val displayName = upgrade.displayName
- newList.addAsSingletonList("§7Preview §a$displayName§7: §a$level $nextLevel")
+ newList.add(Renderable.string("§7Preview §a$displayName§7: §a$level $nextLevel"))
}
- newList.addAsSingletonList("")
+ newList.add(Renderable.string(""))
if (maxLevel) {
upgrade = null
}
@@ -236,7 +239,7 @@ object ComposterOverlay {
var formatPreview =
if (matterMaxDuration != matterMaxDurationPreview) " §c➜ §b" + formatTime(matterMaxDurationPreview) else ""
- newList.addAsSingletonList("§7Full §eOrganic Matter §7empty time: §b$format$formatPreview")
+ newList.add(Renderable.string("§7Full §eOrganic Matter §7empty time: §b$format$formatPreview"))
val maxFuel = ComposterAPI.maxFuel(null)
val maxFuelPreview = ComposterAPI.maxFuel(upgrade)
@@ -251,20 +254,20 @@ object ComposterOverlay {
format = formatTime(fuelMaxDuration)
formatPreview =
if (fuelMaxDuration != fuelMaxDurationPreview) " §c➜ §b" + formatTime(fuelMaxDurationPreview) else ""
- newList.addAsSingletonList("§7Full §2Fuel §7empty time: §b$format$formatPreview")
+ newList.add(Renderable.string("§7Full §2Fuel §7empty time: §b$format$formatPreview"))
- return newList
+ return Renderable.verticalContainer(newList, spacing = 2)
}
private fun formatTime(duration: Duration) = duration.format(maxUnits = 2)
- private fun drawOrganicMatterDisplay(): MutableList> {
+ private fun drawOrganicMatterDisplay(): Renderable {
val maxOrganicMatter = ComposterAPI.maxOrganicMatter(if (maxLevel) null else extraComposterUpgrade)
val currentOrganicMatter = ComposterAPI.getOrganicMatter()
val missingOrganicMatter = (maxOrganicMatter - currentOrganicMatter).toDouble()
- val newList = mutableListOf>()
- newList.addAsSingletonList("§7Items needed to fill §eOrganic Matter")
+ val newList = mutableListOf()
+ newList.add(Renderable.string("§7Items needed to fill §eOrganic Matter"))
val fillList = fillList(newList, organicMatterFactors, missingOrganicMatter, testOffset) {
currentOrganicMatterItem = it
update()
@@ -273,16 +276,16 @@ object ComposterOverlay {
currentOrganicMatterItem = fillList
update()
}
- return newList
+ return Renderable.verticalContainer(newList, spacing = 2)
}
- private fun drawFuelExtraDisplay(): List> {
- val newList = mutableListOf>()
+ private fun drawFuelExtraDisplay(): Renderable {
+ val newList = mutableListOf()
addExtraData(newList)
if (inComposter) {
- newList.addAsSingletonList("§7Items needed to fill §2Fuel")
+ newList.add(Renderable.string("§7Items needed to fill §2Fuel"))
val maxFuel = ComposterAPI.maxFuel(null)
val currentFuel = ComposterAPI.getFuel()
val missingFuel = (maxFuel - currentFuel).toDouble()
@@ -295,10 +298,10 @@ object ComposterOverlay {
update()
}
}
- return newList
+ return Renderable.verticalContainer(newList, spacing = 2)
}
- private fun addExtraData(newList: MutableList>) {
+ private fun addExtraData(newList: MutableList) {
val organicMatterItem = currentOrganicMatterItem ?: return
val fuelItem = currentFuelItem ?: return
if (organicMatterItem == NONE || fuelItem == NONE) return
@@ -313,12 +316,16 @@ object ComposterOverlay {
},
)
- val list = mutableListOf()
- list.add("§7Using: ")
- list.add(organicMatterItem.getItemStack())
- list.add("§7and ")
- list.add(fuelItem.getItemStack())
- newList.add(list)
+ newList.add(
+ Renderable.horizontalContainer(
+ listOf(
+ Renderable.string("§7Using: "),
+ Renderable.itemStack(organicMatterItem.getItemStack()),
+ Renderable.string("§7and "),
+ Renderable.itemStack(fuelItem.getItemStack())
+ )
+ )
+ )
val timePerCompost = ComposterAPI.timePerCompost(null)
val upgrade = if (maxLevel) null else extraComposterUpgrade
@@ -326,7 +333,7 @@ object ComposterOverlay {
val format = timePerCompost.format()
val formatPreview =
if (timePerCompostPreview != timePerCompost) " §c➜ §b" + timePerCompostPreview.format() else ""
- newList.addAsSingletonList(" §7Time per Compost: §b$format$formatPreview")
+ newList.add(Renderable.string(" §7Time per Compost: §b$format$formatPreview"))
val timeText = currentTimeType.display.lowercase()
val timeMultiplier = if (currentTimeType != TimeType.COMPOST) {
@@ -342,9 +349,12 @@ object ComposterOverlay {
val multiplierPreview = multiDropFactorPreview * timeMultiplierPreview
val compostPerTitlePreview =
if (multiplier != multiplierPreview) " §c➜ §e" + multiplierPreview.roundTo(2) else ""
- val compostPerTitle =
- if (currentTimeType == TimeType.COMPOST) "Compost multiplier" else "Composts per $timeText"
- newList.addAsSingletonList(" §7$compostPerTitle: §e${multiplier.roundTo(2)}$compostPerTitlePreview")
+ val compostPerTitle = if (currentTimeType == TimeType.COMPOST) "Compost multiplier" else "Composts per $timeText"
+ newList.add(
+ Renderable.string(
+ " §7$compostPerTitle: §e${multiplier.roundTo(2)}$compostPerTitlePreview"
+ )
+ )
val organicMatterPrice = getPrice(organicMatterItem)
val organicMatterFactor = organicMatterFactors[organicMatterItem] ?: 1.0
@@ -371,7 +381,7 @@ object ComposterOverlay {
if (totalCost != totalCostPreview) " §c➜ §6" + totalCostPreview.shortFormat() else ""
val materialCostFormat =
" §7Material costs per $timeText: §6${totalCost.shortFormat()}$materialCostFormatPreview"
- newList.addAsSingletonList(materialCostFormat)
+ newList.add(Renderable.string(materialCostFormat))
val priceCompost = COMPOST.getPrice()
val profit = ((priceCompost * multiDropFactor) - (fuelPricePer + organicMatterPricePer)) * timeMultiplier
@@ -380,13 +390,13 @@ object ComposterOverlay {
val profitFormatPreview = if (profit != profitPreview) " §c➜ §6" + profitPreview.shortFormat() else ""
val profitFormat = " §7Profit per $timeText: §6${profit.shortFormat()}$profitFormatPreview"
- newList.addAsSingletonList(profitFormat)
+ newList.add(Renderable.string(profitFormat))
- newList.addAsSingletonList("")
+ newList.add(Renderable.string(""))
}
private fun fillList(
- bigList: MutableList>,
+ bigList: MutableList,
factors: Map,
missing: Double,
testOffsetRec: Int = 0,
@@ -405,7 +415,7 @@ object ComposterOverlay {
val first: NEUInternalName? = calculateFirst(map, testOffset, factors, missing, onClick, bigList)
if (testOffset != 0) {
- bigList.addAsSingletonList(
+ bigList.add(
Renderable.link("testOffset = $testOffset") {
ComposterOverlay.testOffset = 0
update()
@@ -422,7 +432,7 @@ object ComposterOverlay {
factors: Map,
missing: Double,
onClick: (NEUInternalName) -> Unit,
- bigList: MutableList>,
+ bigList: MutableList,
): NEUInternalName? {
var i = 0
var first: NEUInternalName? = null
@@ -446,13 +456,13 @@ object ComposterOverlay {
}
val totalPrice = itemsNeeded * price
- val list = mutableListOf()
+ val line = mutableListOf()
if (testOffset != 0) {
- list.add("#$i ")
+ line.add(Renderable.string("#$i "))
}
- list.add(item)
- formatPrice(totalPrice, internalName, item.name, list, itemsNeeded, onClick)
- bigList.add(list)
+ line.add(Renderable.itemStack(item))
+ formatPrice(totalPrice, internalName, item.name, line, itemsNeeded, onClick)
+ bigList.add(Renderable.horizontalContainer(line))
if (i == 10 + testOffset) break
}
return first
@@ -462,7 +472,7 @@ object ComposterOverlay {
totalPrice: Double,
internalName: NEUInternalName,
itemName: String,
- list: MutableList,
+ list: MutableList,
itemsNeeded: Double,
onClick: (NEUInternalName) -> Unit,
) {
@@ -604,11 +614,11 @@ object ComposterOverlay {
if (EstimatedItemValue.isCurrentlyShowing()) return
if (inInventory) {
- config.overlayOrganicMatterPos.renderStringsAndItems(
+ config.overlayOrganicMatterPos.renderRenderable(
organicMatterDisplay,
posLabel = "Composter Overlay Organic Matter",
)
- config.overlayFuelExtrasPos.renderStringsAndItems(
+ config.overlayFuelExtrasPos.renderRenderable(
fuelExtraDisplay,
posLabel = "Composter Overlay Fuel Extras",
)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt
index 9c1c99a8aaef..a60a826b0df3 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt
@@ -9,14 +9,15 @@ import at.hannibal2.skyhanni.features.garden.FarmingFortuneDisplay.getLatestTrue
import at.hannibal2.skyhanni.features.garden.GardenAPI
import at.hannibal2.skyhanni.features.garden.farming.GardenCropSpeed.getLatestBlocksPerSecond
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.NumberUtil.roundTo
-import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable
import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.renderables.Container
+import at.hannibal2.skyhanni.utils.renderables.Renderable
import net.minecraft.item.ItemStack
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.math.ceil
@@ -26,9 +27,9 @@ import kotlin.time.Duration.Companion.milliseconds
object JacobContestFFNeededDisplay {
private val config get() = GardenAPI.config
- private var display = emptyList>()
+ private var display: Renderable? = null
private var lastToolTipTime = SimpleTimeMark.farPast()
- private val cache = mutableMapOf>>()
+ private val cache = mutableMapOf()
@HandleEvent
fun onRenderItemTooltip(event: RenderItemTooltipEvent) {
@@ -58,51 +59,87 @@ object JacobContestFFNeededDisplay {
cache.clear()
}
- private fun drawDisplay(contest: FarmingContest) = buildList> {
- addAsSingletonList("§6Minimum Farming Fortune needed")
- addAsSingletonList("")
+ private fun drawDisplay(contest: FarmingContest) = Container.vertical {
+ string("§6Minimum Farming Fortune needed")
+ spacer()
val crop = contest.crop
- add(listOf("§7For this ", crop.icon, "§7${crop.cropName} contest:"))
+
+ horizontal {
+ string("§7For this ")
+ item(crop.icon)
+ string("§7${crop.cropName} contest:")
+ }
+
for (bracket in ContestBracket.entries) {
- addAsSingletonList(getLine(bracket, contest.brackets, crop))
+ string(getLine(bracket, contest.brackets, crop))
}
- addAsSingletonList("")
+
+ spacer()
val (size, averages) = FarmingContestAPI.calculateAverages(crop)
- add(listOf("§7For the last §e$size ", crop.icon, "§7${crop.cropName} contests:"))
+
+ horizontal {
+ string("§7For the last §e$size ")
+ item(crop.icon)
+ string("§7${crop.cropName} contests:")
+ }
+
for (bracket in ContestBracket.entries) {
- addAsSingletonList(getLine(bracket, averages, crop))
+ string(getLine(bracket, averages, crop))
}
- addAsSingletonList("")
+
+ spacer()
var blocksPerSecond = crop.getLatestBlocksPerSecond()
if (blocksPerSecond == null) {
- add(listOf("§cNo ", crop.icon, "§cblocks/second data,"))
- addAsSingletonList("§cassuming 19.9 instead.")
- } else {
- if (blocksPerSecond < 15.0) {
- add(listOf("§7Your latest ", crop.icon, "§7blocks/second: §e${blocksPerSecond.roundTo(2)}"))
- add(listOf("§cThis is too low, showing 19.9 Blocks/second instead!"))
- blocksPerSecond = 19.9
+ horizontal {
+ string("§cNo ")
+ item(crop.icon)
+ string("§cblocks/second data,")
}
+ string("§cassuming 19.9 instead.")
+ spacer()
+ } else if (blocksPerSecond < 15.0) {
+ val bps = blocksPerSecond
+ horizontal {
+ string("§7Your latest ")
+ item(crop.icon)
+ string("§7blocks/second: §e${bps.roundTo(2)}")
+ }
+ string("§cThis is too low, showing 19.9 Blocks/second instead!")
+
+ blocksPerSecond = 19.9
+ spacer()
+
}
- addAsSingletonList("")
val trueFF = crop.getLatestTrueFarmingFortune()
if (trueFF == null) {
- addAsSingletonList("§cNo latest true FF saved!")
+ string("§cNo latest true FF saved!")
} else {
val farmingFortune = formatFarmingFortune(trueFF)
- add(listOf("§6Your ", crop.icon, "§6FF: $farmingFortune"))
+
+ horizontal {
+ string("§6Your ")
+ item(crop.icon)
+ string("§6FF: $farmingFortune")
+ }
}
- addAsSingletonList("")
+
+ spacer()
+
if (blocksPerSecond == null || trueFF == null) {
- add(listOf("§cMissing data from above!"))
+ string("§cMissing data from above!")
} else {
val predictedScore =
((100.0 + trueFF) * blocksPerSecond * crop.baseDrops * 20 * 60 / 100).toInt().addSeparators()
- add(listOf("§6Predicted ", crop.icon, "§6crops: $predictedScore"))
+
+ horizontal {
+ string("§6Predicted ")
+ item(crop.icon)
+ string("§6crops: $predictedScore")
+ }
}
}
@@ -122,7 +159,7 @@ object JacobContestFFNeededDisplay {
if (!isEnabled()) return
if (!FarmingContestAPI.inInventory) return
if (lastToolTipTime.passedSince() > 200.milliseconds) return
- config.farmingFortuneForContestPos.renderStringsAndItems(display, posLabel = "Jacob Contest Crop Data")
+ config.farmingFortuneForContestPos.renderRenderable(display, posLabel = "Jacob Contest Crop Data")
}
fun isEnabled() = LorenzUtils.inSkyBlock && config.farmingFortuneForContest
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt
index 399c4cfadef2..1fcf65b86546 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt
@@ -7,14 +7,14 @@ import at.hannibal2.skyhanni.features.garden.FarmingFortuneDisplay.getLatestTrue
import at.hannibal2.skyhanni.features.garden.GardenAPI
import at.hannibal2.skyhanni.features.garden.farming.GardenCropSpeed.getLatestBlocksPerSecond
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.CollectionUtils.buildSelector
import at.hannibal2.skyhanni.utils.CollectionUtils.sorted
import at.hannibal2.skyhanni.utils.LorenzUtils
-import at.hannibal2.skyhanni.utils.LorenzUtils.addSelector
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.NumberUtil.roundTo
-import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable
import at.hannibal2.skyhanni.utils.TimeUtils.format
+import at.hannibal2.skyhanni.utils.renderables.Container
import at.hannibal2.skyhanni.utils.renderables.Renderable
import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
@@ -27,7 +27,7 @@ import kotlin.time.Duration.Companion.seconds
object JacobContestTimeNeeded {
private val config get() = GardenAPI.config
- private var display = emptyList>()
+ private var display: Renderable? = null
private var currentBracket = ContestBracket.GOLD
@SubscribeEvent(priority = EventPriority.LOW)
@@ -44,10 +44,10 @@ object JacobContestTimeNeeded {
testCrop(crop, sorted, map)
}
- this.display = buildList {
- addAsSingletonList("§e§lTime Needed for ${currentBracket.displayName} §eMedal!")
+ this.display = Container.vertical {
+ string("§e§lTime Needed for ${currentBracket.displayName} §eMedal!")
- addSelector(
+ selector(
"§7Bracket: ",
getName = { type -> type.name.lowercase() },
isCurrent = { it == currentBracket },
@@ -56,10 +56,16 @@ object JacobContestTimeNeeded {
update()
}
)
- addAsSingletonList("")
+
+ spacer()
+
for (crop in sorted.sorted().keys) {
val text = map[crop]!!
- add(listOf(crop.icon, text))
+
+ horizontal {
+ item(crop.icon)
+ renderable(text)
+ }
}
}
}
@@ -193,7 +199,7 @@ object JacobContestTimeNeeded {
fun onBackgroundDraw(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) {
if (!isEnabled()) return
if (!FarmingContestAPI.inInventory) return
- config.jacobContestTimesPosition.renderStringsAndItems(display, posLabel = "Jacob Contest Time Needed")
+ config.jacobContestTimesPosition.renderRenderable(display, posLabel = "Jacob Contest Time Needed")
}
fun isEnabled() = LorenzUtils.inSkyBlock && config.jacobContestTimes
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Container.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Container.kt
new file mode 100644
index 000000000000..e9abcc1bd7e8
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Container.kt
@@ -0,0 +1,92 @@
+package at.hannibal2.skyhanni.utils.renderables
+import at.hannibal2.skyhanni.utils.CollectionUtils
+import net.minecraft.item.ItemStack
+
+object Container {
+ fun vertical(init: VerticalContainerBuilder.() -> Unit): Renderable {
+ val builder = VerticalContainerBuilder()
+ builder.init()
+ return builder.build()
+ }
+
+ fun horizontal(init: HorizontalContainerBuilder.() -> Unit): Renderable {
+ val builder = HorizontalContainerBuilder()
+ builder.init()
+ return builder.build()
+ }
+}
+
+abstract class ContainerBuilder {
+ protected val children = mutableListOf()
+
+ fun string(text: String) {
+ renderable(Renderable.string(text))
+ }
+
+ fun item(stack: ItemStack) {
+ renderable(Renderable.itemStack(stack))
+ }
+
+ fun spacer(size: Int = 10) {
+ renderable(getSpacer(size))
+ }
+
+ inline fun > selector(
+ prefix: String,
+ noinline getName: (T) -> String,
+ noinline isCurrent: (T) -> Boolean,
+ noinline onChange: (T) -> Unit,
+ ) {
+ selector(prefix, getName, isCurrent, onChange, enumValues())
+ }
+
+ fun selector(
+ prefix: String,
+ getName: (T) -> String,
+ isCurrent: (T) -> Boolean,
+ onChange: (T) -> Unit,
+ universe: Array
+ ) {
+ children.add(
+ Renderable.horizontalContainer(
+ CollectionUtils.buildSelector(prefix, getName, isCurrent, onChange, universe)
+ )
+ )
+ }
+
+ fun renderable(renderable: Renderable) {
+ children.add(renderable)
+ }
+
+ fun renderables(renderables: Collection) {
+ children += renderables
+ }
+
+ fun vertical(init: VerticalContainerBuilder.() -> Unit) {
+ val builder = VerticalContainerBuilder()
+ builder.init()
+ children.add(builder.build())
+ }
+
+ fun horizontal(init: HorizontalContainerBuilder.() -> Unit) {
+ val builder = HorizontalContainerBuilder()
+ builder.init()
+ children.add(builder.build())
+ }
+
+ abstract fun getSpacer(size: Int): Renderable
+
+ abstract fun build(): Renderable
+}
+
+class HorizontalContainerBuilder : ContainerBuilder() {
+ override fun getSpacer(size: Int) = Renderable.placeholder(size, 0)
+
+ override fun build(): Renderable = Renderable.horizontalContainer(children)
+}
+
+class VerticalContainerBuilder : ContainerBuilder() {
+ override fun getSpacer(size: Int) = Renderable.placeholder(0, 10)
+
+ override fun build(): Renderable = Renderable.verticalContainer(children)
+}