Merge branch 'mc-1.21.3'

This commit is contained in:
Linnea Gräf
2024-12-31 16:52:29 +01:00
53 changed files with 2182 additions and 370 deletions

View File

@@ -1,19 +1,22 @@
package moe.nea.firmament.compat.rei
import me.shedaniel.math.Dimension
import me.shedaniel.math.FloatingDimension
import me.shedaniel.math.Point
import me.shedaniel.math.Rectangle
import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.Drawable
import net.minecraft.client.gui.Element
import net.minecraft.client.gui.ParentElement
import net.minecraft.entity.LivingEntity
import moe.nea.firmament.gui.entity.EntityRenderer
import moe.nea.firmament.util.ErrorUtil
class EntityWidget(val entity: LivingEntity?, val point: Point) : WidgetWithBounds() {
class EntityWidget(
val entity: LivingEntity?,
val point: Point,
val size: FloatingDimension = FloatingDimension(defaultSize)
) : WidgetWithBounds() {
override fun children(): List<Element> {
return emptyList()
}
@@ -22,18 +25,35 @@ class EntityWidget(val entity: LivingEntity?, val point: Point) : WidgetWithBoun
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
try {
if (!hasErrored)
EntityRenderer.renderEntity(entity!!, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat())
context.matrices.push()
if (!hasErrored) {
context.matrices.translate(point.x.toDouble(), point.y.toDouble(), 0.0)
val xScale = size.width / defaultSize.width.toDouble()
val yScale = size.height / defaultSize.height.toDouble()
context.matrices.scale(xScale.toFloat(), yScale.toFloat(), 1.0F)
EntityRenderer.renderEntity(
entity!!,
context,
0, 0,
(mouseX - point.x) * xScale,
(mouseY - point.y) * yScale)
}
} catch (ex: Exception) {
ErrorUtil.softError("Failed to render constructed entity: $entity", ex)
hasErrored = true
} finally {
context.matrices.pop()
}
if (hasErrored) {
context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt())
context.fill(point.x, point.y, point.x + size.width.toInt(), point.y + size.height.toInt(), 0xFFAA2222.toInt())
}
}
companion object {
val defaultSize = Dimension(50, 80)
}
override fun getBounds(): Rectangle {
return Rectangle(point, Dimension(50, 80))
return Rectangle(point, size)
}
}

View File

@@ -24,6 +24,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.SBReforgeRecipe
import moe.nea.firmament.events.HandledScreenPushREIEvent
import moe.nea.firmament.features.inventory.CraftingOverlay
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
@@ -78,6 +79,7 @@ class FirmamentReiPlugin : REIClientPlugin {
registry.add(SBForgeRecipe.Category)
registry.add(SBMobDropRecipe.Category)
registry.add(SBKatRecipe.Category)
registry.add(SBReforgeRecipe.Category)
registry.add(SBEssenceUpgradeRecipe.Category)
}
@@ -90,6 +92,10 @@ class FirmamentReiPlugin : REIClientPlugin {
registry.registerDisplayGenerator(
SBCraftingRecipe.Category.catIdentifier,
SkyblockCraftingRecipeDynamicGenerator)
registry.registerDisplayGenerator(
SBReforgeRecipe.catIdentifier,
SBReforgeRecipe.DynamicGenerator
)
registry.registerDisplayGenerator(
SBForgeRecipe.Category.categoryIdentifier,
SkyblockForgeRecipeDynamicGenerator)

View File

@@ -0,0 +1,210 @@
package moe.nea.firmament.compat.rei.recipes
import java.util.Optional
import me.shedaniel.math.Dimension
import me.shedaniel.math.FloatingDimension
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.Label
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.client.registry.display.DynamicDisplayGenerator
import me.shedaniel.rei.api.client.view.ViewSearchBuilder
import me.shedaniel.rei.api.common.category.CategoryIdentifier
import me.shedaniel.rei.api.common.display.Display
import me.shedaniel.rei.api.common.display.DisplaySerializer
import me.shedaniel.rei.api.common.entry.EntryIngredient
import me.shedaniel.rei.api.common.entry.EntryStack
import net.minecraft.entity.EntityType
import net.minecraft.entity.SpawnReason
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.village.VillagerProfession
import moe.nea.firmament.Firmament
import moe.nea.firmament.compat.rei.EntityWidget
import moe.nea.firmament.compat.rei.SBItemEntryDefinition
import moe.nea.firmament.gui.entity.EntityRenderer
import moe.nea.firmament.repo.Reforge
import moe.nea.firmament.repo.ReforgeStore
import moe.nea.firmament.repo.RepoItemTypeCache
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.AprilFoolsUtil
import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.gold
import moe.nea.firmament.util.grey
import moe.nea.firmament.util.skyblock.ItemType
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
class SBReforgeRecipe(
val reforge: Reforge,
val limitToItem: SkyblockId?,
) : Display {
companion object {
val catIdentifier = CategoryIdentifier.of<SBReforgeRecipe>(Firmament.MOD_ID, "reforge_recipe")
}
object Category : DisplayCategory<SBReforgeRecipe> {
override fun getCategoryIdentifier(): CategoryIdentifier<out SBReforgeRecipe> {
return catIdentifier
}
override fun getTitle(): Text {
return tr("firmament.recipecategory.reforge", "Reforge")
}
override fun getIcon(): Renderer {
return SBItemEntryDefinition.getEntry(SkyBlockItems.REFORGE_ANVIL)
}
override fun setupDisplay(display: SBReforgeRecipe, bounds: Rectangle): MutableList<Widget> {
val list = mutableListOf<Widget>()
list.add(Widgets.createRecipeBase(bounds))
val inputSlot = Widgets.createSlot(Point(bounds.minX + 10, bounds.centerY - 9))
.markInput().entries(display.inputItems)
list.add(inputSlot)
if (display.reforgeStone != null) {
list.add(Widgets.createSlot(Point(bounds.minX + 10 + 24, bounds.centerY - 9 - 10))
.markInput().entry(display.reforgeStone))
list.add(Widgets.withTooltip(
Widgets.withTranslate(Widgets.wrapRenderer(
Rectangle(Point(bounds.minX + 10 + 24, bounds.centerY - 9 + 10), Dimension(16, 16)),
SBItemEntryDefinition.getEntry(SkyBlockItems.REFORGE_ANVIL)), 0.0, 0.0, 150.0),
Rarity.entries.mapNotNull { rarity ->
display.reforge.reforgeCosts?.get(rarity)?.let { rarity to it }
}.map { (rarity, cost) ->
Text.literal("")
.append(rarity.text)
.append(": ")
.append(Text.literal("${FirmFormatters.formatCommas(cost, 0)} Coins").gold())
}
))
} else {
val size = if (AprilFoolsUtil.isAprilFoolsDay) 1.2 else 0.6
val dimension =
FloatingDimension(EntityWidget.defaultSize.width * size, EntityWidget.defaultSize.height * size)
list.add(Widgets.withTooltip(
EntityWidget(
EntityType.VILLAGER.create(EntityRenderer.fakeWorld, SpawnReason.COMMAND)
?.also { it.villagerData = it.villagerData.withProfession(VillagerProfession.WEAPONSMITH) },
Point(bounds.minX + 10 + 24 + 8 - dimension.width / 2, bounds.centerY - dimension.height / 2),
dimension
),
tr("firmament.recipecategory.reforge.basic",
"This is a basic reforge, available at the Blacksmith.").grey()
))
}
list.add(Widgets.createSlot(Point(bounds.minX + 10 + 24 + 24, bounds.centerY - 9))
.markInput().entries(display.outputItems))
val statToLineMappings = mutableListOf<Pair<String, Label>>()
for ((i, statId) in display.reforge.statUniverse.withIndex()) {
val label = Widgets.createLabel(
Point(bounds.minX + 10 + 24 + 24 + 20, bounds.minY + 8 + i * 11),
SBItemStack.Companion.StatLine(SBItemStack.statIdToName(statId), null).reconstitute(7))
.horizontalAlignment(Label.LEFT_ALIGNED)
statToLineMappings.add(statId to label)
list.add(label)
}
fun updateStatLines() {
val entry = inputSlot.currentEntry?.castValue<SBItemStack>() ?: return
val stats = display.reforge.reforgeStats?.get(entry.rarity) ?: mapOf()
for ((stat, label) in statToLineMappings) {
label.message =
SBItemStack.Companion.StatLine(
SBItemStack.statIdToName(stat), null,
valueNum = stats[stat]
).reconstitute(7)
}
}
updateStatLines()
inputSlot.withEntriesListener { updateStatLines() }
return list
}
}
object DynamicGenerator : DynamicDisplayGenerator<SBReforgeRecipe> {
fun getRecipesForSBItemStack(item: SBItemStack): Optional<List<SBReforgeRecipe>> {
val reforgeRecipes = mutableListOf<SBReforgeRecipe>()
for (reforge in ReforgeStore.findEligibleForInternalName(item.skyblockId)) {
reforgeRecipes.add(SBReforgeRecipe(reforge, item.skyblockId))
}
for (reforge in ReforgeStore.findEligibleForItem(item.itemType ?: ItemType.NIL)) {
reforgeRecipes.add(SBReforgeRecipe(reforge, item.skyblockId))
}
if (reforgeRecipes.isEmpty()) return Optional.empty()
return Optional.of(reforgeRecipes)
}
override fun getRecipeFor(entry: EntryStack<*>): Optional<List<SBReforgeRecipe>> {
if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
val item = entry.castValue<SBItemStack>()
return getRecipesForSBItemStack(item)
}
override fun getUsageFor(entry: EntryStack<*>): Optional<List<SBReforgeRecipe>> {
if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
val item = entry.castValue<SBItemStack>()
ReforgeStore.byReforgeStone[item.skyblockId]?.let { stoneReforge ->
return Optional.of(listOf(SBReforgeRecipe(stoneReforge, null)))
}
return getRecipesForSBItemStack(item)
}
override fun generate(builder: ViewSearchBuilder): Optional<List<SBReforgeRecipe>> {
// TODO: check builder.recipesFor and such and optionally return all reforge recipes
return Optional.empty()
}
}
private val eligibleItems =
if (limitToItem != null) listOfNotNull(RepoManager.getNEUItem(limitToItem))
else reforge.eligibleItems.flatMap {
when (it) {
is Reforge.ReforgeEligibilityFilter.AllowsInternalName ->
listOfNotNull(RepoManager.getNEUItem(it.internalName))
is Reforge.ReforgeEligibilityFilter.AllowsItemType ->
ReforgeStore.resolveItemType(it.itemType)
.flatMap {
RepoItemTypeCache.byItemType[it] ?: listOf()
}
is Reforge.ReforgeEligibilityFilter.AllowsVanillaItemType -> {
listOf() // TODO: add filter support for this and potentially rework this to search for the declared item type in repo, instead of remapped item type
}
}
}
private val inputItems = eligibleItems.map { SBItemEntryDefinition.getEntry(it.skyblockId) }
private val outputItems =
inputItems.map { SBItemEntryDefinition.getEntry(it.value.copy(reforge = reforge.reforgeId)) }
private val reforgeStone = reforge.reforgeStone?.let(SBItemEntryDefinition::getEntry)
private val inputEntries =
listOf(EntryIngredient.of(inputItems)) + listOfNotNull(reforgeStone?.let(EntryIngredient::of))
private val outputEntries = listOf(EntryIngredient.of(outputItems))
override fun getInputEntries(): List<EntryIngredient> {
return inputEntries
}
override fun getOutputEntries(): List<EntryIngredient> {
return outputEntries
}
override fun getCategoryIdentifier(): CategoryIdentifier<*> {
return catIdentifier
}
override fun getDisplayLocation(): Optional<Identifier> {
return Optional.empty()
}
override fun getSerializer(): DisplaySerializer<out Display>? {
return null
}
}

View File

@@ -11,9 +11,11 @@ import dev.isxander.yacl3.api.OptionGroup
import dev.isxander.yacl3.api.YetAnotherConfigLib
import dev.isxander.yacl3.api.controller.ControllerBuilder
import dev.isxander.yacl3.api.controller.DoubleSliderControllerBuilder
import dev.isxander.yacl3.api.controller.EnumControllerBuilder
import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder
import dev.isxander.yacl3.api.controller.StringControllerBuilder
import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder
import dev.isxander.yacl3.api.controller.ValueFormatter
import dev.isxander.yacl3.gui.YACLScreen
import dev.isxander.yacl3.gui.tab.ListHolderWidget
import kotlin.time.Duration
@@ -23,8 +25,10 @@ import net.minecraft.client.gui.Element
import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.Text
import moe.nea.firmament.gui.config.BooleanHandler
import moe.nea.firmament.gui.config.ChoiceHandler
import moe.nea.firmament.gui.config.ClickHandler
import moe.nea.firmament.gui.config.DurationHandler
import moe.nea.firmament.gui.config.EnumRenderer
import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider
import moe.nea.firmament.gui.config.HudMeta
import moe.nea.firmament.gui.config.HudMetaHandler
@@ -89,6 +93,10 @@ class YaclIntegration : FirmamentConfigScreenProvider {
}
.build()
is ChoiceHandler<*> -> return createDefaultBinding {
createChoiceBinding(handler as ChoiceHandler<*>, managedOption as ManagedOption<*>, it as Option<*>)
}.build()
is BooleanHandler -> return createDefaultBinding(TickBoxControllerBuilder::create).build()
is StringHandler -> return createDefaultBinding(StringControllerBuilder::create).build()
is IntegerHandler -> return createDefaultBinding {
@@ -114,6 +122,27 @@ class YaclIntegration : FirmamentConfigScreenProvider {
}
}
private enum class Sacrifice {}
private fun createChoiceBinding(
handler: ChoiceHandler<*>,
managedOption: ManagedOption<*>,
option: Option<*>
): ControllerBuilder<Any> {
val b = EnumControllerBuilder.create(option as Option<Sacrifice>)
b.enumClass(handler.enumClass as Class<Sacrifice>)
/**
* This is a function with E to avoid realizing the Sacrifice outside of a `X<E>` wrapper.
*/
fun <E : Enum<*>> makeValueFormatter(): ValueFormatter<E> {
return ValueFormatter<E> {
(handler.renderer as EnumRenderer<E>).getName(managedOption as ManagedOption<E>, it)
}
}
b.formatValue(makeValueFormatter())
return b as ControllerBuilder<Any>
}
fun buildConfig(): YetAnotherConfigLib {
return YetAnotherConfigLib.createBuilder()