Add pet upgrade cost recipes

This commit is contained in:
Linnea Gräf
2024-07-10 19:58:51 +02:00
parent c54276ee66
commit 67cc7c22ac
10 changed files with 314 additions and 52 deletions

View File

@@ -1,10 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Firmament
Upstream-Contact: Linnea Gräf <nea@nea.moe>
Source: https://github.com/romangraef/Firmament
# Sample paragraph, commented out:
#
# Files: src/*
# Copyright: $YEAR $NAME <$CONTACT>
# License: ...

9
REUSE.toml Normal file
View File

@@ -0,0 +1,9 @@
#SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
#
#SPDX-License-Identifier: CC0-1.0
version = 1
SPDX-PackageName = "Firmament"
SPDX-PackageSupplier = "Linnea Gräf <nea@nea.moe>"
SPDX-PackageDownloadLocation = "https://github.com/romangraef/Firmament"
annotations = []

View File

@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
@@ -46,7 +47,7 @@ object CraftingOverlay : FirmamentFeature {
val expectedItem = recipe.neuRecipe.inputs[recipeIndex]
val actualStack = slot.stack ?: ItemStack.EMPTY!!
val actualEntry = SBItemEntryDefinition.getEntry(actualStack).value
if ((actualEntry.skyblockId.neuItem != expectedItem.itemId || actualEntry.stackSize < expectedItem.amount) && expectedItem.amount.toInt() != 0) {
if ((actualEntry.skyblockId.neuItem != expectedItem.itemId || actualEntry.getStackSize() < expectedItem.amount) && expectedItem.amount.toInt() != 0) {
event.context.fill(
event.slot.x,
event.slot.y,

View File

@@ -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) {

View File

@@ -47,13 +47,13 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<S
val minecraft = MinecraftClient.getInstance()
override fun getTooltip(entry: EntryStack<SBItemStack>, 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<SBItemStack>): BakedModel {

View File

@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
@@ -27,7 +28,7 @@ object NEUItemEntrySerializer : EntrySerializer<SBItemStack> {
override fun save(entry: EntryStack<SBItemStack>, 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())
}
}
}

View File

@@ -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<Text> = 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<String, String>) {
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<String, String>()
@@ -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<String, String>()
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<String, String>()
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<SBItemStack> {
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<SBItemStack>?, value: SBItemStack): ItemStack {
@@ -167,7 +191,7 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> {
}
override fun isEmpty(entry: EntryStack<SBItemStack>?, value: SBItemStack): Boolean {
return value.stackSize == 0
return value.getStackSize() == 0
}
override fun getIdentifier(entry: EntryStack<SBItemStack>?, value: SBItemStack): Identifier {

View File

@@ -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, NEUCraftingRecipe> {
SBCraftingRecipe(it)
}
val SkyblockCraftingRecipeDynamicGenerator =
neuDisplayGenerator<SBCraftingRecipe, NEUCraftingRecipe> { SBCraftingRecipe(it) }
val SkyblockForgeRecipeDynamicGenerator = neuDisplayGenerator<SBForgeRecipe, NEUForgeRecipe> {
SBForgeRecipe(it)
}
val SkyblockForgeRecipeDynamicGenerator =
neuDisplayGenerator<SBForgeRecipe, NEUForgeRecipe> { SBForgeRecipe(it) }
val SkyblockMobDropRecipeDynamicGenerator = neuDisplayGenerator<SBMobDropRecipe, NEUMobDropRecipe> {
SBMobDropRecipe(it)
}
val SkyblockMobDropRecipeDynamicGenerator =
neuDisplayGenerator<SBMobDropRecipe, NEUMobDropRecipe> { SBMobDropRecipe(it) }
inline fun <D : Display, reified T : NEURecipe> neuDisplayGenerator(noinline mapper: (T) -> D) =
val SkyblockKatRecipeDynamicGenerator =
neuDisplayGenerator<SBKatRecipe, NEUKatUpgradeRecipe> { SBKatRecipe(it) }
inline fun <D : Display, reified T : NEURecipe> neuDisplayGenerator(crossinline mapper: (T) -> D) =
object : DynamicDisplayGenerator<D> {
override fun getRecipeFor(entry: EntryStack<*>): Optional<List<D>> {
if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
@@ -47,7 +49,7 @@ inline fun <D : Display, reified T : NEURecipe> neuDisplayGenerator(noinline map
override fun generate(builder: ViewSearchBuilder): Optional<List<D>> {
if (SBCraftingRecipe.Category.catIdentifier !in builder.categories) return Optional.empty()
return Optional.of(
RepoManager.getAllRecipes().filterIsInstance<T>().map(mapper)
RepoManager.getAllRecipes().filterIsInstance<T>().map { mapper(it) }
.toList()
)
}

View File

@@ -0,0 +1,229 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* 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<SBKatRecipe> {
override fun getCategoryIdentifier(): CategoryIdentifier<SBKatRecipe> =
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<Widget> {
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<Element> {
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()
))
}
}
}

View File

@@ -12,7 +12,7 @@ package moe.nea.firmament.util.item
import com.mojang.authlib.GameProfile
import com.mojang.authlib.minecraft.MinecraftProfileTexture
import com.mojang.authlib.properties.Property
import java.util.*
import java.util.UUID
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable
@@ -63,10 +63,12 @@ fun ItemStack.setEncodedSkullOwner(uuid: UUID, encodedData: String) {
val zeroUUID = UUID.fromString("d3cb85e2-3075-48a1-b213-a9bfb62360c1")
fun ItemStack.setSkullOwner(uuid: UUID, url: String) {
assert(this.item == Items.PLAYER_HEAD)
val gameProfile = GameProfile(uuid, "LameGuy123")
val gameProfile = GameProfile(uuid, "nea89")
gameProfile.setTextures(
MinecraftTexturesPayloadKt(
mapOf(MinecraftProfileTexture.Type.SKIN to MinecraftProfileTextureKt(url))
textures = mapOf(MinecraftProfileTexture.Type.SKIN to MinecraftProfileTextureKt(url)),
profileId = uuid,
profileName = "nea89",
)
)
this.set(DataComponentTypes.PROFILE, ProfileComponent(gameProfile))