|
|
|
|
@@ -1,14 +1,26 @@
|
|
|
|
|
package moe.nea.firmament.features.debug.itemeditor
|
|
|
|
|
|
|
|
|
|
import kotlinx.serialization.json.JsonArray
|
|
|
|
|
import kotlinx.serialization.json.JsonObject
|
|
|
|
|
import kotlinx.serialization.json.JsonPrimitive
|
|
|
|
|
import moe.nea.firmament.annotations.Subscribe
|
|
|
|
|
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
|
|
|
|
import moe.nea.firmament.features.debug.PowerUserTools
|
|
|
|
|
import moe.nea.firmament.repo.ItemNameLookup
|
|
|
|
|
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.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.skyblock.SkyBlockItems
|
|
|
|
|
import moe.nea.firmament.util.tr
|
|
|
|
|
import moe.nea.firmament.util.useMatch
|
|
|
|
|
|
|
|
|
|
object ExportRecipe {
|
|
|
|
|
|
|
|
|
|
@@ -29,29 +41,168 @@ object ExportRecipe {
|
|
|
|
|
if (!event.matches(PowerUserTools.TConfig.exportUIRecipes)) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (!event.screen.title.string.endsWith(" Recipe")) {
|
|
|
|
|
MC.sendChat(tr("firmament.repo.export.recipe.fail", "No Recipe found"))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
slotIndices.forEach { (_, index) ->
|
|
|
|
|
event.screen.getSlotByIndex(index, false)?.stack?.let(ItemExporter::ensureExported)
|
|
|
|
|
}
|
|
|
|
|
val inputs = slotIndices.associate { (name, index) ->
|
|
|
|
|
val id = event.screen.getSlotByIndex(index, false)?.stack?.takeIf { !it.isEmpty() }?.let {
|
|
|
|
|
"${it.skyBlockId?.neuItem}:${it.count}"
|
|
|
|
|
} ?: ""
|
|
|
|
|
name to JsonPrimitive(id)
|
|
|
|
|
}
|
|
|
|
|
val output = event.screen.getSlotByIndex(resultSlot, false)?.stack!!
|
|
|
|
|
val overrideOutputId = output.skyBlockId!!.neuItem
|
|
|
|
|
val count = output.count
|
|
|
|
|
val recipe = JsonObject(
|
|
|
|
|
inputs + mapOf(
|
|
|
|
|
"type" to JsonPrimitive("crafting"),
|
|
|
|
|
"count" to JsonPrimitive(count),
|
|
|
|
|
"overrideOutputId" to JsonPrimitive(overrideOutputId)
|
|
|
|
|
val title = event.screen.title.string
|
|
|
|
|
val sellSlot = event.screen.getSlotByIndex(49, false)?.stack
|
|
|
|
|
if (title.endsWith(" Recipe")) {
|
|
|
|
|
slotIndices.forEach { (_, index) ->
|
|
|
|
|
event.screen.getSlotByIndex(index, false)?.stack?.let(ItemExporter::ensureExported)
|
|
|
|
|
}
|
|
|
|
|
val inputs = slotIndices.associate { (name, index) ->
|
|
|
|
|
val id = event.screen.getSlotByIndex(index, false)?.stack?.takeIf { !it.isEmpty() }?.let {
|
|
|
|
|
"${it.skyBlockId?.neuItem}:${it.count}"
|
|
|
|
|
} ?: ""
|
|
|
|
|
name to JsonPrimitive(id)
|
|
|
|
|
}
|
|
|
|
|
val output = event.screen.getSlotByIndex(resultSlot, false)?.stack!!
|
|
|
|
|
val overrideOutputId = output.skyBlockId!!.neuItem
|
|
|
|
|
val count = output.count
|
|
|
|
|
val recipe = JsonObject(
|
|
|
|
|
inputs + mapOf(
|
|
|
|
|
"type" to JsonPrimitive("crafting"),
|
|
|
|
|
"count" to JsonPrimitive(count),
|
|
|
|
|
"overrideOutputId" to JsonPrimitive(overrideOutputId)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
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) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|