Add collection info to skill page

This commit is contained in:
nea
2023-06-11 02:43:14 +02:00
parent 040f7c7275
commit a36c8f1c0e
12 changed files with 211 additions and 41 deletions

View File

@@ -19,6 +19,7 @@
- Dungeon Map - Dungeon Map
Priority 2: Priority 2:
- missing talismans / pets in pv
- item overlays - item overlays
- tree capitator - tree capitator
- builders wand - builders wand

View File

@@ -7,9 +7,6 @@
- More wider - More wider
- Pet level stat - Pet level stat
- Item List: - Item List:
- Fix crafting recipes for vanilla items
- Either remove vanilla items and replace them
- Or remove vanilla (crafting recipes)
- Add other recipe types (prio -1) - Add other recipe types (prio -1)
- Fix banner data fixer (prio -1) - Fix banner data fixer (prio -1)

View File

@@ -50,7 +50,7 @@ import net.minecraft.util.Identifier
import moe.nea.firmament.commands.registerFirmamentCommand import moe.nea.firmament.commands.registerFirmamentCommand
import moe.nea.firmament.dbus.FirmamentDbusObject import moe.nea.firmament.dbus.FirmamentDbusObject
import moe.nea.firmament.features.FeatureManager import moe.nea.firmament.features.FeatureManager
import moe.nea.firmament.repo.ItemCostData import moe.nea.firmament.repo.HypixelStaticData
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.SBData import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.data.IDataHolder import moe.nea.firmament.util.data.IDataHolder
@@ -119,7 +119,7 @@ object Firmament {
RepoManager.initialize() RepoManager.initialize()
SBData.init() SBData.init()
FeatureManager.autoload() FeatureManager.autoload()
ItemCostData.spawnPriceLoop() HypixelStaticData.spawnDataCollectionLoop()
ClientCommandRegistrationCallback.EVENT.register(this::registerCommands) ClientCommandRegistrationCallback.EVENT.register(this::registerCommands)
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping { ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping {
runBlocking { runBlocking {

View File

@@ -19,6 +19,32 @@ import moe.nea.firmament.util.json.DashlessUUIDSerializer
import moe.nea.firmament.util.json.InstantAsLongSerializer import moe.nea.firmament.util.json.InstantAsLongSerializer
@Serializable
data class CollectionSkillData(
val items: Map<CollectionType, CollectionInfo>
)
@Serializable
data class CollectionResponse(
val success: Boolean,
val collections: Map<Skill, CollectionSkillData>
)
@Serializable
data class CollectionInfo(
val name: String,
val maxTiers: Int,
val tiers: List<CollectionTier>
)
@Serializable
data class CollectionTier(
val tier: Int,
val amountRequired: Long,
val unlocks: List<String>,
)
@Serializable @Serializable
data class Profiles( data class Profiles(
val success: Boolean, val success: Boolean,
@@ -35,18 +61,18 @@ data class Profile(
val members: Map<UUID, Member>, val members: Map<UUID, Member>,
) )
enum class Skill(val accessor: KProperty1<Member, Double>, val color: DyeColor) { enum class Skill(val accessor: KProperty1<Member, Double>, val color: DyeColor, val icon: SkyblockId) {
FARMING(Member::experienceSkillFarming, DyeColor.YELLOW), FARMING(Member::experienceSkillFarming, DyeColor.YELLOW, SkyblockId("ROOKIE_HOE")),
FORAGING(Member::experienceSkillForaging, DyeColor.BROWN), FORAGING(Member::experienceSkillForaging, DyeColor.BROWN, SkyblockId("TREECAPITATOR_AXE")),
MINING(Member::experienceSkillMining, DyeColor.LIGHT_GRAY), MINING(Member::experienceSkillMining, DyeColor.LIGHT_GRAY, SkyblockId("DIAMOND_PICKAXE")),
ALCHEMY(Member::experienceSkillAlchemy, DyeColor.PURPLE), ALCHEMY(Member::experienceSkillAlchemy, DyeColor.PURPLE, SkyblockId("BREWING_STAND")),
TAMING(Member::experienceSkillTaming, DyeColor.GREEN), TAMING(Member::experienceSkillTaming, DyeColor.GREEN, SkyblockId("SUPER_EGG")),
FISHING(Member::experienceSkillFishing, DyeColor.BLUE), FISHING(Member::experienceSkillFishing, DyeColor.BLUE, SkyblockId("FARMER_ROD")),
RUNECRAFTING(Member::experienceSkillRunecrafting, DyeColor.PINK), RUNECRAFTING(Member::experienceSkillRunecrafting, DyeColor.PINK, SkyblockId("MUSIC_RUNE;1")),
CARPENTRY(Member::experienceSkillCarpentry, DyeColor.ORANGE), CARPENTRY(Member::experienceSkillCarpentry, DyeColor.ORANGE, SkyblockId("WORKBENCH")),
COMBAT(Member::experienceSkillCombat, DyeColor.RED), COMBAT(Member::experienceSkillCombat, DyeColor.RED, SkyblockId("UNDEAD_SWORD")),
SOCIAL(Member::experienceSkillSocial, DyeColor.WHITE), SOCIAL(Member::experienceSkillSocial, DyeColor.WHITE, SkyblockId("EGG_HUNT")),
ENCHANTING(Member::experienceSkillEnchanting, DyeColor.MAGENTA), ENCHANTING(Member::experienceSkillEnchanting, DyeColor.MAGENTA, SkyblockId("ENCHANTMENT_TABLE")),
; ;
fun getMaximumLevel(leveling: Leveling) = leveling.maximumLevels[name.lowercase()] ?: TODO("Repo error") fun getMaximumLevel(leveling: Leveling) = leveling.maximumLevels[name.lowercase()] ?: TODO("Repo error")
@@ -58,6 +84,12 @@ enum class Skill(val accessor: KProperty1<Member, Double>, val color: DyeColor)
} }
} }
@Serializable
@JvmInline
value class CollectionType(val string: String) {
val skyblockId get() = SkyblockId(string.replace(":", "-").replace("MUSHROOM_COLLECTION", "HUGE_MUSHROOM_2"))
}
@Serializable @Serializable
data class Member( data class Member(
val pets: List<Pet> = listOf(), val pets: List<Pet> = listOf(),
@@ -85,6 +117,7 @@ data class Member(
val experienceSkillRunecrafting: Double = 0.0, val experienceSkillRunecrafting: Double = 0.0,
@SerialName("experience_skill_carpentry") @SerialName("experience_skill_carpentry")
val experienceSkillCarpentry: Double = 0.0, val experienceSkillCarpentry: Double = 0.0,
val collection: Map<CollectionType, Long> = mapOf()
) )
@Serializable @Serializable

View File

@@ -19,14 +19,13 @@
package moe.nea.firmament.commands package moe.nea.firmament.commands
import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.StringArgumentType.getString
import com.mojang.brigadier.arguments.StringArgumentType.string import com.mojang.brigadier.arguments.StringArgumentType.string
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.features.world.FairySouls import moe.nea.firmament.features.world.FairySouls
import moe.nea.firmament.gui.config.AllConfigsGui import moe.nea.firmament.gui.config.AllConfigsGui
import moe.nea.firmament.gui.profileviewer.ProfileViewer import moe.nea.firmament.gui.profileviewer.ProfileViewer
import moe.nea.firmament.repo.ItemCostData import moe.nea.firmament.repo.HypixelStaticData
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
@@ -74,7 +73,7 @@ fun firmamentCommand() = literal("firmament") {
thenExecute { thenExecute {
val itemName = SkyblockId(get(item)) val itemName = SkyblockId(get(item))
source.sendFeedback(Text.translatable("firmament.price", itemName.neuItem)) source.sendFeedback(Text.translatable("firmament.price", itemName.neuItem))
val bazaarData = ItemCostData.bazaarData[itemName] val bazaarData = HypixelStaticData.bazaarData[itemName]
if (bazaarData != null) { if (bazaarData != null) {
source.sendFeedback(Text.translatable("firmament.price.bazaar")) source.sendFeedback(Text.translatable("firmament.price.bazaar"))
source.sendFeedback( source.sendFeedback(
@@ -108,7 +107,7 @@ fun firmamentCommand() = literal("firmament") {
) )
) )
} }
val lowestBin = ItemCostData.lowestBin[itemName] val lowestBin = HypixelStaticData.lowestBin[itemName]
if (lowestBin != null) { if (lowestBin != null) {
source.sendFeedback( source.sendFeedback(
Text.translatable( Text.translatable(

View File

@@ -0,0 +1,26 @@
package moe.nea.firmament.gui
import io.github.cottonmc.cotton.gui.client.BackgroundPainter
import io.github.cottonmc.cotton.gui.widget.TooltipBuilder
import io.github.cottonmc.cotton.gui.widget.WItem
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.item.TooltipContext
import net.minecraft.item.ItemStack
import net.minecraft.text.Text
import moe.nea.firmament.util.MC
class WTitledItem(val stack: ItemStack, val countString: Text = Text.empty()) : WItem(stack) {
override fun paint(context: DrawContext, x: Int, y: Int, mouseX: Int, mouseY: Int) {
BackgroundPainter.SLOT.paintBackground(context, x, y, this)
super.paint(context, x, y, mouseX, mouseY)
context.matrices.push()
context.matrices.translate(0F, 0F, 200F)
context.drawText(MC.font, countString, x + 19 - 2 - MC.font.getWidth(countString), y + 6 + 3, 0xFFFFFF, true)
context.matrices.push()
}
override fun addTooltip(tooltip: TooltipBuilder) {
tooltip.add(*stack.getTooltip(null, TooltipContext.BASIC).toTypedArray())
}
}

View File

@@ -14,8 +14,10 @@ import net.minecraft.client.item.TooltipContext
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.gui.WTitledItem
import moe.nea.firmament.rei.PetData import moe.nea.firmament.rei.PetData
import moe.nea.firmament.rei.SBItemStack import moe.nea.firmament.rei.SBItemStack
import moe.nea.firmament.repo.RepoManager
object PetsPage : ProfilePage { object PetsPage : ProfilePage {
override fun getElements(profileViewer: ProfileViewer): WWidget { override fun getElements(profileViewer: ProfileViewer): WWidget {
@@ -26,16 +28,7 @@ object PetsPage : ProfilePage {
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, PetData(pet.tier, pet.type.name, pet.exp)).asItemStack() val stack = SBItemStack(pet.itemId, PetData(pet.tier, pet.type.name, pet.exp)).asItemStack()
it.add(object : WItem(stack) { it.add(WTitledItem(stack), i % 5, i / 5, 1, 1)
override fun paint(context: DrawContext?, x: Int, y: Int, mouseX: Int, mouseY: Int) {
BackgroundPainter.SLOT.paintBackground(context, x, y, this)
super.paint(context, x, y, mouseX, mouseY)
}
override fun addTooltip(tooltip: TooltipBuilder) {
tooltip.add(*stack.getTooltip(null, TooltipContext.BASIC).toTypedArray())
}
}, i % 5, i / 5, 1, 1)
} }
it.layout() it.layout()
})), 0, 1, 8, 8) })), 0, 1, 8, 8)

View File

@@ -1,20 +1,39 @@
package moe.nea.firmament.gui.profileviewer package moe.nea.firmament.gui.profileviewer
import io.github.cottonmc.cotton.gui.widget.TooltipBuilder import io.github.cottonmc.cotton.gui.widget.TooltipBuilder
import io.github.cottonmc.cotton.gui.widget.WBox
import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WGridPanel
import io.github.cottonmc.cotton.gui.widget.WPanel
import io.github.cottonmc.cotton.gui.widget.WTabPanel
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.Axis
import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment
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
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.item.Items import net.minecraft.item.Items
import net.minecraft.text.Style
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Formatting
import moe.nea.firmament.apis.CollectionInfo
import moe.nea.firmament.apis.CollectionType
import moe.nea.firmament.apis.Member
import moe.nea.firmament.apis.Skill import moe.nea.firmament.apis.Skill
import moe.nea.firmament.gui.WBar import moe.nea.firmament.gui.WBar
import moe.nea.firmament.gui.WFixedPanel
import moe.nea.firmament.gui.WTitledItem
import moe.nea.firmament.hud.horizontal
import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry
import moe.nea.firmament.rei.SBItemEntryDefinition
import moe.nea.firmament.repo.HypixelStaticData
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.FirmFormatters
import moe.nea.firmament.util.modifyLore
import moe.nea.firmament.util.toShedaniel import moe.nea.firmament.util.toShedaniel
import moe.nea.firmament.util.toTextColor
object SkillPage : ProfilePage { object SkillPage : ProfilePage {
@@ -37,14 +56,83 @@ object SkillPage : ProfilePage {
} }
} }
override fun getElements(profileViewer: ProfileViewer): WWidget { private fun collectionItem(type: CollectionType, info: CollectionInfo, skill: Skill, member: Member): WWidget {
return WGridPanel().also { val collectionCount = member.collection[type] ?: 0
it.insets = Insets.ROOT_PANEL val unlockedTiers = info.tiers.count { it.amountRequired <= collectionCount }
it.add(WText(Text.literal(profileViewer.account.getDisplayName())), 0, 0, 6, 1) return WTitledItem(
for ((i, skill) in Skill.values().withIndex()) { SBItemEntryDefinition.getEntry(type.skyblockId).asItemEntry().value.copy()
it.add(WText(Text.translatable("firmament.pv.skills.${skill.name.lowercase()}")), 0, i + 1, 4, 1) .also {
it.add(skillBar(profileViewer, skill), 4, i + 1, 4, 1) it.setCustomName(
Text.literal(info.name).fillStyle(
Style.EMPTY.withItalic(false).withBold(true)
.withColor(skill.color.toTextColor())
)
)
it.modifyLore { old ->
listOf(
Text.literal("${info.name} Collection: $collectionCount / ${info.tiers.last().amountRequired}"),
Text.literal("Tiers unlocked: $unlockedTiers")
).map {
it.fillStyle(
Style.EMPTY.withItalic(false).withColor(Formatting.GRAY)
)
}
}
}, countString = Text.literal("$unlockedTiers").styled {
if (unlockedTiers == info.maxTiers)
it.withColor(Formatting.YELLOW)
else it
} }
)
}
private fun collectionPanel(profileViewer: ProfileViewer): WTabPanel {
return WTabPanel().also {
val data = HypixelStaticData.collectionData
val panels = mutableListOf<WPanel>()
for ((skill, collections) in data.entries) {
val panel = WBox(Axis.HORIZONTAL).also {
it.horizontalAlignment = HorizontalAlignment.CENTER
it.add(WFixedPanel(WGridPanel().also {
it.insets = Insets.ROOT_PANEL
it.setGaps(2, 2)
var x = 0
var y = 0
for (item in collections.items) {
it.add(collectionItem(item.key, item.value, skill, profileViewer.member), x, y, 1, 1)
x++
if (x == 5) {
x = 0
y++
}
}
}))
}
panels.add(panel)
it.add(panel) {
it.tooltip(
Text.translatable("firmament.pv.skills.${skill.name.lowercase()}")
.styled { it.withColor(skill.color.toTextColor()) })
it.icon(ItemIcon(RepoManager.getNEUItem(skill.icon).asItemStack()))
}
}
it.layout()
val tabWidth = it.width
panels.forEach { it.setSize(tabWidth - Insets.ROOT_PANEL.horizontal, it.height) }
}
}
override fun getElements(profileViewer: ProfileViewer): WWidget {
return WBox(Axis.HORIZONTAL).also {
it.insets = Insets.ROOT_PANEL
it.add(WGridPanel().also {
it.add(WText(Text.literal(profileViewer.account.getDisplayName())), 0, 0, 8, 1)
for ((i, skill) in Skill.values().withIndex()) {
it.add(WText(Text.translatable("firmament.pv.skills.${skill.name.lowercase()}")), 0, i + 1, 4, 1)
it.add(skillBar(profileViewer, skill), 4, i + 1, 4, 1)
}
})
it.add(collectionPanel(profileViewer))
} }
} }

View File

@@ -12,18 +12,23 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.apis.CollectionResponse
import moe.nea.firmament.apis.CollectionSkillData
import moe.nea.firmament.apis.Skill
import moe.nea.firmament.keybindings.IKeyBinding import moe.nea.firmament.keybindings.IKeyBinding
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.async.waitForInput import moe.nea.firmament.util.async.waitForInput
object ItemCostData { object HypixelStaticData {
private val logger = LogManager.getLogger("Firmament.ItemCostData") private val logger = LogManager.getLogger("Firmament.HypixelStaticData")
private val moulberryBaseUrl = "https://moulberry.codes" private val moulberryBaseUrl = "https://moulberry.codes"
private val hypixelApiBaseUrl = "https://api.hypixel.net" private val hypixelApiBaseUrl = "https://api.hypixel.net"
var lowestBin: Map<SkyblockId, Double> = mapOf() var lowestBin: Map<SkyblockId, Double> = mapOf()
private set private set
var bazaarData: Map<SkyblockId, BazaarData> = mapOf() var bazaarData: Map<SkyblockId, BazaarData> = mapOf()
private set private set
var collectionData: Map<Skill, CollectionSkillData> = mapOf()
private set
@Serializable @Serializable
data class BazaarData( data class BazaarData(
@@ -53,7 +58,9 @@ object ItemCostData {
fun getPriceOfItem(item: SkyblockId): Double? = bazaarData[item]?.quickStatus?.buyPrice ?: lowestBin[item] fun getPriceOfItem(item: SkyblockId): Double? = bazaarData[item]?.quickStatus?.buyPrice ?: lowestBin[item]
fun spawnPriceLoop() {
fun spawnDataCollectionLoop() {
Firmament.coroutineScope.launch { updateCollectionData() }
Firmament.coroutineScope.launch { Firmament.coroutineScope.launch {
while (true) { while (true) {
logger.info("Updating NEU prices") logger.info("Updating NEU prices")
@@ -83,4 +90,14 @@ object ItemCostData {
bazaarData = response.products.mapKeys { it.key.toRepoId() } bazaarData = response.products.mapKeys { it.key.toRepoId() }
} }
private suspend fun updateCollectionData() {
val response =
Firmament.httpClient.get("$hypixelApiBaseUrl/resources/skyblock/collections").body<CollectionResponse>()
if (!response.success) {
logger.warn("Retrieved unsuccessful collection data")
}
collectionData = response.collections
logger.info("Downloaded ${collectionData.values.sumOf { it.items.values.size }} collections")
}
} }

View File

@@ -33,6 +33,16 @@ fun ItemStack.appendLore(args: List<Text>) {
} }
} }
fun ItemStack.modifyLore(update: (List<Text>) -> List<Text>) {
val compoundTag = getOrCreateSubNbt("display")
val loreList = compoundTag.getOrCreateList("Lore", NbtString.STRING_TYPE)
val parsed = loreList.map { Text.Serializer.fromJson(it.asString())!! }
val updated = update(parsed)
loreList.clear()
loreList.addAll(updated.map { NbtString.of(Text.Serializer.toJson(it)) })
}
fun NbtCompound.getOrCreateList(label: String, tag: Byte): NbtList = getList(label, tag.toInt()).also { fun NbtCompound.getOrCreateList(label: String, tag: Byte): NbtList = getList(label, tag.toInt()).also {
put(label, it) put(label, it)
} }

View File

@@ -24,6 +24,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
object MC { object MC {
inline val font get() = MinecraftClient.getInstance().textRenderer
inline val soundManager get() = MinecraftClient.getInstance().soundManager inline val soundManager get() = MinecraftClient.getInstance().soundManager
inline val player get() = MinecraftClient.getInstance().player inline val player get() = MinecraftClient.getInstance().player
inline val world get() = MinecraftClient.getInstance().world inline val world get() = MinecraftClient.getInstance().world

View File

@@ -1,6 +1,11 @@
package moe.nea.firmament.util package moe.nea.firmament.util
import net.minecraft.text.TextColor
import net.minecraft.util.DyeColor import net.minecraft.util.DyeColor
fun DyeColor.toShedaniel(): me.shedaniel.math.Color = fun DyeColor.toShedaniel(): me.shedaniel.math.Color =
me.shedaniel.math.Color.ofOpaque(this.signColor) me.shedaniel.math.Color.ofOpaque(this.signColor)
fun DyeColor.toTextColor(): TextColor =
TextColor.fromRgb(this.signColor)