From 67cc7c22ac5f6873eb4549bc69db9f014c09c07f Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Wed, 10 Jul 2024 19:58:51 +0200 Subject: Add pet upgrade cost recipes --- .../moe/nea/firmament/rei/FirmamentReiPlugin.kt | 16 +- .../moe/nea/firmament/rei/NEUItemEntryRenderer.kt | 12 +- .../nea/firmament/rei/NEUItemEntrySerializer.kt | 3 +- .../moe/nea/firmament/rei/SBItemEntryDefinition.kt | 50 +++-- .../rei/SkyblockCraftingRecipeDynamicGenerator.kt | 26 +-- .../moe/nea/firmament/rei/recipes/SBKatRecipe.kt | 229 +++++++++++++++++++++ 6 files changed, 298 insertions(+), 38 deletions(-) create mode 100644 src/main/kotlin/moe/nea/firmament/rei/recipes/SBKatRecipe.kt (limited to 'src/main/kotlin/moe/nea/firmament/rei') diff --git a/src/main/kotlin/moe/nea/firmament/rei/FirmamentReiPlugin.kt b/src/main/kotlin/moe/nea/firmament/rei/FirmamentReiPlugin.kt index 4f897bd..86ad98b 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/FirmamentReiPlugin.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/FirmamentReiPlugin.kt @@ -32,6 +32,7 @@ import moe.nea.firmament.features.inventory.CraftingOverlay import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen import moe.nea.firmament.rei.recipes.SBCraftingRecipe import moe.nea.firmament.rei.recipes.SBForgeRecipe +import moe.nea.firmament.rei.recipes.SBKatRecipe import moe.nea.firmament.rei.recipes.SBMobDropRecipe import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.SkyblockId @@ -70,6 +71,7 @@ class FirmamentReiPlugin : REIClientPlugin { registry.add(SBCraftingRecipe.Category) registry.add(SBForgeRecipe.Category) registry.add(SBMobDropRecipe.Category) + registry.add(SBKatRecipe.Category) } override fun registerExclusionZones(zones: ExclusionZones) { @@ -80,14 +82,16 @@ class FirmamentReiPlugin : REIClientPlugin { override fun registerDisplays(registry: DisplayRegistry) { registry.registerDisplayGenerator( SBCraftingRecipe.Category.catIdentifier, - SkyblockCraftingRecipeDynamicGenerator - ) + SkyblockCraftingRecipeDynamicGenerator) registry.registerDisplayGenerator( SBForgeRecipe.Category.categoryIdentifier, - SkyblockForgeRecipeDynamicGenerator - ) - registry.registerDisplayGenerator(SBMobDropRecipe.Category.categoryIdentifier, - SkyblockMobDropRecipeDynamicGenerator) + SkyblockForgeRecipeDynamicGenerator) + registry.registerDisplayGenerator( + SBMobDropRecipe.Category.categoryIdentifier, + SkyblockMobDropRecipeDynamicGenerator) + registry.registerDisplayGenerator( + SBKatRecipe.Category.categoryIdentifier, + SkyblockKatRecipeDynamicGenerator) } override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) { diff --git a/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntryRenderer.kt b/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntryRenderer.kt index 6b5e3fe..ba99b30 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntryRenderer.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntryRenderer.kt @@ -47,13 +47,13 @@ object NEUItemEntryRenderer : EntryRenderer, BatchedEntryRenderer, tooltipContext: TooltipContext): Tooltip? { - return Tooltip.create( - entry.asItemEntry().value.getTooltip( - Item.TooltipContext.DEFAULT, - null, - TooltipType.BASIC - ) + val stack = entry.value.asImmutableItemStack() + val lore = stack.getTooltip( + Item.TooltipContext.DEFAULT, + null, + TooltipType.BASIC ) + return Tooltip.create(lore) } override fun getExtraData(entry: EntryStack): BakedModel { diff --git a/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntrySerializer.kt b/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntrySerializer.kt index 1da123e..6c47cb2 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntrySerializer.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntrySerializer.kt @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2023 Linnea Gräf + * SPDX-FileCopyrightText: 2024 Linnea Gräf * * SPDX-License-Identifier: GPL-3.0-or-later */ @@ -27,7 +28,7 @@ object NEUItemEntrySerializer : EntrySerializer { override fun save(entry: EntryStack, value: SBItemStack): NbtCompound { return NbtCompound().apply { putString(SKYBLOCK_ID_ENTRY, value.skyblockId.neuItem) - putInt(SKYBLOCK_ITEM_COUNT, value.stackSize) + putInt(SKYBLOCK_ITEM_COUNT, value.getStackSize()) } } } diff --git a/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt b/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt index 2a20ff1..3897d01 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt @@ -46,6 +46,9 @@ data class PetData( fun fromHypixel(petInfo: HypixelPetInfo) = PetData( petInfo.tier, petInfo.type, petInfo.exp, ) + fun forLevel(petId: String, rarity: Rarity, level: Int) = PetData( + rarity, petId, ExpLadders.getExpLadder(petId, rarity).getPetExpForLevel(level).toDouble() + ) } val levelData by lazy { ExpLadders.getExpLadder(petId, rarity).getPetLevel(exp) } @@ -54,10 +57,22 @@ data class PetData( data class SBItemStack( val skyblockId: SkyblockId, val neuItem: NEUItem?, - val stackSize: Int, - val petData: PetData?, + private var stackSize: Int, + private var petData: PetData?, val extraLore: List = emptyList(), ) { + + fun getStackSize() = stackSize + fun setStackSize(newSize: Int) { + this.stackSize = stackSize + this.itemStack_ = null + } + fun getPetData() = petData + fun setPetData(petData: PetData?) { + this.petData = petData + this.itemStack_ = null + } + constructor(skyblockId: SkyblockId, petData: PetData) : this( skyblockId, RepoManager.getNEUItem(skyblockId), @@ -87,7 +102,7 @@ data class SBItemStack( } private fun injectReplacementDataForPets(replacementData: MutableMap) { - if (petData == null) return + val petData = this.petData ?: return val petInfo = RepoManager.neuRepo.constants.petNumbers[petData.petId]?.get(petData.rarity) ?: return if (petData.isStub) { val mapLow = mutableMapOf() @@ -105,14 +120,23 @@ data class SBItemStack( } } - private val itemStack: ItemStack by lazy(LazyThreadSafetyMode.NONE) { - if (skyblockId == SkyblockId.COINS) - return@lazy ItemCache.coinItem(stackSize).also { it.appendLore(extraLore) } - val replacementData = mutableMapOf() - injectReplacementDataForPets(replacementData) - return@lazy neuItem.asItemStack(idHint = skyblockId, replacementData).copyWithCount(stackSize) - .also { it.appendLore(extraLore) } - } + + private var itemStack_: ItemStack? = null + + private val itemStack: ItemStack + get() { + val itemStack = itemStack_ ?: run { + if (skyblockId == SkyblockId.COINS) + return@run ItemCache.coinItem(stackSize).also { it.appendLore(extraLore) } + val replacementData = mutableMapOf() + injectReplacementDataForPets(replacementData) + return@run neuItem.asItemStack(idHint = skyblockId, replacementData).copyWithCount(stackSize) + .also { it.appendLore(extraLore) } + } + if (itemStack_ == null) + itemStack_ = itemStack + return itemStack + } fun asImmutableItemStack(): ItemStack { return itemStack @@ -125,7 +149,7 @@ data class SBItemStack( object SBItemEntryDefinition : EntryDefinition { override fun equals(o1: SBItemStack, o2: SBItemStack, context: ComparisonContext): Boolean { - return o1.skyblockId == o2.skyblockId && o1.stackSize == o2.stackSize + return o1.skyblockId == o2.skyblockId && o1.getStackSize() == o2.getStackSize() } override fun cheatsAs(entry: EntryStack?, value: SBItemStack): ItemStack { @@ -167,7 +191,7 @@ object SBItemEntryDefinition : EntryDefinition { } override fun isEmpty(entry: EntryStack?, value: SBItemStack): Boolean { - return value.stackSize == 0 + return value.getStackSize() == 0 } override fun getIdentifier(entry: EntryStack?, value: SBItemStack): Identifier { diff --git a/src/main/kotlin/moe/nea/firmament/rei/SkyblockCraftingRecipeDynamicGenerator.kt b/src/main/kotlin/moe/nea/firmament/rei/SkyblockCraftingRecipeDynamicGenerator.kt index ac5a1fc..ead2119 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/SkyblockCraftingRecipeDynamicGenerator.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/SkyblockCraftingRecipeDynamicGenerator.kt @@ -9,32 +9,34 @@ package moe.nea.firmament.rei 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.NEURecipe -import java.util.* +import java.util.Optional import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator import me.shedaniel.rei.api.client.view.ViewSearchBuilder import me.shedaniel.rei.api.common.display.Display import me.shedaniel.rei.api.common.entry.EntryStack import moe.nea.firmament.rei.recipes.SBCraftingRecipe import moe.nea.firmament.rei.recipes.SBForgeRecipe +import moe.nea.firmament.rei.recipes.SBKatRecipe import moe.nea.firmament.rei.recipes.SBMobDropRecipe import moe.nea.firmament.repo.RepoManager -val SkyblockCraftingRecipeDynamicGenerator = neuDisplayGenerator { - SBCraftingRecipe(it) -} +val SkyblockCraftingRecipeDynamicGenerator = + neuDisplayGenerator { SBCraftingRecipe(it) } -val SkyblockForgeRecipeDynamicGenerator = neuDisplayGenerator { - SBForgeRecipe(it) -} +val SkyblockForgeRecipeDynamicGenerator = + neuDisplayGenerator { SBForgeRecipe(it) } -val SkyblockMobDropRecipeDynamicGenerator = neuDisplayGenerator { - SBMobDropRecipe(it) -} +val SkyblockMobDropRecipeDynamicGenerator = + neuDisplayGenerator { SBMobDropRecipe(it) } -inline fun neuDisplayGenerator(noinline mapper: (T) -> D) = +val SkyblockKatRecipeDynamicGenerator = + neuDisplayGenerator { SBKatRecipe(it) } + +inline fun neuDisplayGenerator(crossinline mapper: (T) -> D) = object : DynamicDisplayGenerator { override fun getRecipeFor(entry: EntryStack<*>): Optional> { if (entry.type != SBItemEntryDefinition.type) return Optional.empty() @@ -47,7 +49,7 @@ inline fun neuDisplayGenerator(noinline map override fun generate(builder: ViewSearchBuilder): Optional> { if (SBCraftingRecipe.Category.catIdentifier !in builder.categories) return Optional.empty() return Optional.of( - RepoManager.getAllRecipes().filterIsInstance().map(mapper) + RepoManager.getAllRecipes().filterIsInstance().map { mapper(it) } .toList() ) } diff --git a/src/main/kotlin/moe/nea/firmament/rei/recipes/SBKatRecipe.kt b/src/main/kotlin/moe/nea/firmament/rei/recipes/SBKatRecipe.kt new file mode 100644 index 0000000..fc0deaf --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/rei/recipes/SBKatRecipe.kt @@ -0,0 +1,229 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.rei.recipes + +import io.github.moulberry.repo.data.NEUKatUpgradeRecipe +import io.github.notenoughupdates.moulconfig.common.IMinecraft +import io.github.notenoughupdates.moulconfig.gui.GuiComponent +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext +import io.github.notenoughupdates.moulconfig.gui.MouseEvent +import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent +import io.github.notenoughupdates.moulconfig.observer.GetSetter +import io.github.notenoughupdates.moulconfig.observer.Property +import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext +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.WidgetWithBounds +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.time.Duration.Companion.seconds +import net.minecraft.block.Blocks +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.Element +import net.minecraft.item.Items +import net.minecraft.text.Text +import moe.nea.firmament.Firmament +import moe.nea.firmament.rei.PetData +import moe.nea.firmament.rei.SBItemEntryDefinition +import moe.nea.firmament.rei.SBItemStack +import moe.nea.firmament.util.FirmFormatters +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.SkyblockId + +class SBKatRecipe(override val neuRecipe: NEUKatUpgradeRecipe) : SBRecipe() { + override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier + + object Category : DisplayCategory { + override fun getCategoryIdentifier(): CategoryIdentifier = + CategoryIdentifier.of(Firmament.MOD_ID, "kat_recipe") + + override fun getTitle(): Text = Text.literal("Kat Pet Upgrade") + override fun getDisplayHeight(): Int { + return 100 + } + + override fun getIcon(): Renderer = EntryStacks.of(Items.BONE) + override fun setupDisplay(display: SBKatRecipe, bounds: Rectangle): List { + return buildList { + val arrowWidth = 24 + val recipe = display.neuRecipe + val levelValue = Property.upgrade(GetSetter.floating(0F)) + val slider = SliderComponent(levelValue, 1F, 100F, 1f, 100) + val outputStack = SBItemStack(SkyblockId(recipe.output.itemId)) + val inputStack = SBItemStack(SkyblockId(recipe.input.itemId)) + val inputLevelLabelCenter = Point(bounds.minX + 30 - 18 + 5 + 8, bounds.minY + 25) + val inputLevelLabel = Widgets.createLabel( + inputLevelLabelCenter, + Text.literal("")).centered() + val outputLevelLabelCenter = Point(bounds.maxX - 30 + 8, bounds.minY + 25) + val outputLevelLabel = Widgets.createLabel( + outputLevelLabelCenter, + Text.literal("")).centered() + val coinStack = SBItemStack(SkyblockId.COINS, recipe.coins.toInt()) + levelValue.whenChanged { oldValue, newValue -> + if (oldValue.toInt() == newValue.toInt()) return@whenChanged + val oldInput = inputStack.getPetData() ?: return@whenChanged + val newInput = PetData.forLevel(oldInput.petId, oldInput.rarity, newValue.toInt()) + inputStack.setPetData(newInput) + val oldOutput = outputStack.getPetData() ?: return@whenChanged + val newOutput = PetData(oldOutput.rarity, oldOutput.petId, newInput.exp) + outputStack.setPetData(newOutput) + inputLevelLabel.message = Text.literal(newInput.levelData.currentLevel.toString()) + inputLevelLabel.bounds.location = Point( + inputLevelLabelCenter.x - MC.font.getWidth(inputLevelLabel.message) / 2, + inputLevelLabelCenter.y) + outputLevelLabel.message = Text.literal(newOutput.levelData.currentLevel.toString()) + outputLevelLabel.bounds.location = Point( + outputLevelLabelCenter.x - MC.font.getWidth(outputLevelLabel.message) / 2, + outputLevelLabelCenter.y) + coinStack.setStackSize((recipe.coins * (1 - 0.3 * newValue / 100)).toInt()) + } + levelValue.set(1F) + add(Widgets.createRecipeBase(bounds)) + add(wrapWidget(Rectangle(bounds.centerX - slider.width / 2, + bounds.maxY - 30, + slider.width, + slider.height), + slider)) + add(Widgets.withTooltip( + Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)), + Text.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds)))) + + add(Widgets.createResultSlotBackground(Point(bounds.maxX - 30, bounds.minY + 40))) + add(inputLevelLabel) + add(outputLevelLabel) + add(Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground() + .entry(SBItemEntryDefinition.getEntry(outputStack))) + add(Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput() + .entry(SBItemEntryDefinition.getEntry(inputStack))) + + val allInputs = recipe.items.map { SBItemEntryDefinition.getEntry(it) } + + listOf(SBItemEntryDefinition.getEntry(coinStack)) + for ((index, item) in allInputs.withIndex()) { + add(Widgets.createSlot( + Point(bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2, + bounds.minY + 20)) + .markInput() + .entry(item)) + } + } + } + } +} + +fun wrapWidget(bounds: Rectangle, component: GuiComponent): Widget { + return object : WidgetWithBounds() { + override fun getBounds(): Rectangle { + return bounds + } + + override fun children(): List { + return listOf() + } + + override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { + context.matrices.push() + context.matrices.translate(bounds.minX.toFloat(), bounds.minY.toFloat(), 0F) + component.render( + GuiImmediateContext( + ModernRenderContext(context), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseX - bounds.minX, mouseY - bounds.minY, + mouseX, mouseY, + mouseX.toFloat(), mouseY.toFloat() + )) + context.matrices.pop() + } + + override fun mouseMoved(mouseX: Double, mouseY: Double) { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + component.mouseEvent(MouseEvent.Move(0F, 0F), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + } + + override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + return component.mouseEvent(MouseEvent.Click(button, true), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + } + + override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + return component.mouseEvent(MouseEvent.Click(button, false), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + } + + override fun mouseDragged( + mouseX: Double, + mouseY: Double, + button: Int, + deltaX: Double, + deltaY: Double + ): Boolean { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + return component.mouseEvent(MouseEvent.Move(deltaX.toFloat(), deltaY.toFloat()), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + + } + + override fun mouseScrolled( + mouseX: Double, + mouseY: Double, + horizontalAmount: Double, + verticalAmount: Double + ): Boolean { + val mouseXInt = mouseX.toInt() + val mouseYInt = mouseY.toInt() + return component.mouseEvent(MouseEvent.Scroll(verticalAmount.toFloat()), + GuiImmediateContext( + IMinecraft.instance.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + )) + } + } +} -- cgit