Add pet level data to /firm pv

This commit is contained in:
nea
2023-06-03 19:58:34 +02:00
parent d2d032ba05
commit 3c437efa12
8 changed files with 196 additions and 19 deletions

View File

@@ -11,7 +11,7 @@ modmenu = "6.2.1"
ktor = "2.3.0" ktor = "2.3.0"
dbus_java = "4.2.1" dbus_java = "4.2.1"
architectury = "8.1.79" architectury = "8.1.79"
neurepoparser = "1.2.0" neurepoparser = "1.3.1"
qolify = "1.2.2-1.19.4" qolify = "1.2.2-1.19.4"
citresewn = "1.1.3+1.19.4" citresewn = "1.1.3+1.19.4"
lib39 = "1.4.2" lib39 = "1.4.2"

View File

@@ -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.WItem
import io.github.cottonmc.cotton.gui.widget.WText import io.github.cottonmc.cotton.gui.widget.WText
import io.github.cottonmc.cotton.gui.widget.WWidget 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.data.Insets
import io.github.cottonmc.cotton.gui.widget.icon.Icon import io.github.cottonmc.cotton.gui.widget.icon.Icon
import io.github.cottonmc.cotton.gui.widget.icon.ItemIcon 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.item.Items
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.gui.WTightScrollPanel import moe.nea.firmament.gui.WTightScrollPanel
import moe.nea.firmament.rei.PetData
import moe.nea.firmament.rei.SBItemStack import moe.nea.firmament.rei.SBItemStack
import moe.nea.firmament.util.MC
object PetsPage : ProfilePage { object PetsPage : ProfilePage {
override fun getElements(profileViewer: ProfileViewer): WWidget { override fun getElements(profileViewer: ProfileViewer): WWidget {
@@ -26,7 +25,7 @@ object PetsPage : ProfilePage {
it.add((WTightScrollPanel(WGridPanel().also { it.add((WTightScrollPanel(WGridPanel().also {
it.setGaps(8, 8) it.setGaps(8, 8)
for ((i, pet) in profileViewer.member.pets.withIndex()) { 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) { it.add(object : WItem(stack) {
override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {
BackgroundPainter.SLOT.paintBackground(matrices, x, y, this) BackgroundPainter.SLOT.paintBackground(matrices, x, y, this)

View File

@@ -35,6 +35,7 @@ import moe.nea.firmament.recipes.SBForgeRecipe
import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.skyblockId
class FirmamentReiPlugin : REIClientPlugin { class FirmamentReiPlugin : REIClientPlugin {
@@ -73,7 +74,7 @@ class FirmamentReiPlugin : REIClientPlugin {
registry.group( registry.group(
SkyblockId(parent).identifier, SkyblockId(parent).identifier,
Text.literal(RepoManager.getNEUItem(SkyblockId(parent))?.displayName ?: parent), 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) { override fun registerEntries(registry: EntryRegistry) {
RepoManager.neuRepo.items?.items?.values?.forEach { RepoManager.neuRepo.items?.items?.values?.forEach {
if (!it.isVanilla) if (!it.isVanilla)
registry.addEntry(SBItemEntryDefinition.getEntry(it)) registry.addEntry(SBItemEntryDefinition.getEntry(it.skyblockId))
} }
} }
} }

View File

@@ -34,7 +34,7 @@ object NEUItemEntrySerializer : EntrySerializer<SBItemStack> {
override fun read(tag: NbtCompound): SBItemStack { override fun read(tag: NbtCompound): SBItemStack {
val id = SkyblockId(tag.getString(SKYBLOCK_ID_ENTRY)) val id = SkyblockId(tag.getString(SKYBLOCK_ID_ENTRY))
val count = if (tag.contains(SKYBLOCK_ITEM_COUNT)) tag.getInt(SKYBLOCK_ITEM_COUNT) else 1 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<SBItemStack>, value: SBItemStack): NbtCompound { override fun save(entry: EntryStack<SBItemStack>, value: SBItemStack): NbtCompound {

View File

@@ -20,6 +20,7 @@ package moe.nea.firmament.rei
import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUIngredient
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
import io.github.moulberry.repo.data.Rarity
import java.util.stream.Stream import java.util.stream.Stream
import me.shedaniel.rei.api.client.entry.renderer.EntryRenderer import me.shedaniel.rei.api.client.entry.renderer.EntryRenderer
import me.shedaniel.rei.api.common.entry.EntrySerializer 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.text.Text
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry 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
import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.skyblockId
// TODO: add in extra data like pet info, into this structure // 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( data class SBItemStack(
val skyblockId: SkyblockId, val skyblockId: SkyblockId,
val neuItem: NEUItem?, val neuItem: NEUItem?,
val stackSize: Int, 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( constructor(skyblockId: SkyblockId, stackSize: Int = 1) : this(
skyblockId, skyblockId,
RepoManager.getNEUItem(skyblockId), RepoManager.getNEUItem(skyblockId),
stackSize stackSize,
RepoManager.getPotentialStubPetData(skyblockId)
) )
fun asItemStack(): ItemStack { private val itemStack by lazy {
if (skyblockId == SkyblockId.COINS) if (skyblockId == SkyblockId.COINS)
return ItemCache.coinItem(stackSize) return@lazy ItemCache.coinItem(stackSize)
return neuItem.asItemStack(idHint = skyblockId).copyWithCount(stackSize) val replacementData = mutableMapOf<String, String>()
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<SBItemStack> {
fun getEntry(sbItemStack: SBItemStack): EntryStack<SBItemStack> = fun getEntry(sbItemStack: SBItemStack): EntryStack<SBItemStack> =
EntryStack.of(this, sbItemStack) EntryStack.of(this, sbItemStack)
fun getEntry(neuItem: NEUItem?, count: Int = 1): EntryStack<SBItemStack> =
getEntry(SBItemStack(neuItem?.skyblockId ?: SkyblockId.NULL, neuItem, 1))
fun getEntry(skyblockId: SkyblockId, count: Int = 1): EntryStack<SBItemStack> = fun getEntry(skyblockId: SkyblockId, count: Int = 1): EntryStack<SBItemStack> =
getEntry(SBItemStack(skyblockId, RepoManager.getNEUItem(skyblockId), count)) getEntry(SBItemStack(skyblockId, count))
fun getEntry(ingredient: NEUIngredient): EntryStack<SBItemStack> = fun getEntry(ingredient: NEUIngredient): EntryStack<SBItemStack> =
getEntry(SkyblockId(ingredient.itemId), count = ingredient.amount.toInt()) getEntry(SkyblockId(ingredient.itemId), count = ingredient.amount.toInt())

View File

@@ -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<Long>,
) {
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<Key, ExpLadder>() {
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))
}
}

View File

@@ -41,12 +41,15 @@ import net.minecraft.item.Items
import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtHelper import net.minecraft.nbt.NbtHelper
import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtOps import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.NbtString
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.util.LegacyTagParser import moe.nea.firmament.util.LegacyTagParser
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.appendLore 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.MinecraftProfileTextureKt
import moe.nea.firmament.util.item.MinecraftTexturesPayloadKt import moe.nea.firmament.util.item.MinecraftTexturesPayloadKt
import moe.nea.firmament.util.item.setTextures 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<String, String>? = null): ItemStack {
if (this == null) return brokenItemStack(null, idHint) if (this == null) return brokenItemStack(null, idHint)
var s = cache[this.skyblockItemId] var s = cache[this.skyblockItemId]
if (s == null) { if (s == null) {
s = asItemStackNow() s = asItemStackNow()
cache[this.skyblockItemId] = s cache[this.skyblockItemId] = s
} }
if (!loreReplacements.isNullOrEmpty()) {
s.applyLoreReplacements(loreReplacements)
s.setCustomName(s.name.applyLoreReplacements(loreReplacements))
}
return s return s
} }
fun ItemStack.applyLoreReplacements(loreReplacements: Map<String, String>) {
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<String, String>): 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 fun NEUItem.getIdentifier() = skyblockId.identifier

View File

@@ -24,6 +24,7 @@ import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.NEURepositoryException import io.github.moulberry.repo.NEURepositoryException
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
import io.github.moulberry.repo.data.NEURecipe 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 net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
@@ -31,9 +32,10 @@ import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.Firmament.logger import moe.nea.firmament.Firmament.logger
import moe.nea.firmament.hud.ProgressBar
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.gui.config.ManagedConfig 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
object RepoManager { object RepoManager {
object Config : ManagedConfig("repo") { object Config : ManagedConfig("repo") {
@@ -59,6 +61,7 @@ object RepoManager {
val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply { val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply {
registerReloadListener(ItemCache) registerReloadListener(ItemCache)
registerReloadListener(ExpLadders)
registerReloadListener { registerReloadListener {
if (!trySendClientboundUpdateRecipesPacket()) { if (!trySendClientboundUpdateRecipesPacket()) {
logger.warn("Failed to issue a ClientboundUpdateRecipesPacket (to reload REI). This may lead to an outdated item list.") 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)
}
} }