From 3c437efa12d536cdc347895b3e37bc3f5acc0193 Mon Sep 17 00:00:00 2001 From: nea Date: Sat, 3 Jun 2023 19:58:34 +0200 Subject: Add pet level data to /firm pv --- .../nea/firmament/gui/profileviewer/PetsPage.kt | 5 +- .../moe/nea/firmament/rei/FirmamentReiPlugin.kt | 5 +- .../nea/firmament/rei/NEUItemEntrySerializer.kt | 2 +- .../moe/nea/firmament/rei/SBItemEntryDefinition.kt | 52 +++++++++--- .../kotlin/moe/nea/firmament/repo/ExpLadder.kt | 92 ++++++++++++++++++++++ .../kotlin/moe/nea/firmament/repo/ItemCache.kt | 35 +++++++- .../kotlin/moe/nea/firmament/repo/RepoManager.kt | 20 ++++- 7 files changed, 194 insertions(+), 17 deletions(-) create mode 100644 src/main/kotlin/moe/nea/firmament/repo/ExpLadder.kt (limited to 'src') diff --git a/src/main/kotlin/moe/nea/firmament/gui/profileviewer/PetsPage.kt b/src/main/kotlin/moe/nea/firmament/gui/profileviewer/PetsPage.kt index bf412b9..4c669df 100644 --- a/src/main/kotlin/moe/nea/firmament/gui/profileviewer/PetsPage.kt +++ b/src/main/kotlin/moe/nea/firmament/gui/profileviewer/PetsPage.kt @@ -6,7 +6,6 @@ import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItem import io.github.cottonmc.cotton.gui.widget.WText import io.github.cottonmc.cotton.gui.widget.WWidget -import io.github.cottonmc.cotton.gui.widget.data.InputResult import io.github.cottonmc.cotton.gui.widget.data.Insets import io.github.cottonmc.cotton.gui.widget.icon.Icon import io.github.cottonmc.cotton.gui.widget.icon.ItemIcon @@ -15,8 +14,8 @@ import net.minecraft.client.util.math.MatrixStack import net.minecraft.item.Items import net.minecraft.text.Text import moe.nea.firmament.gui.WTightScrollPanel +import moe.nea.firmament.rei.PetData import moe.nea.firmament.rei.SBItemStack -import moe.nea.firmament.util.MC object PetsPage : ProfilePage { override fun getElements(profileViewer: ProfileViewer): WWidget { @@ -26,7 +25,7 @@ object PetsPage : ProfilePage { it.add((WTightScrollPanel(WGridPanel().also { it.setGaps(8, 8) for ((i, pet) in profileViewer.member.pets.withIndex()) { - val stack = SBItemStack(pet.itemId, 1).asItemStack() + val stack = SBItemStack(pet.itemId, PetData(pet.tier, pet.type.name, pet.exp)).asItemStack() it.add(object : WItem(stack) { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { BackgroundPainter.SLOT.paintBackground(matrices, x, y, this) diff --git a/src/main/kotlin/moe/nea/firmament/rei/FirmamentReiPlugin.kt b/src/main/kotlin/moe/nea/firmament/rei/FirmamentReiPlugin.kt index 87efaac..8b8383a 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/FirmamentReiPlugin.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/FirmamentReiPlugin.kt @@ -35,6 +35,7 @@ import moe.nea.firmament.recipes.SBForgeRecipe import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.SkyblockId +import moe.nea.firmament.util.skyblockId class FirmamentReiPlugin : REIClientPlugin { @@ -73,7 +74,7 @@ class FirmamentReiPlugin : REIClientPlugin { registry.group( SkyblockId(parent).identifier, Text.literal(RepoManager.getNEUItem(SkyblockId(parent))?.displayName ?: parent), - (children + parent).map { SBItemEntryDefinition.getEntry(RepoManager.getNEUItem(SkyblockId(it))) }) + (children + parent).map { SBItemEntryDefinition.getEntry(SkyblockId(it)) }) } } @@ -84,7 +85,7 @@ class FirmamentReiPlugin : REIClientPlugin { override fun registerEntries(registry: EntryRegistry) { RepoManager.neuRepo.items?.items?.values?.forEach { if (!it.isVanilla) - registry.addEntry(SBItemEntryDefinition.getEntry(it)) + registry.addEntry(SBItemEntryDefinition.getEntry(it.skyblockId)) } } } diff --git a/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntrySerializer.kt b/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntrySerializer.kt index bf59940..7d98558 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntrySerializer.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/NEUItemEntrySerializer.kt @@ -34,7 +34,7 @@ object NEUItemEntrySerializer : EntrySerializer { override fun read(tag: NbtCompound): SBItemStack { val id = SkyblockId(tag.getString(SKYBLOCK_ID_ENTRY)) val count = if (tag.contains(SKYBLOCK_ITEM_COUNT)) tag.getInt(SKYBLOCK_ITEM_COUNT) else 1 - return SBItemStack(id, RepoManager.getNEUItem(id), count) + return SBItemStack(id, count) } override fun save(entry: EntryStack, value: SBItemStack): NbtCompound { diff --git a/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt b/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt index c38f350..f5f3401 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt @@ -20,6 +20,7 @@ package moe.nea.firmament.rei import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUItem +import io.github.moulberry.repo.data.Rarity import java.util.stream.Stream import me.shedaniel.rei.api.client.entry.renderer.EntryRenderer import me.shedaniel.rei.api.common.entry.EntrySerializer @@ -33,28 +34,64 @@ import net.minecraft.registry.tag.TagKey import net.minecraft.text.Text import net.minecraft.util.Identifier import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry +import moe.nea.firmament.repo.ExpLadders import moe.nea.firmament.repo.ItemCache import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.RepoManager +import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.util.skyblockId // TODO: add in extra data like pet info, into this structure +data class PetData( + val rarity: Rarity, + val petId: String, + val exp: Double, +) { + val levelData by lazy { ExpLadders.getExpLadder(petId, rarity).getPetLevel(exp) } +} + data class SBItemStack( val skyblockId: SkyblockId, val neuItem: NEUItem?, val stackSize: Int, + val petData: PetData?, ) { + constructor(skyblockId: SkyblockId, petData: PetData) : this( + skyblockId, + RepoManager.getNEUItem(skyblockId), + 1, + petData + ) + constructor(skyblockId: SkyblockId, stackSize: Int = 1) : this( skyblockId, RepoManager.getNEUItem(skyblockId), - stackSize + stackSize, + RepoManager.getPotentialStubPetData(skyblockId) ) - fun asItemStack(): ItemStack { + private val itemStack by lazy { if (skyblockId == SkyblockId.COINS) - return ItemCache.coinItem(stackSize) - return neuItem.asItemStack(idHint = skyblockId).copyWithCount(stackSize) + return@lazy ItemCache.coinItem(stackSize) + val replacementData = mutableMapOf() + if (petData != null) { + val stats = RepoManager.neuRepo.constants.petNumbers[petData.petId]?.get(petData.rarity) + ?.interpolatedStatsAtLevel(petData.levelData.currentLevel) + if (stats != null) { + stats.otherNumbers.forEachIndexed { index, it -> + replacementData[index.toString()] = FirmFormatters.toString(it, 0) + } + stats.statNumbers.forEach { (t, u) -> + replacementData[t] = FirmFormatters.toString(u, 0) + } + } + replacementData["LVL"] = petData.levelData.currentLevel.toString() + } + return@lazy neuItem.asItemStack(idHint = skyblockId, replacementData).copyWithCount(stackSize) + } + + fun asItemStack(): ItemStack { + return itemStack.copy() } } @@ -112,11 +149,8 @@ object SBItemEntryDefinition : EntryDefinition { fun getEntry(sbItemStack: SBItemStack): EntryStack = EntryStack.of(this, sbItemStack) - fun getEntry(neuItem: NEUItem?, count: Int = 1): EntryStack = - getEntry(SBItemStack(neuItem?.skyblockId ?: SkyblockId.NULL, neuItem, 1)) - fun getEntry(skyblockId: SkyblockId, count: Int = 1): EntryStack = - getEntry(SBItemStack(skyblockId, RepoManager.getNEUItem(skyblockId), count)) + getEntry(SBItemStack(skyblockId, count)) fun getEntry(ingredient: NEUIngredient): EntryStack = getEntry(SkyblockId(ingredient.itemId), count = ingredient.amount.toInt()) diff --git a/src/main/kotlin/moe/nea/firmament/repo/ExpLadder.kt b/src/main/kotlin/moe/nea/firmament/repo/ExpLadder.kt new file mode 100644 index 0000000..082483b --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/repo/ExpLadder.kt @@ -0,0 +1,92 @@ +package moe.nea.firmament.repo + +import com.google.common.cache.CacheBuilder +import com.google.common.cache.CacheLoader +import io.github.moulberry.repo.IReloadable +import io.github.moulberry.repo.NEURepository +import io.github.moulberry.repo.constants.PetLevelingBehaviourOverride +import io.github.moulberry.repo.data.Rarity + +object ExpLadders : IReloadable { + + data class PetLevel( + val currentLevel: Int, + val maxLevel: Int, + val expRequiredForNextLevel: Long, + val expRequiredForMaxLevel: Long, + val expInCurrentLevel: Float, + var expTotal: Float, + ) { + val percentageToNextLevel: Float = expInCurrentLevel / expRequiredForNextLevel + } + + data class ExpLadder( + val individualLevelCost: List, + ) { + val cumulativeLevelCost = individualLevelCost.runningFold(0F) { a, b -> a + b }.map { it.toLong() } + fun getPetLevel(currentExp: Double): PetLevel { + val currentOneIndexedLevel = cumulativeLevelCost.indexOfLast { it <= currentExp } + 1 + val expForNextLevel = if (currentOneIndexedLevel > individualLevelCost.size) { // Max leveled pet + individualLevelCost.last() + } else { + individualLevelCost[currentOneIndexedLevel - 1] + } + val expInCurrentLevel = + if (currentOneIndexedLevel >= cumulativeLevelCost.size) + currentExp.toFloat() - cumulativeLevelCost.last() + else + (expForNextLevel - (cumulativeLevelCost[currentOneIndexedLevel] - currentExp.toFloat())).coerceAtLeast( + 0F + ) + return PetLevel( + currentLevel = currentOneIndexedLevel, + maxLevel = cumulativeLevelCost.size, + expRequiredForNextLevel = expForNextLevel, + expRequiredForMaxLevel = cumulativeLevelCost.last(), + expInCurrentLevel = expInCurrentLevel, + expTotal = currentExp.toFloat() + ) + } + + fun getPetExpForLevel(level: Int): Long { + if (level < 2) return 0L + if (level >= cumulativeLevelCost.size) return cumulativeLevelCost.last() + return cumulativeLevelCost[level - 1] + } + } + + private data class Key(val petIdWithoutRarity: String, val rarity: Rarity) + + private val expLadders = CacheBuilder.newBuilder() + .build(object : CacheLoader() { + override fun load(key: Key): ExpLadder { + val pld = RepoManager.neuRepo.constants.petLevelingData + var exp = pld.petExpCostForLevel + var offset = pld.petLevelStartOffset[key.rarity]!! + var maxLevel = 100 + val override = pld.petLevelingBehaviourOverrides[key.petIdWithoutRarity] + if (override != null) { + maxLevel = override.maxLevel ?: maxLevel + offset = override.petLevelStartOffset?.get(key.rarity) ?: offset + when (override.petExpCostModifierType) { + PetLevelingBehaviourOverride.PetExpModifierType.APPEND -> + exp = exp + override.petExpCostModifier + + PetLevelingBehaviourOverride.PetExpModifierType.REPLACE -> + exp = override.petExpCostModifier + + null -> {} + } + } + return ExpLadder(exp.drop(offset).take(maxLevel - 1).map { it.toLong() }) + } + }) + + override fun reload(repository: NEURepository?) { + expLadders.invalidateAll() + } + + fun getExpLadder(petId: String, rarity: Rarity): ExpLadder { + return expLadders.get(Key(petId, rarity)) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt b/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt index 5452d33..681dba5 100644 --- a/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt +++ b/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt @@ -41,12 +41,15 @@ import net.minecraft.item.Items import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtElement import net.minecraft.nbt.NbtHelper +import net.minecraft.nbt.NbtList import net.minecraft.nbt.NbtOps +import net.minecraft.nbt.NbtString import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.util.LegacyTagParser import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.appendLore +import moe.nea.firmament.util.getOrCreateList import moe.nea.firmament.util.item.MinecraftProfileTextureKt import moe.nea.firmament.util.item.MinecraftTexturesPayloadKt import moe.nea.firmament.util.item.setTextures @@ -103,16 +106,46 @@ object ItemCache : IReloadable { } } - fun NEUItem?.asItemStack(idHint: SkyblockId? = null): ItemStack { + fun NEUItem?.asItemStack(idHint: SkyblockId? = null, loreReplacements: Map? = null): ItemStack { if (this == null) return brokenItemStack(null, idHint) var s = cache[this.skyblockItemId] if (s == null) { s = asItemStackNow() cache[this.skyblockItemId] = s } + if (!loreReplacements.isNullOrEmpty()) { + s.applyLoreReplacements(loreReplacements) + s.setCustomName(s.name.applyLoreReplacements(loreReplacements)) + } return s } + fun ItemStack.applyLoreReplacements(loreReplacements: Map) { + val component = getOrCreateSubNbt("display") + val lore = component.getOrCreateList("Lore", NbtString.STRING_TYPE) + val newLore = NbtList() + lore.forEach { + newLore.add( + NbtString.of( + Text.Serializer.toJson( + Text.Serializer.fromJson(it.asString())!!.applyLoreReplacements(loreReplacements) + ) + ) + ) + } + component["Lore"] = newLore + } + + fun Text.applyLoreReplacements(loreReplacements: Map): Text { + assert(this.siblings.isEmpty()) + var string = this.string + loreReplacements.forEach { (find, replace) -> + string = string.replace("{$find}", replace) + } + return Text.literal(string).styled { this.style } + } + + fun NEUItem.getIdentifier() = skyblockId.identifier diff --git a/src/main/kotlin/moe/nea/firmament/repo/RepoManager.kt b/src/main/kotlin/moe/nea/firmament/repo/RepoManager.kt index b431d28..1880617 100644 --- a/src/main/kotlin/moe/nea/firmament/repo/RepoManager.kt +++ b/src/main/kotlin/moe/nea/firmament/repo/RepoManager.kt @@ -24,6 +24,7 @@ import io.github.moulberry.repo.NEURepository import io.github.moulberry.repo.NEURepositoryException import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEURecipe +import io.github.moulberry.repo.data.Rarity import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import kotlinx.coroutines.launch import net.minecraft.client.MinecraftClient @@ -31,9 +32,10 @@ import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament.logger +import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.hud.ProgressBar +import moe.nea.firmament.rei.PetData import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.gui.config.ManagedConfig object RepoManager { object Config : ManagedConfig("repo") { @@ -59,6 +61,7 @@ object RepoManager { val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply { registerReloadListener(ItemCache) + registerReloadListener(ExpLadders) registerReloadListener { if (!trySendClientboundUpdateRecipesPacket()) { logger.warn("Failed to issue a ClientboundUpdateRecipesPacket (to reload REI). This may lead to an outdated item list.") @@ -121,4 +124,19 @@ object RepoManager { } } + fun getPotentialStubPetData(skyblockId: SkyblockId): PetData? { + val parts = skyblockId.neuItem.split(";") + if (parts.size != 2) { + return null + } + val (petId, rarityIndex) = parts + if (!rarityIndex.all { it.isDigit() }) { + return null + } + val intIndex = rarityIndex.toInt() + if (intIndex !in rarityIndex.indices) return null + if (petId !in neuRepo.constants.petNumbers) return null + return PetData(Rarity.values()[intIndex], petId, 0.0) + } + } -- cgit