diff options
| author | Linnea Gräf <nea@nea.moe> | 2025-11-18 22:17:39 +0100 |
|---|---|---|
| committer | Linnea Gräf <nea@nea.moe> | 2025-11-18 22:17:39 +0100 |
| commit | 5d9b084be9af0f97adad002f3389add22ebf07e5 (patch) | |
| tree | aa79511785386e4af6672429142bd3b38c94edd9 | |
| parent | 54206c51148255320ad8bc8d03c122d24b98b459 (diff) | |
| download | Firmament-5d9b084be9af0f97adad002f3389add22ebf07e5.tar.gz Firmament-5d9b084be9af0f97adad002f3389add22ebf07e5.tar.bz2 Firmament-5d9b084be9af0f97adad002f3389add22ebf07e5.zip | |
feat: add generic reforge renderer
| -rw-r--r-- | src/main/kotlin/gui/entity/EntityRenderer.kt | 3 | ||||
| -rw-r--r-- | src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt | 2 | ||||
| -rw-r--r-- | src/main/kotlin/repo/recipes/RecipeLayouter.kt | 33 | ||||
| -rw-r--r-- | src/main/kotlin/repo/recipes/SBReforgeRecipeRenderer.kt | 167 |
4 files changed, 200 insertions, 5 deletions
diff --git a/src/main/kotlin/gui/entity/EntityRenderer.kt b/src/main/kotlin/gui/entity/EntityRenderer.kt index 2381b69..4972709 100644 --- a/src/main/kotlin/gui/entity/EntityRenderer.kt +++ b/src/main/kotlin/gui/entity/EntityRenderer.kt @@ -3,6 +3,7 @@ package moe.nea.firmament.gui.entity import com.google.gson.Gson import com.google.gson.JsonArray import com.google.gson.JsonObject +import me.shedaniel.math.Dimension import org.joml.Quaternionf import org.joml.Vector3f import kotlin.math.atan @@ -236,5 +237,5 @@ object EntityRenderer { context.disableScissor() } - + val defaultSize = Dimension(50, 80) } diff --git a/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt index 6bc79d5..c029494 100644 --- a/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt +++ b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt @@ -8,7 +8,7 @@ import net.minecraft.network.chat.Component import net.minecraft.resources.ResourceLocation import moe.nea.firmament.repo.SBItemStack -interface GenericRecipeRenderer<T : NEURecipe> { +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> diff --git a/src/main/kotlin/repo/recipes/RecipeLayouter.kt b/src/main/kotlin/repo/recipes/RecipeLayouter.kt index 2c013b7..b211d9c 100644 --- a/src/main/kotlin/repo/recipes/RecipeLayouter.kt +++ b/src/main/kotlin/repo/recipes/RecipeLayouter.kt @@ -4,6 +4,9 @@ import io.github.notenoughupdates.moulconfig.gui.GuiComponent 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 { @@ -15,24 +18,48 @@ 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, } + + fun createCyclingItemSlot( + x: Int, y: Int, + content: List<SBItemStack>, + slotKind: SlotKind + ): CyclingItemSlot + fun createItemSlot( x: Int, y: Int, content: SBItemStack?, slotKind: SlotKind, - ) + ): ItemSlot + + 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 createTooltip(rectangle: Rectangle, label: Component) fun createLabel( x: Int, y: Int, text: Component - ) + ): Updater<Component> fun createArrow(x: Int, y: Int): Rectangle fun createMoulConfig(x: Int, y: Int, w: Int, h: Int, component: GuiComponent) fun createFire(ingredientsCenter: Point, animationTicks: Int) + fun createEntity(rectangle: Rectangle, entity: LivingEntity) } 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 +} |
