diff options
Diffstat (limited to 'src/main/java')
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.HorizontalAlignment.CENTER), + SkyblockStat.fontSizeOfLargestIcon, + ), + tips, + ), + fieldColor.toColor(), radius = 15, padding = 1, + ) + return if (alreadySelected) { + sortField + } else { + Renderable.clickable( + sortField, + { + sortAfter = stat + updateDisplay() + }, + ) + } + } + + @SubscribeEvent + fun onRender(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { + if (!isEnabled()) return + config.position.renderRenderables(display, posLabel = "Reforge Overlay") + } + + @SubscribeEvent + fun onForegroundDrawn(event: GuiContainerEvent.ForegroundDrawnEvent) { + if (!isEnabled()) return + if (currentReforge == null) return + + inventoryContainer?.getSlot(reforgeItem)?.let { + event.drawSlotText(it.xDisplayPosition - 5, it.yDisplayPosition, "§e${currentReforge?.name}", 1f) + } + } + + @SubscribeEvent + fun onGuiContainerBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (hoveredReforge != null && isInHexReforgeMenu) { + if (hoveredReforge != currentReforge) { + colorReforgeStone(hoverColor, hoveredReforge?.rawReforgeStoneName ?: "Random Basic Reforge") + } else { + inventoryContainer?.getSlot(reforgeItem)?.highlight(hoverColor) + |
