From dadf6c04d526c11237ccd9bd1162a08d3fc8a5f6 Mon Sep 17 00:00:00 2001 From: martimavocado <39881008+martimavocado@users.noreply.github.com> Date: Sun, 16 Jun 2024 22:52:47 +0100 Subject: Feature: Add SkyHanni User Luck to stats breakdown (#1288) --- .../skyhanni/features/misc/UserLuckBreakdown.kt | 341 +++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt new file mode 100644 index 000000000..8592a0d72 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt @@ -0,0 +1,341 @@ +package at.hannibal2.skyhanni.features.misc + +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryOpenEvent +import at.hannibal2.skyhanni.events.LorenzToolTipEvent +import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent +import at.hannibal2.skyhanni.features.skillprogress.SkillType +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import at.hannibal2.skyhanni.utils.NEUItems.getItemStack +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import io.github.moulberry.notenoughupdates.util.Utils +import net.minecraft.client.player.inventory.ContainerLocalMenu +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration.Companion.seconds + +@SkyHanniModule +object UserLuckBreakdown { + private var inMiscStats = false + private var replaceSlot: Int? = null + private var itemCreateCoolDown = SimpleTimeMark.farPast() + private var skillCalcCoolDown = SimpleTimeMark.farPast() + + private val storage get() = ProfileStorageData.playerSpecific + + private lateinit var mainLuckItem: ItemStack + private val mainLuckID = "ENDER_PEARL".asInternalName() + private val mainLuckName = "§a✴ SkyHanni User Luck" + + private lateinit var fillerItem: ItemStack + private var fillerID = "STAINED_GLASS_PANE".asInternalName() + private val fillerName = " " + + private lateinit var limboItem: ItemStack + private var limboID = "ENDER_PEARL".asInternalName() + private val limboName = "§a✴ Limbo Personal Best" + + private lateinit var skillsItem: ItemStack + private var skillsID = "DIAMOND_SWORD".asInternalName() + private val skillsName = "§a✴ Category: Skills" + + private var showAllStats = true + + /** + * REGEX-TEST: §7Show all stats: §aYes + * REGEX-TEST: §7Show all stats: §cNope + */ + private val showAllStatsPattern by RepoPattern.pattern( + "misc.statsbreakdown.showallstats", + "§7Show all stats: §.(?.*)", + ) + + private val luckTooltipString = "§5§o §a✴ SkyHanni User Luck §f" + private var inCustomBreakdown = false + + private val validItemSlots = (10..53).filter { it !in listOf(17, 18, 26, 27, 35, 36) && it !in 44..53 } + private val invalidItemSlots = (0..53).filter { it !in validItemSlots } + + private var skillOverflowLuck = mutableMapOf() + + @SubscribeEvent + fun replaceItem(event: ReplaceItemEvent) { + if (event.inventory !is ContainerLocalMenu) return + if (!inMiscStats) return + + if (event.slot == replaceSlot && !inCustomBreakdown) { + val limboUserLuck = storage?.limbo?.userLuck ?: 0.0f + if (limboUserLuck == 0.0f && !showAllStats) return + if (itemCreateCoolDown.passedSince() > 3.seconds) { + itemCreateCoolDown = SimpleTimeMark.now() + createItems() + } + event.replace(mainLuckItem) + return + } + if (inCustomBreakdown) { + if (itemCreateCoolDown.passedSince() > 3.seconds) { + itemCreateCoolDown = SimpleTimeMark.now() + createItems() + } + checkItemSlot(event) + } + } + + private fun checkItemSlot(event: ReplaceItemEvent) { + when (event.slot) { + 48, 49 -> return + + 10 -> event.replace(skillsItem) + 11 -> event.replace(limboItem) + + in validItemSlots -> event.replace(null) + + in invalidItemSlots -> { + if (event.originalItem.item == limboID.getItemStack().item) return + event.replace(fillerItem) + return + } + } + } + + @SubscribeEvent + fun openInventory(event: InventoryOpenEvent) { + if (event.inventoryName != "Your Stats Breakdown") { + inMiscStats = false + return + } + val inventoryName = event.inventoryItems[4]?.name ?: "" + if (inventoryName != "§dMisc Stats") return + inMiscStats = true + replaceSlot = findValidSlot(event.inventoryItems) + val showAllStatsLore = event.inventoryItems[50]?.getLore() ?: listOf("") + for (line in showAllStatsLore) { + showAllStatsPattern.matchMatcher(line) { + showAllStats = when (group("toggle")) { + "Yes" -> true + else -> false + } + } + } + return + } + + @SubscribeEvent + fun closeInventory(event: InventoryCloseEvent) { + inMiscStats = false + inCustomBreakdown = false + } + + private fun findValidSlot(input: Map): Int? { + for (slot in input.keys) { + if (slot !in validItemSlots && slot < 44) continue + val itemStack = input[slot] + if (itemStack?.name == " ") { + return slot + } + } + return null + } + + @SubscribeEvent + fun onHoverItem(event: LorenzToolTipEvent) { + if (!LorenzUtils.inSkyBlock) return + if (skillCalcCoolDown.passedSince() > 3.seconds) { + skillCalcCoolDown = SimpleTimeMark.now() + calcSkillLuck() + } + val limboLuck = storage?.limbo?.userLuck?.round(1) ?: 0.0f + when (event.slot.inventory.name) { + "Your Equipment and Stats" -> equipmentMenuTooltip(event, limboLuck) + "Your Stats Breakdown" -> statsBreakdownLoreTooltip(event, limboLuck) + "SkyBlock Menu" -> skyblockMenuTooltip(event, limboLuck) + } + } + + private fun equipmentMenuTooltip(event: LorenzToolTipEvent, limboLuck: Float) { + if (event.slot.slotIndex != 25) return + if (limboLuck == 0.0f && !showAllStats) return + + val skillLuck = skillOverflowLuck.values.sum() + val totalLuck = skillLuck + limboLuck + val lastIndex = event.toolTip.indexOfLast { it == "§5§o" } + if (lastIndex == -1) return + + val luckString = tryTruncateFloat(totalLuck) + event.toolTip.add(lastIndex, "$luckTooltipString$luckString") + } + + private fun statsBreakdownLoreTooltip(event: LorenzToolTipEvent, limboLuck: Float) { + if (!inMiscStats) return + if (inCustomBreakdown && event.slot.slotIndex == 48) { + event.toolTip[1] = "§7To Your Stats Breakdown" + } + if (event.slot.slotIndex != 4) return + if (limboLuck == 0.0f && !showAllStats) return + + val skillLuck = skillOverflowLuck.values.sum() + val totalLuck = skillLuck + limboLuck + val luckString = tryTruncateFloat(totalLuck) + event.toolTip.add("§5§o §a✴ SkyHanni User Luck §f$luckString") + } + + private fun skyblockMenuTooltip(event: LorenzToolTipEvent, limboLuck: Float) { + if (event.slot.slotIndex != 13) return + val lastIndex = event.toolTip.indexOfLast { it == "§5§o" } + if (lastIndex == -1) return + + val skillLuck = skillOverflowLuck.values.sum() + val totalLuck = skillLuck + limboLuck + if (totalLuck == 0f) return + + val luckString = tryTruncateFloat(totalLuck) + event.toolTip.add(lastIndex, "$luckTooltipString$luckString") + } + + private fun tryTruncateFloat(input: Float): String { + val string = input.addSeparators() + return if (string.endsWith(".0")) return string.dropLast(2) + else string + } + + @SubscribeEvent + fun onStackClick(event: GuiContainerEvent.SlotClickEvent) { + if (!inMiscStats) return + val limboUserLuck = storage?.limbo?.userLuck ?: 0.0f + if (limboUserLuck == 0.0f && !showAllStats) return + + if (inCustomBreakdown && event.slotId != 49) event.cancel() + when (event.slotId) { + replaceSlot -> { + if (inCustomBreakdown) return + event.cancel() + inCustomBreakdown = true + } + 48 -> { + if (!inCustomBreakdown) return + inCustomBreakdown = false + } + else -> return + } + } + + private fun createItems() { + fillerItem = Utils.createItemStack( + fillerID.getItemStack().item, + fillerName, + 15, + ) + + val limboLuck = storage?.limbo?.userLuck ?: 0.0f + val skillLuck = skillOverflowLuck.values.sum() + val totalLuck = skillLuck + limboLuck + + mainLuckItem = Utils.createItemStack( + mainLuckID.getItemStack().item, + "$mainLuckName §f${tryTruncateFloat(totalLuck)}", + *createItemLore("mainMenu", totalLuck), + ) + limboItem = Utils.createItemStack( + limboID.getItemStack().item, + limboName, + *createItemLore("limbo", limboLuck), + ) + skillsItem = Utils.createItemStack( + skillsID.getItemStack().item, + skillsName, + *createItemLore("skills"), + ) + } + + private fun createItemLore(type: String, luckInput: Float = 0.0f): Array { + calcSkillLuck() + return when (type) { + "mainMenu" -> { + val luckString = tryTruncateFloat(luckInput.round(2)) + if (luckInput == 0.0f) { + arrayOf( + "§7SkyHanni User Luck is the best stat.", + "", + "§7Flat: §a+$luckString✴", + "", + "§8You have none of this stat!", + "§eClick to view!", + ) + } else { + arrayOf( + "§7SkyHanni User Luck increases your", + "§7overall fortune around Hypixel SkyBlock.", + "", + "§7(Disclaimer: May not affect real drop chances)", + "", + "§eClick to view!", + ) + } + } + + "limbo" -> { + val luckString = tryTruncateFloat(luckInput.round(2)) + arrayOf( + "§8Action", + "", + "§7Value: §a+$luckString✴", + "", + "§8Gain more by going to Limbo,", + "§8and obtaining a higher Personal Best§8.", + ) + } + + "skills" -> { + val luckString = skillOverflowLuck.values.sum() + val firstHalf = arrayOf( + "§8Grouped", + "", + "§7Value: §a+$luckString✴", + "", + ) + val secondHalf = arrayOf( + "§8Stats from your overflow skills.", + "§8Obtain more each 5 overflow levels!", + ) + val sourcesList = mutableListOf() + for ((skillType, luck) in skillOverflowLuck) { + if (luck == 0) continue + sourcesList.add(" §a+$luck✴ §f${skillType.displayName} Skill") + } + val finalList = mutableListOf() + finalList.addAll(firstHalf) + if (sourcesList.isNotEmpty()) { + finalList.addAll(sourcesList) + finalList.add("") + } + finalList.addAll(secondHalf) + finalList.toTypedArray() + } + + else -> arrayOf("") + } + } + + private fun calcSkillLuck() { + val storage = ProfileStorageData.profileSpecific?.skillData ?: return + skillOverflowLuck.clear() + for ((skillType, skillInfo) in storage) { + val level = skillInfo.level + val overflow = skillInfo.overflowLevel + val luck = ((overflow - level) / 5) * 50 + skillOverflowLuck.addOrPut(skillType, luck) + } + } +} -- cgit