HTTPS
https://git.nageyqma.icu/nageyqma/odin_next.git
SSH
git@git.nageyqma.icu:nageyqma/odin_next.git
HTTPS 链接可直接 git clone;推送代码请使用 SSH。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
package com.odtheking.odin.commands
import com.github.stivais.commodore.Commodore
import com.github.stivais.commodore.utils.GreedyString
import com.odtheking.odin.OdinMod.mc
import com.odtheking.odin.OdinMod.scope
import com.odtheking.odin.utils.*
import com.odtheking.odin.utils.network.hypixelapi.HypixelData
import com.odtheking.odin.utils.network.hypixelapi.RequestUtils
import com.odtheking.odin.utils.skyblock.dungeon.DungeonClass
import kotlinx.coroutines.launch
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.HoverEvent
val cataCommand = Commodore("cata", "catacombs", "catacomb") {
runs { playerName: GreedyString? ->
val name = playerName?.string ?: mc.user.name
modMessage("§aFetching dungeon stats for §6$name§a...")
scope.launch {
fetchAndDisplayCataStats(RequestUtils.getProfile(name))
}
}
}
fun fetchAndDisplayCataStats(result: Result<HypixelData.PlayerInfo>) {
result.fold(
onSuccess = { playerInfo ->
playerInfo.memberData?.let { displayCataStats(playerInfo.name, it) }
?: modMessage("§cNo profile found for §6${playerInfo.name}§c!")
},
onFailure = { modMessage("§c${it.message}") }
)
}
private fun displayCataStats(name: String, member: HypixelData.MemberData) {
with(member.dungeons) {
val cata = dungeonTypes.catacombs
val mm = dungeonTypes.mastermode
val cataLevel = calculateDungeonLevel(cata.experience)
val classLevels = DungeonClass.entries
.mapNotNull { if (it != DungeonClass.EMPTY) calculateDungeonLevel(classes[it.name.lowercase()]?.experience ?: 0.0) else null }
Component.literal("§d§m${" ".repeat(11)}§r §b$name §d§m${" ".repeat(11)}§r\n")
.append(buildCataSecretsWatcherLine(cataLevel, cata.experience, secrets, avrSecrets, totalRuns,
member.playerStats.bloodMobKills, cata.watcherKills.values.sum().toInt()))
.append(buildClassLevelsLine(classes, classLevels.average()))
.append(buildFloorTimesLine(cata, mm, member.assumedMagicalPower, member.tunings))
.apply {
getArmorPieces(member).takeIf { it.isNotEmpty() }?.let { append(buildArmorLine(it)) }
checkMissingItems(member).takeIf { it.isNotEmpty() }?.let { append(buildMissingItemsLine(it)) }
}
.append(Component.literal("§d§m${" ".repeat(27)}§r"))
.let { modMessage(it, "") }
}
}
private fun buildCataSecretsWatcherLine(cataLevel: Double, cataXp: Double, secrets: Long, secretAvg: Double, totalRuns: Int, watcherKills: Int, bloodMobKills: Int) =
Component.literal("§7Cata: §e${cataLevel.toFixed()}")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(
Component.literal("§7Catacombs Level\n§7XP: §b${formatNumber(cataXp.toString())}")
))}
.append(Component.literal(" §8| §7Secrets: §e${formatNumber(secrets.toString())} §8(§b${secretAvg.toFixed(1)}§8)")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(
Component.literal("§7Total Secrets: §e${formatNumber(secrets.toString())}\n§7Total Runs: §b$totalRuns\n§7Average: §a${secretAvg.toFixed()}")
))})
.append(Component.literal(" §8| §7Blood: §c${formatNumber(watcherKills.toString())}")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(
Component.literal("§7Total Watcher Kills: §c${formatNumber(watcherKills.toString())}\n§7Blood Mobs Killed: §5${formatNumber(bloodMobKills.toString())}")
))})
.append(Component.literal("\n"))
private fun buildClassLevelsLine(classes: Map<String, HypixelData.ClassData>, classAvg: Double) =
Component.literal("§7Classes: ").apply {
var totalClassXp = 0.0
DungeonClass.entries.forEach { dungeonClass ->
if (dungeonClass == DungeonClass.EMPTY) return@forEach
val className = dungeonClass.name.lowercase()
val level = calculateDungeonLevel(classes[className]?.experience ?: 0.0).toFixed()
val xp = classes[className]?.experience ?: 0.0
totalClassXp += xp
val colorCode = "§${dungeonClass.colorCode}"
append(Component.literal("$colorCode$level")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(
Component.literal("$colorCode${dungeonClass.name} ${colorCode}Level\n§7XP: §b${formatNumber(xp.toString())}")
))})
if (dungeonClass != DungeonClass.TANK) append(Component.literal("§8/"))
}
append(Component.literal(" §8(§7Avg: §a${classAvg.toFixed(1)}§8)")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(
Component.literal("§7Class Average\n§7Total Class XP: §b${formatNumber(totalClassXp.toString())}")
)) })
append(Component.literal("\n"))
}
private fun buildFloorHover(dungeonType: HypixelData.DungeonTypeData, title: String, floorPrefix: String) =
Component.literal(title).apply {
(1..7).forEach { floor ->
val key = "$floor"
val sPlusMs = dungeonType.fastestTimeSPlus[key]?.toLong() ?: 0
val bestMs = dungeonType.fastestTimes[key]?.toLong() ?: 0
val comps = dungeonType.tierComps[key]?.toInt() ?: 0
val timeStr = when {
sPlusMs > 0 -> "§a${formatTime(sPlusMs, 2)}"
bestMs > 0 -> "§7${formatTime(bestMs, 2)}"
else -> "§8None"
}
append(Component.literal("\n$floorPrefix$floor: $timeStr §8(§b$comps§8)"))
}
}
private fun buildFloorTimesLine(cata: HypixelData.DungeonTypeData, mm: HypixelData.DungeonTypeData, magicalPower: Int, tunings: List<String>) =
Component.literal("§7Floors: ")
.append(Component.literal("§6Normal")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(buildFloorHover(cata, "§6§lNormal Floors", "§eF"))) })
.append(Component.literal(" §8| "))
.append(Component.literal("§cMaster")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(buildFloorHover(mm, "§c§lMaster Floors", "§cM"))) })
.append(Component.literal(" §8| "))
.append(Component.literal("§7MP: §d${formatNumber(magicalPower.toString())}")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(
Component.literal("§bTunings").apply {
tunings.forEach {
append(Component.literal("\n§7- §e$it"))
}
}
)) })
.append(Component.literal("\n"))
private data class ArmorPiece(val slot: String, val itemStack: HypixelData.ItemData?)
private data class MissingItem(val name: String, val shortName: String)
private fun getArmorPieces(member: HypixelData.MemberData) = member.inventory?.invArmor?.itemStacks
?.takeIf { it.size >= 4 }
?.let { listOfNotNull(
it[3]?.let { stack -> ArmorPiece("⛑", stack) },
it[2]?.let { stack -> ArmorPiece("\uD83D\uDEE1", stack) },
it[1]?.let { stack -> ArmorPiece("\uD83D\uDC56", stack) },
it[0]?.let { stack -> ArmorPiece("\uD83D\uDC62", stack) }
)} ?: emptyList()
private fun buildArmorLine(armorPieces: List<ArmorPiece>) = Component.literal("§7Armor: ").apply {
armorPieces.forEachIndexed { index, (slot, itemStack) ->
val displayName = itemStack?.name ?: "§8Empty"
append(Component.literal(slot).withStyle { style ->
itemStack?.let {
val hover = Component.empty().append(Component.literal("$displayName\n"))
it.lore.forEach { loreLine -> hover.append(loreLine).append(Component.literal("\n")) }
style.withHoverEvent(HoverEvent.ShowText(hover))
} ?: style.withHoverEvent(HoverEvent.ShowText(Component.literal("§8Empty Slot")))
})
if (index < armorPieces.lastIndex) append(Component.literal(" §8| "))
}
append(Component.literal("\n"))
}
private fun hasItem(member: HypixelData.MemberData, vararg itemId: String) =
member.allItems.any { item -> itemId.any { item?.id?.contains(it) == true } }
private fun checkMissingItems(member: HypixelData.MemberData) = buildList {
if (!hasItem(member, "HYPERION", "ASTRAEA", "SCYLLA", "VALKYRIE")) add(MissingItem("Blade", "§5Wither Blade"))
if (!hasItem(member, "TERMINATOR")) add(MissingItem("Terminator", "§cTerm"))
if (!member.pets.pets.any { it.type.equals("GOLDEN_DRAGON", true) })
add(MissingItem("Golden Dragon", "§6GDrag"))
if (!member.pets.pets.any { it.type.equals("ENDER_DRAGON", true) })
add(MissingItem("Ender Dragon", "§5EDrag"))
}
private fun buildMissingItemsLine(missing: List<MissingItem>) = Component.literal("§7Missing: ").apply {
missing.forEachIndexed { index, item ->
append(Component.literal("§c✖ ${item.shortName}")
.withStyle { it.withHoverEvent(HoverEvent.ShowText(Component.literal("§cMissing ${item.name}"))) })
if (index < missing.lastIndex) append(Component.literal(" §8| "))
}
append(Component.literal("\n"))
}