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

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

View File

@@ -19,6 +19,32 @@ import moe.nea.firmament.util.json.DashlessUUIDSerializer
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
data class Profiles(
val success: Boolean,
@@ -35,18 +61,18 @@ data class Profile(
val members: Map<UUID, Member>,
)
enum class Skill(val accessor: KProperty1<Member, Double>, val color: DyeColor) {
FARMING(Member::experienceSkillFarming, DyeColor.YELLOW),
FORAGING(Member::experienceSkillForaging, DyeColor.BROWN),
MINING(Member::experienceSkillMining, DyeColor.LIGHT_GRAY),
ALCHEMY(Member::experienceSkillAlchemy, DyeColor.PURPLE),
TAMING(Member::experienceSkillTaming, DyeColor.GREEN),
FISHING(Member::experienceSkillFishing, DyeColor.BLUE),
RUNECRAFTING(Member::experienceSkillRunecrafting, DyeColor.PINK),
CARPENTRY(Member::experienceSkillCarpentry, DyeColor.ORANGE),
COMBAT(Member::experienceSkillCombat, DyeColor.RED),
SOCIAL(Member::experienceSkillSocial, DyeColor.WHITE),
ENCHANTING(Member::experienceSkillEnchanting, DyeColor.MAGENTA),
enum class Skill(val accessor: KProperty1<Member, Double>, val color: DyeColor, val icon: SkyblockId) {
FARMING(Member::experienceSkillFarming, DyeColor.YELLOW, SkyblockId("ROOKIE_HOE")),
FORAGING(Member::experienceSkillForaging, DyeColor.BROWN, SkyblockId("TREECAPITATOR_AXE")),
MINING(Member::experienceSkillMining, DyeColor.LIGHT_GRAY, SkyblockId("DIAMOND_PICKAXE")),
ALCHEMY(Member::experienceSkillAlchemy, DyeColor.PURPLE, SkyblockId("BREWING_STAND")),
TAMING(Member::experienceSkillTaming, DyeColor.GREEN, SkyblockId("SUPER_EGG")),
FISHING(Member::experienceSkillFishing, DyeColor.BLUE, SkyblockId("FARMER_ROD")),
RUNECRAFTING(Member::experienceSkillRunecrafting, DyeColor.PINK, SkyblockId("MUSIC_RUNE;1")),
CARPENTRY(Member::experienceSkillCarpentry, DyeColor.ORANGE, SkyblockId("WORKBENCH")),
COMBAT(Member::experienceSkillCombat, DyeColor.RED, SkyblockId("UNDEAD_SWORD")),
SOCIAL(Member::experienceSkillSocial, DyeColor.WHITE, SkyblockId("EGG_HUNT")),
ENCHANTING(Member::experienceSkillEnchanting, DyeColor.MAGENTA, SkyblockId("ENCHANTMENT_TABLE")),
;
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
data class Member(
val pets: List<Pet> = listOf(),
@@ -85,6 +117,7 @@ data class Member(
val experienceSkillRunecrafting: Double = 0.0,
@SerialName("experience_skill_carpentry")
val experienceSkillCarpentry: Double = 0.0,
val collection: Map<CollectionType, Long> = mapOf()
)
@Serializable

View File

@@ -19,14 +19,13 @@
package moe.nea.firmament.commands
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.StringArgumentType.getString
import com.mojang.brigadier.arguments.StringArgumentType.string
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import net.minecraft.text.Text
import moe.nea.firmament.features.world.FairySouls
import moe.nea.firmament.gui.config.AllConfigsGui
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.util.FirmFormatters
import moe.nea.firmament.util.MC
@@ -74,7 +73,7 @@ fun firmamentCommand() = literal("firmament") {
thenExecute {
val itemName = SkyblockId(get(item))
source.sendFeedback(Text.translatable("firmament.price", itemName.neuItem))
val bazaarData = ItemCostData.bazaarData[itemName]
val bazaarData = HypixelStaticData.bazaarData[itemName]
if (bazaarData != null) {
source.sendFeedback(Text.translatable("firmament.price.bazaar"))
source.sendFeedback(
@@ -108,7 +107,7 @@ fun firmamentCommand() = literal("firmament") {
)
)
}
val lowestBin = ItemCostData.lowestBin[itemName]
val lowestBin = HypixelStaticData.lowestBin[itemName]
if (lowestBin != null) {
source.sendFeedback(
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.text.Text
import moe.nea.firmament.gui.WTightScrollPanel
import moe.nea.firmament.gui.WTitledItem
import moe.nea.firmament.rei.PetData
import moe.nea.firmament.rei.SBItemStack
import moe.nea.firmament.repo.RepoManager
object PetsPage : ProfilePage {
override fun getElements(profileViewer: ProfileViewer): WWidget {
@@ -26,16 +28,7 @@ object PetsPage : ProfilePage {
it.setGaps(8, 8)
for ((i, pet) in profileViewer.member.pets.withIndex()) {
val stack = SBItemStack(pet.itemId, PetData(pet.tier, pet.type.name, pet.exp)).asItemStack()
it.add(object : 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)
}
override fun addTooltip(tooltip: TooltipBuilder) {
tooltip.add(*stack.getTooltip(null, TooltipContext.BASIC).toTypedArray())
}
}, i % 5, i / 5, 1, 1)
it.add(WTitledItem(stack), i % 5, i / 5, 1, 1)
}
it.layout()
})), 0, 1, 8, 8)

View File

@@ -1,20 +1,39 @@
package moe.nea.firmament.gui.profileviewer
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.WPanel
import io.github.cottonmc.cotton.gui.widget.WTabPanel
import io.github.cottonmc.cotton.gui.widget.WText
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.icon.Icon
import io.github.cottonmc.cotton.gui.widget.icon.ItemIcon
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.text.Style
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.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.util.FirmFormatters
import moe.nea.firmament.util.modifyLore
import moe.nea.firmament.util.toShedaniel
import moe.nea.firmament.util.toTextColor
object SkillPage : ProfilePage {
@@ -37,14 +56,83 @@ object SkillPage : ProfilePage {
}
}
override fun getElements(profileViewer: ProfileViewer): WWidget {
return WGridPanel().also {
it.insets = Insets.ROOT_PANEL
it.add(WText(Text.literal(profileViewer.account.getDisplayName())), 0, 0, 6, 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)
private fun collectionItem(type: CollectionType, info: CollectionInfo, skill: Skill, member: Member): WWidget {
val collectionCount = member.collection[type] ?: 0
val unlockedTiers = info.tiers.count { it.amountRequired <= collectionCount }
return WTitledItem(
SBItemEntryDefinition.getEntry(type.skyblockId).asItemEntry().value.copy()
.also {
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 kotlin.time.Duration.Companion.minutes
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.util.SkyblockId
import moe.nea.firmament.util.async.waitForInput
object ItemCostData {
private val logger = LogManager.getLogger("Firmament.ItemCostData")
object HypixelStaticData {
private val logger = LogManager.getLogger("Firmament.HypixelStaticData")
private val moulberryBaseUrl = "https://moulberry.codes"
private val hypixelApiBaseUrl = "https://api.hypixel.net"
var lowestBin: Map<SkyblockId, Double> = mapOf()
private set
var bazaarData: Map<SkyblockId, BazaarData> = mapOf()
private set
var collectionData: Map<Skill, CollectionSkillData> = mapOf()
private set
@Serializable
data class BazaarData(
@@ -53,7 +58,9 @@ object ItemCostData {
fun getPriceOfItem(item: SkyblockId): Double? = bazaarData[item]?.quickStatus?.buyPrice ?: lowestBin[item]
fun spawnPriceLoop() {
fun spawnDataCollectionLoop() {
Firmament.coroutineScope.launch { updateCollectionData() }
Firmament.coroutineScope.launch {
while (true) {
logger.info("Updating NEU prices")
@@ -83,4 +90,14 @@ object ItemCostData {
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 {
put(label, it)
}

View File

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

View File

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