summaryrefslogtreecommitdiff
path: root/src/main/java/at
diff options
context:
space:
mode:
authorThunderblade73 <85900443+Thunderblade73@users.noreply.github.com>2024-06-23 20:06:17 +0200
committerGitHub <noreply@github.com>2024-06-23 20:06:17 +0200
commit6eb085f4e2ba69e98934ffa9489c8679ba23d244 (patch)
tree6f3f4abe26118a5c14ed84ce48408b17665be79f /src/main/java/at
parent78f5f7b94bdc48c3395ef17f30022b8cd653a776 (diff)
downloadskyhanni-6eb085f4e2ba69e98934ffa9489c8679ba23d244.tar.gz
skyhanni-6eb085f4e2ba69e98934ffa9489c8679ba23d244.tar.bz2
skyhanni-6eb085f4e2ba69e98934ffa9489c8679ba23d244.zip
Feature: Reforge helper (#1437)
Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Co-authored-by: hannibal2 <24389977+hannibal002@users.noreply.github.com> Co-authored-by: Cal <cwolfson58@gmail.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt198
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/ReforgeHelperConfig.java36
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/neu/NeuReforgeJson.kt (renamed from src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/neu/NeuReforgeStoneJson.kt)28
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt80
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt7
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/ReforgeHelper.kt432
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValue.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt13
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt70
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt29
12 files changed, 843 insertions, 63 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt
new file mode 100644
index 000000000..70f6910c9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt
@@ -0,0 +1,198 @@
+package at.hannibal2.skyhanni.api
+
+import at.hannibal2.skyhanni.data.jsonobjects.repo.neu.NeuReforgeJson
+import at.hannibal2.skyhanni.data.model.SkyblockStat
+import at.hannibal2.skyhanni.data.model.SkyblockStatList
+import at.hannibal2.skyhanni.events.NeuRepositoryReloadEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ItemCategory
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
+import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull
+import at.hannibal2.skyhanni.utils.ItemUtils.itemNameWithoutColor
+import at.hannibal2.skyhanni.utils.LorenzRarity
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NEUInternalName
+import at.hannibal2.skyhanni.utils.json.BaseGsonBuilder
+import at.hannibal2.skyhanni.utils.json.SkyHanniTypeAdapters
+import com.google.gson.Gson
+import com.google.gson.TypeAdapter
+import com.google.gson.stream.JsonReader
+import com.google.gson.stream.JsonWriter
+import net.minecraft.item.ItemStack
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+@SkyHanniModule
+object ReforgeAPI {
+ var reforgeList: List<Reforge> = emptyList()
+ private set(value) {
+ field = value
+ nonePowerStoneReforge = value.filterNot { it.isReforgeStone }
+ onlyPowerStoneReforge = value.filter { it.isReforgeStone }
+ }
+
+ var nonePowerStoneReforge: List<Reforge> = emptyList()
+ private set
+
+ var onlyPowerStoneReforge: List<Reforge> = emptyList()
+ private set
+
+ enum class ReforgeType {
+ SWORD,
+ BOW,
+ ARMOR,
+ CHESTPLATE,
+ HELMET,
+ CLOAK,
+ AXE,
+ HOE,
+ AXE_AND_HOE,
+ PICKAXE,
+ EQUIPMENT,
+ ROD,
+ SWORD_AND_ROD,
+ SPECIAL_ITEMS,
+ VACUUM
+ }
+
+ class Reforge(
+ val name: String,
+ val type: ReforgeType,
+ val stats: Map<LorenzRarity, SkyblockStatList>,
+ val reforgeStone: NEUInternalName? = null,
+ val specialItems: List<NEUInternalName>? = null,
+ val extraProperty: Map<LorenzRarity, String> = emptyMap(),
+ val costs: Map<LorenzRarity, Long>? = null,
+ ) {
+
+ val isReforgeStone = reforgeStone != null
+
+ val rawReforgeStoneName = reforgeStone?.itemNameWithoutColor
+
+ val lowercaseName = name.lowercase()
+
+ fun isValid(itemStack: ItemStack) = isValid(itemStack.getItemCategoryOrNull(), itemStack.getInternalName())
+
+ fun isValid(itemCategory: ItemCategory?, internalName: NEUInternalName) = when (type) {
+ ReforgeType.SWORD -> setOf(
+ ItemCategory.SWORD,
+ ItemCategory.GAUNTLET,
+ ItemCategory.LONGSWORD,
+ ItemCategory.FISHING_WEAPON,
+ ).contains(itemCategory)
+
+ ReforgeType.BOW -> itemCategory == ItemCategory.BOW || itemCategory == ItemCategory.SHORT_BOW
+ ReforgeType.ARMOR -> setOf(
+ ItemCategory.HELMET,
+ ItemCategory.CHESTPLATE,
+ ItemCategory.LEGGINGS,
+ ItemCategory.BOOTS,
+ ).contains(itemCategory)
+
+ ReforgeType.CHESTPLATE -> itemCategory == ItemCategory.CHESTPLATE
+ ReforgeType.HELMET -> itemCategory == ItemCategory.HELMET
+ ReforgeType.CLOAK -> itemCategory == ItemCategory.CLOAK
+ ReforgeType.AXE -> itemCategory == ItemCategory.AXE
+ ReforgeType.HOE -> itemCategory == ItemCategory.HOE
+ ReforgeType.AXE_AND_HOE -> itemCategory == ItemCategory.HOE || itemCategory == ItemCategory.AXE
+ ReforgeType.PICKAXE ->
+ itemCategory == ItemCategory.PICKAXE ||
+ itemCategory == ItemCategory.DRILL ||
+ itemCategory == ItemCategory.GAUNTLET
+
+ ReforgeType.EQUIPMENT -> setOf(
+ ItemCategory.CLOAK,
+ ItemCategory.BELT,
+ ItemCategory.NECKLACE,
+ ItemCategory.BRACELET,
+ ItemCategory.GLOVES,
+ ).contains(itemCategory)
+
+ ReforgeType.ROD -> itemCategory == ItemCategory.FISHING_ROD || itemCategory == ItemCategory.FISHING_WEAPON
+ ReforgeType.SWORD_AND_ROD -> setOf(
+ ItemCategory.SWORD,
+ ItemCategory.GAUNTLET,
+ ItemCategory.LONGSWORD,
+ ItemCategory.FISHING_ROD,
+ ItemCategory.FISHING_WEAPON,
+ ).contains(itemCategory)
+
+ ReforgeType.VACUUM -> itemCategory == ItemCategory.VACUUM
+ ReforgeType.SPECIAL_ITEMS -> specialItems?.contains(internalName) ?: false
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Reforge
+
+ if (name != other.name) return false
+ if (type != other.type) return false
+ if (stats != other.stats) return false
+ if (reforgeStone != other.reforgeStone) return false
+ if (specialItems != other.specialItems) return false
+ if (extraProperty != other.extraProperty) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = name.hashCode()
+ result = 31 * result + type.hashCode()
+ result = 31 * result + stats.hashCode()
+ result = 31 * result + (reforgeStone?.hashCode() ?: 0)
+ result = 31 * result + (specialItems?.hashCode() ?: 0)
+ result = 31 * result + extraProperty.hashCode()
+ return result
+ }
+
+ override fun toString(): String = "Reforge $name"
+ }
+
+ @SubscribeEvent
+ fun onNeuRepoReload(event: NeuRepositoryReloadEvent) {
+ val reforgeStoneData = event.readConstant<Map<String, NeuReforgeJson>>("reforgestones", reforgeGson).values
+ val reforgeData = event.readConstant<Map<String, NeuReforgeJson>>("reforges", reforgeGson).values
+ reforgeList = (reforgeStoneData + reforgeData).map(::mapReforge)
+ }
+
+ private val reforgeGson: Gson = BaseGsonBuilder.gson()
+ .registerTypeAdapter(SkyblockStat::class.java, SkyHanniTypeAdapters.SKYBLOCK_STAT.nullSafe())
+ .registerTypeAdapter(
+ SkyblockStatList::class.java,
+ object : TypeAdapter<SkyblockStatList>() {
+ override fun write(out: JsonWriter, value: SkyblockStatList) {
+ out.beginObject()
+ value.entries.forEach {
+ out.name(it.key.name.lowercase()).value(it.value)
+ }
+ out.endObject()
+ }
+
+ override fun read(reader: JsonReader): SkyblockStatList {
+ reader.beginObject()
+ val list = SkyblockStatList()
+ while (reader.hasNext()) {
+ val name = reader.nextName()
+ val value = reader.nextDouble()
+ list[SkyblockStat.valueOf(name.uppercase())] = value
+ }
+ reader.endObject()
+ return list
+ }
+ },
+ ).create()
+
+ private fun mapReforge(it: NeuReforgeJson): Reforge {
+ val type = it.itemType
+ return Reforge(
+ name = it.reforgeName,
+ type = LorenzUtils.enumValueOf<ReforgeType>(type.first),
+ stats = it.reforgeStats ?: emptyMap(),
+ reforgeStone = it.internalName,
+ specialItems = type.second.takeIf { it.isNotEmpty() },
+ extraProperty = it.reforgeAbility,
+ costs = it.reforgeCosts,
+ )
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java
index 990399d62..f6818d2c8 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java
@@ -53,6 +53,11 @@ public class HelperConfig {
public TiaRelayConfig tiaRelay = new TiaRelayConfig();
@Expose
+ @ConfigOption(name = "Reforge Helper", desc = "")
+ @Accordion
+ public ReforgeHelperConfig reforge = new ReforgeHelperConfig();
+
+ @Expose
@ConfigOption(name = "Enchanting", desc = "")
@Accordion
public EnchantingConfig enchanting = new EnchantingConfig();
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/ReforgeHelperConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/ReforgeHelperConfig.java
new file mode 100644
index 000000000..43bba7f8c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/ReforgeHelperConfig.java
@@ -0,0 +1,36 @@
+package at.hannibal2.skyhanni.config.features.inventory.helper;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.config.core.config.Position;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class ReforgeHelperConfig {
+
+ @Expose
+ @ConfigLink(owner = ReforgeHelperConfig.class, field = "enabled")
+ public Position position = new Position(80, 85, true, true);
+
+ @Expose
+ @ConfigOption(name = "Enable", desc = "Enables the reforge helper.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(name = "Stones Hex Only", desc = "Displays reforge stones only when in Hex.")
+ @ConfigEditorBoolean
+ public boolean reforgeStonesOnlyHex = true;
+
+ @Expose
+ @ConfigOption(name = "Show Diff", desc = "Shows the difference of the new reforge to the current one in the slecetion list.")
+ @ConfigEditorBoolean
+ public boolean showDiff = false;
+
+ @Expose
+ @ConfigOption(name = "Hide chat", desc = "Hides the vanilla chat messages from reforging.")
+ @ConfigEditorBoolean
+ public boolean hideChat = false;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/neu/NeuReforgeStoneJson.kt b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/neu/NeuReforgeJson.kt
index 8ebe2dea6..a8c8f3fe4 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/neu/NeuReforgeStoneJson.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/neu/NeuReforgeJson.kt
@@ -1,21 +1,26 @@
package at.hannibal2.skyhanni.data.jsonobjects.repo.neu
+import at.hannibal2.skyhanni.data.model.SkyblockStatList
import at.hannibal2.skyhanni.utils.LorenzRarity
import at.hannibal2.skyhanni.utils.NEUInternalName
+import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName
+import at.hannibal2.skyhanni.utils.NEUItems
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
+import net.minecraft.item.Item
-data class NeuReforgeStoneJson(
- @Expose val internalName: NEUInternalName,
+data class NeuReforgeJson(
+ @Expose val internalName: NEUInternalName?,
@Expose val reforgeName: String,
@Expose @SerializedName("itemTypes") val rawItemTypes: Any,
@Expose val requiredRarities: List<LorenzRarity>,
- @Expose val reforgeCosts: Map<LorenzRarity, Long>,
- @Expose val reforgeStats: Map<LorenzRarity, Map<String, Double>>,
+ @Expose val reforgeCosts: Map<LorenzRarity, Long>?,
+ @Expose val reforgeStats: Map<LorenzRarity, SkyblockStatList>?,
@Expose @SerializedName("reforgeAbility") val rawReforgeAbility: Any?,
) {
private lateinit var reforgeAbilityField: Map<LorenzRarity, String>
+ private lateinit var itemTypeField: Pair<String, List<NEUInternalName>>
val reforgeAbility
get() = if (this::reforgeAbilityField.isInitialized) reforgeAbilityField
@@ -27,7 +32,7 @@ data class NeuReforgeStoneJson(
is Map<*, *> -> (this.rawReforgeAbility as? Map<String, String>)?.mapKeys {
LorenzRarity.valueOf(
- it.key.uppercase().replace(" ", "_")
+ it.key.uppercase().replace(" ", "_"),
)
} ?: emptyMap()
@@ -36,28 +41,27 @@ data class NeuReforgeStoneJson(
reforgeAbilityField
}
- /* used in ReforgeAPI which isn't in beta yet
- val itemType: Pair<String, List<NEUInternalName>> by lazy {
+ val itemType: Pair<String, List<NEUInternalName>>
+ get() = if (this::itemTypeField.isInitialized) itemTypeField
+ else run {
val any = this.rawItemTypes
- return@lazy when (any) {
+ return when (any) {
is String -> {
any.replace("/", "_AND_").uppercase() to emptyList()
}
is Map<*, *> -> {
val type = "SPECIAL_ITEMS"
- val map = any as? Map<String, List<String>> ?: return@lazy type to emptyList()
+ val map = any as? Map<String, List<String>> ?: return type to emptyList()
val internalNames = map["internalName"]?.map { it.asInternalName() } ?: emptyList()
val itemType = map["itemid"]?.map {
NEUItems.getInternalNamesForItemId(Item.getByNameOrId(it))
- }?.flatten()
- ?: emptyList()
+ }?.flatten() ?: emptyList()
type to (internalNames + itemType)
}
else -> throw IllegalStateException()
}
}
-*/
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt b/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt
new file mode 100644
index 000000000..161ec76f5
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt
@@ -0,0 +1,80 @@
+package at.hannibal2.skyhanni.data.model
+
+import at.hannibal2.skyhanni.utils.StringUtils.allLettersFirstUppercase
+import net.minecraft.client.Minecraft
+import java.util.EnumMap
+
+enum class SkyblockStat(val icon: String) {
+ DAMAGE("§c❁"),
+ HEALTH("§c❤"),
+ DEFENSE("§a❈"),
+ STRENGTH("§c❁"),
+ INTELLIGENCE("§b✎"),
+ CRIT_DAMAGE("§9☠"),
+ CRIT_CHANCE("§9☣"),
+ FEROCITY("§c⫽"),
+ BONUS_ATTACK_SPEED("§e⚔"),
+ ABILITY_DAMAGE("§c๑"),
+ HEALTH_REGEN("§c❣"),
+ VITALITY("§4♨"),
+ MENDING("§a☄"),
+ TRUE_DEFENCE("§7❂"),
+ SWING_RANGE("§eⓈ"),
+ SPEED("§f✦"),
+ SEA_CREATURE_CHANCE("§3α"),
+ MAGIC_FIND("§b✯"),
+ PET_LUCK("§d♣"),
+ FISHING_SPEED("§b☂"),
+ BONUS_PEST_CHANCE("§2ൠ"),
+ COMBAT_WISDOM("§3☯"),
+ MINING_WISDOM("§3☯"),
+ FARMING_WISDOM("§3☯"),
+ FORAGING_WISDOM("§3☯"),
+ FISHING_WISDOM("§3☯"),
+ ENCHANTING_WISDOM("§3☯"),
+ ALCHEMY_WISDOM("§3☯"),
+ CARPENTRY_WISDOM("§3☯"),
+ RUNECRAFTING_WISDOM("§3☯"),
+ SOCIAL_WISDOM("§3☯"),
+ TAMING_WISDOM("§3☯"),
+ MINING_SPEED("§6⸕"),
+ BREAKING_POWER("§2Ⓟ"),
+ PRISTINE("§5✧"),
+ FORAGING_FORTUNE("§☘"),
+ FARMING_FORTUNE("§6☘"),
+ MINING_FORTUNE("§6☘"),
+ FEAR("§a☠")
+ ;
+
+ val capitalizedName = name.lowercase().allLettersFirstUppercase()
+
+ val iconWithName = "$icon $capitalizedName"
+
+ fun asString(value: Int) = (if (value > 0) "+" else "") + value.toString() + " " + this.icon
+
+ companion object {
+ val fontSizeOfLargestIcon by lazy {
+ entries.maxOf { Minecraft.getMinecraft().fontRendererObj.getStringWidth(it.icon) } + 1
+ }
+ }
+}
+
+class SkyblockStatList : EnumMap<SkyblockStat, Double>(SkyblockStat::class.java), Map<SkyblockStat, Double> {
+ operator fun minus(other: SkyblockStatList): SkyblockStatList {
+ return SkyblockStatList().apply {
+ val keys = this.keys + other.keys
+ for (key in keys) {
+ this[key] = (this@SkyblockStatList[key] ?: 0.0) - (other[key] ?: 0.0)
+ }
+ }
+ }
+
+ companion object {
+ fun mapOf(vararg list: Pair<SkyblockStat, Double>) = SkyblockStatList().apply {
+ for ((key, value) in list) {
+ this[key] = value
+ }
+ }
+ }
+}
+
diff --git a/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt
index a02cb308f..6f7df5964 100644
--- a/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt
+++ b/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt
@@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.config.ConfigManager
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.NEUItems.manager
import at.hannibal2.skyhanni.utils.json.fromJson
+import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import java.io.File
@@ -13,14 +14,14 @@ class NeuRepositoryReloadEvent : LorenzEvent() {
return manager.getJsonFromFile(File(manager.repoLocation, "constants/$file.json"))
}
- inline fun <reified T : Any> readConstant(file: String): T {
+ inline fun <reified T : Any> readConstant(file: String, gson: Gson = ConfigManager.gson): T {
val data = getConstant(file) ?: ErrorManager.skyHanniError("$file failed to load from neu repo!")
return try {
- ConfigManager.gson.fromJson<T>(data)
+ gson.fromJson<T>(data)
} catch (e: JsonSyntaxException) {
ErrorManager.logErrorWithData(
e, "$file failed to read from neu repo!",
- "data" to data
+ "data" to data,
)
throw e
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/ReforgeHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/ReforgeHelper.kt
new file mode 100644
index 000000000..d1826b481
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/ReforgeHelper.kt
@@ -0,0 +1,432 @@
+package at.hannibal2.skyhanni.features.inventory
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.api.ReforgeAPI
+import at.hannibal2.skyhanni.data.model.SkyblockStat
+import at.hannibal2.skyhanni.data.model.SkyblockStatList
+import at.hannibal2.skyhanni.events.GuiContainerEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.InventoryCloseEvent
+import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.DelayedRun
+import at.hannibal2.skyhanni.utils.ItemUtils.cleanName
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
+import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull
+import at.hannibal2.skyhanni.utils.ItemUtils.getItemRarityOrNull
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LorenzColor
+import at.hannibal2.skyhanni.utils.LorenzRarity
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil.toStringWithPlus
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.RenderUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.drawSlotText
+import at.hannibal2.skyhanni.utils.RenderUtils.highlight
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables
+import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getReforgeName
+import at.hannibal2.skyhanni.utils.SoundUtils
+import at.hannibal2.skyhanni.utils.TimeUtils.ticks
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraft.client.Minecraft
+import net.minecraft.init.Items
+import net.minecraft.inventory.Container
+import net.minecraft.item.ItemStack
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.awt.Color
+import java.util.concurrent.atomic.AtomicBoolean
+import at.hannibal2.skyhanni.utils.renderables.Renderable.Companion.string as renderableString
+
+@SkyHanniModule
+object ReforgeHelper {
+
+ private val config get() = SkyHanniMod.feature.inventory.helper.reforge
+
+ private val repoGroup = RepoPattern.group("reforge")
+
+ private val reforgeMenu by repoGroup.pattern(
+ "menu.blacksmith",
+ "Reforge Item",
+ )
+ private val reforgeHexMenu by repoGroup.pattern(
+ "menu.hex",
+ "The Hex ➜ Reforges",
+ )
+ private val reforgeChatMessage by repoGroup.pattern(
+ "chat.success",
+ "§aYou reforged your .* §r§ainto a .*!|§aYou applied a .* §r§ato your .*!",
+ )
+ private val reforgeChatFail by repoGroup.pattern(
+ "chat.fail",
+ "§cWait a moment before reforging again!|§cWhoa! Slow down there!",
+ )
+
+ private var isInReforgeMenu = false
+ private var isInHexReforgeMenu = false
+
+ private fun isReforgeMenu(chestName: String) = reforgeMenu.matches(chestName)
+ private fun isHexReforgeMenu(chestName: String) = reforgeHexMenu.matches(chestName)
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled && isInReforgeMenu
+
+ private var itemToReforge: ItemStack? = null
+ private var inventoryContainer: Container? = null
+
+ private var currentReforge: ReforgeAPI.Reforge? = null
+ private var reforgeToSearch: ReforgeAPI.Reforge? = null
+
+ private var hoveredReforge: ReforgeAPI.Reforge? = null
+
+ private val reforgeItem get() = if (isInHexReforgeMenu) 19 else 13
+ private val reforgeButton get() = if (isInHexReforgeMenu) 48 else 22
+
+ private const val HEX_REFORGE_NEXT_DOWN_BUTTON = 35
+ private const val HEX_REFORGE_NEXT_UP_BUTTON = 17
+
+ private const val EXIT_BUTTON = 40
+
+ private var waitForChat = AtomicBoolean(false)
+
+ /** Gatekeeps instant double switches of the state */
+ private var waitDelay = false
+
+ private var sortAfter: SkyblockStat? = null
+
+ private var display: List<Renderable> = generateDisplay()
+
+ private val hoverColor = LorenzColor.GOLD.addOpacity(50)
+ private val selectedColor = LorenzColor.BLUE.addOpacity(100)
+ private val finishedColor = LorenzColor.GREEN.addOpacity(75)
+
+ private fun itemUpdate() {
+ val newItem = inventoryContainer?.getSlot(reforgeItem)?.stack
+ if (newItem?.getInternalName() != itemToReforge?.getInternalName()) {
+ reforgeToSearch = null
+ }
+ itemToReforge = newItem
+ val newReforgeName = itemToReforge?.getReforgeName() ?: ""
+ if (newReforgeName == currentReforge?.lowercaseName) return
+ currentReforge = ReforgeAPI.reforgeList.firstOrNull { it.lowercaseName == newReforgeName }
+ updateDisplay()
+ }
+
+ @SubscribeEvent
+ fun onClick(event: GuiContainerEvent.SlotClickEvent) {
+ if (!isEnabled()) return
+ if (event.slot?.slotNumber == reforgeButton) {
+ if (event.slot.stack?.name == "§eReforge Item" || event.slot.stack?.name == "§cError!") return
+ if (handleReforgeButtonClick(event)) return
+ }
+
+ DelayedRun.runNextTick {
+ itemUpdate()
+ }
+ }
+
+ private fun handleReforgeButtonClick(event: GuiContainerEvent.SlotClickEvent): Boolean {
+ if (currentReforge == reforgeToSearch) {
+ event.cancel()
+ waitForChat.set(false)
+ SoundUtils.playBeepSound()
+ } else if (waitForChat.get()) {
+ waitDelay = true
+ event.cancel()
+ } else {
+ if (event.clickedButton == 2) return true
+ if (waitDelay) {
+ waitDelay = false
+ } else {
+ waitForChat.set(true)
+ }
+ }
+ return false
+ }
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!isEnabled()) return
+ when {
+ reforgeChatMessage.matches(event.message) -> {
+ DelayedRun.runDelayed(2.ticks) {
+ itemUpdate()
+ waitForChat.set(false)
+ }
+ if (config?.hideChat == true) {
+ event.blockedReason = "reforge_hide"
+ }
+ }
+
+ reforgeChatFail.matches(event.message) -> {
+ DelayedRun.runDelayed(2.ticks) {
+ waitForChat.set(false)
+ }
+ if (config?.hideChat == true) {
+ event.blockedReason = "reforge_hide"
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onOpen(event: InventoryFullyOpenedEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+ when {
+ isHexReforgeMenu(event.inventoryName) -> {
+ isInHexReforgeMenu = true
+ DelayedRun.runDelayed(2.ticks) {
+ itemUpdate() // update since an item must already be in place
+ }
+ }
+
+ isReforgeMenu(event.inventoryName) -> {
+ itemToReforge = null
+ currentReforge = null
+ }
+
+ else -> return
+ }
+ isInReforgeMenu = true
+ waitForChat.set(false)
+ DelayedRun.runNextTick {
+ inventoryContainer = Minecraft.getMinecraft().thePlayer.openContainer
+ }
+ }
+
+ @SubscribeEvent
+ fun onClose(event: InventoryCloseEvent) {
+ if (!isInReforgeMenu) return
+ isInReforgeMenu = false
+ isInHexReforgeMenu = false
+ reforgeToSearch = null
+ currentReforge = null
+ hoveredReforge = null
+ sortAfter = null
+ itemToReforge = null
+ display = emptyList()
+ }
+
+ private fun updateDisplay() {
+ display = generateDisplay()
+ }
+
+ private fun generateDisplay() = buildList<Renderable> {
+ this.add(renderableString("§6Reforge Overlay"))
+
+ val item = itemToReforge ?: run {
+ reforgeToSearch = null
+ return@buildList
+ }
+
+ val internalName = item.getInternalName()
+ val itemType = item.getItemCategoryOrNull()
+ val itemRarity = item.getItemRarityOrNull() ?: return@buildList
+
+ val rawReforgeList =
+ if (!isInHexReforgeMenu && config.reforgeStonesOnlyHex) ReforgeAPI.nonePowerStoneReforge else ReforgeAPI.reforgeList
+ val reforgeList = rawReforgeList.filter { it.isValid(itemType, internalName) }
+
+ val statTypes = reforgeList.mapNotNull { it.stats[itemRarity]?.keys }.flatten().toSet()
+
+ val statTypeButtons = (listOf(getStatButton(null)) + statTypes.map { getStatButton(it) }).chunked(9)
+ this.add(Renderable.table(statTypeButtons, xPadding = 3, yPadding = 2))
+
+ val list = reforgeList.sortedWith(getSortSelector(itemRarity, sortAfter)).map(getReforgeView(itemRarity))
+ this.addAll(list)
+ }
+
+ private fun getReforgeColour(reforge: ReforgeAPI.Reforge) = when {
+ currentReforge == reforge -> "§6"
+ reforgeToSearch == reforge -> "§3"
+ reforge.isReforgeStone -> "§9"
+ else -> "§7"
+ }
+
+ private fun getReforgeView(itemRarity: LorenzRarity): (ReforgeAPI.Reforge) -> Renderable = { reforge ->
+ val text = getReforgeColour(reforge) + reforge.name
+ val tips = getReforgeTips(reforge, itemRarity)
+ val onHover = if (!isInHexReforgeMenu) {
+ {}
+ } else {
+ { hoveredReforge = reforge }
+ }
+
+ Renderable.clickAndHover(
+ text, tips,
+ onClick = {
+ SoundUtils.playClickSound()
+ reforgeToSearch = reforge
+ updateDisplay()
+ },
+ onHover = onHover,
+ )
+ }
+
+ private fun getReforgeTips(
+ reforge: ReforgeAPI.Reforge,
+ itemRarity: LorenzRarity,
+ ): List<Renderable> {
+ val stats: List<Renderable>
+ val removedEffect: List<Renderable>
+ val addEffectText: String
+ val click: List<Renderable>
+ if (currentReforge == reforge) {
+ stats = currentReforge?.stats?.get(itemRarity)?.print() ?: emptyList()
+ removedEffect = emptyList()
+ addEffectText = "§aEffect:"
+ click = listOf(renderableString(""), renderableString("§3Reforge is currently applied!"))
+ } else {
+ stats = reforge.stats[itemRarity]?.print(currentReforge?.stats?.get(itemRarity)) ?: emptyList()
+ removedEffect = getReforgeEffect(
+ currentReforge,
+ itemRarity,
+ )?.let { listOf(renderableString("§cRemoves Effect:")) + it }?.takeIf { config.showDiff } ?: emptyList()
+ addEffectText = "§aAdds Effect:"
+ click = if (reforgeToSearch != reforge) {
+ listOf(renderableString(""), renderableString("§eClick to select!"))
+ } else emptyList()
+ }
+
+ val addedEffect = getReforgeEffect(reforge, itemRarity)?.let { listOf(renderableString(addEffectText)) + it } ?: emptyList()
+
+ return listOf(renderableString("§6Reforge Stats")) + stats + removedEffect + addedEffect + click
+ }
+
+ private fun getReforgeEffect(reforge: ReforgeAPI.Reforge?, rarity: LorenzRarity) =
+ reforge?.extraProperty?.get(rarity)?.let {
+ Renderable.wrappedString(
+ it,
+ 190,
+ color = LorenzColor.GRAY.toColor(),
+ )
+ }
+
+ private fun getSortSelector(
+ itemRarity: LorenzRarity,
+ sorting: SkyblockStat?,
+ ): Comparator<ReforgeAPI.Reforge> =
+ if (sorting != null) {
+ Comparator.comparing<ReforgeAPI.Reforge, Double> { it.stats[itemRarity]?.get(sorting) ?: 0.0 }.reversed()
+ } else {
+ Comparator.comparing { it.isReforgeStone }
+ }
+
+ private fun getStatButton(stat: SkyblockStat?): Renderable {
+ val icon: String
+ val tip: String
+ if (stat == null) {
+ icon = "§7D"
+ tip = "§7Default"
+ } else {
+ icon = stat.icon
+ tip = stat.iconWithName
+ }
+
+ val alreadySelected = sortAfter == stat
+ val fieldColor = if (alreadySelected) LorenzColor.GRAY else LorenzColor.DARK_GRAY
+
+
+ val tips = if (alreadySelected) {
+ listOf("§6Sort by", tip)
+ } else {
+ listOf("§6Sort by", tip, "", "§eClick to apply sorting!")
+ }
+ val sortField =
+ Renderable.drawInsideRoundedRect(
+ Renderable.hoverTips(
+ Renderable.fixedSizeLine(
+ renderableString(icon, horizontalAlign = RenderUtils.Horizontal