feat: Add item shop recipe exporter
This commit is contained in:
@@ -1,14 +1,26 @@
|
|||||||
package moe.nea.firmament.features.debug.itemeditor
|
package moe.nea.firmament.features.debug.itemeditor
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import moe.nea.firmament.annotations.Subscribe
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
||||||
import moe.nea.firmament.features.debug.PowerUserTools
|
import moe.nea.firmament.features.debug.PowerUserTools
|
||||||
|
import moe.nea.firmament.repo.ItemNameLookup
|
||||||
import moe.nea.firmament.util.MC
|
import moe.nea.firmament.util.MC
|
||||||
|
import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
|
||||||
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.ifDropLast
|
||||||
import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex
|
import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex
|
||||||
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
|
import moe.nea.firmament.util.parseShortNumber
|
||||||
|
import moe.nea.firmament.util.red
|
||||||
|
import moe.nea.firmament.util.removeColorCodes
|
||||||
import moe.nea.firmament.util.skyBlockId
|
import moe.nea.firmament.util.skyBlockId
|
||||||
|
import moe.nea.firmament.util.skyblock.SkyBlockItems
|
||||||
import moe.nea.firmament.util.tr
|
import moe.nea.firmament.util.tr
|
||||||
|
import moe.nea.firmament.util.useMatch
|
||||||
|
|
||||||
object ExportRecipe {
|
object ExportRecipe {
|
||||||
|
|
||||||
@@ -29,10 +41,9 @@ object ExportRecipe {
|
|||||||
if (!event.matches(PowerUserTools.TConfig.exportUIRecipes)) {
|
if (!event.matches(PowerUserTools.TConfig.exportUIRecipes)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!event.screen.title.string.endsWith(" Recipe")) {
|
val title = event.screen.title.string
|
||||||
MC.sendChat(tr("firmament.repo.export.recipe.fail", "No Recipe found"))
|
val sellSlot = event.screen.getSlotByIndex(49, false)?.stack
|
||||||
return
|
if (title.endsWith(" Recipe")) {
|
||||||
}
|
|
||||||
slotIndices.forEach { (_, index) ->
|
slotIndices.forEach { (_, index) ->
|
||||||
event.screen.getSlotByIndex(index, false)?.stack?.let(ItemExporter::ensureExported)
|
event.screen.getSlotByIndex(index, false)?.stack?.let(ItemExporter::ensureExported)
|
||||||
}
|
}
|
||||||
@@ -53,5 +64,145 @@ object ExportRecipe {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
ItemExporter.appendRecipe(output.skyBlockId!!, recipe)
|
ItemExporter.appendRecipe(output.skyBlockId!!, recipe)
|
||||||
|
MC.sendChat(tr("firmament.repo.export.recipe", "Recipe for ${output.skyBlockId} exported."))
|
||||||
|
return
|
||||||
|
} else if (sellSlot?.displayNameAccordingToNbt?.string == "Sell Item" || (sellSlot?.loreAccordingToNbt
|
||||||
|
?: listOf()).any { it.string == "Click to buyback!" }
|
||||||
|
) {
|
||||||
|
val shopId = SkyblockId(title.uppercase().replace(" ", "_") + "_NPC")
|
||||||
|
if (!ItemExporter.isExported(shopId)) {
|
||||||
|
// TODO: export location + skin of last clicked npc
|
||||||
|
ItemExporter.exportStub(shopId, "$title (NPC)")
|
||||||
}
|
}
|
||||||
|
for (index in (9..9 * 5)) {
|
||||||
|
val item = event.screen.getSlotByIndex(index, false)?.stack ?: continue
|
||||||
|
val skyblockId = item.skyBlockId ?: continue
|
||||||
|
val costLines = item.loreAccordingToNbt
|
||||||
|
.map { it.string.trim() }
|
||||||
|
.dropWhile { !it.startsWith("Cost") }
|
||||||
|
.dropWhile { it == "Cost" }
|
||||||
|
.takeWhile { it != "Click to trade!" }
|
||||||
|
.takeWhile { it != "Stock" }
|
||||||
|
.filter { !it.isBlank() }
|
||||||
|
.map { it.removePrefix("Cost: ") }
|
||||||
|
|
||||||
|
|
||||||
|
val costs = costLines.mapNotNull { lineText ->
|
||||||
|
val line = findStackableItemByName(lineText)
|
||||||
|
if (line == null) {
|
||||||
|
MC.sendChat(
|
||||||
|
tr(
|
||||||
|
"firmament.repo.itemshop.fail",
|
||||||
|
"Could not parse cost item ${lineText} for ${item.displayNameAccordingToNbt}"
|
||||||
|
).red()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
line
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ItemExporter.appendRecipe(
|
||||||
|
shopId, JsonObject(
|
||||||
|
mapOf(
|
||||||
|
"type" to JsonPrimitive("npc_shop"),
|
||||||
|
"cost" to JsonArray(costs.map { JsonPrimitive("${it.first.neuItem}:${it.second}") }),
|
||||||
|
"result" to JsonPrimitive("${skyblockId.neuItem}:${item.count}"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MC.sendChat(tr("firmament.repo.export.itemshop", "Item Shop export for ${title} complete."))
|
||||||
|
} else {
|
||||||
|
MC.sendChat(tr("firmament.repo.export.recipe.fail", "No Recipe found"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val coinRegex = "(?<amount>$SHORT_NUMBER_FORMAT) Coins?".toPattern()
|
||||||
|
private val stackedItemRegex = "(?<name>.*) x(?<count>$SHORT_NUMBER_FORMAT)".toPattern()
|
||||||
|
private val reverseStackedItemRegex = "(?<count>$SHORT_NUMBER_FORMAT)x (?<name>.*)".toPattern()
|
||||||
|
private val essenceRegex = "(?<essence>.*) Essence x(?<count>$SHORT_NUMBER_FORMAT)".toPattern()
|
||||||
|
private val numberedItemRegex = "(?<count>$SHORT_NUMBER_FORMAT) (?<what>.*)".toPattern()
|
||||||
|
|
||||||
|
private val etherialRewardPattern = "\\+(?<amount>${SHORT_NUMBER_FORMAT})x? (?<what>.*)".toPattern()
|
||||||
|
|
||||||
|
fun findForName(name: String, fallbackToGenerated: Boolean = true): SkyblockId? {
|
||||||
|
var id = ItemNameLookup.guessItemByName(name, true)
|
||||||
|
if (id == null && fallbackToGenerated) {
|
||||||
|
id = generateName(name)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
fun skill(name: String): SkyblockId {
|
||||||
|
return SkyblockId("SKYBLOCK_SKILL_${name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateName(name: String): SkyblockId {
|
||||||
|
return SkyblockId(name.uppercase().replace(" ", "_"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findStackableItemByName(name: String, fallbackToGenerated: Boolean = false): Pair<SkyblockId, Double>? {
|
||||||
|
val properName = name.removeColorCodes().trim()
|
||||||
|
if (properName == "FREE" || properName == "This Chest is Free!") {
|
||||||
|
return Pair(SkyBlockItems.COINS, 0.0)
|
||||||
|
}
|
||||||
|
coinRegex.useMatch(properName) {
|
||||||
|
return Pair(SkyBlockItems.COINS, parseShortNumber(group("amount")))
|
||||||
|
}
|
||||||
|
etherialRewardPattern.useMatch(properName) {
|
||||||
|
val id = when (val id = group("what")) {
|
||||||
|
"Copper" -> SkyblockId("SKYBLOCK_COPPER")
|
||||||
|
"Bits" -> SkyblockId("SKYBLOCK_BIT")
|
||||||
|
"Garden Experience" -> SkyblockId("SKYBLOCK_SKILL_GARDEN")
|
||||||
|
"Farming XP" -> SkyblockId("SKYBLOCK_SKILL_FARMING")
|
||||||
|
"Gold Essence" -> SkyblockId("ESSENCE_GOLD")
|
||||||
|
"Gemstone Powder" -> SkyblockId("SKYBLOCK_POWDER_GEMSTONE")
|
||||||
|
"Mithril Powder" -> SkyblockId("SKYBLOCK_POWDER_MITHRIL")
|
||||||
|
"Pelts" -> SkyblockId("SKYBLOCK_PELT")
|
||||||
|
"Fine Flour" -> SkyblockId("FINE_FLOUR")
|
||||||
|
else -> {
|
||||||
|
id.ifDropLast(" Experience") {
|
||||||
|
skill(generateName(it).neuItem)
|
||||||
|
} ?: id.ifDropLast(" XP") {
|
||||||
|
skill(generateName(it).neuItem)
|
||||||
|
} ?: id.ifDropLast(" Powder") {
|
||||||
|
SkyblockId("SKYBLOCK_POWDER_${generateName(it).neuItem}")
|
||||||
|
} ?: id.ifDropLast(" Essence") {
|
||||||
|
SkyblockId("ESSENCE_${generateName(it).neuItem}")
|
||||||
|
} ?: generateName(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair(id, parseShortNumber(group("amount")))
|
||||||
|
}
|
||||||
|
essenceRegex.useMatch(properName) {
|
||||||
|
return Pair(
|
||||||
|
SkyblockId("ESSENCE_${group("essence").uppercase()}"),
|
||||||
|
parseShortNumber(group("count"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
stackedItemRegex.useMatch(properName) {
|
||||||
|
val item = findForName(group("name"), fallbackToGenerated)
|
||||||
|
if (item != null) {
|
||||||
|
val count = parseShortNumber(group("count"))
|
||||||
|
return Pair(item, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reverseStackedItemRegex.useMatch(properName) {
|
||||||
|
val item = findForName(group("name"), fallbackToGenerated)
|
||||||
|
if (item != null) {
|
||||||
|
val count = parseShortNumber(group("count"))
|
||||||
|
return Pair(item, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numberedItemRegex.useMatch(properName) {
|
||||||
|
val item = findForName(group("what"), fallbackToGenerated)
|
||||||
|
if (item != null) {
|
||||||
|
val count = parseShortNumber(group("count"))
|
||||||
|
return Pair(item, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return findForName(properName, fallbackToGenerated)?.let { Pair(it, 1.0) }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import kotlin.io.path.readText
|
|||||||
import kotlin.io.path.relativeTo
|
import kotlin.io.path.relativeTo
|
||||||
import kotlin.io.path.writeText
|
import kotlin.io.path.writeText
|
||||||
import net.minecraft.item.ItemStack
|
import net.minecraft.item.ItemStack
|
||||||
|
import net.minecraft.item.Items
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import moe.nea.firmament.Firmament
|
import moe.nea.firmament.Firmament
|
||||||
import moe.nea.firmament.annotations.Subscribe
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
@@ -18,9 +19,13 @@ import moe.nea.firmament.features.debug.ExportedTestConstantMeta
|
|||||||
import moe.nea.firmament.features.debug.PowerUserTools
|
import moe.nea.firmament.features.debug.PowerUserTools
|
||||||
import moe.nea.firmament.repo.RepoDownloadManager
|
import moe.nea.firmament.repo.RepoDownloadManager
|
||||||
import moe.nea.firmament.repo.RepoManager
|
import moe.nea.firmament.repo.RepoManager
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
import moe.nea.firmament.util.focusedItemStack
|
import moe.nea.firmament.util.focusedItemStack
|
||||||
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString
|
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString
|
||||||
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
|
import moe.nea.firmament.util.setSkyBlockId
|
||||||
import moe.nea.firmament.util.skyBlockId
|
import moe.nea.firmament.util.skyBlockId
|
||||||
import moe.nea.firmament.util.tr
|
import moe.nea.firmament.util.tr
|
||||||
|
|
||||||
@@ -52,8 +57,11 @@ object ItemExporter {
|
|||||||
fun pathFor(skyBlockId: SkyblockId) =
|
fun pathFor(skyBlockId: SkyblockId) =
|
||||||
RepoManager.neuRepo.baseFolder.resolve("items/${skyBlockId.neuItem}.json")
|
RepoManager.neuRepo.baseFolder.resolve("items/${skyBlockId.neuItem}.json")
|
||||||
|
|
||||||
|
fun isExported(skyblockId: SkyblockId) =
|
||||||
|
pathFor(skyblockId).exists()
|
||||||
|
|
||||||
fun ensureExported(itemStack: ItemStack) {
|
fun ensureExported(itemStack: ItemStack) {
|
||||||
if (!pathFor(itemStack.skyBlockId ?: return).exists())
|
if (!isExported(itemStack.skyBlockId ?: return))
|
||||||
exportItem(itemStack)
|
exportItem(itemStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,4 +81,13 @@ object ItemExporter {
|
|||||||
PowerUserTools.lastCopiedStack = (itemStack to exportItem(itemStack))
|
PowerUserTools.lastCopiedStack = (itemStack to exportItem(itemStack))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun exportStub(skyblockId: SkyblockId, title: String) {
|
||||||
|
exportItem(ItemStack(Items.PLAYER_HEAD).also {
|
||||||
|
it.displayNameAccordingToNbt = Text.literal(title)
|
||||||
|
it.loreAccordingToNbt = listOf(Text.literal(""))
|
||||||
|
it.setSkyBlockId(skyblockId)
|
||||||
|
})
|
||||||
|
MC.sendChat(tr("firmament.repo.export.stub", "Exported a stub item for $skyblockId"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ class LegacyItemExporter private constructor(var itemStack: ItemStack) {
|
|||||||
deleteLineUntilNextSpace { it.startsWith("Held Item: ") }
|
deleteLineUntilNextSpace { it.startsWith("Held Item: ") }
|
||||||
deleteLineUntilNextSpace { it.startsWith("Progress to Level ") }
|
deleteLineUntilNextSpace { it.startsWith("Progress to Level ") }
|
||||||
deleteLineUntilNextSpace { it.startsWith("MAX LEVEL") }
|
deleteLineUntilNextSpace { it.startsWith("MAX LEVEL") }
|
||||||
|
deleteLineUntilNextSpace { it.startsWith("Click to view recipe!") }
|
||||||
collapseWhitespaces()
|
collapseWhitespaces()
|
||||||
|
|
||||||
name = name.transformEachRecursively {
|
name = name.transformEachRecursively {
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ inline fun <T> Pattern.useMatch(string: String?, block: Matcher.() -> T): T? {
|
|||||||
?.let(block)
|
?.let(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> String.ifDropLast(suffix: String, block: (String) -> T): T? {
|
||||||
|
if (endsWith(suffix)) {
|
||||||
|
return block(dropLast(suffix.length))
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@Language("RegExp")
|
@Language("RegExp")
|
||||||
val TIME_PATTERN = "[0-9]+[ms]"
|
val TIME_PATTERN = "[0-9]+[ms]"
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package moe.nea.firmament.util.skyblock
|
|||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
|
||||||
object SkyBlockItems {
|
object SkyBlockItems {
|
||||||
|
val COINS = SkyblockId("SKYBLOCK_COIN")
|
||||||
val ROTTEN_FLESH = SkyblockId("ROTTEN_FLESH")
|
val ROTTEN_FLESH = SkyblockId("ROTTEN_FLESH")
|
||||||
val ENCHANTED_DIAMOND = SkyblockId("ENCHANTED_DIAMOND")
|
val ENCHANTED_DIAMOND = SkyblockId("ENCHANTED_DIAMOND")
|
||||||
val DIAMOND = SkyblockId("DIAMOND")
|
val DIAMOND = SkyblockId("DIAMOND")
|
||||||
|
|||||||
Reference in New Issue
Block a user