From a27f8e6fed4f8f6565e311fe3684beb0377229f7 Mon Sep 17 00:00:00 2001
From: Linnea Gräf <nea@nea.moe>
Date: Fri, 18 Oct 2024 17:47:32 +0200
Subject: Add super craft to the move item REI button

---
 .../kotlin/features/inventory/CraftingOverlay.kt   | 105 ++++++-----
 src/main/kotlin/rei/FirmamentReiPlugin.kt          | 192 +++++++++++----------
 src/main/kotlin/repo/RepoManager.kt                |   2 +
 src/main/kotlin/util/SkyblockId.kt                 |  13 ++
 .../resources/assets/firmament/lang/en_us.json     |   4 +-
 5 files changed, 180 insertions(+), 136 deletions(-)

diff --git a/src/main/kotlin/features/inventory/CraftingOverlay.kt b/src/main/kotlin/features/inventory/CraftingOverlay.kt
index 031ef78..8e75478 100644
--- a/src/main/kotlin/features/inventory/CraftingOverlay.kt
+++ b/src/main/kotlin/features/inventory/CraftingOverlay.kt
@@ -1,11 +1,10 @@
-
-
 package moe.nea.firmament.features.inventory
 
 import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
 import net.minecraft.item.ItemStack
 import net.minecraft.util.Formatting
 import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.ScreenChangeEvent
 import moe.nea.firmament.events.SlotRenderEvents
 import moe.nea.firmament.features.FirmamentFeature
 import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry
@@ -15,52 +14,66 @@ import moe.nea.firmament.util.MC
 
 object CraftingOverlay : FirmamentFeature {
 
-    private var screen: GenericContainerScreen? = null
-    private var recipe: SBCraftingRecipe? = null
-    private val craftingOverlayIndices = listOf(
-        10, 11, 12,
-        19, 20, 21,
-        28, 29, 30,
-    )
+	private var screen: GenericContainerScreen? = null
+	private var recipe: SBCraftingRecipe? = null
+	private var useNextScreen = false
+	private val craftingOverlayIndices = listOf(
+		10, 11, 12,
+		19, 20, 21,
+		28, 29, 30,
+	)
+	val CRAFTING_SCREEN_NAME = "Craft Item"
 
+	fun setOverlay(screen: GenericContainerScreen?, recipe: SBCraftingRecipe) {
+		this.screen = screen
+		if (screen == null) {
+			useNextScreen = true
+		}
+		this.recipe = recipe
+	}
 
-    fun setOverlay(screen: GenericContainerScreen, recipe: SBCraftingRecipe) {
-        this.screen = screen
-        this.recipe = recipe
-    }
+	@Subscribe
+	fun onScreenChange(event: ScreenChangeEvent) {
+		if (useNextScreen && event.new is GenericContainerScreen
+			&& event.new.title?.string == "Craft Item"
+		) {
+			useNextScreen = false
+			screen = event.new
+		}
+	}
 
-    override val identifier: String
-        get() = "crafting-overlay"
+	override val identifier: String
+		get() = "crafting-overlay"
 
-    @Subscribe
-    fun onSlotRender(event: SlotRenderEvents.After) {
-        val slot = event.slot
-        val recipe = this.recipe ?: return
-        if (slot.inventory != screen?.screenHandler?.inventory) return
-        val recipeIndex = craftingOverlayIndices.indexOf(slot.index)
-        if (recipeIndex < 0) return
-        val expectedItem = recipe.neuRecipe.inputs[recipeIndex]
-        val actualStack = slot.stack ?: ItemStack.EMPTY!!
-        val actualEntry = SBItemEntryDefinition.getEntry(actualStack).value
-        if ((actualEntry.skyblockId.neuItem != expectedItem.itemId || actualEntry.getStackSize() < expectedItem.amount) && expectedItem.amount.toInt() != 0) {
-            event.context.fill(
-                event.slot.x,
-                event.slot.y,
-                event.slot.x + 16,
-                event.slot.y + 16,
-                0x80FF0000.toInt()
-            )
-        }
-        if (!slot.hasStack()) {
-            val itemStack = SBItemEntryDefinition.getEntry(expectedItem).asItemEntry().value
-            event.context.drawItem(itemStack, event.slot.x, event.slot.y)
-            event.context.drawItemInSlot(
-                MC.font,
-                itemStack,
-                event.slot.x,
-                event.slot.y,
-                "${Formatting.RED}${expectedItem.amount.toInt()}"
-            )
-        }
-    }
+	@Subscribe
+	fun onSlotRender(event: SlotRenderEvents.After) {
+		val slot = event.slot
+		val recipe = this.recipe ?: return
+		if (slot.inventory != screen?.screenHandler?.inventory) return
+		val recipeIndex = craftingOverlayIndices.indexOf(slot.index)
+		if (recipeIndex < 0) return
+		val expectedItem = recipe.neuRecipe.inputs[recipeIndex]
+		val actualStack = slot.stack ?: ItemStack.EMPTY!!
+		val actualEntry = SBItemEntryDefinition.getEntry(actualStack).value
+		if ((actualEntry.skyblockId.neuItem != expectedItem.itemId || actualEntry.getStackSize() < expectedItem.amount) && expectedItem.amount.toInt() != 0) {
+			event.context.fill(
+				event.slot.x,
+				event.slot.y,
+				event.slot.x + 16,
+				event.slot.y + 16,
+				0x80FF0000.toInt()
+			)
+		}
+		if (!slot.hasStack()) {
+			val itemStack = SBItemEntryDefinition.getEntry(expectedItem).asItemEntry().value
+			event.context.drawItem(itemStack, event.slot.x, event.slot.y)
+			event.context.drawItemInSlot(
+				MC.font,
+				itemStack,
+				event.slot.x,
+				event.slot.y,
+				"${Formatting.RED}${expectedItem.amount.toInt()}"
+			)
+		}
+	}
 }
diff --git a/src/main/kotlin/rei/FirmamentReiPlugin.kt b/src/main/kotlin/rei/FirmamentReiPlugin.kt
index b585336..f234f3e 100644
--- a/src/main/kotlin/rei/FirmamentReiPlugin.kt
+++ b/src/main/kotlin/rei/FirmamentReiPlugin.kt
@@ -1,5 +1,3 @@
-
-
 package moe.nea.firmament.rei
 
 import me.shedaniel.rei.api.client.plugins.REIClientPlugin
@@ -31,98 +29,114 @@ import moe.nea.firmament.rei.recipes.SBForgeRecipe
 import moe.nea.firmament.rei.recipes.SBKatRecipe
 import moe.nea.firmament.rei.recipes.SBMobDropRecipe
 import moe.nea.firmament.repo.RepoManager
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.ScreenUtil
 import moe.nea.firmament.util.SkyblockId
+import moe.nea.firmament.util.guessRecipeId
 import moe.nea.firmament.util.skyblockId
 import moe.nea.firmament.util.unformattedString
 
 
 class FirmamentReiPlugin : REIClientPlugin {
 
-    companion object {
-        fun EntryStack<SBItemStack>.asItemEntry(): EntryStack<ItemStack> {
-            return EntryStack.of(VanillaEntryTypes.ITEM, value.asImmutableItemStack())
-        }
-
-        val SKYBLOCK_ITEM_TYPE_ID = Identifier.of("firmament", "skyblockitems")
-    }
-
-    override fun registerTransferHandlers(registry: TransferHandlerRegistry) {
-        registry.register(TransferHandler { context ->
-            val screen = context.containerScreen
-            val display = context.display
-            if (display !is SBCraftingRecipe || screen !is GenericContainerScreen || screen.title?.unformattedString != "Craft Item") {
-                return@TransferHandler TransferHandler.Result.createNotApplicable()
-            }
-            if (context.isActuallyCrafting)
-                CraftingOverlay.setOverlay(screen, display)
-            return@TransferHandler TransferHandler.Result.createSuccessful().blocksFurtherHandling(true)
-        })
-    }
-
-    override fun registerEntryTypes(registry: EntryTypeRegistry) {
-        registry.register(SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition)
-    }
-
-    override fun registerCategories(registry: CategoryRegistry) {
-        registry.add(SBCraftingRecipe.Category)
-        registry.add(SBForgeRecipe.Category)
-        registry.add(SBMobDropRecipe.Category)
-        registry.add(SBKatRecipe.Category)
-        registry.add(SBEssenceUpgradeRecipe.Category)
-    }
-
-    override fun registerExclusionZones(zones: ExclusionZones) {
-        zones.register(HandledScreen::class.java) { HandledScreenPushREIEvent.publish(HandledScreenPushREIEvent(it)).rectangles }
-        zones.register(StorageOverlayScreen::class.java) { it.getBounds() }
-    }
-
-    override fun registerDisplays(registry: DisplayRegistry) {
-        registry.registerDisplayGenerator(
-            SBCraftingRecipe.Category.catIdentifier,
-            SkyblockCraftingRecipeDynamicGenerator)
-        registry.registerDisplayGenerator(
-            SBForgeRecipe.Category.categoryIdentifier,
-            SkyblockForgeRecipeDynamicGenerator)
-        registry.registerDisplayGenerator(
-            SBMobDropRecipe.Category.categoryIdentifier,
-            SkyblockMobDropRecipeDynamicGenerator)
-        registry.registerDisplayGenerator(
-            SBKatRecipe.Category.categoryIdentifier,
-            SkyblockKatRecipeDynamicGenerator)
-        registry.registerDisplayGenerator(
-            SBEssenceUpgradeRecipe.Category.categoryIdentifier,
-            SkyblockEssenceRecipeDynamicGenerator
-        )
-    }
-
-    override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) {
-        if (!RepoManager.Config.disableItemGroups)
-            RepoManager.neuRepo.constants.parents.parents
-                .forEach { (parent, children) ->
-                    registry.group(
-                        SkyblockId(parent).identifier,
-                        Text.literal(RepoManager.getNEUItem(SkyblockId(parent))?.displayName ?: parent),
-                        (children + parent).map { SBItemEntryDefinition.getEntry(SkyblockId(it)) })
-                }
-    }
-
-    override fun registerScreens(registry: ScreenRegistry) {
-        registry.registerDecider(object : OverlayDecider {
-            override fun <R : Screen?> isHandingScreen(screen: Class<R>?): Boolean {
-                return screen == StorageOverlayScreen::class.java
-            }
-
-            override fun <R : Screen?> shouldScreenBeOverlaid(screen: R): ActionResult {
-                return ActionResult.SUCCESS
-            }
-        })
-        registry.registerFocusedStack(SkyblockItemIdFocusedStackProvider)
-    }
-
-    override fun registerEntries(registry: EntryRegistry) {
-        registry.removeEntryIf { true }
-        RepoManager.neuRepo.items?.items?.values?.forEach { neuItem ->
-            registry.addEntry(SBItemEntryDefinition.getEntry(neuItem.skyblockId))
-        }
-    }
+	companion object {
+		fun EntryStack<SBItemStack>.asItemEntry(): EntryStack<ItemStack> {
+			return EntryStack.of(VanillaEntryTypes.ITEM, value.asImmutableItemStack())
+		}
+
+		val SKYBLOCK_ITEM_TYPE_ID = Identifier.of("firmament", "skyblockitems")
+	}
+
+	override fun registerTransferHandlers(registry: TransferHandlerRegistry) {
+		registry.register(TransferHandler { context ->
+			val screen = context.containerScreen
+			val display = context.display
+			if (display !is SBCraftingRecipe) return@TransferHandler TransferHandler.Result.createNotApplicable()
+			val neuItem = RepoManager.getNEUItem(SkyblockId(display.neuRecipe.output.itemId))
+				?: error("Could not find neu item ${display.neuRecipe.output.itemId} which is used in a recipe output")
+			val useSuperCraft = context.isStackedCrafting || RepoManager.Config.alwaysSuperCraft
+			if (neuItem.isVanilla && useSuperCraft) return@TransferHandler TransferHandler.Result.createFailed(Text.translatable(
+				"firmament.recipe.novanilla"))
+			var shouldReturn = true
+			if (context.isActuallyCrafting && !useSuperCraft) {
+				if (screen !is GenericContainerScreen || screen.title?.unformattedString != CraftingOverlay.CRAFTING_SCREEN_NAME) {
+					MC.sendCommand("craft")
+					shouldReturn = false
+				}
+				CraftingOverlay.setOverlay(screen as? GenericContainerScreen, display)
+			}
+			if (context.isActuallyCrafting && useSuperCraft) {
+				shouldReturn = false
+				MC.sendCommand("viewrecipe ${neuItem.guessRecipeId()}")
+			}
+			return@TransferHandler TransferHandler.Result.createSuccessful().blocksFurtherHandling(shouldReturn)
+		})
+	}
+
+	override fun registerEntryTypes(registry: EntryTypeRegistry) {
+		registry.register(SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition)
+	}
+
+	override fun registerCategories(registry: CategoryRegistry) {
+		registry.add(SBCraftingRecipe.Category)
+		registry.add(SBForgeRecipe.Category)
+		registry.add(SBMobDropRecipe.Category)
+		registry.add(SBKatRecipe.Category)
+		registry.add(SBEssenceUpgradeRecipe.Category)
+	}
+
+	override fun registerExclusionZones(zones: ExclusionZones) {
+		zones.register(HandledScreen::class.java) { HandledScreenPushREIEvent.publish(HandledScreenPushREIEvent(it)).rectangles }
+		zones.register(StorageOverlayScreen::class.java) { it.getBounds() }
+	}
+
+	override fun registerDisplays(registry: DisplayRegistry) {
+		registry.registerDisplayGenerator(
+			SBCraftingRecipe.Category.catIdentifier,
+			SkyblockCraftingRecipeDynamicGenerator)
+		registry.registerDisplayGenerator(
+			SBForgeRecipe.Category.categoryIdentifier,
+			SkyblockForgeRecipeDynamicGenerator)
+		registry.registerDisplayGenerator(
+			SBMobDropRecipe.Category.categoryIdentifier,
+			SkyblockMobDropRecipeDynamicGenerator)
+		registry.registerDisplayGenerator(
+			SBKatRecipe.Category.categoryIdentifier,
+			SkyblockKatRecipeDynamicGenerator)
+		registry.registerDisplayGenerator(
+			SBEssenceUpgradeRecipe.Category.categoryIdentifier,
+			SkyblockEssenceRecipeDynamicGenerator
+		)
+	}
+
+	override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) {
+		if (!RepoManager.Config.disableItemGroups)
+			RepoManager.neuRepo.constants.parents.parents
+				.forEach { (parent, children) ->
+					registry.group(
+						SkyblockId(parent).identifier,
+						Text.literal(RepoManager.getNEUItem(SkyblockId(parent))?.displayName ?: parent),
+						(children + parent).map { SBItemEntryDefinition.getEntry(SkyblockId(it)) })
+				}
+	}
+
+	override fun registerScreens(registry: ScreenRegistry) {
+		registry.registerDecider(object : OverlayDecider {
+			override fun <R : Screen?> isHandingScreen(screen: Class<R>?): Boolean {
+				return screen == StorageOverlayScreen::class.java
+			}
+
+			override fun <R : Screen?> shouldScreenBeOverlaid(screen: R): ActionResult {
+				return ActionResult.SUCCESS
+			}
+		})
+		registry.registerFocusedStack(SkyblockItemIdFocusedStackProvider)
+	}
+
+	override fun registerEntries(registry: EntryRegistry) {
+		registry.removeEntryIf { true }
+		RepoManager.neuRepo.items?.items?.values?.forEach { neuItem ->
+			registry.addEntry(SBItemEntryDefinition.getEntry(neuItem.skyblockId))
+		}
+	}
 }
diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt
index 5cf84bd..ab0d9cf 100644
--- a/src/main/kotlin/repo/RepoManager.kt
+++ b/src/main/kotlin/repo/RepoManager.kt
@@ -40,6 +40,8 @@ object RepoManager {
 			save()
 			RepoManager.launchAsyncUpdate(true)
 		}
+		val alwaysSuperCraft by toggle("enable-super-craft") { true }
+
 	}
 
 	val currentDownloadedSha by RepoDownloadManager::latestSavedVersionHash
diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt
index 5b96dfa..31227e2 100644
--- a/src/main/kotlin/util/SkyblockId.kt
+++ b/src/main/kotlin/util/SkyblockId.kt
@@ -13,8 +13,10 @@ import kotlin.jvm.optionals.getOrNull
 import net.minecraft.component.DataComponentTypes
 import net.minecraft.component.type.NbtComponent
 import net.minecraft.item.ItemStack
+import net.minecraft.item.Items
 import net.minecraft.nbt.NbtCompound
 import net.minecraft.util.Identifier
+import moe.nea.firmament.repo.ItemCache.asItemStack
 import moe.nea.firmament.repo.set
 import moe.nea.firmament.util.collections.WeakCache
 import moe.nea.firmament.util.json.DashlessUUIDSerializer
@@ -69,6 +71,17 @@ value class SkyblockId(val neuItem: String) {
 
 val NEUItem.skyblockId get() = SkyblockId(skyblockItemId)
 
+fun NEUItem.guessRecipeId(): String? {
+	if (!skyblockItemId.contains(";")) return skyblockItemId
+	val item = this.asItemStack()
+	val (id, extraId) = skyblockItemId.split(";")
+	if (item.item == Items.ENCHANTED_BOOK) {
+		return "ENCHANTED_BOOK_${id}_${extraId}"
+	}
+	if (item.petData != null) return id
+	return null
+}
+
 @Serializable
 data class HypixelPetInfo(
 	val type: String,
diff --git a/src/main/resources/assets/firmament/lang/en_us.json b/src/main/resources/assets/firmament/lang/en_us.json
index 9a204f7..fef8996 100644
--- a/src/main/resources/assets/firmament/lang/en_us.json
+++ b/src/main/resources/assets/firmament/lang/en_us.json
@@ -81,6 +81,7 @@
 	"firmament.config.repo.branch.hint": "dangerous",
     "firmament.config.repo.reset": "Reset",
     "firmament.config.repo.disable-item-groups": "Disable Item Groups",
+    "firmament.config.repo.enable-super-craft": "Always use Super Craft",
     "firmament.config.repo.reload": "Reload Item List",
     "firmament.config.repo.redownload": "Redownload Item List",
     "firmament.config.pets": "Pets",
@@ -178,7 +179,7 @@
     "firmament.config.slot-locking.lock": "Lock Slot",
     "firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)",
     "firmament.config.slot-locking.bind": "Bind Slot",
-    "firmament.config.slot-locking.require-quick-move": "Require Shift-Click for Bind",
+    "firmament.config.slot-locking.require-quick-move": "Require Shift-Click for Bound Slots",
     "firmament.config.fixes.auto-sprint": "Auto Sprint",
     "firmament.config.fixes.auto-sprint-keybinding": "Auto Sprint KeyBinding",
     "firmament.config.fixes.auto-sprint-hud": "Sprint State Hud",
@@ -187,6 +188,7 @@
     "firmament.fixes.auto-sprint.on": "Sprint toggled",
     "firmament.fixes.auto-sprint.sprinting": "Sprinting",
     "firmament.fixes.auto-sprint.not-sprinting": "Not Sprinting",
+	"firmament.recipe.novanilla": "Hypixel cannot super craft vanilla recipes",
     "firmament.config.custom-skyblock-textures": "Custom SkyBlock Item Textures",
     "firmament.config.custom-skyblock-textures.cache-duration": "Model Cache Duration",
     "firmament.config.custom-skyblock-textures.model-overrides": "Enable model overrides/predicates",
-- 
cgit