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<List<Any>>() + private var display: Renderable? = null private val mobList = mutableListOf<BestiaryMob>() private val stackList = mutableMapOf<Int, ItemStack>() private val catList = mutableListOf<Category>() @@ -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<List<Any>> { - val newDisplay = mutableListOf<List<Any>>() + private fun drawDisplay(): Renderable { + val display = mutableListOf<Renderable>() 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<BestiaryMob> { @@ -281,26 +274,21 @@ object BestiaryData { return sortedMobList } - private fun addList(newDisplay: MutableList<List<Any>>) { + private fun addList(newDisplay: MutableList<Renderable>) { 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<List<Any>>) { + private fun addButtons(newDisplay: MutableList<Renderable>) { 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<List<Any>>) { + private fun addCategories(newDisplay: MutableList<Renderable>) { 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<Int, ItemStack>): 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<List<Any>>() + 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<NEUInternalName, Int>) = buildList<List<Any>> { - addAsSingletonList("§7City Project Materials") + private fun buildList(materials: MutableMap<NEUInternalName, Int>): Renderable? { + val lines = mutableListOf<Renderable>() + 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<Any>() - list.add(" §7- ") - list.add(stack) + val line = mutableListOf<Renderable>() + 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<NEUInternalName, Int>) { @@ -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<List<Any>>() + 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<DataType, String>): List<Any> { - return map[this]?.let { listOf(displayItem, it) }.orEmpty() - } + fun renderableLine(map: Map<DataType, String>): 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<List<Any>>() - newDisplay.addAsSingletonList("§bComposter") + private fun updateDisplay(): Renderable? { + if (!config.displayEnabled) return null + + val lines = mutableListOf<Renderable>() + 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<Any>() - 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<Any> { + 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<NEUInternalName, Double> = emptyMap() private val config get() = GardenAPI.config.composters - private var organicMatterDisplay = emptyList<List<Any>>() - private var fuelExtraDisplay = emptyList<List<Any>>() + 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<List<Any>> { - val newList = mutableListOf<List<Any>>() + private fun drawUpgradeStats(): Renderable { + val newList = mutableListOf<Renderable>() 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<List<Any>> { + 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<List<Any>>() - newList.addAsSingletonList("§7Items needed to fill §eOrganic Matter") + val newList = mutableListOf<Renderable>() + 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<List<Any>> { - val newList = mutableListOf<List<Any>>() + private fun drawFuelExtraDisplay(): Renderable { + val newList = mutableListOf<Renderable>() 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<List<Any>>) { + private fun addExtraData(newList: MutableList<Renderable>) { val organicMatterItem = currentOrganicMatterItem ?: return val fuelItem = currentFuelItem ?: return if (organicMatterItem == NONE || fuelItem == NONE) return @@ -313,12 +316,16 @@ object ComposterOverlay { }, ) - val list = mutableListOf<Any>() - 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<List<Any>>, + bigList: MutableList<Renderable>, factors: Map<NEUInternalName, Double>, 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<NEUInternalName, Double>, missing: Double, onClick: (NEUInternalName) -> Unit, - bigList: MutableList<List<Any>>, + bigList: MutableList<Renderable>, ): NEUInternalName? { var i = 0 var first: NEUInternalName? = null @@ -446,13 +456,13 @@ object ComposterOverlay { } val totalPrice = itemsNeeded * price - val list = mutableListOf<Any>() + val line = mutableListOf<Renderable>() 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<Any>, + list: MutableList<Renderable>, 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<List<Any>>() + private var display: Renderable? = null private var lastToolTipTime = SimpleTimeMark.farPast() - private val cache = mutableMapOf<ItemStack, List<List<Any>>>() + private val cache = mutableMapOf<ItemStack, Renderable>() @HandleEvent fun onRenderItemTooltip(event: RenderItemTooltipEvent) { @@ -58,51 +59,87 @@ object JacobContestFFNeededDisplay { cache.clear() } - private fun drawDisplay(contest: FarmingContest) = buildList<List<Any>> { - 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<List<Any>>() + 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<ContestBracket>( + selector<ContestBracket>( "§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<Renderable>() + + 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 <reified T : Enum<T>> selector( + prefix: String, + noinline getName: (T) -> String, + noinline isCurrent: (T) -> Boolean, + noinline onChange: (T) -> Unit, + ) { + selector(prefix, getName, isCurrent, onChange, enumValues()) + } + + fun <T> selector( + prefix: String, + getName: (T) -> String, + isCurrent: (T) -> Boolean, + onChange: (T) -> Unit, + universe: Array<T> + ) { + children.add( + Renderable.horizontalContainer( + CollectionUtils.buildSelector(prefix, getName, isCurrent, onChange, universe) + ) + ) + } + + fun renderable(renderable: Renderable) { + children.add(renderable) + } + + fun renderables(renderables: Collection<Renderable>) { + 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) +}