diff options
Diffstat (limited to 'src/main/kotlin/util')
-rw-r--r-- | src/main/kotlin/util/AprilFoolsUtil.kt | 10 | ||||
-rw-r--r-- | src/main/kotlin/util/FirmFormatters.kt | 42 | ||||
-rw-r--r-- | src/main/kotlin/util/LegacyFormattingCode.kt | 54 | ||||
-rw-r--r-- | src/main/kotlin/util/MC.kt | 5 | ||||
-rw-r--r-- | src/main/kotlin/util/MoulConfigUtils.kt | 13 | ||||
-rw-r--r-- | src/main/kotlin/util/SBData.kt | 125 | ||||
-rw-r--r-- | src/main/kotlin/util/ScoreboardUtil.kt | 72 | ||||
-rw-r--r-- | src/main/kotlin/util/SkyBlockIsland.kt | 2 | ||||
-rw-r--r-- | src/main/kotlin/util/SkyblockId.kt | 29 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/IntrospectableItemModelManager.kt | 7 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/NbtItemData.kt | 4 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/TolerantRegistriesOps.kt | 29 | ||||
-rw-r--r-- | src/main/kotlin/util/render/TranslatedScissors.kt | 6 | ||||
-rw-r--r-- | src/main/kotlin/util/skyblock/DungeonUtil.kt | 33 | ||||
-rw-r--r-- | src/main/kotlin/util/skyblock/ItemType.kt | 31 | ||||
-rw-r--r-- | src/main/kotlin/util/skyblock/Rarity.kt | 38 | ||||
-rw-r--r-- | src/main/kotlin/util/skyblock/SkyBlockItems.kt | 1 | ||||
-rw-r--r-- | src/main/kotlin/util/textutil.kt | 113 |
18 files changed, 427 insertions, 187 deletions
diff --git a/src/main/kotlin/util/AprilFoolsUtil.kt b/src/main/kotlin/util/AprilFoolsUtil.kt new file mode 100644 index 0000000..a940fa1 --- /dev/null +++ b/src/main/kotlin/util/AprilFoolsUtil.kt @@ -0,0 +1,10 @@ +package moe.nea.firmament.util + +import java.time.LocalDateTime +import java.time.Month + +object AprilFoolsUtil { + val isAprilFoolsDay = LocalDateTime.now().let { + it.dayOfMonth == 1 && it.month == Month.APRIL + } +} diff --git a/src/main/kotlin/util/FirmFormatters.kt b/src/main/kotlin/util/FirmFormatters.kt index 92fb9e5..acb7102 100644 --- a/src/main/kotlin/util/FirmFormatters.kt +++ b/src/main/kotlin/util/FirmFormatters.kt @@ -9,27 +9,59 @@ import kotlin.io.path.isReadable import kotlin.io.path.isRegularFile import kotlin.io.path.listDirectoryEntries import kotlin.math.absoluteValue +import kotlin.math.roundToInt import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import net.minecraft.text.Text object FirmFormatters { + + private inline fun shortIf( + value: Double, breakpoint: Double, char: String, + return_: (String) -> Nothing + ) { + if (value >= breakpoint) { + val broken = (value / breakpoint * 10).roundToInt() + if (broken > 99) + return_((broken / 10).toString() + char) + val decimals = broken.toString() + decimals.singleOrNull()?.let { + return_("0.$it$char") + } + return_("${decimals[0]}.${decimals[1]}$char") + } + } + + fun shortFormat(double: Double): String { + if (double < 0) return "-" + shortFormat(-double) + shortIf(double, 1_000_000_000_000.0, "t") { return it } + shortIf(double, 1_000_000_000.0, "b") { return it } + shortIf(double, 1_000_000.0, "m") { return it } + shortIf(double, 1_000.0, "k") { return it } + shortIf(double, 1.0, "") { return it } + return double.toString() + } + fun formatCommas(int: Int, segments: Int = 3): String = formatCommas(int.toLong(), segments) - fun formatCommas(long: Long, segments: Int = 3): String { + fun formatCommas(long: Long, segments: Int = 3, includeSign: Boolean = false): String { + if (long < 0 && long != Long.MIN_VALUE) { + return "-" + formatCommas(-long, segments, false) + } + val prefix = if (includeSign) "+" else "" val α = long / 1000 if (α != 0L) { - return formatCommas(α, segments) + "," + (long - α * 1000).toString().padStart(3, '0') + return prefix + formatCommas(α, segments) + "," + (long - α * 1000).toString().padStart(3, '0') } - return long.toString() + return prefix + long.toString() } fun formatCommas(float: Float, fractionalDigits: Int): String = formatCommas(float.toDouble(), fractionalDigits) - fun formatCommas(double: Double, fractionalDigits: Int): String { + fun formatCommas(double: Double, fractionalDigits: Int, includeSign: Boolean = false): String { val long = double.toLong() val δ = (double - long).absoluteValue val μ = pow(10, fractionalDigits) val digits = (μ * δ).toInt().toString().padStart(fractionalDigits, '0').trimEnd('0') - return formatCommas(long) + (if (digits.isEmpty()) "" else ".$digits") + return formatCommas(long, includeSign = includeSign) + (if (digits.isEmpty()) "" else ".$digits") } fun formatDistance(distance: Double): String { diff --git a/src/main/kotlin/util/LegacyFormattingCode.kt b/src/main/kotlin/util/LegacyFormattingCode.kt index 44bacfc..1a5d1dd 100644 --- a/src/main/kotlin/util/LegacyFormattingCode.kt +++ b/src/main/kotlin/util/LegacyFormattingCode.kt @@ -1,35 +1,37 @@ - - package moe.nea.firmament.util import net.minecraft.util.Formatting enum class LegacyFormattingCode(val label: String, val char: Char, val index: Int) { - BLACK("BLACK", '0', 0), - DARK_BLUE("DARK_BLUE", '1', 1), - DARK_GREEN("DARK_GREEN", '2', 2), - DARK_AQUA("DARK_AQUA", '3', 3), - DARK_RED("DARK_RED", '4', 4), - DARK_PURPLE("DARK_PURPLE", '5', 5), - GOLD("GOLD", '6', 6), - GRAY("GRAY", '7', 7), - DARK_GRAY("DARK_GRAY", '8', 8), - BLUE("BLUE", '9', 9), - GREEN("GREEN", 'a', 10), - AQUA("AQUA", 'b', 11), - RED("RED", 'c', 12), - LIGHT_PURPLE("LIGHT_PURPLE", 'd', 13), - YELLOW("YELLOW", 'e', 14), - WHITE("WHITE", 'f', 15), - OBFUSCATED("OBFUSCATED", 'k', -1), - BOLD("BOLD", 'l', -1), - STRIKETHROUGH("STRIKETHROUGH", 'm', -1), - UNDERLINE("UNDERLINE", 'n', -1), - ITALIC("ITALIC", 'o', -1), - RESET("RESET", 'r', -1); + BLACK("BLACK", '0', 0), + DARK_BLUE("DARK_BLUE", '1', 1), + DARK_GREEN("DARK_GREEN", '2', 2), + DARK_AQUA("DARK_AQUA", '3', 3), + DARK_RED("DARK_RED", '4', 4), + DARK_PURPLE("DARK_PURPLE", '5', 5), + GOLD("GOLD", '6', 6), + GRAY("GRAY", '7', 7), + DARK_GRAY("DARK_GRAY", '8', 8), + BLUE("BLUE", '9', 9), + GREEN("GREEN", 'a', 10), + AQUA("AQUA", 'b', 11), + RED("RED", 'c', 12), + LIGHT_PURPLE("LIGHT_PURPLE", 'd', 13), + YELLOW("YELLOW", 'e', 14), + WHITE("WHITE", 'f', 15), + OBFUSCATED("OBFUSCATED", 'k', -1), + BOLD("BOLD", 'l', -1), + STRIKETHROUGH("STRIKETHROUGH", 'm', -1), + UNDERLINE("UNDERLINE", 'n', -1), + ITALIC("ITALIC", 'o', -1), + RESET("RESET", 'r', -1); + + companion object { + val byCode = entries.associateBy { it.char } + } - val modern = Formatting.byCode(char)!! + val modern = Formatting.byCode(char)!! - val formattingCode = "§$char" + val formattingCode = "§$char" } diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt index 294334a..215d2a8 100644 --- a/src/main/kotlin/util/MC.kt +++ b/src/main/kotlin/util/MC.kt @@ -64,6 +64,8 @@ object MC { } fun sendCommand(command: String) { + // TODO: add a queue to this and sendServerChat + ErrorUtil.softCheck("Server commands have an implied /", !command.startsWith("/")) player?.networkHandler?.sendCommand(command) } @@ -96,8 +98,9 @@ object MC { inline val camera: Entity? get() = instance.cameraEntity inline val guiAtlasManager get() = instance.guiAtlasManager inline val world: ClientWorld? get() = TestUtil.unlessTesting { instance.world } + inline val playerName: String? get() = player?.name?.unformattedString inline var screen: Screen? - get() = TestUtil.unlessTesting{ instance.currentScreen } + get() = TestUtil.unlessTesting { instance.currentScreen } set(value) = instance.setScreen(value) val screenName get() = screen?.title?.unformattedString?.trim() inline val handledScreen: HandledScreen<*>? get() = instance.currentScreen as? HandledScreen<*> diff --git a/src/main/kotlin/util/MoulConfigUtils.kt b/src/main/kotlin/util/MoulConfigUtils.kt index 62bf3dd..362a4d9 100644 --- a/src/main/kotlin/util/MoulConfigUtils.kt +++ b/src/main/kotlin/util/MoulConfigUtils.kt @@ -25,6 +25,7 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.util.InputUtil import moe.nea.firmament.gui.BarComponent import moe.nea.firmament.gui.FirmButtonComponent import moe.nea.firmament.gui.FirmHoverComponent @@ -257,7 +258,17 @@ object MoulConfigUtils { keyboardEvent: KeyboardEvent ): Boolean { val immContext = createInPlaceFullContext(null, IMinecraft.instance.mouseX, IMinecraft.instance.mouseY) - return component.keyboardEvent(keyboardEvent, immContext.translated(x, y, w, h)) + if (component.keyboardEvent(keyboardEvent, immContext.translated(x, y, w, h))) + return true + if (component.context.getFocusedElement() != null) { + if (keyboardEvent is KeyboardEvent.KeyPressed + && keyboardEvent.pressed && keyboardEvent.keycode == InputUtil.GLFW_KEY_ESCAPE + ) { + component.context.setFocusedElement(null) + } + return true + } + return false } fun clickMCComponentInPlace( diff --git a/src/main/kotlin/util/SBData.kt b/src/main/kotlin/util/SBData.kt index 051d070..b2f9449 100644 --- a/src/main/kotlin/util/SBData.kt +++ b/src/main/kotlin/util/SBData.kt @@ -1,5 +1,6 @@ package moe.nea.firmament.util +import java.time.ZoneId import java.util.UUID import net.hypixel.modapi.HypixelModAPI import net.hypixel.modapi.packet.impl.clientbound.event.ClientboundLocationPacket @@ -10,63 +11,75 @@ import moe.nea.firmament.events.ProcessChatEvent import moe.nea.firmament.events.ProfileSwitchEvent import moe.nea.firmament.events.ServerConnectedEvent import moe.nea.firmament.events.SkyblockServerUpdateEvent -import moe.nea.firmament.events.WorldReadyEvent object SBData { - private val profileRegex = "Profile ID: ([a-z0-9\\-]+)".toRegex() - val profileSuggestTexts = listOf( - "CLICK THIS TO SUGGEST IT IN CHAT [DASHES]", - "CLICK THIS TO SUGGEST IT IN CHAT [NO DASHES]", - ) - var profileId: UUID? = null + private val profileRegex = "Profile ID: ([a-z0-9\\-]+)".toRegex() + val profileSuggestTexts = listOf( + "CLICK THIS TO SUGGEST IT IN CHAT [DASHES]", + "CLICK THIS TO SUGGEST IT IN CHAT [NO DASHES]", + ) + var profileId: UUID? = null + get() { + // TODO: allow unfiltered access to this somehow + if (!isOnSkyblock) return null + return field + } - private var hasReceivedProfile = false - var locraw: Locraw? = null - val skyblockLocation: SkyBlockIsland? get() = locraw?.skyblockLocation - val hasValidLocraw get() = locraw?.server !in listOf("limbo", null) - val isOnSkyblock get() = locraw?.gametype == "SKYBLOCK" - var profileIdCommandDebounce = TimeMark.farPast() - fun init() { - ServerConnectedEvent.subscribe("SBData:onServerConnected") { - HypixelModAPI.getInstance().subscribeToEventPacket(ClientboundLocationPacket::class.java) - } - HypixelModAPI.getInstance().createHandler(ClientboundLocationPacket::class.java) { - MC.onMainThread { - val lastLocraw = locraw - locraw = Locraw(it.serverName, - it.serverType.getOrNull()?.name?.uppercase(), - it.mode.getOrNull(), - it.map.getOrNull()) - SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw)) - profileIdCommandDebounce = TimeMark.now() - } - } - SkyblockServerUpdateEvent.subscribe("SBData:sendProfileId") { - if (!hasReceivedProfile && isOnSkyblock && profileIdCommandDebounce.passedTime() > 10.seconds) { - profileIdCommandDebounce = TimeMark.now() - MC.sendServerCommand("profileid") - } - } - AllowChatEvent.subscribe("SBData:hideProfileSuggest") { event -> - if (event.unformattedString in profileSuggestTexts && profileIdCommandDebounce.passedTime() < 5.seconds) { - event.cancel() - } - } - ProcessChatEvent.subscribe(receivesCancelled = true, "SBData:loadProfile") { event -> - val profileMatch = profileRegex.matchEntire(event.unformattedString) - if (profileMatch != null) { - val oldProfile = profileId - try { - profileId = UUID.fromString(profileMatch.groupValues[1]) - hasReceivedProfile = true - } catch (e: IllegalArgumentException) { - profileId = null - e.printStackTrace() - } - if (oldProfile != profileId) { - ProfileSwitchEvent.publish(ProfileSwitchEvent(oldProfile, profileId)) - } - } - } - } + /** + * Source: https://hypixel-skyblock.fandom.com/wiki/Time_Systems + */ + val hypixelTimeZone = ZoneId.of("US/Eastern") + private var hasReceivedProfile = false + var locraw: Locraw? = null + val skyblockLocation: SkyBlockIsland? get() = locraw?.skyblockLocation + val hasValidLocraw get() = locraw?.server !in listOf("limbo", null) + val isOnSkyblock get() = locraw?.gametype == "SKYBLOCK" + var profileIdCommandDebounce = TimeMark.farPast() + fun init() { + ServerConnectedEvent.subscribe("SBData:onServerConnected") { + HypixelModAPI.getInstance().subscribeToEventPacket(ClientboundLocationPacket::class.java) + } + HypixelModAPI.getInstance().createHandler(ClientboundLocationPacket::class.java) { + MC.onMainThread { + val lastLocraw = locraw + val oldProfileId = profileId + locraw = Locraw(it.serverName, + it.serverType.getOrNull()?.name?.uppercase(), + it.mode.getOrNull(), + it.map.getOrNull()) + SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw)) + if(oldProfileId != profileId) { + ProfileSwitchEvent.publish(ProfileSwitchEvent(oldProfileId, profileId)) + } + profileIdCommandDebounce = TimeMark.now() + } + } + SkyblockServerUpdateEvent.subscribe("SBData:sendProfileId") { + if (!hasReceivedProfile && isOnSkyblock && profileIdCommandDebounce.passedTime() > 10.seconds) { + profileIdCommandDebounce = TimeMark.now() + MC.sendServerCommand("profileid") + } + } + AllowChatEvent.subscribe("SBData:hideProfileSuggest") { event -> + if (event.unformattedString in profileSuggestTexts && profileIdCommandDebounce.passedTime() < 5.seconds) { + event.cancel() + } + } + ProcessChatEvent.subscribe(receivesCancelled = true, "SBData:loadProfile") { event -> + val profileMatch = profileRegex.matchEntire(event.unformattedString) + if (profileMatch != null) { + val oldProfile = profileId + try { + profileId = UUID.fromString(profileMatch.groupValues[1]) + hasReceivedProfile = true + } catch (e: IllegalArgumentException) { + profileId = null + e.printStackTrace() + } + if (oldProfile != profileId) { + ProfileSwitchEvent.publish(ProfileSwitchEvent(oldProfile, profileId)) + } + } + } + } } diff --git a/src/main/kotlin/util/ScoreboardUtil.kt b/src/main/kotlin/util/ScoreboardUtil.kt index 4311971..0970892 100644 --- a/src/main/kotlin/util/ScoreboardUtil.kt +++ b/src/main/kotlin/util/ScoreboardUtil.kt @@ -1,8 +1,6 @@ - - package moe.nea.firmament.util -import java.util.* +import java.util.Optional import net.minecraft.client.gui.hud.InGameHud import net.minecraft.scoreboard.ScoreboardDisplaySlot import net.minecraft.scoreboard.Team @@ -10,36 +8,48 @@ import net.minecraft.text.StringVisitable import net.minecraft.text.Style import net.minecraft.text.Text import net.minecraft.util.Formatting +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.TickEvent -fun getScoreboardLines(): List<Text> { - val scoreboard = MC.player?.scoreboard ?: return listOf() - val activeObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) ?: return listOf() - return scoreboard.getScoreboardEntries(activeObjective) - .filter { !it.hidden() } - .sortedWith(InGameHud.SCOREBOARD_ENTRY_COMPARATOR) - .take(15).map { - val team = scoreboard.getScoreHolderTeam(it.owner) - val text = it.name() - Team.decorateName(team, text) - } -} +object ScoreboardUtil { + var scoreboardLines: List<Text> = listOf() + var simplifiedScoreboardLines: List<String> = listOf() + @Subscribe + fun onTick(event: TickEvent) { + scoreboardLines = getScoreboardLinesUncached() + simplifiedScoreboardLines = scoreboardLines.map { it.unformattedString } + } + + private fun getScoreboardLinesUncached(): List<Text> { + val scoreboard = MC.player?.scoreboard ?: return listOf() + val activeObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) ?: return listOf() + return scoreboard.getScoreboardEntries(activeObjective) + .filter { !it.hidden() } + .sortedWith(InGameHud.SCOREBOARD_ENTRY_COMPARATOR) + .take(15).map { + val team = scoreboard.getScoreHolderTeam(it.owner) + val text = it.name() + Team.decorateName(team, text) + } + } +} fun Text.formattedString(): String { - val sb = StringBuilder() - visit(StringVisitable.StyledVisitor<Unit> { style, string -> - val c = Formatting.byName(style.color?.name) - if (c != null) { - sb.append("§${c.code}") - } - if (style.isUnderlined) { - sb.append("§n") - } - if (style.isBold) { - sb.append("§l") - } - sb.append(string) - Optional.empty() - }, Style.EMPTY) - return sb.toString().replace("§[^a-f0-9]".toRegex(), "") + val sb = StringBuilder() + visit(StringVisitable.StyledVisitor<Unit> { style, string -> + val c = Formatting.byName(style.color?.name) + if (c != null) { + sb.append("§${c.code}") + } + if (style.isUnderlined) { + sb.append("§n") + } + if (style.isBold) { + sb.append("§l") + } + sb.append(string) + Optional.empty() + }, Style.EMPTY) + return sb.toString().replace("§[^a-f0-9]".toRegex(), "") } diff --git a/src/main/kotlin/util/SkyBlockIsland.kt b/src/main/kotlin/util/SkyBlockIsland.kt index c42a55c..a86543c 100644 --- a/src/main/kotlin/util/SkyBlockIsland.kt +++ b/src/main/kotlin/util/SkyBlockIsland.kt @@ -35,6 +35,8 @@ private constructor( val PRIVATE_ISLAND = forMode("dynamic") val RIFT = forMode("rift") val MINESHAFT = forMode("mineshaft") + val GARDEN = forMode("garden") + val DUNGEON = forMode("dungeon") } val userFriendlyName diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt index 1c1aa77..a99afda 100644 --- a/src/main/kotlin/util/SkyblockId.kt +++ b/src/main/kotlin/util/SkyblockId.kt @@ -106,7 +106,10 @@ data class HypixelPetInfo( private val jsonparser = Json { ignoreUnknownKeys = true } -val ItemStack.extraAttributes: NbtCompound +var ItemStack.extraAttributes: NbtCompound + set(value) { + set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(value)) + } get() { val customData = get(DataComponentTypes.CUSTOM_DATA) ?: run { val component = NbtComponent.of(NbtCompound()) @@ -116,6 +119,12 @@ val ItemStack.extraAttributes: NbtCompound return customData.nbt } +fun ItemStack.modifyExtraAttributes(block: (NbtCompound) -> Unit) { + val baseNbt = get(DataComponentTypes.CUSTOM_DATA)?.copyNbt() ?: NbtCompound() + block(baseNbt) + set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(baseNbt)) +} + val ItemStack.skyblockUUIDString: String? get() = extraAttributes.getString("uuid")?.takeIf { it.isNotBlank() } @@ -125,10 +134,26 @@ val ItemStack.skyblockUUID: UUID? private val petDataCache = WeakCache.memoize<ItemStack, Optional<HypixelPetInfo>>("PetInfo") { val jsonString = it.extraAttributes.getString("petInfo") if (jsonString.isNullOrBlank()) return@memoize Optional.empty() - ErrorUtil.catch<HypixelPetInfo?>("Could not decode hypixel pet info") { jsonparser.decodeFromString<HypixelPetInfo>(jsonString) } + ErrorUtil.catch<HypixelPetInfo?>("Could not decode hypixel pet info") { + jsonparser.decodeFromString<HypixelPetInfo>(jsonString) + } .or { null }.intoOptional() } +fun ItemStack.getUpgradeStars(): Int { + return extraAttributes.getInt("upgrade_level").takeIf { it > 0 } + ?: extraAttributes.getInt("dungeon_item_level").takeIf { it > 0 } + ?: 0 +} + +@Serializable +@JvmInline +value class ReforgeId(val id: String) + +fun ItemStack.getReforgeId(): ReforgeId? { + return extraAttributes.getString("modifier").takeIf { it.isNotBlank() }?.let(::ReforgeId) +} + val ItemStack.petData: HypixelPetInfo? get() = petDataCache(this).getOrNull() diff --git a/src/main/kotlin/util/mc/IntrospectableItemModelManager.kt b/src/main/kotlin/util/mc/IntrospectableItemModelManager.kt new file mode 100644 index 0000000..e546fd3 --- /dev/null +++ b/src/main/kotlin/util/mc/IntrospectableItemModelManager.kt @@ -0,0 +1,7 @@ +package moe.nea.firmament.util.mc + +import net.minecraft.util.Identifier + +interface IntrospectableItemModelManager { + fun hasModel_firmament(identifier: Identifier): Boolean +} diff --git a/src/main/kotlin/util/mc/NbtItemData.kt b/src/main/kotlin/util/mc/NbtItemData.kt index e8a908f..0c49862 100644 --- a/src/main/kotlin/util/mc/NbtItemData.kt +++ b/src/main/kotlin/util/mc/NbtItemData.kt @@ -5,8 +5,8 @@ import net.minecraft.component.type.LoreComponent import net.minecraft.item.ItemStack import net.minecraft.text.Text -var ItemStack.loreAccordingToNbt - get() = get(DataComponentTypes.LORE)?.lines ?: listOf() +var ItemStack.loreAccordingToNbt: List<Text> + get() = get(DataComponentTypes.LORE)?.lines ?: listOf() set(value) { set(DataComponentTypes.LORE, LoreComponent(value)) } diff --git a/src/main/kotlin/util/mc/TolerantRegistriesOps.kt b/src/main/kotlin/util/mc/TolerantRegistriesOps.kt new file mode 100644 index 0000000..ce596a0 --- /dev/null +++ b/src/main/kotlin/util/mc/TolerantRegistriesOps.kt @@ -0,0 +1,29 @@ +package moe.nea.firmament.util.mc + +import com.mojang.serialization.DynamicOps +import java.util.Optional +import net.minecraft.registry.Registry +import net.minecraft.registry.RegistryKey +import net.minecraft.registry.RegistryOps +import net.minecraft.registry.RegistryWrapper +import net.minecraft.registry.entry.RegistryEntryOwner + +class TolerantRegistriesOps<T>( + delegate: DynamicOps<T>, + registryInfoGetter: RegistryInfoGetter +) : RegistryOps<T>(delegate, registryInfoGetter) { + constructor(delegate: DynamicOps<T>, registry: RegistryWrapper.WrapperLookup) : + this(delegate, CachedRegistryInfoGetter(registry)) + + class TolerantOwner<E> : RegistryEntryOwner<E> { + override fun ownerEquals(other: RegistryEntryOwner<E>?): Boolean { + return true + } + } + + override fun <E : Any?> getOwner(registryRef: RegistryKey<out Registry<out E>>?): Optional<RegistryEntryOwner<E>> { + return super.getOwner(registryRef).map { + TolerantOwner() + } + } +} diff --git a/src/main/kotlin/util/render/TranslatedScissors.kt b/src/main/kotlin/util/render/TranslatedScissors.kt index c1e6544..8f8bdcf 100644 --- a/src/main/kotlin/util/render/TranslatedScissors.kt +++ b/src/main/kotlin/util/render/TranslatedScissors.kt @@ -1,11 +1,15 @@ package moe.nea.firmament.util.render +import org.joml.Matrix4f import org.joml.Vector4f import net.minecraft.client.gui.DrawContext fun DrawContext.enableScissorWithTranslation(x1: Float, y1: Float, x2: Float, y2: Float) { - val pMat = matrices.peek().positionMatrix + enableScissor(x1.toInt(), y1.toInt(), x2.toInt(), y2.toInt()) +} +fun DrawContext.enableScissorWithoutTranslation(x1: Float, y1: Float, x2: Float, y2: Float) { + val pMat = matrices.peek().positionMatrix.invert(Matrix4f()) val target = Vector4f() target.set(x1, y1, 0f, 1f) diff --git a/src/main/kotlin/util/skyblock/DungeonUtil.kt b/src/main/kotlin/util/skyblock/DungeonUtil.kt new file mode 100644 index 0000000..488b158 --- /dev/null +++ b/src/main/kotlin/util/skyblock/DungeonUtil.kt @@ -0,0 +1,33 @@ +package moe.nea.firmament.util.skyblock + +import moe.nea.firmament.util.SBData +import moe.nea.firmament.util.ScoreboardUtil +import moe.nea.firmament.util.SkyBlockIsland +import moe.nea.firmament.util.TIME_PATTERN + +object DungeonUtil { + val isInDungeonIsland get() = SBData.skyblockLocation == SkyBlockIsland.DUNGEON + private val timeElapsedRegex = "Time Elapsed: $TIME_PATTERN".toRegex() + val isInActiveDungeon get() = isInDungeonIsland && ScoreboardUtil.simplifiedScoreboardLines.any { it.matches( + timeElapsedRegex) } + +/*Title: + +§f§lSKYBLOCK§B§L CO-OP + +' Late Spring 7th' +' §75:20am' +' §7⏣ §cThe Catacombs §7(M3)' +' §7♲ §7Ironman' +' ' +'Keys: §c■ §c✗ §8■ §a1x' +'Time Elapsed: §a46s' +'Cleared: §660% §8(105)' +' ' +'§e[B] §b151_Dragon §e2,062§c❤' +'§e[A] §6Lennart0312 §a17,165§c' +'§e[T] §b187i §a14,581§c❤' +'§e[H] §bFlameeke §a8,998§c❤' +' ' +'§ewww.hypixel.net'*/ +} diff --git a/src/main/kotlin/util/skyblock/ItemType.kt b/src/main/kotlin/util/skyblock/ItemType.kt index 6ddb077..7a776b5 100644 --- a/src/main/kotlin/util/skyblock/ItemType.kt +++ b/src/main/kotlin/util/skyblock/ItemType.kt @@ -13,6 +13,13 @@ value class ItemType private constructor(val name: String) { return ItemType(name) } + private val obfuscatedRegex = "§[kK].*?(§[0-9a-fA-FrR]|$)".toRegex() + fun fromEscapeCodeLore(lore: String): ItemType? { + return lore.replace(obfuscatedRegex, "").trim().substringAfter(" ", "") + .takeIf { it.isNotEmpty() } + ?.let(::ofName) + } + fun fromItemStack(itemStack: ItemStack): ItemType? { if (itemStack.petData != null) return PET @@ -26,13 +33,31 @@ value class ItemType private constructor(val name: String) { if (type.isEmpty()) return null return ofName(type) } - return null + return itemStack.loreAccordingToNbt.lastOrNull()?.directLiteralStringContent?.let(::fromEscapeCodeLore) } + // TODO: some of those are not actual in game item types, but rather ones included in the repository to splat to multiple in game types. codify those somehow + val SWORD = ofName("SWORD") val DRILL = ofName("DRILL") val PICKAXE = ofName("PICKAXE") val GAUNTLET = ofName("GAUNTLET") + val LONGSWORD = ofName("LONG SWORD") + val EQUIPMENT = ofName("EQUIPMENT") + val FISHING_WEAPON = ofName("FISHING WEAPON") + val CLOAK = ofName("CLOAK") + val BELT = ofName("BELT") + val NECKLACE = ofName("NECKLACE") + val BRACELET = ofName("BRACELET") + val GLOVES = ofName("GLOVES") + val ROD = ofName("ROD") + val FISHING_ROD = ofName("FISHING ROD") + val VACUUM = ofName("VACUUM") + val CHESTPLATE = ofName("CHESTPLATE") + val LEGGINGS = ofName("LEGGINGS") + val HELMET = ofName("HELMET") + val BOOTS = ofName("BOOTS") + val NIL = ofName("__NIL") /** * This one is not really official (it never shows up in game). @@ -40,6 +65,10 @@ value class ItemType private constructor(val name: String) { val PET = ofName("PET") } + val dungeonVariant get() = ofName("DUNGEON $name") + + val isDungeon get() = name.startsWith("DUNGEON ") + override fun toString(): String { return name } diff --git a/src/main/kotlin/util/skyblock/Rarity.kt b/src/main/kotlin/util/skyblock/Rarity.kt index f26cefe..b19f371 100644 --- a/src/main/kotlin/util/skyblock/Rarity.kt +++ b/src/main/kotlin/util/skyblock/Rarity.kt @@ -1,7 +1,16 @@ package moe.nea.firmament.util.skyblock +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import net.minecraft.item.ItemStack +import net.minecraft.text.Style import net.minecraft.text.Text +import net.minecraft.util.Formatting import moe.nea.firmament.util.StringUtil.words import moe.nea.firmament.util.collections.lastNotNullOfOrNull import moe.nea.firmament.util.mc.loreAccordingToNbt @@ -10,6 +19,7 @@ import moe.nea.firmament.util.unformattedString typealias RepoRarity = io.github.moulberry.repo.data.Rarity +@Serializable(with = Rarity.Serializer::class) enum class Rarity(vararg altNames: String) { COMMON, UNCOMMON, @@ -24,11 +34,37 @@ enum class Rarity(vararg altNames: String) { UNKNOWN ; - val names = setOf(name) + altNames + object Serializer : KSerializer<Rarity> { + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor(Rarity::class.java.name, PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): Rarity { + return valueOf(decoder.decodeString().replace(" ", "_")) + } + override fun serialize(encoder: Encoder, value: Rarity) { + encoder.encodeString(value.name) + } + } + + val names = setOf(name) + altNames + val text: Text get() = Text.literal(name).setStyle(Style.EMPTY.withColor(colourMap[this])) val neuRepoRarity: RepoRarity? = RepoRarity.entries.find { it.name == name } companion object { + // TODO: inline those formattings as fields + val colourMap = mapOf( + Rarity.COMMON to Formatting.WHITE, + Rarity.UNCOMMON to Formatting.GREEN, + Rarity.RARE to Formatting.BLUE, + Rarity.EPIC to Formatting.DARK_PURPLE, + Rarity.LEGENDARY to Formatting.GOLD, + Rarity.MYTHIC to Formatting.LIGHT_PURPLE, + Rarity.DIVINE to Formatting.AQUA, + Rarity.SPECIAL to Formatting.RED, + Rarity.VERY_SPECIAL to Formatting.RED, + Rarity.SUPREME to Formatting.DARK_RED, + ) val byName = entries.flatMap { en -> en.names.map { it to en } }.toMap() val fromNeuRepo = entries.associateBy { it.neuRepoRarity } diff --git a/src/main/kotlin/util/skyblock/SkyBlockItems.kt b/src/main/kotlin/util/skyblock/SkyBlockItems.kt index c94ebfe..cfd8429 100644 --- a/src/main/kotlin/util/skyblock/SkyBlockItems.kt +++ b/src/main/kotlin/util/skyblock/SkyBlockItems.kt @@ -7,4 +7,5 @@ object SkyBlockItems { val ENCHANTED_DIAMOND = SkyblockId("ENCHANTED_DIAMOND") val DIAMOND = SkyblockId("DIAMOND") val ANCESTRAL_SPADE = SkyblockId("ANCESTRAL_SPADE") + val REFORGE_ANVIL = SkyblockId("REFORGE_ANVIL") } diff --git a/src/main/kotlin/util/textutil.kt b/src/main/kotlin/util/textutil.kt index 5d95d7a..c295ae0 100644 --- a/src/main/kotlin/util/textutil.kt +++ b/src/main/kotlin/util/textutil.kt @@ -1,72 +1,18 @@ package moe.nea.firmament.util +import java.util.Optional import net.minecraft.text.ClickEvent import net.minecraft.text.MutableText +import net.minecraft.text.OrderedText import net.minecraft.text.PlainTextContent +import net.minecraft.text.StringVisitable +import net.minecraft.text.Style import net.minecraft.text.Text import net.minecraft.text.TextColor import net.minecraft.text.TranslatableTextContent import net.minecraft.util.Formatting -import moe.nea.firmament.Firmament - - -class TextMatcher(text: Text) { - data class State( - var iterator: MutableList<Text>, - var currentText: Text?, - var offset: Int, - var textContent: String, - ) - - var state = State( - mutableListOf(text), - null, - 0, - "" - ) - - fun pollChunk(): Boolean { - val firstOrNull = state.iterator.removeFirstOrNull() ?: return false - state.offset = 0 - state.currentText = firstOrNull - state.textContent = when (val content = firstOrNull.content) { - is PlainTextContent.Literal -> content.string - else -> { - Firmament.logger.warn("TextContent of type ${content.javaClass} not understood.") - return false - } - } - state.iterator.addAll(0, firstOrNull.siblings) - return true - } - - fun pollChunks(): Boolean { - while (state.offset !in state.textContent.indices) { - if (!pollChunk()) { - return false - } - } - return true - } - - fun pollChar(): Char? { - if (!pollChunks()) return null - return state.textContent[state.offset++] - } - fun expectString(string: String): Boolean { - var found = "" - while (found.length < string.length) { - if (!pollChunks()) return false - val takeable = state.textContent.drop(state.offset).take(string.length - found.length) - state.offset += takeable.length - found += takeable - } - return found == string - } -} - val formattingChars = "kmolnrKMOLNR".toSet() fun CharSequence.removeColorCodes(keepNonColorCodes: Boolean = false): String { var nextParagraph = indexOf('§') @@ -89,6 +35,47 @@ fun CharSequence.removeColorCodes(keepNonColorCodes: Boolean = false): String { return stringBuffer.toString() } +fun OrderedText.reconstitute(): MutableText { + val base = Text.literal("") + base.setStyle(Style.EMPTY.withItalic(false)) + var lastColorCode = Style.EMPTY + val text = StringBuilder() + this.accept { index, style, codePoint -> + if (style != lastColorCode) { + if (text.isNotEmpty()) + base.append(Text.literal(text.toString()).setStyle(lastColorCode)) + lastColorCode = style + text.clear() + } + text.append(codePoint.toChar()) + true + } + if (text.isNotEmpty()) + base.append(Text.literal(text.toString()).setStyle(lastColorCode)) + return base + +} +fun StringVisitable.reconstitute(): MutableText { + val base = Text.literal("") + base.setStyle(Style.EMPTY.withItalic(false)) + var lastColorCode = Style.EMPTY + val text = StringBuilder() + this.visit({ style, string -> + if (style != lastColorCode) { + if (text.isNotEmpty()) + base.append(Text.literal(text.toString()).setStyle(lastColorCode)) + lastColorCode = style + text.clear() + } + text.append(string) + Optional.empty<Unit>() + }, Style.EMPTY) + if (text.isNotEmpty()) + base.append(Text.literal(text.toString()).setStyle(lastColorCode)) + return base + +} + val Text.unformattedString: String get() = string.removeColorCodes() // TODO: maybe shortcircuit this with .visit @@ -133,7 +120,9 @@ fun MutableText.darkGreen() = withColor(Formatting.DARK_GREEN) fun MutableText.purple() = withColor(Formatting.DARK_PURPLE) fun MutableText.pink() = withColor(Formatting.LIGHT_PURPLE) fun MutableText.yellow() = withColor(Formatting.YELLOW) +fun MutableText.gold() = withColor(Formatting.GOLD) fun MutableText.grey() = withColor(Formatting.GRAY) +fun MutableText.darkGrey() = withColor(Formatting.DARK_GRAY) fun MutableText.red() = withColor(Formatting.RED) fun MutableText.white() = withColor(Formatting.WHITE) fun MutableText.bold(): MutableText = styled { it.withBold(true) } @@ -142,11 +131,15 @@ fun MutableText.bold(): MutableText = styled { it.withBold(true) } fun MutableText.clickCommand(command: String): MutableText { require(command.startsWith("/")) return this.styled { - it.withClickEvent(ClickEvent(ClickEvent.Action.RUN_COMMAND, - "/firm disablereiwarning")) + it.withClickEvent(ClickEvent(ClickEvent.Action.RUN_COMMAND, command)) } } +fun MutableText.prepend(text: Text): MutableText { + siblings.addFirst(text) + return this +} + fun Text.transformEachRecursively(function: (Text) -> Text): Text { val c = this.content if (c is TranslatableTextContent) { |