diff options
Diffstat (limited to 'src/main/kotlin/repo/recipes')
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 +} |
