Add basic sack util
[no changelog]
This commit is contained in:
8
src/main/kotlin/util/accessors/chathud.kt
Normal file
8
src/main/kotlin/util/accessors/chathud.kt
Normal file
@@ -0,0 +1,8 @@
|
||||
package moe.nea.firmament.util.accessors
|
||||
|
||||
import net.minecraft.client.gui.hud.ChatHud
|
||||
import net.minecraft.client.gui.hud.ChatHudLine
|
||||
import moe.nea.firmament.mixins.accessor.AccessorChatHud
|
||||
|
||||
val ChatHud.messages: MutableList<ChatHudLine>
|
||||
get() = (this as AccessorChatHud).messages_firmament
|
||||
28
src/main/kotlin/util/mc/InventoryUtil.kt
Normal file
28
src/main/kotlin/util/mc/InventoryUtil.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package moe.nea.firmament.util.mc
|
||||
|
||||
import java.util.Spliterator
|
||||
import java.util.Spliterators
|
||||
import net.minecraft.inventory.Inventory
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
val Inventory.indices get() = 0 until size()
|
||||
val Inventory.iterableView
|
||||
get() = object : Iterable<ItemStack> {
|
||||
override fun spliterator(): Spliterator<ItemStack> {
|
||||
return Spliterators.spliterator(iterator(), size().toLong(), 0)
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<ItemStack> {
|
||||
return object : Iterator<ItemStack> {
|
||||
var i = 0
|
||||
override fun hasNext(): Boolean {
|
||||
return i < size()
|
||||
}
|
||||
|
||||
override fun next(): ItemStack {
|
||||
if (!hasNext()) throw NoSuchElementException()
|
||||
return getStack(i++)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.util
|
||||
package moe.nea.firmament.util.mc
|
||||
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtList
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.util.item.loreAccordingToNbt
|
||||
|
||||
|
||||
fun ItemStack.appendLore(args: List<Text>) {
|
||||
if (args.isEmpty()) return
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.util.item
|
||||
package moe.nea.firmament.util.mc
|
||||
|
||||
import net.minecraft.component.DataComponentTypes
|
||||
import net.minecraft.component.type.LoreComponent
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
|
||||
@file:UseSerializers(DashlessUUIDSerializer::class, InstantAsLongSerializer::class)
|
||||
|
||||
package moe.nea.firmament.util.item
|
||||
package moe.nea.firmament.util.mc
|
||||
|
||||
import com.mojang.authlib.GameProfile
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.util
|
||||
|
||||
import java.util.regex.Matcher
|
||||
@@ -10,12 +8,12 @@ import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? =
|
||||
regex.matchEntire(this)?.let(block)
|
||||
regex.matchEntire(this)?.let(block)
|
||||
|
||||
inline fun <T> Pattern.useMatch(string: String, block: Matcher.() -> T): T? =
|
||||
matcher(string)
|
||||
.takeIf(Matcher::matches)
|
||||
?.let(block)
|
||||
matcher(string)
|
||||
.takeIf(Matcher::matches)
|
||||
?.let(block)
|
||||
|
||||
@Language("RegExp")
|
||||
val TIME_PATTERN = "[0-9]+[ms]"
|
||||
@@ -25,31 +23,33 @@ val SHORT_NUMBER_FORMAT = "[0-9]+(?:,[0-9]+)*(?:\\.[0-9]+)?[kKmMbB]?"
|
||||
|
||||
|
||||
val siScalars = mapOf(
|
||||
'k' to 1_000.0,
|
||||
'K' to 1_000.0,
|
||||
'm' to 1_000_000.0,
|
||||
'M' to 1_000_000.0,
|
||||
'b' to 1_000_000_000.0,
|
||||
'B' to 1_000_000_000.0,
|
||||
'k' to 1_000.0,
|
||||
'K' to 1_000.0,
|
||||
'm' to 1_000_000.0,
|
||||
'M' to 1_000_000.0,
|
||||
'b' to 1_000_000_000.0,
|
||||
'B' to 1_000_000_000.0,
|
||||
)
|
||||
|
||||
fun parseTimePattern(text: String): Duration {
|
||||
val length = text.dropLast(1).toInt()
|
||||
return when (text.last()) {
|
||||
'm' -> length.minutes
|
||||
's' -> length.seconds
|
||||
else -> error("Invalid pattern for time $text")
|
||||
}
|
||||
val length = text.dropLast(1).toInt()
|
||||
return when (text.last()) {
|
||||
'm' -> length.minutes
|
||||
's' -> length.seconds
|
||||
else -> error("Invalid pattern for time $text")
|
||||
}
|
||||
}
|
||||
|
||||
fun parseShortNumber(string: String): Double {
|
||||
var k = string.replace(",", "")
|
||||
val scalar = k.last()
|
||||
var scalarMultiplier = siScalars[scalar]
|
||||
if (scalarMultiplier == null) {
|
||||
scalarMultiplier = 1.0
|
||||
} else {
|
||||
k = k.dropLast(1)
|
||||
}
|
||||
return k.toDouble() * scalarMultiplier
|
||||
if (string.startsWith("-")) return -parseShortNumber(string.substring(1))
|
||||
if (string.startsWith("+")) return parseShortNumber(string.substring(1))
|
||||
var k = string.replace(",", "")
|
||||
val scalar = k.last()
|
||||
var scalarMultiplier = siScalars[scalar]
|
||||
if (scalarMultiplier == null) {
|
||||
scalarMultiplier = 1.0
|
||||
} else {
|
||||
k = k.dropLast(1)
|
||||
}
|
||||
return k.toDouble() * scalarMultiplier
|
||||
}
|
||||
|
||||
110
src/main/kotlin/util/skyblock/SackUtil.kt
Normal file
110
src/main/kotlin/util/skyblock/SackUtil.kt
Normal file
@@ -0,0 +1,110 @@
|
||||
package moe.nea.firmament.util.skyblock
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
|
||||
import net.minecraft.text.HoverEvent
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.ChestInventoryUpdateEvent
|
||||
import moe.nea.firmament.events.ProcessChatEvent
|
||||
import moe.nea.firmament.repo.ItemNameLookup
|
||||
import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
|
||||
import moe.nea.firmament.util.SkyblockId
|
||||
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
||||
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||
import moe.nea.firmament.util.mc.iterableView
|
||||
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||
import moe.nea.firmament.util.parseShortNumber
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
import moe.nea.firmament.util.unformattedString
|
||||
import moe.nea.firmament.util.useMatch
|
||||
|
||||
object SackUtil {
|
||||
@Serializable
|
||||
data class SackContents(
|
||||
// TODO: store the certainty of knowledge for each item.
|
||||
val contents: MutableMap<SkyblockId, Long> = mutableMapOf(),
|
||||
// val sackTypes:
|
||||
)
|
||||
|
||||
object Store : ProfileSpecificDataHolder<SackContents>(serializer(), "Sacks", ::SackContents)
|
||||
|
||||
val items get() = Store.data?.contents ?: mutableMapOf()
|
||||
val storedRegex = "^Stored: (?<stored>$SHORT_NUMBER_FORMAT)/(?<max>$SHORT_NUMBER_FORMAT)$".toPattern()
|
||||
|
||||
@Subscribe
|
||||
fun storeDataFromInventory(event: ChestInventoryUpdateEvent) {
|
||||
val screen = event.inventory as? GenericContainerScreen ?: return
|
||||
if (!screen.title.unformattedString.endsWith(" Sack")) return
|
||||
val inv = screen.screenHandler?.inventory ?: return
|
||||
if (inv.size() < 18) return
|
||||
val backSlot = inv.getStack(inv.size() - 5)
|
||||
if (backSlot.displayNameAccordingToNbt.unformattedString != "Go Back") return
|
||||
if (backSlot.loreAccordingToNbt.map { it.unformattedString } != listOf("To Sack of Sacks")) return
|
||||
for (itemStack in inv.iterableView) {
|
||||
// TODO: handle runes and gemstones
|
||||
val stored = itemStack.loreAccordingToNbt.firstNotNullOfOrNull {
|
||||
storedRegex.useMatch(it.unformattedString) {
|
||||
val stored = parseShortNumber(group("stored")).toLong()
|
||||
val max = parseShortNumber(group("max")).toLong()
|
||||
stored
|
||||
}
|
||||
} ?: continue
|
||||
val itemId = itemStack.skyBlockId ?: continue
|
||||
items[itemId] = stored
|
||||
}
|
||||
Store.markDirty()
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun updateFromChat(event: ProcessChatEvent) {
|
||||
if (!event.unformattedString.startsWith("[Sacks]")) return
|
||||
val update = ChatUpdate()
|
||||
event.text.siblings.forEach(update::updateFromHoverText)
|
||||
}
|
||||
|
||||
data class SackUpdate(
|
||||
val itemId: SkyblockId?,
|
||||
val itemName: String,
|
||||
val changeAmount: Long,
|
||||
)
|
||||
|
||||
private class ChatUpdate {
|
||||
val updates = mutableListOf<SackUpdate>()
|
||||
var foundAdded = false
|
||||
var foundRemoved = false
|
||||
|
||||
fun updateFromCleanText(cleanedText: String) {
|
||||
cleanedText.split("\n").forEach { line ->
|
||||
changePattern.useMatch(line) {
|
||||
val amount = parseShortNumber(group("amount")).toLong()
|
||||
val itemName = group("itemName")
|
||||
val itemId = ItemNameLookup.guessItemByName(itemName, false)
|
||||
updates.add(SackUpdate(itemId, itemName, amount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFromHoverText(text: Text) {
|
||||
text.siblings.forEach(::updateFromHoverText)
|
||||
val hoverText = text.style.hoverEvent?.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
|
||||
val cleanedText = hoverText.unformattedString
|
||||
if (cleanedText.startsWith("Added items:\n")) {
|
||||
if (!foundAdded) {
|
||||
updateFromCleanText(cleanedText)
|
||||
foundAdded = true
|
||||
}
|
||||
}
|
||||
if (cleanedText.startsWith("Removed items:\n")) {
|
||||
if (!foundRemoved) {
|
||||
updateFromCleanText(cleanedText)
|
||||
foundRemoved = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val changePattern = " (?<amount>[+\\-]$SHORT_NUMBER_FORMAT) (?<itemName>[^(]+) \\(.*\\)".toPattern()
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
|
||||
|
||||
package moe.nea.firmament.util
|
||||
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.PlainTextContent
|
||||
import net.minecraft.text.Style
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.text.TranslatableTextContent
|
||||
import net.minecraft.util.Formatting
|
||||
@@ -12,106 +9,107 @@ 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,
|
||||
)
|
||||
data class State(
|
||||
var iterator: MutableList<Text>,
|
||||
var currentText: Text?,
|
||||
var offset: Int,
|
||||
var textContent: String,
|
||||
)
|
||||
|
||||
var state = State(
|
||||
mutableListOf(text),
|
||||
null,
|
||||
0,
|
||||
""
|
||||
)
|
||||
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 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 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 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
|
||||
}
|
||||
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('§')
|
||||
if (nextParagraph < 0) return this.toString()
|
||||
val stringBuffer = StringBuilder(this.length)
|
||||
var readIndex = 0
|
||||
while (nextParagraph >= 0) {
|
||||
stringBuffer.append(this, readIndex, nextParagraph)
|
||||
if (keepNonColorCodes && nextParagraph + 1 < length && this[nextParagraph + 1] in formattingChars) {
|
||||
readIndex = nextParagraph
|
||||
nextParagraph = indexOf('§', startIndex = readIndex + 1)
|
||||
} else {
|
||||
readIndex = nextParagraph + 2
|
||||
nextParagraph = indexOf('§', startIndex = readIndex)
|
||||
}
|
||||
if (readIndex > this.length)
|
||||
readIndex = this.length
|
||||
}
|
||||
stringBuffer.append(this, readIndex, this.length)
|
||||
return stringBuffer.toString()
|
||||
var nextParagraph = indexOf('§')
|
||||
if (nextParagraph < 0) return this.toString()
|
||||
val stringBuffer = StringBuilder(this.length)
|
||||
var readIndex = 0
|
||||
while (nextParagraph >= 0) {
|
||||
stringBuffer.append(this, readIndex, nextParagraph)
|
||||
if (keepNonColorCodes && nextParagraph + 1 < length && this[nextParagraph + 1] in formattingChars) {
|
||||
readIndex = nextParagraph
|
||||
nextParagraph = indexOf('§', startIndex = readIndex + 1)
|
||||
} else {
|
||||
readIndex = nextParagraph + 2
|
||||
nextParagraph = indexOf('§', startIndex = readIndex)
|
||||
}
|
||||
if (readIndex > this.length)
|
||||
readIndex = this.length
|
||||
}
|
||||
stringBuffer.append(this, readIndex, this.length)
|
||||
return stringBuffer.toString()
|
||||
}
|
||||
|
||||
val Text.unformattedString: String
|
||||
get() = string.removeColorCodes()
|
||||
get() = string.removeColorCodes()
|
||||
|
||||
fun Text.allSiblings(): List<Text> = listOf(this) + siblings.flatMap { it.allSiblings() }
|
||||
|
||||
fun MutableText.withColor(formatting: Formatting) = this.styled { it.withColor(formatting).withItalic(false) }
|
||||
|
||||
fun Text.transformEachRecursively(function: (Text) -> Text): Text {
|
||||
val c = this.content
|
||||
if (c is TranslatableTextContent) {
|
||||
return Text.translatableWithFallback(c.key, c.fallback, *c.args.map {
|
||||
(if (it is Text) it else Text.literal(it.toString())).transformEachRecursively(function)
|
||||
}.toTypedArray()).also { new ->
|
||||
new.style = this.style
|
||||
new.siblings.clear()
|
||||
this.siblings.forEach { child ->
|
||||
new.siblings.add(child.transformEachRecursively(function))
|
||||
}
|
||||
}
|
||||
}
|
||||
return function(this.copy().also { it.siblings.clear() }).also { tt ->
|
||||
this.siblings.forEach {
|
||||
tt.siblings.add(it.transformEachRecursively(function))
|
||||
}
|
||||
}
|
||||
val c = this.content
|
||||
if (c is TranslatableTextContent) {
|
||||
return Text.translatableWithFallback(c.key, c.fallback, *c.args.map {
|
||||
(if (it is Text) it else Text.literal(it.toString())).transformEachRecursively(function)
|
||||
}.toTypedArray()).also { new ->
|
||||
new.style = this.style
|
||||
new.siblings.clear()
|
||||
this.siblings.forEach { child ->
|
||||
new.siblings.add(child.transformEachRecursively(function))
|
||||
}
|
||||
}
|
||||
}
|
||||
return function(this.copy().also { it.siblings.clear() }).also { tt ->
|
||||
this.siblings.forEach {
|
||||
tt.siblings.add(it.transformEachRecursively(function))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user