From 4e03682aaa5ef469362b4138bfb554845b75eab9 Mon Sep 17 00:00:00 2001 From: Empa <42304516+ItsEmpa@users.noreply.github.com> Date: Tue, 22 Oct 2024 20:49:24 +0200 Subject: [PATCH] Feature: Attribute Overlay (#2001) Co-authored-by: ItsEmpa Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Co-authored-by: Cal Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../inventory/AttributeOverlayConfig.java | 58 ++++++++++ .../features/inventory/InventoryConfig.java | 5 + .../repo/AttributeGoodRollsJson.kt | 13 +++ .../inventory/attribute/AttributeAPI.kt | 100 ++++++++++++++++++ .../inventory/attribute/AttributeOverlay.kt | 60 +++++++++++ .../skyhanni/utils/CollectionUtils.kt | 7 ++ 6 files changed, 243 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/inventory/AttributeOverlayConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/AttributeGoodRollsJson.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/attribute/AttributeAPI.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/attribute/AttributeOverlay.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/AttributeOverlayConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/AttributeOverlayConfig.java new file mode 100644 index 000000000000..2761f7461e8b --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/AttributeOverlayConfig.java @@ -0,0 +1,58 @@ +package at.hannibal2.skyhanni.config.features.inventory; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.features.inventory.attribute.AttributeAPI; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +import java.util.ArrayList; +import java.util.List; + +public class AttributeOverlayConfig { + + @Expose + @ConfigOption(name = "Enabled", desc = "Show the attribute name and level on the item.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + // TODO: add way of making config options with data classes from repo + @Expose + @ConfigOption(name = "Attributes Shown", desc = "List of attributes shown.") + @ConfigEditorDraggableList + public List attributesList = new ArrayList<>(AttributeAPI.AttributeType.getEntries()); + + @Expose + @ConfigOption( + name = "Min Level", + desc = "Minimum level to show the attributes of." + ) + @ConfigEditorSlider(minValue = 1, maxValue = 10, minStep = 1) + public int minimumLevel = 1; + + @Expose + @ConfigOption( + name = "Highlight Good Rolls", + desc = "Highlights Good attribute combinations.\n" + + "§cNote: These are subjective and ever changing. If you\n" + + "§c want to suggest changes, please do so in the discord." + ) + @ConfigEditorBoolean + public boolean highlightGoodRolls = true; + + @Expose + @ConfigOption( + name = "Good Rolls Override Level", + desc = "Makes it so that Good Rolls are always shown no matter the attribute level." + ) + @ConfigEditorBoolean + public boolean goodRollsOverrideLevel = true; + + @Expose + @ConfigOption(name = "Hide non Good Rolls", desc = "Hides attributes that are not considered good rolls.") + @ConfigEditorBoolean + public boolean hideNonGoodRolls = false; +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java index c0a51a979048..608fac71ccda 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java @@ -139,6 +139,11 @@ public class InventoryConfig { @Accordion public MagicalPowerConfig magicalPower = new MagicalPowerConfig(); + @Expose + @ConfigOption(name = "Attribute Overlay", desc = "") + @Accordion + public AttributeOverlayConfig attributeOverlay = new AttributeOverlayConfig(); + @Expose @ConfigOption(name = "Item Number", desc = "Showing the item number as a stack size for these items.") @ConfigEditorDraggableList diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/AttributeGoodRollsJson.kt b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/AttributeGoodRollsJson.kt new file mode 100644 index 000000000000..0e16db883b36 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/AttributeGoodRollsJson.kt @@ -0,0 +1,13 @@ +package at.hannibal2.skyhanni.data.jsonobjects.repo + +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName + +data class AttributeGoodRollsJson( + @Expose @SerializedName("good_rolls") val goodRolls: Map, +) + +data class ItemRoll( + @Expose val regex: String, + @Expose val list: List>, +) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/attribute/AttributeAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/attribute/AttributeAPI.kt new file mode 100644 index 000000000000..f1711f2561b8 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/attribute/AttributeAPI.kt @@ -0,0 +1,100 @@ +package at.hannibal2.skyhanni.features.inventory.attribute + +import at.hannibal2.skyhanni.data.jsonobjects.repo.AttributeGoodRollsJson +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.CollectionUtils.equalsIgnoreOrder +import at.hannibal2.skyhanni.utils.CollectionUtils.toPair +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getAttributes +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +@SkyHanniModule +object AttributeAPI { + + private var goodRolls = listOf() + + enum class AttributeType(val displayName: String, val internalName: String, val shortName: String) { + ARACHNO("Arachno", "arachno", "AR"), + ATTACK_SPEED("Attack Speed", "attack_speed", "AS"), + BLAZING("Blazing", "blazing", "BL"), + COMBO("Combo", "combo", "CO"), + ELITE("Elite", "elite", "EL"), + ENDER("Ender", "ender", "EN"), + IGNITION("Ignition", "ignition", "IG"), + LIFE_RECOVERY("Life Recovery", "life_recovery", "LR"), + MANA_STEAL("Mana Steal", "mana_steal", "MS"), + MIDAS_TOUCH("Midas Touch", "midas_touch", "MT"), + UNDEAD("Undead", "undead", "UN"), + WARRIOR("Warrior", "warrior", "WA"), + DEADEYE("Deadeye", "deadeye", "DE"), + ARACHNO_RESISTANCE("Arachno Resistance", "arachno_resistance", "AR"), + BLAZING_RESISTANCE("Blazing Resistance", "blazing_resistance", "BR"), + BREEZE("Breeze", "breeze", "BZ"), + DOMINANCE("Dominance", "dominance", "DO"), + ENDER_RESISTANCE("Ender Resistance", "ender_resistance", "ER"), + EXPERIENCE("Experience", "experience", "EX"), + FORTITUDE("Fortitude", "fortitude", "FO"), + LIFE_REGENERATION("Life Regeneration", "life_regeneration", "LR"), + LIFELINE("Lifeline", "lifeline", "LL"), + MAGIC_FIND("Magic Find", "magic_find", "MF"), + MANA_POOL("Mana Pool", "mana_pool", "MP"), + MANA_REGENERATION("Mana Regeneration", "mana_regeneration", "MR"), + VITALITY("Vitality", "mending", "VI"), + SPEED("Speed", "speed", "SP"), + UNDEAD_RESISTANCE("Undead Resistance", "undead_resistance", "UR"), + VETERAN("Veteran", "veteran", "VE"), + BLAZING_FORTUNE("Blazing Fortune", "blazing_fortune", "BF"), + FISHING_EXPERIENCE("Fishing Experience", "fishing_experience", "FE"), + INFECTION("Infection", "infection", "IN"), + DOUBLE_HOOK("Double Hook", "double_hook", "DH"), + FISHERMAN("Fisherman", "fisherman", "FH"), + FISHING_SPEED("Fishing Speed", "fishing_speed", "FS"), + HUNTER("Hunter", "hunter", "HU"), + TROPHY_HUNTER("Trophy Hunter", "trophy_hunter", "TH"), + UNKNOWN("Unknown", "unknown", "??"), + ; + + override fun toString() = "§b$displayName" + + companion object { + + fun getByInternalNameOrNull(internalName: String) = entries.find { it.internalName == internalName } + + fun getByInternalName(internalName: String) = getByInternalNameOrNull(internalName) ?: UNKNOWN + } + } + + data class Attribute(val type: AttributeType, val level: Int) + + private data class GoodRollItem(val regex: Pattern, val attributes: List>) + + @SubscribeEvent + fun onRepoReload(event: RepositoryReloadEvent) { + val data = event.getConstant("AttributeGoodRolls") + goodRolls = data.goodRolls.values.map { + val regex = it.regex.toPattern() + val list = it.list.map { combo -> + val first = AttributeType.getByInternalName(combo[0]) + val second = AttributeType.getByInternalName(combo[1]) + first to second + } + GoodRollItem(regex, list) + } + } + + fun ItemStack.getAttributesLevels(): Pair? = + getAttributes()?.takeIf { it.isNotEmpty() }?.mapNotNull { (name, level) -> + AttributeType.getByInternalNameOrNull(name.lowercase())?.let { Attribute(it, level) } + }?.toPair() + + fun Pair.isGoodRoll(internalName: NEUInternalName): Boolean = + goodRolls.firstOrNull { it.regex.matches(internalName.asString()) }?.let { goodRoll -> + val attributes = first.type to second.type + goodRoll.attributes.any { it.equalsIgnoreOrder(attributes) } + } ?: false + +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/attribute/AttributeOverlay.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/attribute/AttributeOverlay.kt new file mode 100644 index 000000000000..2787c98d14c5 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/attribute/AttributeOverlay.kt @@ -0,0 +1,60 @@ +package at.hannibal2.skyhanni.features.inventory.attribute + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.GuiRenderItemEvent +import at.hannibal2.skyhanni.features.inventory.attribute.AttributeAPI.getAttributesLevels +import at.hannibal2.skyhanni.features.inventory.attribute.AttributeAPI.isGoodRoll +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RenderUtils.drawSlotText +import at.hannibal2.skyhanni.utils.StringUtils.width +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object AttributeOverlay { + + private const val SCALE = 0.5714286f + + private val config get() = SkyHanniMod.feature.inventory.attributeOverlay + + @SubscribeEvent + fun onRenderItemOverlayPost(event: GuiRenderItemEvent.RenderOverlayEvent.GuiRenderItemPost) { + if (!isEnabled()) return + + val stack = event.stack ?: return + val attributes = stack.getAttributesLevels() ?: return + val internalName = stack.getInternalNameOrNull() ?: return + + val isGoodRoll = attributes.isGoodRoll(internalName) + attributes.toList().filter { (attr, level) -> + val inConfig = attr in config.attributesList + val isLevel = level >= config.minimumLevel + val goodRoll = !config.hideNonGoodRolls || isGoodRoll + inConfig && goodRoll && (!config.goodRollsOverrideLevel || isLevel) + }.forEachIndexed { index, attribute -> + event.drawAttribute(attribute, isGoodRoll && config.highlightGoodRolls, index) + } + } + + private fun GuiRenderItemEvent.RenderOverlayEvent.GuiRenderItemPost.drawAttribute( + attribute: AttributeAPI.Attribute, + goodRoll: Boolean, + index: Int, + ) { + val color = if (goodRoll) "§e" else "§b" + val attributeString = color + attribute.type.shortName + val attributeWidth = attributeString.width() + val attributeX = x + attributeWidth + (if (index == 1) 16 - attributeWidth * SCALE else 0).toInt() + val attributeY = y + drawSlotText(attributeX, attributeY, attributeString, SCALE) + + val levelString = "§a${attribute.level}" + val levelWidth = levelString.width() + val levelX = x + levelWidth + (if (index == 1) 16 - levelWidth * SCALE else 0).toInt() + val levelY = y + (10 * SCALE).toInt() + drawSlotText(levelX, levelY, levelString, SCALE) + } + + private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt index 860a6a3d6103..fd3cadd4baa4 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt @@ -273,6 +273,13 @@ object CollectionUtils { fun Collection.takeIfNotEmpty(): Collection? = takeIf { it.isNotEmpty() } + + fun List.toPair(): Pair? = if (size == 2) this[0] to this[1] else null + + fun Pair.equalsIgnoreOrder(other: Pair): Boolean = toSet() == other.toSet() + + fun Pair.toSet(): Set = setOf(first, second) + // TODO add cache fun MutableList.addString( text: String,