diff options
author | Linnea Gräf <nea@nea.moe> | 2025-03-17 22:49:49 +0100 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2025-03-17 22:49:49 +0100 |
commit | aacd527cb8e226b92e5ad28d8d075da06fd79ec4 (patch) | |
tree | 2a2745cd528e0302aa9ebe46d6a46a04c80507c4 /src | |
parent | 207161dc5e2ae93648b563bae7b7427ec2524d64 (diff) | |
download | Firmament-aacd527cb8e226b92e5ad28d8d075da06fd79ec4.tar.gz Firmament-aacd527cb8e226b92e5ad28d8d075da06fd79ec4.tar.bz2 Firmament-aacd527cb8e226b92e5ad28d8d075da06fd79ec4.zip |
refactor: Refactor stat blocks
Diffstat (limited to 'src')
-rw-r--r-- | src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt | 12 | ||||
-rw-r--r-- | src/main/kotlin/repo/SBItemStack.kt | 140 | ||||
-rw-r--r-- | src/main/kotlin/repo/item/StatBlock.kt | 163 |
3 files changed, 182 insertions, 133 deletions
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt index b8313a6..8e86967 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt @@ -31,6 +31,7 @@ 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.repo.item.StatBlock import moe.nea.firmament.util.AprilFoolsUtil import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.SkyblockId @@ -106,7 +107,10 @@ class SBReforgeRecipe( 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)) + StatBlock.StatLine( // TODO: add helper methods for constructing stat lines + StatBlock.findStatFormatting(SBItemStack.statIdToName(statId)), + 0.0 + ).reconstitute(7)) .horizontalAlignment(Label.LEFT_ALIGNED) statToLineMappings.add(statId to label) list.add(label) @@ -116,9 +120,9 @@ class SBReforgeRecipe( 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] + StatBlock.StatLine( + StatBlock.findStatFormatting(SBItemStack.statIdToName(stat)), + stats[stat] ?: 0.0 ).reconstitute(7) } } diff --git a/src/main/kotlin/repo/SBItemStack.kt b/src/main/kotlin/repo/SBItemStack.kt index 3690866..93cf52c 100644 --- a/src/main/kotlin/repo/SBItemStack.kt +++ b/src/main/kotlin/repo/SBItemStack.kt @@ -11,17 +11,16 @@ import net.minecraft.network.codec.PacketCodec import net.minecraft.network.codec.PacketCodecs import net.minecraft.text.Style import net.minecraft.text.Text -import net.minecraft.text.TextColor import net.minecraft.util.Formatting import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.ItemCache.withFallback +import moe.nea.firmament.repo.item.StatBlock import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.LegacyFormattingCode import moe.nea.firmament.util.MC import moe.nea.firmament.util.ReforgeId import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.blue -import moe.nea.firmament.util.directLiteralStringContent import moe.nea.firmament.util.extraAttributes import moe.nea.firmament.util.getReforgeId import moe.nea.firmament.util.getUpgradeStars @@ -107,13 +106,6 @@ data class SBItemStack constructor( return SBItemStack(SkyblockId.NULL, null, itemStack.count, null, fallback = itemStack) } - fun parseStatBlock(itemStack: ItemStack): List<StatLine> { - return itemStack.loreAccordingToNbt - .map { parseStatLine(it) } - .takeWhile { it != null } - .filterNotNull() - } - fun appendEnhancedStats( itemStack: ItemStack, reforgeStats: Map<String, Double>, @@ -121,81 +113,23 @@ data class SBItemStack constructor( ) { val namedReforgeStats = reforgeStats .mapKeysTo(mutableMapOf()) { statIdToName(it.key) } + val loreMut = itemStack.loreAccordingToNbt.toMutableList() - var statBlockLastIndex = -1 - for (i in loreMut.indices) { - val statLine = parseStatLine(loreMut[i]) - if (statLine == null && statBlockLastIndex >= 0) { - break - } - if (statLine == null) { - continue - } - statBlockLastIndex = i - val statBuff = namedReforgeStats.remove(statLine.statName) ?: continue - loreMut[i] = statLine.addStat(statBuff, buffKind).reconstitute() - } - if (namedReforgeStats.isNotEmpty() && statBlockLastIndex == -1) { - loreMut.add(0, Text.literal("")) - } - // If there is no stat block the statBlockLastIndex falls through to -1 - // TODO: this is good enough for some items. some other items might have their stats at a different place. + val statBlock = StatBlock.fromLore(loreMut) for ((statName, statBuff) in namedReforgeStats) { - val statLine = StatLine(statName, null).addStat(statBuff, buffKind) - loreMut.add(statBlockLastIndex + 1, statLine.reconstitute()) + statBlock.modify(statName) { it.addStat(statBuff, buffKind) } } + statBlock.applyModifications(loreMut) itemStack.loreAccordingToNbt = loreMut } data class StatFormatting( + val name: String, val postFix: String, val color: Formatting, val isStarAffected: Boolean = true, ) - val formattingOverrides = mapOf( - "Sea Creature Chance" to StatFormatting("%", Formatting.RED), - "Strength" to StatFormatting("", Formatting.RED), - "Damage" to StatFormatting("", Formatting.RED), - "Bonus Attack Speed" to StatFormatting("%", Formatting.RED), - "Shot Cooldown" to StatFormatting("s", Formatting.GREEN, false), - "Ability Damage" to StatFormatting("%", Formatting.RED), - "Crit Damage" to StatFormatting("%", Formatting.RED), - "Crit Chance" to StatFormatting("%", Formatting.RED), - "Ability Damage" to StatFormatting("%", Formatting.RED), - "Trophy Fish Chance" to StatFormatting("%", Formatting.GREEN), - "Health" to StatFormatting("", Formatting.GREEN), - "Defense" to StatFormatting("", Formatting.GREEN), - "Fishing Speed" to StatFormatting("", Formatting.GREEN), - "Double Hook Chance" to StatFormatting("%", Formatting.GREEN), - "Mining Speed" to StatFormatting("", Formatting.GREEN), - "Mining Fortune" to StatFormatting("", Formatting.GREEN), - "Heat Resistance" to StatFormatting("", Formatting.GREEN), - "Swing Range" to StatFormatting("", Formatting.GREEN), - "Rift Time" to StatFormatting("", Formatting.GREEN), - "Speed" to StatFormatting("", Formatting.GREEN), - "Farming Fortune" to StatFormatting("", Formatting.GREEN), - "True Defense" to StatFormatting("", Formatting.GREEN), - "Mending" to StatFormatting("", Formatting.GREEN), - "Foraging Wisdom" to StatFormatting("", Formatting.GREEN), - "Farming Wisdom" to StatFormatting("", Formatting.GREEN), - "Foraging Fortune" to StatFormatting("", Formatting.GREEN), - "Magic Find" to StatFormatting("", Formatting.GREEN), - "Ferocity" to StatFormatting("", Formatting.GREEN), - "Bonus Pest Chance" to StatFormatting("%", Formatting.GREEN), - "Cold Resistance" to StatFormatting("", Formatting.GREEN), - "Pet Luck" to StatFormatting("", Formatting.GREEN), - "Fear" to StatFormatting("", Formatting.GREEN), - "Mana Regen" to StatFormatting("%", Formatting.GREEN), - "Rift Damage" to StatFormatting("", Formatting.GREEN), - "Hearts" to StatFormatting("", Formatting.GREEN), - "Vitality" to StatFormatting("", Formatting.GREEN), - // TODO: make this a repo json - ) - - - private val statLabelRegex = "(?<statName>.*): ".toPattern() - enum class BuffKind( val color: Formatting, val prefix: String, @@ -208,62 +142,10 @@ data class SBItemStack constructor( ; } - data class StatLine( - val statName: String, - val value: Text?, - val rest: List<Text> = listOf(), - val valueNum: Double? = value?.directLiteralStringContent?.trim(' ', 's', '%', '+')?.toDoubleOrNull() - ) { - fun addStat(amount: Double, buffKind: BuffKind): StatLine { - val formattedAmount = FirmFormatters.formatCommas(amount, 1, includeSign = true) - return copy( - valueNum = (valueNum ?: 0.0) + amount, - value = null, - rest = rest + - if (buffKind.isHidden) emptyList() - else listOf( - Text.literal( - buffKind.prefix + formattedAmount + - statFormatting.postFix + - buffKind.postFix + " ") - .withColor(buffKind.color))) - } - - fun formatValue() = - Text.literal(FirmFormatters.formatCommas(valueNum ?: 0.0, - 1, - includeSign = true) + statFormatting.postFix + " ") - .setStyle(Style.EMPTY.withColor(statFormatting.color)) - - val statFormatting = formattingOverrides[statName] ?: StatFormatting("", Formatting.GREEN) - private fun abbreviate(abbreviateTo: Int): String { - if (abbreviateTo >= statName.length) return statName - val segments = statName.split(" ") - return segments.joinToString(" ") { - it.substring(0, maxOf(1, abbreviateTo / segments.size)) - } - } - - fun reconstitute(abbreviateTo: Int = Int.MAX_VALUE): Text = - Text.literal("").setStyle(Style.EMPTY.withItalic(false)) - .append(Text.literal("${abbreviate(abbreviateTo)}: ").grey()) - .append(value ?: formatValue()) - .also { rest.forEach(it::append) } - } - fun statIdToName(statId: String): String { val segments = statId.split("_") return segments.joinToString(" ") { it.replaceFirstChar { it.uppercaseChar() } } } - - private fun parseStatLine(line: Text): StatLine? { - val sibs = line.siblings - val stat = sibs.firstOrNull() ?: return null - if (stat.style.color != TextColor.fromFormatting(Formatting.GRAY)) return null - val statLabel = stat.directLiteralStringContent ?: return null - val statName = statLabelRegex.useMatch(statLabel) { group("statName") } ?: return null - return StatLine(statName, sibs[1], sibs.subList(2, sibs.size)) - } } constructor(skyblockId: SkyblockId, petData: PetData) : this( @@ -369,7 +251,7 @@ data class SBItemStack constructor( val baseItem = neuItem.asItemStack(idHint = skyblockId, replacementData) .withFallback(fallback) .copyWithCount(stackSize) - val baseStats = parseStatBlock(baseItem) + val baseStats = StatBlock.fromLore(baseItem.loreAccordingToNbt) appendReforgeInfo(baseItem) baseItem.appendLore(extraLore) enhanceStatsByStars(baseItem, stars, baseStats) @@ -403,9 +285,8 @@ data class SBItemStack constructor( return starString } - private fun enhanceStatsByStars(itemStack: ItemStack, stars: Int, baseStats: List<StatLine>) { + private fun enhanceStatsByStars(itemStack: ItemStack, stars: Int, baseStats: StatBlock) { if (stars == 0) return - // TODO: increase stats and add the star level into the nbt data so star displays work itemStack.modifyExtraAttributes { it.putInt("upgrade_level", stars) } @@ -415,9 +296,10 @@ data class SBItemStack constructor( val truncatedStarCount = if (isDungeon) minOf(5, stars) else stars appendEnhancedStats(itemStack, baseStats - .filter { it.statFormatting.isStarAffected } + .indexedByName.values + .filter { it.stat.isStarAffected } .associate { - it.statName to ((it.valueNum ?: 0.0) * (truncatedStarCount * 0.02)) + it.statName to (it.value * (truncatedStarCount * 0.02)) }, BuffKind.STAR_BUFF) } diff --git a/src/main/kotlin/repo/item/StatBlock.kt b/src/main/kotlin/repo/item/StatBlock.kt new file mode 100644 index 0000000..94643a2 --- /dev/null +++ b/src/main/kotlin/repo/item/StatBlock.kt @@ -0,0 +1,163 @@ +package moe.nea.firmament.repo.item + +import kotlin.collections.forEach +import kotlin.collections.plus +import net.minecraft.text.Style +import net.minecraft.text.Text +import net.minecraft.text.TextColor +import net.minecraft.util.Formatting +import moe.nea.firmament.repo.SBItemStack.Companion.BuffKind +import moe.nea.firmament.repo.SBItemStack.Companion.StatFormatting +import moe.nea.firmament.util.FirmFormatters +import moe.nea.firmament.util.directLiteralStringContent +import moe.nea.firmament.util.grey +import moe.nea.firmament.util.useMatch +import moe.nea.firmament.util.withColor + +data class StatBlock( + val indexedByName: Map<String, StatLine>, + val startIndex: Int, + val endIndex: Int, + private val modifiedLines: MutableMap<String, StatLine> = mutableMapOf() +) { + + /** + * Note that the returned stat line must be created by copying from the original stat line (to keep indexes in sync). + */ + fun modify(statName: String, mod: (StatLine) -> StatLine) { + val existing = modifiedLines[statName] ?: indexedByName[statName] ?: StatLine(findStatFormatting(statName), 0.0) + modifiedLines[statName] = mod(existing) + } + + fun applyModifications(lore: MutableList<Text>) { + if (modifiedLines.isEmpty()) return + var nextAppendIndex = endIndex + if (startIndex < 0) // No existing stat block, insert the after space. + lore.add(0, Text.literal("")) + modifiedLines.values.forEach { line -> + val loreLine = if (line.loreIndex < 0) { + lore.add(nextAppendIndex, Text.literal("")) + nextAppendIndex++ + } else line.loreIndex + lore[loreLine] = line.reconstitute() + } + } + + companion object { + fun fromLore(lore: List<Text>): StatBlock { + val map = mutableMapOf<String, StatLine>() + var start = -1 + var end = 0 + for ((index, text) in lore.withIndex()) { + val statLine = parseStatLine(text) + ?.copy(loreIndex = index) + if (statLine == null) { + if (start < 0) continue + else break + } + map[statLine.statName] = statLine + if (start < 0) + start = index + end = index + 1 + } + return StatBlock(map, start, end) + } + + val allFormattingOverrides = listOf( + StatFormatting("Sea Creature Chance", "%", Formatting.RED), + StatFormatting("Strength", "", Formatting.RED), + StatFormatting("Damage", "", Formatting.RED), + StatFormatting("Bonus Attack Speed", "%", Formatting.RED), + StatFormatting("Shot Cooldown", "s", Formatting.GREEN, false), + StatFormatting("Ability Damage", "%", Formatting.RED), + StatFormatting("Crit Damage", "%", Formatting.RED), + StatFormatting("Crit Chance", "%", Formatting.RED), + StatFormatting("Ability Damage", "%", Formatting.RED), + StatFormatting("Trophy Fish Chance", "%", Formatting.GREEN), + StatFormatting("Health", "", Formatting.GREEN), + StatFormatting("Defense", "", Formatting.GREEN), + StatFormatting("Fishing Speed", "", Formatting.GREEN), + StatFormatting("Double Hook Chance", "%", Formatting.GREEN), + StatFormatting("Mining Speed", "", Formatting.GREEN), + StatFormatting("Mining Fortune", "", Formatting.GREEN), + StatFormatting("Heat Resistance", "", Formatting.GREEN), + StatFormatting("Swing Range", "", Formatting.GREEN), + StatFormatting("Rift Time", "", Formatting.GREEN), + StatFormatting("Speed", "", Formatting.GREEN), + StatFormatting("Farming Fortune", "", Formatting.GREEN), + StatFormatting("True Defense", "", Formatting.GREEN), + StatFormatting("Mending", "", Formatting.GREEN), + StatFormatting("Foraging Wisdom", "", Formatting.GREEN), + StatFormatting("Farming Wisdom", "", Formatting.GREEN), + StatFormatting("Foraging Fortune", "", Formatting.GREEN), + StatFormatting("Magic Find", "", Formatting.GREEN), + StatFormatting("Ferocity", "", Formatting.GREEN), + StatFormatting("Bonus Pest Chance", "%", Formatting.GREEN), + StatFormatting("Cold Resistance", "", Formatting.GREEN), + StatFormatting("Pet Luck", "", Formatting.GREEN), + StatFormatting("Fear", "", Formatting.GREEN), + StatFormatting("Mana Regen", "%", Formatting.GREEN), + StatFormatting("Rift Damage", "", Formatting.GREEN), + StatFormatting("Hearts", "", Formatting.GREEN), + StatFormatting("Vitality", "", Formatting.GREEN), + // TODO: make this a repo json + ) + val formattingOverrides = allFormattingOverrides.associateBy { it.name } + fun findStatFormatting(name: String) = + formattingOverrides[name] ?: StatFormatting(name, "", Formatting.GREEN) + + private val statLabelRegex = "(?<statName>.*): ".toPattern() + private fun parseStatLine(line: Text): StatLine? { + val sibs = line.siblings + val stat = sibs.firstOrNull() ?: return null + if (stat.style.color != TextColor.fromFormatting(Formatting.GRAY)) return null + val statLabel = stat.directLiteralStringContent ?: return null + val statName = statLabelRegex.useMatch(statLabel) { group("statName") } ?: return null + return StatLine(findStatFormatting(statName), + sibs[1]?.directLiteralStringContent?.trim(' ', 's', '%', '+')?.toDoubleOrNull() ?: 0.0, + sibs.subList(2, sibs.size)) + } + + } + + data class StatLine( + val stat: StatFormatting, + val value: Double, + val modifiers: List<Text> = listOf(), + val loreIndex: Int = -1, + ) { + fun formatValue() = + Text.literal(FirmFormatters.formatCommas( + value, 1, includeSign = true) + stat.postFix + " ") + .setStyle(Style.EMPTY.withColor(stat.color)) + + val statName get() = stat.name + fun reconstitute(abbreviateTo: Int = Int.MAX_VALUE): Text = + Text.literal("").setStyle(Style.EMPTY.withItalic(false)) + .append(Text.literal("${abbreviate(abbreviateTo)}: ").grey()) + .append(formatValue()) + .also { modifiers.forEach(it::append) } + + fun addStat(amount: Double, buffKind: BuffKind): StatLine { + val formattedAmount = FirmFormatters.formatCommas(amount, 1, includeSign = true) + return copy( + value = value + amount, + modifiers = modifiers + + if (buffKind.isHidden) emptyList() + else listOf( + Text.literal( + buffKind.prefix + formattedAmount + + stat.postFix + + buffKind.postFix + " ") + .withColor(buffKind.color))) + } + + private fun abbreviate(abbreviateTo: Int): String { + if (abbreviateTo >= statName.length) return statName + val segments = statName.split(" ") + return segments.joinToString(" ") { + it.substring(0, maxOf(1, abbreviateTo / segments.size)) + } + } + } +} |