From 2a3a4c07f5db951169adfc5626e8cf28c0812cf6 Mon Sep 17 00:00:00 2001
From: Linnea Gräf <nea@nea.moe>
Date: Tue, 21 Jan 2025 02:09:11 +0100
Subject: feat: Add npc shop recipes

---
 .../nea/firmament/compat/rei/FirmamentReiPlugin.kt |  5 ++
 .../firmament/compat/rei/NEUItemEntryRenderer.kt   | 13 ++++-
 .../rei/SkyblockCraftingRecipeDynamicGenerator.kt  |  5 +-
 .../compat/rei/recipes/SBCraftingRecipe.kt         |  2 +-
 .../firmament/compat/rei/recipes/SBForgeRecipe.kt  |  2 +-
 .../firmament/compat/rei/recipes/SBShopRecipe.kt   | 61 ++++++++++++++++++++++
 6 files changed, 83 insertions(+), 5 deletions(-)
 create mode 100644 src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBShopRecipe.kt

(limited to 'src/compat/rei/java')

diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt
index 92f2cfc..aade59e 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt
@@ -25,6 +25,7 @@ import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe
 import moe.nea.firmament.compat.rei.recipes.SBKatRecipe
 import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe
 import moe.nea.firmament.compat.rei.recipes.SBReforgeRecipe
+import moe.nea.firmament.compat.rei.recipes.SBShopRecipe
 import moe.nea.firmament.events.HandledScreenPushREIEvent
 import moe.nea.firmament.features.inventory.CraftingOverlay
 import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
@@ -81,6 +82,7 @@ class FirmamentReiPlugin : REIClientPlugin {
 		registry.add(SBKatRecipe.Category)
 		registry.add(SBReforgeRecipe.Category)
 		registry.add(SBEssenceUpgradeRecipe.Category)
+		registry.add(SBShopRecipe.Category)
 	}
 
 	override fun registerExclusionZones(zones: ExclusionZones) {
@@ -102,6 +104,9 @@ class FirmamentReiPlugin : REIClientPlugin {
 		registry.registerDisplayGenerator(
 			SBMobDropRecipe.Category.categoryIdentifier,
 			SkyblockMobDropRecipeDynamicGenerator)
+		registry.registerDisplayGenerator(
+			SBShopRecipe.Category.categoryIdentifier,
+			SkyblockShopRecipeDynamicGenerator)
 		registry.registerDisplayGenerator(
 			SBKatRecipe.Category.categoryIdentifier,
 			SkyblockKatRecipeDynamicGenerator)
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt
index c40f00b..35a1e1b 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt
@@ -18,10 +18,13 @@ import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback
 import net.minecraft.client.MinecraftClient
 import net.minecraft.client.gui.DrawContext
 import net.minecraft.item.tooltip.TooltipType
+import net.minecraft.text.Text
 import moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry
 import moe.nea.firmament.events.ItemTooltipEvent
 import moe.nea.firmament.repo.SBItemStack
 import moe.nea.firmament.util.ErrorUtil
+import moe.nea.firmament.util.FirmFormatters
+import moe.nea.firmament.util.darkGrey
 import moe.nea.firmament.util.mc.displayNameAccordingToNbt
 import moe.nea.firmament.util.mc.loreAccordingToNbt
 
@@ -40,8 +43,12 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> {
 		context.matrices.translate(bounds.centerX.toFloat(), bounds.centerY.toFloat(), 0F)
 		context.matrices.scale(bounds.width.toFloat() / 16F, bounds.height.toFloat() / 16F, 1f)
 		val item = entry.asItemEntry().value
-		context.drawItemWithoutEntity(item, -8, -8, )
-		context.drawStackOverlay(minecraft.textRenderer, item, -8, -8)
+		context.drawItemWithoutEntity(item, -8, -8)
+		context.drawStackOverlay(minecraft.textRenderer, item, -8, -8,
+		                         if (entry.value.getStackSize() > 1000) FirmFormatters.shortFormat(entry.value.getStackSize()
+			                                                                                           .toDouble())
+		                         else null
+		)
 		context.matrices.pop()
 	}
 
@@ -70,6 +77,8 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> {
 				lore
 			))
 		}
+		if (entry.value.getStackSize() > 1000 && lore.isNotEmpty())
+			lore.add(1, Text.literal("${entry.value.getStackSize()}x").darkGrey())
 		// TODO: tags aren't sent as early now so some tooltip components that use tags will crash the game
 //		stack.getTooltip(
 //			Item.TooltipContext.create(
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt
index 72bd28c..e80840f 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt
@@ -4,6 +4,7 @@ import io.github.moulberry.repo.data.NEUCraftingRecipe
 import io.github.moulberry.repo.data.NEUForgeRecipe
 import io.github.moulberry.repo.data.NEUKatUpgradeRecipe
 import io.github.moulberry.repo.data.NEUMobDropRecipe
+import io.github.moulberry.repo.data.NEUNpcShopRecipe
 import io.github.moulberry.repo.data.NEURecipe
 import java.util.Optional
 import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator
@@ -15,6 +16,7 @@ import moe.nea.firmament.compat.rei.recipes.SBEssenceUpgradeRecipe
 import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe
 import moe.nea.firmament.compat.rei.recipes.SBKatRecipe
 import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe
+import moe.nea.firmament.compat.rei.recipes.SBShopRecipe
 import moe.nea.firmament.repo.EssenceRecipeProvider
 import moe.nea.firmament.repo.RepoManager
 import moe.nea.firmament.repo.SBItemStack
@@ -28,7 +30,8 @@ val SkyblockForgeRecipeDynamicGenerator =
 
 val SkyblockMobDropRecipeDynamicGenerator =
 	neuDisplayGenerator<SBMobDropRecipe, NEUMobDropRecipe> { SBMobDropRecipe(it) }
-
+val SkyblockShopRecipeDynamicGenerator =
+	neuDisplayGenerator<SBShopRecipe, NEUNpcShopRecipe> { SBShopRecipe(it) }
 val SkyblockKatRecipeDynamicGenerator =
 	neuDisplayGenerator<SBKatRecipe, NEUKatUpgradeRecipe> { SBKatRecipe(it) }
 val SkyblockEssenceRecipeDynamicGenerator =
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt
index 8db3d75..c02e078 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt
@@ -22,7 +22,7 @@ class SBCraftingRecipe(override val neuRecipe: NEUCraftingRecipe) : SBRecipe() {
 	override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier
 
 	object Category : DisplayCategory<SBCraftingRecipe> {
-		val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafing_recipe")
+		val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafting_recipe")
 		override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier
 
 		override fun getTitle(): Text = Text.literal("SkyBlock Crafting")
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt
index 92b2f3f..7a0ec78 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt
@@ -8,7 +8,6 @@ import me.shedaniel.rei.api.client.gui.widgets.Widget
 import me.shedaniel.rei.api.client.gui.widgets.Widgets
 import me.shedaniel.rei.api.client.registry.display.DisplayCategory
 import me.shedaniel.rei.api.common.category.CategoryIdentifier
-import me.shedaniel.rei.api.common.util.EntryStacks
 import kotlin.math.cos
 import kotlin.math.sin
 import kotlin.time.Duration.Companion.seconds
@@ -41,6 +40,7 @@ class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() {
 				                          Text.stringifiedTranslatable("firmament.recipe.forge.time",
 				                                                       display.neuRecipe.duration.seconds)))
 				val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8)
+				add(Widgets.createBurningFire(ingredientsCenter).animationDurationTicks(25.0))
 				val count = display.neuRecipe.inputs.size
 				if (count == 1) {
 					add(
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBShopRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBShopRecipe.kt
new file mode 100644
index 0000000..a252802
--- /dev/null
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBShopRecipe.kt
@@ -0,0 +1,61 @@
+package moe.nea.firmament.compat.rei.recipes
+
+import io.github.moulberry.repo.data.NEUNpcShopRecipe
+import me.shedaniel.math.Point
+import me.shedaniel.math.Rectangle
+import me.shedaniel.rei.api.client.gui.Renderer
+import me.shedaniel.rei.api.client.gui.widgets.Widget
+import me.shedaniel.rei.api.client.gui.widgets.Widgets
+import me.shedaniel.rei.api.client.registry.display.DisplayCategory
+import me.shedaniel.rei.api.common.category.CategoryIdentifier
+import me.shedaniel.rei.api.common.entry.EntryIngredient
+import net.minecraft.item.Items
+import net.minecraft.text.Text
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.compat.rei.SBItemEntryDefinition
+import moe.nea.firmament.util.skyblockId
+
+class SBShopRecipe(override val neuRecipe: NEUNpcShopRecipe) : SBRecipe() {
+	override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier
+	val merchant = SBItemEntryDefinition.getEntry(neuRecipe.isSoldBy.skyblockId)
+	override fun getInputEntries(): List<EntryIngredient> {
+		return listOf(EntryIngredient.of(merchant)) + super.getInputEntries()
+	}
+
+	object Category : DisplayCategory<SBShopRecipe> {
+		val catIdentifier = CategoryIdentifier.of<SBShopRecipe>(Firmament.MOD_ID, "npc_shopping")
+		override fun getCategoryIdentifier(): CategoryIdentifier<SBShopRecipe> = catIdentifier
+
+		override fun getTitle(): Text = Text.literal("SkyBlock NPC Shopping")
+
+		override fun getIcon(): Renderer = SBItemEntryDefinition.getPassthrough(Items.EMERALD)
+		override fun setupDisplay(display: SBShopRecipe, bounds: Rectangle): List<Widget> {
+			val point = Point(bounds.centerX, bounds.centerY)
+			return buildList {
+				add(Widgets.createRecipeBase(bounds))
+				add(Widgets.createSlot(Point(point.x - 2 - 18 / 2, point.y - 18 - 6))
+					    .unmarkInputOrOutput()
+					    .entry(display.merchant)
+					    .disableBackground())
+				add(Widgets.createArrow(Point(point.x - 2 - 24 / 2, point.y - 6)))
+				val cost = display.neuRecipe.cost
+				for ((i, item) in cost.withIndex()) {
+					add(Widgets.createSlot(Point(
+						point.x - 14 - 18,
+						point.y + i * 18 - 18 * cost.size / 2))
+						    .entry(SBItemEntryDefinition.getEntry(item))
+						    .markInput())
+					// TODO: fix frame clipping
+				}
+				add(Widgets.createResultSlotBackground(Point(point.x + 18, point.y - 18 / 2)))
+				add(
+					Widgets.createSlot(Point(point.x + 18, point.y - 18 / 2))
+						.entry(SBItemEntryDefinition.getEntry(display.neuRecipe.result))
+						.disableBackground().markOutput()
+				)
+			}
+		}
+
+	}
+
+}
-- 
cgit