aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2
diff options
context:
space:
mode:
authorHiZe_ <superhize@hotmail.com>2023-08-10 12:23:02 +0200
committerGitHub <noreply@github.com>2023-08-10 12:23:02 +0200
commit1efe50bff3fbb0e6a782aaf5284fab3fd60ec637 (patch)
tree8ba814aee575609da3461d2be0107b8fc46f2f8b /src/main/java/at/hannibal2
parentd0bbd687ca9d33cc7bd8f53e3103ecc92905f8dc (diff)
downloadskyhanni-1efe50bff3fbb0e6a782aaf5284fab3fd60ec637.tar.gz
skyhanni-1efe50bff3fbb0e6a782aaf5284fab3fd60ec637.tar.bz2
skyhanni-1efe50bff3fbb0e6a782aaf5284fab3fd60ec637.zip
Merge pull request #348
* Chest Value
Diffstat (limited to 'src/main/java/at/hannibal2')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt1
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/Features.java25
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java71
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java1
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/BestiaryData.kt32
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/ChestValue.kt261
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt33
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt50
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderLineTooltips.kt217
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt63
10 files changed, 642 insertions, 112 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index 8ca36a65b..6ef789465 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -366,6 +366,7 @@ class SkyHanniMod {
loadModule(LivingCaveDefenseBlocks())
loadModule(LivingCaveLivingMetalHelper())
loadModule(RiftMotesOrb())
+ loadModule(ChestValue())
loadModule(SlayerBossSpawnSoon())
loadModule(RiftBloodEffigies())
loadModule(RiftWiltedBerberisHelper())
diff --git a/src/main/java/at/hannibal2/skyhanni/config/Features.java b/src/main/java/at/hannibal2/skyhanni/config/Features.java
index d43b3fe39..10fe1ce42 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/Features.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java
@@ -1,30 +1,7 @@
package at.hannibal2.skyhanni.config;
import at.hannibal2.skyhanni.SkyHanniMod;
-import at.hannibal2.skyhanni.config.features.About;
-import at.hannibal2.skyhanni.config.features.AshfangConfig;
-import at.hannibal2.skyhanni.config.features.BazaarConfig;
-import at.hannibal2.skyhanni.config.features.BingoConfig;
-import at.hannibal2.skyhanni.config.features.ChatConfig;
-import at.hannibal2.skyhanni.config.features.CommandsConfig;
-import at.hannibal2.skyhanni.config.features.DamageIndicatorConfig;
-import at.hannibal2.skyhanni.config.features.DevConfig;
-import at.hannibal2.skyhanni.config.features.DianaConfig;
-import at.hannibal2.skyhanni.config.features.DungeonConfig;
-import at.hannibal2.skyhanni.config.features.FishingConfig;
-import at.hannibal2.skyhanni.config.features.GUIConfig;
-import at.hannibal2.skyhanni.config.features.GardenConfig;
-import at.hannibal2.skyhanni.config.features.GhostCounterConfig;
-import at.hannibal2.skyhanni.config.features.InventoryConfig;
-import at.hannibal2.skyhanni.config.features.ItemAbilityConfig;
-import at.hannibal2.skyhanni.config.features.MarkedPlayerConfig;
-import at.hannibal2.skyhanni.config.features.MinionsConfig;
-import at.hannibal2.skyhanni.config.features.MiscConfig;
-import at.hannibal2.skyhanni.config.features.MobsConfig;
-import at.hannibal2.skyhanni.config.features.OldHidden;
-import at.hannibal2.skyhanni.config.features.RiftConfig;
-import at.hannibal2.skyhanni.config.features.SlayerConfig;
-import at.hannibal2.skyhanni.config.features.SummoningsConfig;
+import at.hannibal2.skyhanni.config.features.*;
import com.google.gson.annotations.Expose;
import io.github.moulberry.moulconfig.Config;
import io.github.moulberry.moulconfig.Social;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java
index c3cab81db..13a6d758c 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/InventoryConfig.java
@@ -188,6 +188,77 @@ public class InventoryConfig {
}
@Expose
+ @ConfigOption(name = "Chest Value", desc = "")
+ @Accordion
+ public ChestValueConfig chestValueConfig = new ChestValueConfig();
+
+ public static class ChestValueConfig {
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Enabled estimated value of chest")
+ @ConfigEditorBoolean
+ public boolean enabled = false;
+
+ @Expose
+ @ConfigOption(name = "Show Stacks", desc = "Show the item icon before name.")
+ @ConfigEditorBoolean
+ public boolean showStacks = true;
+
+ @Expose
+ @ConfigOption(name = "Display Type", desc = "Try to align everything to look nicer.")
+ @ConfigEditorBoolean
+ public boolean alignedDisplay = true;
+
+ @Expose
+ @ConfigOption(name = "Name Length", desc = "Reduce item name length to gain extra space on screen.\n§cCalculated in pixels!")
+ @ConfigEditorSlider(minStep = 1, minValue = 10, maxValue = 200)
+ public int nameLength = 100;
+
+ @Expose
+ @ConfigOption(name = "Highlight slot", desc = "Highlight slot where the item is when you hover over it in the display.")
+ @ConfigEditorBoolean
+ public boolean enableHighlight = true;
+
+ @Expose
+ @ConfigOption(name = "Highlight color", desc = "Choose the highlight color.")
+ @ConfigEditorColour
+ public String highlightColor = "0:249:0:255:88";
+
+ @Expose
+ @ConfigOption(name = "Sorting Type", desc = "Price sorting type.")
+ @ConfigEditorDropdown(values = {"Descending", "Ascending"})
+ public int sortingType = 0;
+
+ @Expose
+ @ConfigOption(name = "Value formatting Type", desc = "Format of the price.")
+ @ConfigEditorDropdown(values = {"Short", "Long"})
+ public int formatType = 0;
+
+ @Expose
+ @ConfigOption(name = "Item To Show", desc = "Choose how many items are displayed.\n" +
+ "All items in the chest are still counted for the total value.")
+ @ConfigEditorSlider(
+ minValue = 0,
+ maxValue = 54,
+ minStep = 1
+ )
+ public int itemToShow = 15;
+
+ @Expose
+ @ConfigOption(name = "Hide below", desc = "Item item value below configured amount.\n" +
+ "Items are still counted for the total value.")
+ @ConfigEditorSlider(
+ minValue = 50_000,
+ maxValue = 10_000_000,
+ minStep = 50_000
+ )
+ public int hideBelow = 100_000;
+
+
+ @Expose
+ public Position position = new Position(107, 141, false, true);
+ }
+
+ @Expose
@ConfigOption(
name = "Item number",
desc = "Showing the item number as a stack size for these items."
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java
index eb7b5d2c6..a97fe1afe 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java
@@ -673,7 +673,6 @@ public class MiscConfig {
@ConfigEditorDropdown(values = {"Short", "Long"})
public int numberFormat = 0;
-
@Expose
@ConfigOption(name = "Display type", desc = "Choose what the display should show")
@ConfigEditorDropdown(values = {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/BestiaryData.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/BestiaryData.kt
index 87ab04dca..626d24f03 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/misc/BestiaryData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/BestiaryData.kt
@@ -8,6 +8,9 @@ import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
import at.hannibal2.skyhanni.utils.*
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.LorenzUtils.addButton
+import at.hannibal2.skyhanni.utils.LorenzUtils.toBoolean
+import at.hannibal2.skyhanni.utils.LorenzUtils.toInt
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber
import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNeeded
@@ -230,7 +233,7 @@ object BestiaryData {
if (isMaxed && config.hideMaxed) continue
val text = getMobLine(mob, isMaxed)
val tips = getMobHover(mob)
- newDisplay.addAsSingletonList(Renderable.hoverTips(text, tips, false) { true })
+ newDisplay.addAsSingletonList(Renderable.hoverTips(text, tips) { true })
}
}
@@ -413,9 +416,6 @@ object BestiaryData {
else -> "0"
}
- private fun Int.toBoolean() = this != 0
- private fun Boolean.toInt() = if (!this) 0 else 1
-
data class Category(
val name: String,
val familiesFound: Long,
@@ -452,30 +452,6 @@ object BestiaryData {
fun getNextLevel() = level.getNextLevel()
}
- private fun MutableList<List<Any>>.addButton(
- prefix: String,
- getName: String,
- onChange: () -> Unit,
- tips: List<String> = emptyList(),
- ) {
- val onClick = {
- if ((System.currentTimeMillis() - lastclicked) > 100) { // funny thing happen if I don't do that
- onChange()
- SoundUtils.playClickSound()
- lastclicked = System.currentTimeMillis()
- }
- }
- add(buildList {
- add(prefix)
- add("§a[")
- if (tips.isEmpty()) {
- add(Renderable.link("§e$getName", false, onClick))
- } else {
- add(Renderable.clickAndHover("§e$getName", tips, false, onClick))
- }
- add("§a]")
- })
- }
private fun String.romanOrInt() = romanToDecimalIfNeeded().let {
if (config.replaceRoman || it == 0) it.toString() else it.toRoman()
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/ChestValue.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/ChestValue.kt
new file mode 100644
index 000000000..e0f2e860e
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/ChestValue.kt
@@ -0,0 +1,261 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.events.*
+import at.hannibal2.skyhanni.features.misc.items.EstimatedItemValue
+import at.hannibal2.skyhanni.utils.*
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
+import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.LorenzUtils.addButton
+import at.hannibal2.skyhanni.utils.LorenzUtils.toBoolean
+import at.hannibal2.skyhanni.utils.LorenzUtils.toInt
+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.renderables.Renderable
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.inventory.GuiChest
+import net.minecraft.init.Items
+import net.minecraft.item.ItemStack
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.awt.Color
+
+class ChestValue {
+
+ private val config get() = SkyHanniMod.feature.inventory.chestValueConfig
+ private var display = emptyList<List<Any>>()
+ private val chestItems = mutableMapOf<String, Item>()
+ private val inInventory get() = InventoryUtils.openInventoryName().isValidStorage()
+
+ @SubscribeEvent
+ fun onBackgroundDraw(event: GuiRenderEvent.ChestBackgroundRenderEvent) {
+ if (!isEnabled()) return
+ if (InventoryUtils.openInventoryName() == "") return
+ if (inInventory) {
+ config.position.renderStringsAndItems(
+ display,
+ extraSpace = -1,
+ itemScale = 1.3,
+ posLabel = "Estimated Chest Value"
+ )
+ }
+ }
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (!isEnabled()) return
+ if (event.isMod(5)) {
+ update()
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryOpen(event: InventoryOpenEvent) {
+ if (!isEnabled()) return
+ if (inInventory) {
+ update()
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryClose(event: InventoryCloseEvent) {
+ chestItems.clear()
+ Renderable.list.clear()
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOW)
+ fun onDrawBackground(event: GuiContainerEvent.BackgroundDrawnEvent) {
+ if (!isEnabled()) return
+ if (!config.enableHighlight) return
+ if (inInventory) {
+ for ((_, indexes) in Renderable.list) {
+ for (slot in InventoryUtils.getItemsInOpenChest()) {
+ if (indexes.contains(slot.slotIndex)) {
+ slot highlight Color(SpecialColour.specialToChromaRGB(config.highlightColor), true)
+ }
+ }
+ }
+ }
+ }
+
+ private fun update() {
+ display = drawDisplay()
+ }
+
+ private fun drawDisplay(): List<List<Any>> {
+ val newDisplay = mutableListOf<List<Any>>()
+
+ init()
+
+ if (chestItems.isEmpty()) return newDisplay
+
+ addList(newDisplay)
+ addButton(newDisplay)
+
+ return newDisplay
+ }
+
+ private fun addList(newDisplay: MutableList<List<Any>>) {
+ val sortedList = sortedList()
+ var totalPrice = 0.0
+ var rendered = 0
+ val amountShowing = if (config.itemToShow > sortedList.size) sortedList.size else config.itemToShow
+ newDisplay.addAsSingletonList("§7Estimated Chest Value: §o(Showing $amountShowing of ${sortedList.size} items)")
+ for ((index, amount, stack, total, tips) in sortedList) {
+ totalPrice += total
+ if (rendered >= config.itemToShow) continue
+ if (total < config.hideBelow) continue
+ val textAmount = " §7x$amount:"
+ val width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(textAmount)
+ val name = "${stack.displayName.reduceStringLength((config.nameLength - width), ' ')} $textAmount"
+ val price = "§b${(total).formatPrice()}"
+ val text = if (config.alignedDisplay)
+ "$name $price"
+ else
+ "${stack.displayName} §7x$amount: §b${total.formatPrice()}"
+ newDisplay.add(buildList {
+ val renderable = Renderable.hoverTips(
+ text,
+ tips,
+ stack = stack,
+ indexes = index)
+ add(" §7- ")
+ if (config.showStacks) add(stack)
+ add(renderable)
+ })
+ rendered++
+ }
+ newDisplay.addAsSingletonList("§6Total value : §b${totalPrice.formatPrice()}")
+ }
+
+ private fun sortedList(): MutableList<Item> {
+ return when (config.sortingType) {
+ 0 -> chestItems.values.sortedByDescending { it.total }
+ 1 -> chestItems.values.sortedBy { it.total }
+ else -> chestItems.values.sortedByDescending { it.total }
+ }.toMutableList()
+ }
+
+ private fun addButton(newDisplay: MutableList<List<Any>>) {
+ newDisplay.addButton("§7Sorted By: ",
+ getName = SortType.entries[config.sortingType].longName,
+ onChange = {
+ config.sortingType = (config.sortingType + 1) % 2
+ update()
+ })
+
+ newDisplay.addButton("§7Value format: ",
+ getName = FormatType.entries[config.formatType].type,
+ onChange = {
+ config.formatType = (config.formatType + 1) % 2
+ update()
+ })
+
+ newDisplay.addButton("§7Display Type: ",
+ getName = DisplayType.entries[config.alignedDisplay.toInt()].type,
+ onChange = {
+ config.alignedDisplay = ((config.alignedDisplay.toInt() + 1) % 2).toBoolean()
+ update()
+ })
+ }
+
+ private fun init() {
+ if (inInventory) {
+ val isMinion = InventoryUtils.openInventoryName().contains(" Minion ")
+ val slots = InventoryUtils.getItemsInOpenChest().filter {
+ it.hasStack && it.inventory != Minecraft.getMinecraft().thePlayer.inventory && (!isMinion || it.slotNumber % 9 != 1)
+ }
+ val stacks = buildMap {
+ slots.forEach {
+ put(it.slotIndex, it.stack)
+ }
+ }
+ chestItems.clear()
+ for ((i, stack) in stacks) {
+ val internalName = stack.getInternalName()
+ if (internalName == "") continue
+ if (NEUItems.getItemStackOrNull(internalName) == null) continue
+ val list = mutableListOf<String>()
+ val pair = EstimatedItemValue.getEstimatedItemPrice(stack, list)
+ var (total, _) = pair
+ if (stack.item == Items.enchanted_book)
+ total /= 2
+ list.add("§aTotal: §6§l${total.formatPrice()}")
+ if (total == 0.0) continue
+ val item = chestItems.getOrPut(internalName) {
+ Item(mutableListOf(), 0, stack, 0.0, list)
+ }
+ item.index.add(i)
+ item.amount += stack.stackSize
+ item.total += total * stack.stackSize
+ }
+ }
+ }
+
+ private fun Double.formatPrice(): String {
+ return when (config.formatType) {
+ 0 -> if (this > 1_000_000_000) NumberUtil.format(this, true) else NumberUtil.format(this)
+ 1 -> this.addSeparators()
+ else -> "0"
+ }
+ }
+
+ enum class SortType(val shortName: String, val longName: String) {
+ PRICE_DESC("Price D", "Price Descending"),
+ PRICE_ASC("Price A", "Price Ascending")
+ ;
+ }
+
+ enum class FormatType(val type: String) {
+ SHORT("Formatted"),
+ LONG("Unformatted")
+ ;
+ }
+
+ enum class DisplayType(val type: String) {
+ NORMAL("Normal"),
+ COMPACT("Aligned")
+ }
+
+ private fun String.isValidStorage() = Minecraft.getMinecraft().currentScreen is GuiChest && ((this == "Chest" ||
+ this == "Large Chest") ||
+ (contains("Minion") && !contains("Recipe") && LorenzUtils.skyBlockIsland == IslandType.PRIVATE_ISLAND) ||
+ this == "Personal Vault")
+
+
+ private fun String.reduceStringLength(targetLength: Int, char: Char): String {
+ val mc = Minecraft.getMinecraft()
+ val spaceWidth = mc.fontRendererObj.getCharWidth(char)
+
+ var currentString = this
+ var currentLength = mc.fontRendererObj.getStringWidth(currentString)
+
+ while (currentLength > targetLength) {
+ currentString = currentString.dropLast(1)
+ currentLength = mc.fontRendererObj.getStringWidth(currentString)
+ }
+
+ val difference = targetLength - currentLength
+
+ if (difference > 0) {
+ val numSpacesToAdd = difference / spaceWidth
+ val spaces = " ".repeat(numSpacesToAdd)
+ return currentString + spaces
+ }
+
+ return currentString
+ }
+
+
+ data class Item(
+ val index: MutableList<Int>,
+ var amount: Int,
+ val stack: ItemStack,
+ var total: Double,
+ val tips: MutableList<String>
+ )
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
index 6a2dd34fc..a2bcc4020 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
@@ -57,6 +57,7 @@ object LorenzUtils {
const val DEBUG_PREFIX = "[SkyHanni Debug] §7"
private val log = LorenzLogger("chat/mod_sent")
+ var lastButtonClicked = 0L
fun debug(message: String) {
if (SkyHanniMod.feature.dev.debugEnabled) {
@@ -333,6 +334,31 @@ object LorenzUtils {
})
}
+ inline fun MutableList<List<Any>>.addButton(
+ prefix: String,
+ getName: String,
+ crossinline onChange: () -> Unit,
+ tips: List<String> = emptyList(),
+ ) {
+ val onClick = {
+ if ((System.currentTimeMillis() - lastButtonClicked) > 150) { // funny thing happen if I don't do that
+ onChange()
+ SoundUtils.playClickSound()
+ lastButtonClicked = System.currentTimeMillis()
+ }
+ }
+ add(buildList {
+ add(prefix)
+ add("§a[")
+ if (tips.isEmpty()) {
+ add(Renderable.link("§e$getName", false, onClick))
+ } else {
+ add(Renderable.clickAndHover("§e$getName", tips, false, onClick))
+ }
+ add("§a]")
+ })
+ }
+
// TODO nea?
// fun <T> dynamic(block: () -> KMutableProperty0<T>?): ReadWriteProperty<Any?, T?> {
// return object : ReadWriteProperty<Any?, T?> {
@@ -383,8 +409,8 @@ object LorenzUtils {
val tileSign = (this as AccessorGuiEditSign).tileSign
return (tileSign.signText[1].unformattedText.removeColor() == "^^^^^^"
- && tileSign.signText[2].unformattedText.removeColor() == "Set your"
- && tileSign.signText[3].unformattedText.removeColor() == "speed cap!")
+ && tileSign.signText[2].unformattedText.removeColor() == "Set your"
+ && tileSign.signText[3].unformattedText.removeColor() == "speed cap!")
}
fun inIsland(island: IslandType) = inSkyBlock && skyBlockIsland == island
@@ -448,4 +474,7 @@ object LorenzUtils {
javaClass.getDeclaredField("modifiers").makeAccessible().set(this, modifiers and (Modifier.FINAL.inv()))
return this
}
+
+ fun Int.toBoolean() = this != 0
+ fun Boolean.toInt() = if (!this) 0 else 1
} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
index efdb545d2..5e40a2daf 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
@@ -39,19 +39,57 @@ object NumberUtil {
* @link https://stackoverflow.com/a/30661479
* @author assylias
*/
+
@JvmStatic
- fun format(value: Number): String {
+ fun format(value: Number, preciseBillions: Boolean = false): String {
@Suppress("NAME_SHADOWING")
val value = value.toLong()
- //Long.MIN_VALUE == -Long.MIN_VALUE, so we need an adjustment here
- if (value == Long.MIN_VALUE) return format(Long.MIN_VALUE + 1)
- if (value < 0) return "-" + format(-value)
+ //Long.MIN_VALUE == -Long.MIN_VALUE so we need an adjustment here
+ if (value == Long.MIN_VALUE) return format(Long.MIN_VALUE + 1, preciseBillions)
+ if (value < 0) return "-" + format(-value, preciseBillions)
+
if (value < 1000) return value.toString() //deal with small numbers
+
val (divideBy, suffix) = suffixes.floorEntry(value)
+
val truncated = value / (divideBy / 10) //the number part of the output times 10
- val truncatedAt = if (suffix == "M") 1000 else 100
+
+ val truncatedAt = if (suffix == "M") 1000 else if (suffix == "B") 1000000 else 100
+
+ val hasDecimal = truncated < truncatedAt && truncated / 10.0 != (truncated / 10).toDouble()
+
+ return if (value > 1_000_000_000 && hasDecimal && preciseBillions) {
+ val decimalPart = (value % 1_000_000_000) / 1_000_000
+ "${truncated / 10}.$decimalPart$suffix"
+ } else {
+ if (hasDecimal) (truncated / 10.0).toString() + suffix else (truncated / 10).toString() + suffix
+ }
+ }
+
+ @JvmStatic
+ fun format3(value: Number, digit: Int): String {
+ @Suppress("NAME_SHADOWING")
+ val value = value.toLong()
+ //Long.MIN_VALUE == -Long.MIN_VALUE so we need an adjustment here
+ if (value == Long.MIN_VALUE) return format3(Long.MIN_VALUE + 1, digit)
+ if (value < 0) return "-" + format3(-value, digit)
+ if (value < 1000) return value.toString() //deal with small numbers
+
+ val (divideBy, suffix) = suffixes.floorEntry(value)
+
+ var truncated = value / (divideBy / 10) //the number part of the output times 10
+
+ val truncatedAt = if (suffix == "M") 1000 else if (suffix == "B") 1000000 else 100
+
val hasDecimal = truncated < truncatedAt && truncated / 10.0 != (truncated / 10).toDouble()
- return if (hasDecimal) (truncated / 10.0).toString() + suffix else (truncated / 10).toString() + suffix
+
+ // Add check for value greater than 1000000000 (1 Billion)
+ return if (value > 1000000000 && hasDecimal) {
+ val decimalPart = (value % 1000000000) / 1000000 // Extract 3 digits after the decimal point
+ "${(truncated / 10).toDouble().toString().take(digit + 2)}$suffix"
+ } else {
+ if (hasDecimal) (truncated / 10.0).toString().take(digit + 2) + suffix else (truncated / 10).toString() + suffix
+ }
}
/**
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderLineTooltips.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderLineTooltips.kt
new file mode 100644
index 000000000..2cf20ee72
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderLineTooltips.kt
@@ -0,0 +1,217 @@
+package at.hannibal2.skyhanni.utils.renderables
+
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import io.github.moulberry.notenoughupdates.util.Utils
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.ScaledResolution
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraft.client.renderer.RenderHelper
+import net.minecraft.client.renderer.Tessellator
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats
+import net.minecraft.item.ItemStack
+import java.awt.Color
+
+object RenderLineTooltips {
+
+ fun drawHoveringText(posX: Int, posY: Int, tips: List<String?>, stack: ItemStack? = null) {
+ if (tips.isNotEmpty()) {
+ var textLines = tips
+ val x = Utils.getMouseX() + 12 - posX
+ val y = Utils.getMouseY() - 10 - posY
+ val color: Char = stack?.getLore()?.lastOrNull()?.take(4)?.get(1)
+ ?: Utils.getPrimaryColourCode(textLines[0])
+ val colourInt = Minecraft.getMinecraft().fontRendererObj.getColorCode(color)
+ val borderColorStart = Color(colourInt).darker().rgb and 0x00FFFFFF or (200 shl 24)
+ val font = Minecraft.getMinecraft().fontRendererObj
+ val scaled = ScaledResolution(Minecraft.getMinecraft())
+ GlStateManager.disableRescaleNormal()
+ RenderHelper.disableStandardItemLighting()
+ GlStateManager.disableLighting()
+ GlStateManager.enableDepth()
+ var tooltipTextWidth = 0
+ for (textLine in textLines) {
+ val textLineWidth = font.getStringWidth(textLine)
+ if (textLineWidth > tooltipTextWidth) {
+ tooltipTextWidth = textLineWidth
+ }
+ }
+ var needsWrap = false
+ var titleLinesCount = 1
+ var tooltipX = x
+ if (tooltipX + tooltipTextWidth + 4 > scaled.scaledWidth) {
+ tooltipX = x - 16 - tooltipTextWidth
+ if (tooltipX < 4) {
+ tooltipTextWidth = if (x > scaled.scaledWidth / 2) {
+ x - 12 - 8
+ } else {
+ scaled.scaledWidth - 16 - x
+ }
+ needsWrap = true
+ }
+ }
+ if (needsWrap) {
+ var wrappedTooltipWidth = 0
+ val wrappedTextLines: MutableList<String?> = ArrayList()
+ for (i in textLines.indices) {
+ val textLine = textLines[i]
+ val wrappedLine = font.listFormattedStringToWidth(textLine, tooltipTextWidth)
+ if (i == 0) {
+ titleLinesCount = wrappedLine.size
+ }
+ for (line in wrappedLine) {
+ val lineWidth = font.getStringWidth(line)
+ if (lineWidth > wrappedTooltipWidth) {
+ wrappedTooltipWidth = lineWidth
+ }
+ wrappedTextLines.add(line)
+ }
+ }
+ tooltipTextWidth = wrappedTooltipWidth
+ textLines = wrappedTextLines.toList()
+ tooltipX = if (x > scaled.scaledWidth / 2) {
+ x - 16 - tooltipTextWidth
+ } else {
+ x + 12
+ }
+ }
+ var tooltipY = y - 12
+ var tooltipHeight = 8
+ if (textLines.size > 1) {
+ tooltipHeight += (textLines.size - 1) * 10
+ if (textLines.size > titleLinesCount) {
+ tooltipHeight += 2
+ }
+ }
+
+ if (tooltipY + tooltipHeight + 6 > scaled.scaledHeight) {
+ tooltipY = scaled.scaledHeight - tooltipHeight - 6
+ }
+ val zLevel = 300
+ val backgroundColor = -0xfeffff0
+ drawGradientRect(
+ zLevel,
+ tooltipX - 3,
+ tooltipY - 4,
+ tooltipX + tooltipTextWidth + 3,
+ tooltipY - 3,
+ backgroundColor,
+ backgroundColor
+ )
+ drawGradientRect(
+ zLevel,
+ tooltipX - 3,
+ tooltipY + tooltipHeight + 3,
+ tooltipX + tooltipTextWidth + 3,
+ tooltipY + tooltipHeight + 4,
+ backgroundColor,
+ backgroundColor
+ )
+ drawGradientRect(
+ zLevel,
+ tooltipX - 3,
+ tooltipY - 3,
+ tooltipX + tooltipTextWidth + 3,
+ tooltipY + tooltipHeight + 3,
+ backgroundColor,
+ backgroundColor
+ )
+ drawGradientRect(
+ zLevel,
+ tooltipX - 4,
+ tooltipY - 3,
+ tooltipX - 3,
+ tooltipY + tooltipHeight + 3,
+ backgroundColor,
+ backgroundColor
+ )
+ drawGradientRect(
+ zLevel,
+ tooltipX + tooltipTextWidth + 3,
+ tooltipY - 3,
+ tooltipX + tooltipTextWidth + 4,
+ tooltipY + tooltipHeight + 3,
+ backgroundColor,
+ backgroundColor
+ )
+ val borderColorEnd = borderColorStart and 0xFEFEFE shr 1 or (borderColorStart and -0x1000000)
+ drawGradientRect(
+ zLevel,
+ tooltipX - 3,
+ tooltipY - 3 + 1,
+ tooltipX - 3 + 1,
+ tooltipY + tooltipHeight + 3 - 1,
+ borderColorStart,
+ borderColorEnd
+ )
+ drawGradientRect(
+ zLevel,
+ tooltipX + tooltipTextWidth + 2,
+ tooltipY - 3 + 1,
+ tooltipX + tooltipTextWidth + 3,
+ tooltipY + tooltipHeight + 3 - 1,
+ borderColorStart,
+ borderColorEnd
+ )
+ drawGradientRect(
+ zLevel,
+ tooltipX - 3,
+ tooltipY - 3,
+ tooltipX + tooltipTextWidth + 3,
+ tooltipY - 3 + 1,
+ borderColorStart,
+ borderColorStart
+ )
+ drawGradientRect(
+ zLevel,
+ tooltipX - 3,
+ tooltipY + tooltipHeight + 2,
+ tooltipX + tooltipTextWidth + 3,
+ tooltipY + tooltipHeight + 3,
+ borderColorEnd,
+ borderColorEnd
+ )
+ GlStateManager.disableDepth()
+ for (lineNumber in textLines.indices) {
+ val line = textLines[lineNumber]
+ font.drawStringWithShadow(line, 1f + tooltipX.toFloat(), 1f + tooltipY.toFloat(), -1)
+ if (lineNumber + 1 == titleLinesCount) {
+ tooltipY += 2
+ }
+ tooltipY += 10
+ }
+ GlStateManager.enableLighting()
+ GlStateManager.enableDepth()
+ RenderHelper.enableStandardItemLighting()
+ GlStateManager.enableRescaleNormal()
+ }
+ GlStateManager.disableLighting()
+ }
+
+ private fun drawGradientRect(zLevel: Int, left: Int, top: Int, right: Int, bottom: Int, startColor: Int, endColor: Int) {
+ val startAlpha = (startColor shr 24 and 255).toFloat() / 255.0f
+ val startRed = (startColor shr 16 and 255).toFloat() / 255.0f
+ val startGreen = (startColor shr 8 and 255).toFloat() / 255.0f
+ val startBlue = (startColor and 255).toFloat() / 255.0f
+ val endAlpha = (endColor shr 24 and 255).toFloat() / 255.0f
+ val endRed = (endColor shr 16 and 255).toFloat() / 255.0f
+ val endGreen = (endColor shr 8 and 255).toFloat() / 255.0f
+ val endBlue = (endColor and 255).toFloat() / 255.0f
+ GlStateManager.disableTexture2D()
+ GlStateManager.enableBlend()
+ GlStateManager.disableAlpha()
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0)
+ GlStateManager.shadeModel(7425)
+ val tessellator = Tessellator.getInstance()
+ val worldrenderer = tessellator.worldRenderer
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_COLOR)
+ worldrenderer.pos(right.toDouble(), top.toDouble(), zLevel.toDouble()).color(startRed, startGreen, startBlue, startAlpha).endVertex()
+ worldrenderer.pos(left.toDouble(), top.toDouble(), zLevel.toDouble()).color(startRed, startGreen, startBlue, startAlpha).endVertex()
+ worldrenderer.pos(left.toDouble(), bottom.toDouble(), zLevel.toDouble()).color(endRed, endGreen, endBlue, endAlpha).endVertex()
+ worldrenderer.pos(right.toDouble(), bottom.toDouble(), zLevel.toDouble()).color(endRed, endGreen, endBlue, endAlpha).endVertex()
+ tessellator.draw()
+ GlStateManager.shadeModel(7424)
+ GlStateManager.disableBlend()
+ GlStateManager.enableAlpha()
+ GlStateManager.enableTexture2D()
+ }
+} \ No newline at end of file
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 c554b8bde..dabf428d4 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt
@@ -2,14 +2,12 @@ package at.hannibal2.skyhanni.utils.renderables
import at.hannibal2.skyhanni.config.core.config.gui.GuiPositionEditor
import at.hannibal2.skyhanni.data.ToolTipData
-import at.hannibal2.skyhanni.utils.LorenzColor
import at.hannibal2.skyhanni.utils.LorenzLogger
import at.hannibal2.skyhanni.utils.NEUItems.renderOnScreen
import io.github.moulberry.moulconfig.gui.GuiScreenElementWrapper
import io.github.moulberry.notenoughupdates.util.Utils
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Gui
-import net.minecraft.client.gui.GuiScreen
import net.minecraft.client.gui.inventory.GuiEditSign
import net.minecraft.client.renderer.GlStateManager
import net.minecraft.item.ItemStack
@@ -21,7 +19,7 @@ interface Renderable {
val height: Int
fun isHovered(posX: Int, posY: Int) =
Utils.getMouseX() in (posX..posX + width)
- && Utils.getMouseY() in (posY..posY + height) // TODO: adjust for variable height?
+ && Utils.getMouseY() in (posY..posY + height) // TODO: adjust for variable height?
/**
* N.B.: the offset is absolute, not relative to the position and shouldn't be used for rendering
@@ -31,6 +29,7 @@ interface Renderable {
companion object {
val logger = LorenzLogger("debug/renderable")
+ val list = mutableMapOf<Pair<Int, Int>, List<Int>>()
fun fromAny(any: Any?, itemScale: Double = 1.0): Renderable? = when (any) {
null -> placeholder(12)
@@ -101,12 +100,8 @@ interface Renderable {
}
}
- fun hoverTips(
- text: String,
- tips: List<String>,
- bypassChecks: Boolean = false,
- condition: () -> Boolean = { true }
- ): Renderable {
+ fun hoverTips(text: String, tips: List<String>, indexes: List<Int> = listOf(), stack: ItemStack? = null, bypassChecks: Boolean = false, condition: () -> Boolean = { true }): Renderable {
+
val render = string(text)
return object : Renderable {
override val width: Int
@@ -117,53 +112,18 @@ interface Renderable {
render.render(posX, posY)
if (isHovered(posX, posY)) {
if (condition() && shouldAllowLink(true, bypassChecks)) {
- renderToolTips(posX, posY, tips)
+ list[Pair(posX, posY)] = indexes
+ RenderLineTooltips.drawHoveringText(posX, posY, tips, stack)
+ }
+ } else {
+ if (list.contains(Pair(posX, posY))) {
+ list.remove(Pair(posX, posY))
}
}
}
}
}
- private fun renderToolTips(posX: Int, posY: Int, tips: List<String>, border: Int = 1) {
- GlStateManager.pushMatrix()
-// GlStateManager.translate(0f, 0f, 2f)
-// GuiRenderUtils.drawTooltip(tips, posX, posY, 0)
-// GlStateManager.translate(0f, 0f, -2f)
-
- val x = Utils.getMouseX() - posX + 10
- val startY = Utils.getMouseY() - posY - 10
- var maxX = 0
- var y = startY
- val renderer = Minecraft.getMinecraft().fontRendererObj
-
- GlStateManager.translate(0f, 0f, 2f)
- for (line in tips) {
- renderer.drawStringWithShadow(
- "§f$line",
- 1f + x,
- 1f + y,
- 0
- )
- val currentX = renderer.getStringWidth(line)
- if (currentX > maxX) {
- maxX = currentX
- }
- y += 10
- }
- GlStateManager.translate(0f, 0f, -1f)
-
- GuiScreen.drawRect(
- x - border,
- startY - border,
- x + maxX + 10 + border,
- y + border,
- LorenzColor.DARK_GRAY.toColor().rgb
- )
- GlStateManager.translate(0f, 0f, -1f)
-
- GlStateManager.popMatrix()
- }
-
private fun shouldAllowLink(debug: Boolean = false, bypassChecks: Boolean): Boolean {
val isGuiScreen = Minecraft.getMinecraft().currentScreen != null
if (bypassChecks) {
@@ -252,4 +212,5 @@ interface Renderable {
}
}
}
-} \ No newline at end of file
+}
+