aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/repo/recipes
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/repo/recipes')
-rw-r--r--src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt18
-rw-r--r--src/main/kotlin/repo/recipes/RecipeLayouter.kt43
-rw-r--r--src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt44
-rw-r--r--src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt74
-rw-r--r--src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt88
-rw-r--r--src/main/kotlin/repo/recipes/SBReforgeRecipeRenderer.kt167
6 files changed, 409 insertions, 25 deletions
diff --git a/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt
index 9a1aea5..84f1f48 100644
--- a/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt
+++ b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt
@@ -3,17 +3,21 @@ package moe.nea.firmament.repo.recipes
import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.data.NEURecipe
import me.shedaniel.math.Rectangle
-import net.minecraft.item.ItemStack
-import net.minecraft.text.Text
-import net.minecraft.util.Identifier
+import net.minecraft.world.item.ItemStack
+import net.minecraft.network.chat.Component
+import net.minecraft.resources.ResourceLocation
import moe.nea.firmament.repo.SBItemStack
-interface GenericRecipeRenderer<T : NEURecipe> {
- fun render(recipe: T, bounds: Rectangle, layouter: RecipeLayouter)
+interface GenericRecipeRenderer<T : Any> {
+ fun render(recipe: T, bounds: Rectangle, layouter: RecipeLayouter, mainItem: SBItemStack?)
fun getInputs(recipe: T): Collection<SBItemStack>
fun getOutputs(recipe: T): Collection<SBItemStack>
val icon: ItemStack
- val title: Text
- val identifier: Identifier
+ val title: Component
+ val identifier: ResourceLocation
fun findAllRecipes(neuRepository: NEURepository): Iterable<T>
+ fun discoverExtraRecipes(neuRepository: NEURepository, itemStack: SBItemStack, mustBeInOutputs: Boolean): Iterable<T> = emptyList()
+ val displayHeight: Int get() = 66
+ val displayWidth: Int get() = 150
+ val typ: Class<T>
}
diff --git a/src/main/kotlin/repo/recipes/RecipeLayouter.kt b/src/main/kotlin/repo/recipes/RecipeLayouter.kt
index 109bff5..7a63941 100644
--- a/src/main/kotlin/repo/recipes/RecipeLayouter.kt
+++ b/src/main/kotlin/repo/recipes/RecipeLayouter.kt
@@ -1,7 +1,12 @@
package moe.nea.firmament.repo.recipes
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
-import net.minecraft.text.Text
+import me.shedaniel.math.Point
+import me.shedaniel.math.Rectangle
+import net.minecraft.network.chat.Component
+import net.minecraft.world.entity.Entity
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.entity.npc.Villager
import moe.nea.firmament.repo.SBItemStack
interface RecipeLayouter {
@@ -13,21 +18,49 @@ interface RecipeLayouter {
* Create a bigger background and mark the slot as output. The coordinates should still refer the upper left corner of the item stack, not of the bigger background.
*/
BIG_OUTPUT,
+ DISPLAY,;
+ val isBig get() = this == BIG_OUTPUT
}
+
+ fun createCyclingItemSlot(
+ x: Int, y: Int,
+ content: List<SBItemStack>,
+ slotKind: SlotKind
+ ): CyclingItemSlot
+
fun createItemSlot(
x: Int, y: Int,
content: SBItemStack?,
slotKind: SlotKind,
- )
+ ): ItemSlot = createCyclingItemSlot(x, y, listOfNotNull(content), slotKind)
+
+ interface CyclingItemSlot : ItemSlot {
+ fun onUpdate(action: () -> Unit)
+ }
+
+ interface ItemSlot : Updater<SBItemStack> {
+ fun current(): SBItemStack
+ }
+
+ interface Updater<T> {
+ fun update(newValue: T)
+ }
+
+ fun createTooltip(rectangle: Rectangle, label: List<Component>)
+ fun createTooltip(rectangle: Rectangle, vararg label: Component) =
+ createTooltip(rectangle, label.toList())
+
fun createLabel(
x: Int, y: Int,
- text: Text
- )
+ text: Component
+ ): Updater<Component>
- fun createArrow(x: Int, y: Int)
+ fun createArrow(x: Int, y: Int): Rectangle
fun createMoulConfig(x: Int, y: Int, w: Int, h: Int, component: GuiComponent)
+ fun createFire(point: Point, animationTicks: Int)
+ fun createEntity(rectangle: Rectangle, entity: LivingEntity)
}
diff --git a/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt
index 679aec8..0a0d5e2 100644
--- a/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt
+++ b/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt
@@ -4,25 +4,40 @@ import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.data.NEUCraftingRecipe
import me.shedaniel.math.Point
import me.shedaniel.math.Rectangle
-import net.minecraft.block.Blocks
-import net.minecraft.item.ItemStack
-import net.minecraft.text.Text
-import net.minecraft.util.Identifier
+import net.minecraft.world.level.block.Blocks
+import net.minecraft.world.item.ItemStack
+import net.minecraft.network.chat.Component
+import net.minecraft.resources.ResourceLocation
import moe.nea.firmament.Firmament
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.tr
-class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
- override fun render(recipe: NEUCraftingRecipe, bounds: Rectangle, layouter: RecipeLayouter) {
+object SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
+ override fun render(
+ recipe: NEUCraftingRecipe,
+ bounds: Rectangle,
+ layouter: RecipeLayouter,
+ mainItem: SBItemStack?,
+ ) {
val point = Point(bounds.centerX - 58, bounds.centerY - 27)
- layouter.createArrow(point.x + 60, point.y + 18)
+ val arrow = layouter.createArrow(point.x + 60, point.y + 18)
+
+ if (recipe.extraText != null && recipe.extraText!!.isNotBlank()) {
+ layouter.createTooltip(
+ arrow,
+ Component.nullToEmpty(recipe.extraText!!),
+ )
+ }
+
for (i in 0 until 3) {
for (j in 0 until 3) {
val item = recipe.inputs[i + j * 3]
- layouter.createItemSlot(point.x + 1 + i * 18,
- point.y + 1 + j * 18,
- SBItemStack(item),
- RecipeLayouter.SlotKind.SMALL_INPUT)
+ layouter.createItemSlot(
+ point.x + 1 + i * 18,
+ point.y + 1 + j * 18,
+ SBItemStack(item),
+ RecipeLayouter.SlotKind.SMALL_INPUT
+ )
}
}
layouter.createItemSlot(
@@ -32,6 +47,9 @@ class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
)
}
+ override val typ: Class<NEUCraftingRecipe>
+ get() = NEUCraftingRecipe::class.java
+
override fun getInputs(recipe: NEUCraftingRecipe): Collection<SBItemStack> {
return recipe.allInputs.mapNotNull { SBItemStack(it) }
}
@@ -45,6 +63,6 @@ class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
}
override val icon: ItemStack = ItemStack(Blocks.CRAFTING_TABLE)
- override val title: Text = tr("firmament.category.crafting", "SkyBlock Crafting") // TODO: fix tr not being included in jars
- override val identifier: Identifier = Firmament.identifier("crafting_recipe")
+ override val title: Component = tr("firmament.category.crafting", "SkyBlock Crafting")
+ override val identifier: ResourceLocation = Firmament.identifier("crafting_recipe")
}
diff --git a/src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt
new file mode 100644
index 0000000..15785bd
--- /dev/null
+++ b/src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt
@@ -0,0 +1,74 @@
+package moe.nea.firmament.repo.recipes
+
+import io.github.moulberry.repo.NEURepository
+import me.shedaniel.math.Rectangle
+import net.minecraft.world.item.ItemStack
+import net.minecraft.network.chat.Component
+import net.minecraft.resources.ResourceLocation
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.repo.EssenceRecipeProvider
+import moe.nea.firmament.repo.ExpensiveItemCacheApi
+import moe.nea.firmament.repo.RepoManager
+import moe.nea.firmament.repo.SBItemStack
+import moe.nea.firmament.util.SkyblockId
+import moe.nea.firmament.util.tr
+
+object SBEssenceUpgradeRecipeRenderer : GenericRecipeRenderer<EssenceRecipeProvider.EssenceUpgradeRecipe> {
+ override fun render(
+ recipe: EssenceRecipeProvider.EssenceUpgradeRecipe,
+ bounds: Rectangle,
+ layouter: RecipeLayouter,
+ mainItem: SBItemStack?
+ ) {
+ val sourceItem = mainItem ?: SBItemStack(recipe.itemId)
+ layouter.createItemSlot(
+ bounds.minX + 12,
+ bounds.centerY - 8 - 18 / 2,
+ sourceItem.copy(stars = recipe.starCountAfter - 1),
+ RecipeLayouter.SlotKind.SMALL_INPUT
+ )
+ layouter.createItemSlot(
+ bounds.minX + 12, bounds.centerY - 8 + 18 / 2,
+ SBItemStack(recipe.essenceIngredient),
+ RecipeLayouter.SlotKind.SMALL_INPUT
+ )
+ layouter.createItemSlot(
+ bounds.maxX - 12 - 16, bounds.centerY - 8,
+ sourceItem.copy(stars = recipe.starCountAfter),
+ RecipeLayouter.SlotKind.SMALL_OUTPUT
+ )
+ val extraItems = recipe.extraItems
+ layouter.createArrow(
+ bounds.centerX - 24 / 2,
+ if (extraItems.isEmpty()) bounds.centerY - 17 / 2
+ else bounds.centerY + 18 / 2
+ )
+ for ((index, item) in extraItems.withIndex()) {
+ layouter.createItemSlot(
+ bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18,
+ bounds.centerY - 18 / 2,
+ SBItemStack(item),
+ RecipeLayouter.SlotKind.SMALL_INPUT,
+ )
+ }
+ }
+
+ override fun getInputs(recipe: EssenceRecipeProvider.EssenceUpgradeRecipe): Collection<SBItemStack> {
+ return recipe.allInputs.mapNotNull { SBItemStack(it) }
+ }
+
+ override fun getOutputs(recipe: EssenceRecipeProvider.EssenceUpgradeRecipe): Collection<SBItemStack> {
+ return listOfNotNull(SBItemStack(recipe.itemId))
+ }
+
+ @OptIn(ExpensiveItemCacheApi::class)
+ override val icon: ItemStack get() = SBItemStack(SkyblockId("ESSENCE_WITHER")).asImmutableItemStack()
+ override val title: Component = tr("firmament.category.essence", "Essence Upgrades")
+ override val identifier: ResourceLocation = Firmament.identifier("essence_upgrade")
+ override fun findAllRecipes(neuRepository: NEURepository): Iterable<EssenceRecipeProvider.EssenceUpgradeRecipe> {
+ return RepoManager.essenceRecipeProvider.recipes
+ }
+
+ override val typ: Class<EssenceRecipeProvider.EssenceUpgradeRecipe>
+ get() = EssenceRecipeProvider.EssenceUpgradeRecipe::class.java
+}
diff --git a/src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt
new file mode 100644
index 0000000..b595f07
--- /dev/null
+++ b/src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt
@@ -0,0 +1,88 @@
+package moe.nea.firmament.repo.recipes
+
+import io.github.moulberry.repo.NEURepository
+import io.github.moulberry.repo.data.NEUForgeRecipe
+import me.shedaniel.math.Point
+import me.shedaniel.math.Rectangle
+import kotlin.math.cos
+import kotlin.math.sin
+import kotlin.time.Duration.Companion.seconds
+import net.minecraft.world.level.block.Blocks
+import net.minecraft.world.item.ItemStack
+import net.minecraft.network.chat.Component
+import net.minecraft.resources.ResourceLocation
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.repo.SBItemStack
+import moe.nea.firmament.util.tr
+
+object SBForgeRecipeRenderer : GenericRecipeRenderer<NEUForgeRecipe> {
+ override fun render(
+ recipe: NEUForgeRecipe,
+ bounds: Rectangle,
+ layouter: RecipeLayouter,
+ mainItem: SBItemStack?,
+ ) {
+ val arrow = layouter.createArrow(bounds.minX + 90, bounds.minY + 54 - 18 / 2)
+ val tooltip = Component.empty()
+ .append(Component.translatableEscape(
+ "firmament.recipe.forge.time",
+ recipe.duration.seconds,
+ ))
+
+ if (recipe.extraText != null && recipe.extraText!!.isNotBlank()) {
+ tooltip
+ .append(Component.nullToEmpty("\n"))
+ .append(Component.nullToEmpty(recipe.extraText))
+ }
+
+ layouter.createTooltip(arrow, tooltip)
+
+ val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8)
+ layouter.createFire(ingredientsCenter, 25)
+ val count = recipe.inputs.size
+ if (count == 1) {
+ layouter.createItemSlot(
+ ingredientsCenter.x, ingredientsCenter.y - 18,
+ SBItemStack(recipe.inputs.single()),
+ RecipeLayouter.SlotKind.SMALL_INPUT,
+ )
+ } else {
+ recipe.inputs.forEachIndexed { idx, ingredient ->
+ val rad = Math.PI * 2 * idx / count
+ layouter.createItemSlot(
+ (ingredientsCenter.x + cos(rad) * 30).toInt(), (ingredientsCenter.y + sin(rad) * 30).toInt(),
+ SBItemStack(ingredient),
+ RecipeLayouter.SlotKind.SMALL_INPUT,
+ )
+ }
+ }
+ layouter.createItemSlot(
+ bounds.minX + 124, bounds.minY + 46,
+ SBItemStack(recipe.outputStack),
+ RecipeLayouter.SlotKind.BIG_OUTPUT
+ )
+ }
+
+ override val displayHeight: Int
+ get() = 104
+
+ override fun getInputs(recipe: NEUForgeRecipe): Collection<SBItemStack> {
+ return recipe.inputs.mapNotNull { SBItemStack(it) }
+ }
+
+ override fun getOutputs(recipe: NEUForgeRecipe): Collection<SBItemStack> {
+ return listOfNotNull(SBItemStack(recipe.outputStack))
+ }
+
+ override val icon: ItemStack = ItemStack(Blocks.ANVIL)
+ override val title: Component = tr("firmament.category.forge", "Forge Recipes")
+ override val identifier: ResourceLocation = Firmament.identifier("forge_recipe")
+
+ override fun findAllRecipes(neuRepository: NEURepository): Iterable<NEUForgeRecipe> {
+ // TODO: theres gotta be an index for these tbh.
+ return neuRepository.items.items.values.flatMap { it.recipes }.filterIsInstance<NEUForgeRecipe>()
+ }
+
+ override val typ: Class<NEUForgeRecipe>
+ get() = NEUForgeRecipe::class.java
+}
diff --git a/src/main/kotlin/repo/recipes/SBReforgeRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBReforgeRecipeRenderer.kt
new file mode 100644
index 0000000..c841dd9
--- /dev/null
+++ b/src/main/kotlin/repo/recipes/SBReforgeRecipeRenderer.kt
@@ -0,0 +1,167 @@
+package moe.nea.firmament.repo.recipes
+
+import io.github.moulberry.repo.NEURepository
+import me.shedaniel.math.Point
+import me.shedaniel.math.Rectangle
+import net.minecraft.network.chat.Component
+import net.minecraft.resources.ResourceLocation
+import net.minecraft.world.entity.EntitySpawnReason
+import net.minecraft.world.entity.EntityType
+import net.minecraft.world.entity.npc.VillagerProfession
+import net.minecraft.world.item.ItemStack
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.gui.entity.EntityRenderer
+import moe.nea.firmament.repo.ExpensiveItemCacheApi
+import moe.nea.firmament.repo.Reforge
+import moe.nea.firmament.repo.ReforgeStore
+import moe.nea.firmament.repo.RepoItemTypeCache
+import moe.nea.firmament.repo.SBItemStack
+import moe.nea.firmament.util.FirmFormatters.formatCommas
+import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.gold
+import moe.nea.firmament.util.grey
+import moe.nea.firmament.util.skyblock.Rarity
+import moe.nea.firmament.util.skyblock.SkyBlockItems
+import moe.nea.firmament.util.skyblockId
+import moe.nea.firmament.util.tr
+
+object SBReforgeRecipeRenderer : GenericRecipeRenderer<Reforge> {
+ @OptIn(ExpensiveItemCacheApi::class)
+ override fun render(
+ recipe: Reforge,
+ bounds: Rectangle,
+ layouter: RecipeLayouter,
+ mainItem: SBItemStack?
+ ) {
+ val inputSlot = layouter.createCyclingItemSlot(
+ bounds.minX + 10, bounds.centerY - 9,
+ if (mainItem != null) listOf(mainItem)
+ else generateAllItems(recipe),
+ RecipeLayouter.SlotKind.SMALL_INPUT
+ )
+ val outputSlut = layouter.createItemSlot(
+ bounds.minX + 10 + 24 + 24, bounds.centerY - 9,
+ null,
+ RecipeLayouter.SlotKind.SMALL_OUTPUT
+ )
+ val statLines = mutableListOf<Pair<String, RecipeLayouter.Updater<Component>>>()
+ for ((i, statId) in recipe.statUniverse.withIndex()) {
+ val label = layouter.createLabel(
+ bounds.minX + 10 + 24 + 24 + 20, bounds.minY + 8 + i * 11,
+ Component.empty()
+ )
+ statLines.add(statId to label)
+ }
+
+ fun updateOutput() {
+ val currentBaseItem = inputSlot.current()
+ outputSlut.update(currentBaseItem.copy(reforge = recipe.reforgeId))
+ val stats = recipe.reforgeStats?.get(currentBaseItem.rarity) ?: mapOf()
+ for ((stat, label) in statLines) {
+ label.update(
+ SBItemStack.Companion.StatLine(
+ SBItemStack.statIdToName(stat), null,
+ valueNum = stats[stat]
+ ).reconstitute(7)
+ )
+ }
+ }
+
+ if (recipe.reforgeStone != null) {
+ layouter.createItemSlot(
+ bounds.minX + 10 + 24, bounds.centerY - 9 - 10,
+ SBItemStack(recipe.reforgeStone),
+ RecipeLayouter.SlotKind.SMALL_INPUT
+ )
+ val d = Rectangle(
+ bounds.minX + 10 + 24, bounds.centerY - 9 + 10,
+ 16, 16
+ )
+ layouter.createItemSlot(
+ d.x, d.y,
+ SBItemStack(SkyBlockItems.REFORGE_ANVIL),
+ RecipeLayouter.SlotKind.DISPLAY
+ )
+ layouter.createTooltip(
+ d,
+ Rarity.entries.mapNotNull { rarity ->
+ recipe.reforgeCosts?.get(rarity)?.let { rarity to it }
+ }.map { (rarity, cost) ->
+ Component.literal("")
+ .append(rarity.text)
+ .append(": ")
+ .append(Component.literal("${formatCommas(cost, 0)} Coins").gold())
+ }
+ )
+ } else {
+ val entity = EntityType.VILLAGER.create(EntityRenderer.fakeWorld, EntitySpawnReason.COMMAND)
+ ?.also {
+ it.villagerData =
+ it.villagerData.withProfession(
+ MC.currentOrDefaultRegistries,
+ VillagerProfession.WEAPONSMITH
+ )
+ }
+ val dim = EntityRenderer.defaultSize
+ val d = Rectangle(
+ Point(bounds.minX + 10 + 24 + 8 - dim.width / 2, bounds.centerY - dim.height / 2),
+ dim
+ )
+ if (entity != null)
+ layouter.createEntity(
+ d,
+ entity
+ )
+ layouter.createTooltip(
+ d,
+ tr(
+ "firmament.recipecategory.reforge.basic",
+ "This is a basic reforge, available at the Blacksmith."
+ ).grey()
+ )
+ }
+ }
+
+ private fun generateAllItems(recipe: Reforge): List<SBItemStack> {
+ return recipe.eligibleItems.flatMap {
+ when (it) {
+ is Reforge.ReforgeEligibilityFilter.AllowsInternalName -> listOf(SBItemStack(it.internalName))
+ is Reforge.ReforgeEligibilityFilter.AllowsItemType ->
+ ReforgeStore.resolveItemType(it.itemType)
+ .flatMapTo(mutableSetOf()) { itemType ->
+ listOf(itemType, itemType.dungeonVariant)
+ }
+ .flatMapTo(mutableSetOf()) { itemType ->
+ RepoItemTypeCache.byItemType[itemType] ?: listOf()
+ }
+ .map { SBItemStack(it.skyblockId) }
+
+ is Reforge.ReforgeEligibilityFilter.AllowsVanillaItemType -> listOf()
+ }
+ }
+ }
+
+ override fun getInputs(recipe: Reforge): Collection<SBItemStack> {
+ val reforgeStone = recipe.reforgeStone ?: return emptyList()
+ return listOf(SBItemStack(reforgeStone))
+ }
+
+ override fun getOutputs(recipe: Reforge): Collection<SBItemStack> {
+ return listOf()
+ }
+
+ @OptIn(ExpensiveItemCacheApi::class)
+ override val icon: ItemStack
+ get() = SBItemStack(SkyBlockItems.REFORGE_ANVIL).asImmutableItemStack()
+ override val title: Component
+ get() = tr("firmament.recipecategory.reforge", "Reforge")
+ override val identifier: ResourceLocation
+ get() = Firmament.identifier("reforge_recipe")
+
+ override fun findAllRecipes(neuRepository: NEURepository): Iterable<Reforge> {
+ return ReforgeStore.allReforges
+ }
+
+ override val typ: Class<Reforge>
+ get() = Reforge::class.java
+}