Skip to content

Commit

Permalink
Feature: Attribute Overlay (#2001)
Browse files Browse the repository at this point in the history
Co-authored-by: ItsEmpa <itsempa@users.noreply.github.com>
Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com>
Co-authored-by: Cal <cwolfson58@gmail.com>
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
  • Loading branch information
5 people authored Oct 22, 2024
1 parent 51dc464 commit 4e03682
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<AttributeAPI.AttributeType> 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, ItemRoll>,
)

data class ItemRoll(
@Expose val regex: String,
@Expose val list: List<List<String>>,
)
Original file line number Diff line number Diff line change
@@ -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<GoodRollItem>()

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<Pair<AttributeType, AttributeType>>)

@SubscribeEvent
fun onRepoReload(event: RepositoryReloadEvent) {
val data = event.getConstant<AttributeGoodRollsJson>("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<Attribute, Attribute>? =
getAttributes()?.takeIf { it.isNotEmpty() }?.mapNotNull { (name, level) ->
AttributeType.getByInternalNameOrNull(name.lowercase())?.let { Attribute(it, level) }
}?.toPair()

fun Pair<Attribute, Attribute>.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

}
Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 7 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/utils/CollectionUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ object CollectionUtils {

fun <T> Collection<T>.takeIfNotEmpty(): Collection<T>? = takeIf { it.isNotEmpty() }


fun <T> List<T>.toPair(): Pair<T, T>? = if (size == 2) this[0] to this[1] else null

fun <T> Pair<T, T>.equalsIgnoreOrder(other: Pair<T, T>): Boolean = toSet() == other.toSet()

fun <T> Pair<T, T>.toSet(): Set<T> = setOf(first, second)

// TODO add cache
fun MutableList<Renderable>.addString(
text: String,
Expand Down

0 comments on commit 4e03682

Please sign in to comment.