feat: Add 1.8.9 item exporter
This commit is contained in:
@@ -26,7 +26,6 @@ import net.fabricmc.loader.api.Version
|
|||||||
import net.fabricmc.loader.api.metadata.ModMetadata
|
import net.fabricmc.loader.api.metadata.ModMetadata
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.apache.logging.log4j.Logger
|
import org.apache.logging.log4j.Logger
|
||||||
import org.spongepowered.asm.launch.MixinBootstrap
|
|
||||||
import kotlinx.coroutines.CoroutineName
|
import kotlinx.coroutines.CoroutineName
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -74,10 +73,22 @@ object Firmament {
|
|||||||
allowTrailingComma = true
|
allowTrailingComma = true
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
encodeDefaults = true
|
encodeDefaults = true
|
||||||
|
prettyPrintIndent = "\t"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FUCK two space indentation
|
||||||
|
*/
|
||||||
|
val twoSpaceJson = Json(from = json) {
|
||||||
|
prettyPrintIndent = " "
|
||||||
}
|
}
|
||||||
val gson = Gson()
|
val gson = Gson()
|
||||||
val tightJson = Json(from = json) {
|
val tightJson = Json(from = json) {
|
||||||
prettyPrint = false
|
prettyPrint = false
|
||||||
|
// Reset pretty print indent back to default to prevent getting yelled at by json
|
||||||
|
prettyPrintIndent = " "
|
||||||
|
encodeDefaults = false
|
||||||
|
explicitNulls = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ object PowerUserTools : FirmamentFeature {
|
|||||||
val copyEntityData by keyBindingWithDefaultUnbound("entity-data")
|
val copyEntityData by keyBindingWithDefaultUnbound("entity-data")
|
||||||
val copyItemStack by keyBindingWithDefaultUnbound("copy-item-stack")
|
val copyItemStack by keyBindingWithDefaultUnbound("copy-item-stack")
|
||||||
val copyTitle by keyBindingWithDefaultUnbound("copy-title")
|
val copyTitle by keyBindingWithDefaultUnbound("copy-title")
|
||||||
|
val exportItemStackToRepo by keyBindingWithDefaultUnbound("export-item-stack")
|
||||||
}
|
}
|
||||||
|
|
||||||
override val config
|
override val config
|
||||||
@@ -64,14 +65,13 @@ object PowerUserTools : FirmamentFeature {
|
|||||||
var lastCopiedStack: Pair<ItemStack, Text>? = null
|
var lastCopiedStack: Pair<ItemStack, Text>? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
if (value != null) lastCopiedStackViewTime = true
|
if (value != null) lastCopiedStackViewTime = 2
|
||||||
}
|
}
|
||||||
var lastCopiedStackViewTime = false
|
var lastCopiedStackViewTime = 0
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun resetLastCopiedStack(event: TickEvent) {
|
fun resetLastCopiedStack(event: TickEvent) {
|
||||||
if (!lastCopiedStackViewTime) lastCopiedStack = null
|
if (lastCopiedStackViewTime-- < 0) lastCopiedStack = null
|
||||||
lastCopiedStackViewTime = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@@ -232,7 +232,7 @@ object PowerUserTools : FirmamentFeature {
|
|||||||
lastCopiedStack = null
|
lastCopiedStack = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastCopiedStackViewTime = true
|
lastCopiedStackViewTime = 0
|
||||||
it.lines.add(text)
|
it.lines.add(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
246
src/main/kotlin/features/debug/itemeditor/ItemExporter.kt
Normal file
246
src/main/kotlin/features/debug/itemeditor/ItemExporter.kt
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
package moe.nea.firmament.features.debug.itemeditor
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import kotlinx.serialization.json.put
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.io.path.createParentDirectories
|
||||||
|
import kotlin.io.path.relativeTo
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
import net.minecraft.component.DataComponentTypes
|
||||||
|
import net.minecraft.item.ItemStack
|
||||||
|
import net.minecraft.nbt.NbtCompound
|
||||||
|
import net.minecraft.nbt.NbtInt
|
||||||
|
import net.minecraft.nbt.NbtString
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import net.minecraft.util.Unit
|
||||||
|
import moe.nea.firmament.Firmament
|
||||||
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
|
import moe.nea.firmament.events.ClientStartedEvent
|
||||||
|
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
||||||
|
import moe.nea.firmament.features.debug.PowerUserTools
|
||||||
|
import moe.nea.firmament.repo.RepoDownloadManager
|
||||||
|
import moe.nea.firmament.util.HypixelPetInfo
|
||||||
|
import moe.nea.firmament.util.LegacyTagWriter.Companion.toLegacyString
|
||||||
|
import moe.nea.firmament.util.StringUtil.words
|
||||||
|
import moe.nea.firmament.util.directLiteralStringContent
|
||||||
|
import moe.nea.firmament.util.extraAttributes
|
||||||
|
import moe.nea.firmament.util.focusedItemStack
|
||||||
|
import moe.nea.firmament.util.getLegacyFormatString
|
||||||
|
import moe.nea.firmament.util.json.toJsonArray
|
||||||
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
|
import moe.nea.firmament.util.mc.toNbtList
|
||||||
|
import moe.nea.firmament.util.skyBlockId
|
||||||
|
import moe.nea.firmament.util.skyblock.Rarity
|
||||||
|
import moe.nea.firmament.util.tr
|
||||||
|
import moe.nea.firmament.util.transformEachRecursively
|
||||||
|
import moe.nea.firmament.util.unformattedString
|
||||||
|
|
||||||
|
class ItemExporter(var itemStack: ItemStack) {
|
||||||
|
var lore = itemStack.loreAccordingToNbt
|
||||||
|
var name = itemStack.displayNameAccordingToNbt
|
||||||
|
val extraAttribs = itemStack.extraAttributes.copy()
|
||||||
|
val legacyNbt = NbtCompound()
|
||||||
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
|
fun preprocess() {
|
||||||
|
// TODO: split up preprocess steps into preprocess actions that can be toggled in a ui
|
||||||
|
extraAttribs.remove("timestamp")
|
||||||
|
extraAttribs.remove("uuid")
|
||||||
|
extraAttribs.remove("modifier")
|
||||||
|
extraAttribs.getString("petInfo").ifPresent { petInfoJson ->
|
||||||
|
var petInfo = Firmament.json.decodeFromString<HypixelPetInfo>(petInfoJson)
|
||||||
|
petInfo = petInfo.copy(candyUsed = 0, heldItem = null, exp = 0.0, active = null, uuid = null)
|
||||||
|
extraAttribs.putString("petInfo", Firmament.tightJson.encodeToString(petInfo))
|
||||||
|
}
|
||||||
|
itemStack.skyBlockId?.let {
|
||||||
|
extraAttribs.putString("id", it.neuItem)
|
||||||
|
}
|
||||||
|
trimLore()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trimLore() {
|
||||||
|
val rarityIdx = lore.indexOfLast {
|
||||||
|
val firstWordInLine = it.unformattedString.words().filter { it.length > 2 }.firstOrNull()
|
||||||
|
firstWordInLine?.let(Rarity::fromString) != null
|
||||||
|
}
|
||||||
|
if (rarityIdx >= 0) {
|
||||||
|
lore = lore.subList(0, rarityIdx + 1)
|
||||||
|
}
|
||||||
|
deleteLineUntilNextSpace { it.startsWith("Held Item: ") }
|
||||||
|
deleteLineUntilNextSpace { it.startsWith("Progress to Level ") }
|
||||||
|
deleteLineUntilNextSpace { it.startsWith("MAX LEVEL") }
|
||||||
|
collapseWhitespaces()
|
||||||
|
|
||||||
|
name = name.transformEachRecursively {
|
||||||
|
var string = it.directLiteralStringContent ?: return@transformEachRecursively it
|
||||||
|
string = string.replace("Lvl \\d+".toRegex(), "Lvl {LVL}")
|
||||||
|
Text.literal(string).setStyle(it.style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collapseWhitespaces() {
|
||||||
|
lore = (listOf(null as Text?) + lore).zipWithNext()
|
||||||
|
.filter { !it.first?.unformattedString.isNullOrBlank() || !it.second?.unformattedString.isNullOrBlank() }
|
||||||
|
.map { it.second!! }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteLineUntilNextSpace(search: (String) -> Boolean) {
|
||||||
|
val idx = lore.indexOfFirst { search(it.unformattedString) }
|
||||||
|
if (idx < 0) return
|
||||||
|
val l = lore.toMutableList()
|
||||||
|
val p = l.subList(idx, l.size)
|
||||||
|
val nextBlank = p.indexOfFirst { it.unformattedString.isEmpty() }
|
||||||
|
if (nextBlank < 0)
|
||||||
|
p.clear()
|
||||||
|
else
|
||||||
|
p.subList(0, nextBlank).clear()
|
||||||
|
lore = l
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processNbt() {
|
||||||
|
// TODO: calculate hideflags
|
||||||
|
legacyNbt.put("HideFlags", NbtInt.of(254))
|
||||||
|
copyUnbreakable()
|
||||||
|
copyExtraAttributes()
|
||||||
|
copyLegacySkullNbt()
|
||||||
|
copyDisplay()
|
||||||
|
copyEnchantments()
|
||||||
|
copyEnchantGlint()
|
||||||
|
// TODO: copyDisplay
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyDisplay() {
|
||||||
|
legacyNbt.put("display", NbtCompound().apply {
|
||||||
|
put("Lore", lore.map { NbtString.of(it.getLegacyFormatString(trimmed = true)) }.toNbtList())
|
||||||
|
putString("Name", name.getLegacyFormatString(trimmed = true))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportJson(): JsonElement {
|
||||||
|
preprocess()
|
||||||
|
processNbt()
|
||||||
|
return buildJsonObject {
|
||||||
|
val (itemId, damage) = legacyifyItemStack()
|
||||||
|
put("itemid", itemId)
|
||||||
|
put("displayname", name.getLegacyFormatString(trimmed = true))
|
||||||
|
put("nbttag", legacyNbt.toLegacyString())
|
||||||
|
put("damage", damage)
|
||||||
|
put("lore", lore.map { it.getLegacyFormatString(trimmed = true) }.toJsonArray())
|
||||||
|
val sbId = itemStack.skyBlockId
|
||||||
|
if (sbId == null)
|
||||||
|
warnings.add("Could not find skyblock id")
|
||||||
|
put("internalname", sbId?.neuItem)
|
||||||
|
put("clickcommand", "")
|
||||||
|
put("crafttext", "")
|
||||||
|
put("modver", "Firmament ${Firmament.version.friendlyString}")
|
||||||
|
put("infoType", "")
|
||||||
|
put("info", JsonArray(listOf()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Subscribe
|
||||||
|
fun load(event: ClientStartedEvent) {
|
||||||
|
thread(start = true, name = "ItemExporter Meta Load Thread") {
|
||||||
|
LegacyItemData.itemLut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onKeyBind(event: HandledScreenKeyPressedEvent) {
|
||||||
|
if (event.matches(PowerUserTools.TConfig.exportItemStackToRepo)) {
|
||||||
|
val itemStack = event.screen.focusedItemStack ?: return
|
||||||
|
val exporter = ItemExporter(itemStack)
|
||||||
|
val json = exporter.exportJson()
|
||||||
|
val jsonFormatted = Firmament.twoSpaceJson.encodeToString(json)
|
||||||
|
val itemFile = RepoDownloadManager.repoSavedLocation.resolve("items")
|
||||||
|
.resolve("${json.jsonObject["internalname"]!!.jsonPrimitive.content}.json")
|
||||||
|
itemFile.createParentDirectories()
|
||||||
|
itemFile.writeText(jsonFormatted)
|
||||||
|
PowerUserTools.lastCopiedStack = Pair(
|
||||||
|
itemStack,
|
||||||
|
tr(
|
||||||
|
"firmament.repoexport.success",
|
||||||
|
"Exported item to ${itemFile.relativeTo(RepoDownloadManager.repoSavedLocation)}${
|
||||||
|
exporter.warnings.joinToString(
|
||||||
|
""
|
||||||
|
) { "\nWarning: $it" }
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyEnchantGlint() {
|
||||||
|
if (itemStack.get(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE) == true) {
|
||||||
|
val ench = legacyNbt.getListOrEmpty("ench")
|
||||||
|
legacyNbt.put("ench", ench)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyUnbreakable() {
|
||||||
|
if (itemStack.get(DataComponentTypes.UNBREAKABLE) == Unit.INSTANCE) {
|
||||||
|
legacyNbt.putBoolean("Unbreakable", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyEnchantments() {
|
||||||
|
val enchantments = itemStack.get(DataComponentTypes.ENCHANTMENTS)?.takeIf { !it.isEmpty } ?: return
|
||||||
|
val enchTag = legacyNbt.getListOrEmpty("ench")
|
||||||
|
legacyNbt.put("ench", enchTag)
|
||||||
|
enchantments.enchantmentEntries.forEach { entry ->
|
||||||
|
val id = entry.key.key.get().value
|
||||||
|
val legacyId = LegacyItemData.enchantmentLut[id]
|
||||||
|
if (legacyId == null) {
|
||||||
|
warnings.add("Could not find legacy enchantment id for ${id}")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
enchTag.add(NbtCompound().apply {
|
||||||
|
putShort("lvl", entry.intValue.toShort())
|
||||||
|
putShort(
|
||||||
|
"id",
|
||||||
|
legacyId.id.toShort()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyExtraAttributes() {
|
||||||
|
legacyNbt.put("ExtraAttributes", extraAttribs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyLegacySkullNbt() {
|
||||||
|
val profile = itemStack.get(DataComponentTypes.PROFILE) ?: return
|
||||||
|
legacyNbt.put("SkullOwner", NbtCompound().apply {
|
||||||
|
profile.id.ifPresent {
|
||||||
|
putString("Id", it.toString())
|
||||||
|
}
|
||||||
|
putBoolean("hypixelPopulated", true)
|
||||||
|
put("Properties", NbtCompound().apply {
|
||||||
|
profile.properties().forEach { prop, value ->
|
||||||
|
val list = getListOrEmpty(prop)
|
||||||
|
put(prop, list)
|
||||||
|
list.add(NbtCompound().apply {
|
||||||
|
value.signature?.let {
|
||||||
|
putString("Signature", it)
|
||||||
|
}
|
||||||
|
putString("Value", value.value)
|
||||||
|
putString("Name", value.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun legacyifyItemStack(): LegacyItemData.LegacyItemType {
|
||||||
|
// TODO: add a default here
|
||||||
|
return LegacyItemData.itemLut[itemStack.item]!!
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/main/kotlin/features/debug/itemeditor/LegacyItemData.kt
Normal file
73
src/main/kotlin/features/debug/itemeditor/LegacyItemData.kt
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package moe.nea.firmament.features.debug.itemeditor
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
import net.minecraft.item.ItemStack
|
||||||
|
import net.minecraft.nbt.NbtCompound
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import moe.nea.firmament.Firmament
|
||||||
|
import moe.nea.firmament.repo.ItemCache
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load data based on [prismarine.js' 1.8 item data](https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/1.8/items.json)
|
||||||
|
*/
|
||||||
|
object LegacyItemData {
|
||||||
|
@Serializable
|
||||||
|
data class ItemData(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
val displayName: String,
|
||||||
|
val stackSize: Int,
|
||||||
|
val variations: List<Variation> = listOf()
|
||||||
|
) {
|
||||||
|
val properId = if (name.contains(":")) name else "minecraft:$name"
|
||||||
|
|
||||||
|
fun allVariants() =
|
||||||
|
variations.map { LegacyItemType(properId, it.metadata.toShort()) } + LegacyItemType(properId, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Variation(
|
||||||
|
val metadata: Int, val displayName: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class LegacyItemType(
|
||||||
|
val name: String,
|
||||||
|
val metadata: Short
|
||||||
|
) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$name:$metadata"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class EnchantmentData(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
val displayName: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
inline fun <reified T : Any> getLegacyData(name: String) =
|
||||||
|
Firmament.tryDecodeJsonFromStream<T>(
|
||||||
|
LegacyItemData::class.java.getResourceAsStream("/legacy_data/$name.json")!!
|
||||||
|
).getOrThrow()
|
||||||
|
|
||||||
|
val enchantmentData = getLegacyData<List<EnchantmentData>>("enchantments")
|
||||||
|
val enchantmentLut = enchantmentData.associateBy { Identifier.ofVanilla(it.name) }
|
||||||
|
|
||||||
|
val itemDat = getLegacyData<List<ItemData>>("items")
|
||||||
|
val itemLut = itemDat.flatMap { item ->
|
||||||
|
item.allVariants().map { legacyItemType ->
|
||||||
|
val nbt = ItemCache.convert189ToModern(NbtCompound().apply {
|
||||||
|
putString("id", legacyItemType.name)
|
||||||
|
putByte("Count", 1)
|
||||||
|
putShort("Damage", legacyItemType.metadata)
|
||||||
|
})!!
|
||||||
|
val stack = ItemStack.fromNbt(MC.defaultRegistries, nbt).getOrNull()
|
||||||
|
?: error("Could not transform ${legacyItemType}")
|
||||||
|
stack.item to legacyItemType
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
}
|
||||||
103
src/main/kotlin/util/LegacyTagWriter.kt
Normal file
103
src/main/kotlin/util/LegacyTagWriter.kt
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package moe.nea.firmament.util
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
import net.minecraft.nbt.AbstractNbtList
|
||||||
|
import net.minecraft.nbt.NbtByte
|
||||||
|
import net.minecraft.nbt.NbtCompound
|
||||||
|
import net.minecraft.nbt.NbtDouble
|
||||||
|
import net.minecraft.nbt.NbtElement
|
||||||
|
import net.minecraft.nbt.NbtEnd
|
||||||
|
import net.minecraft.nbt.NbtFloat
|
||||||
|
import net.minecraft.nbt.NbtInt
|
||||||
|
import net.minecraft.nbt.NbtLong
|
||||||
|
import net.minecraft.nbt.NbtShort
|
||||||
|
import net.minecraft.nbt.NbtString
|
||||||
|
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.SIMPLE_NAME
|
||||||
|
|
||||||
|
class LegacyTagWriter(val compact: Boolean) {
|
||||||
|
companion object {
|
||||||
|
fun stringify(nbt: NbtElement, compact: Boolean): String {
|
||||||
|
return LegacyTagWriter(compact).also { it.writeElement(nbt) }
|
||||||
|
.stringWriter.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NbtElement.toLegacyString(pretty: Boolean = false): String {
|
||||||
|
return stringify(this, !pretty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val stringWriter = StringBuilder()
|
||||||
|
var indent = 0
|
||||||
|
fun newLine() {
|
||||||
|
if (compact) return
|
||||||
|
stringWriter.append('\n')
|
||||||
|
repeat(indent) {
|
||||||
|
stringWriter.append(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeElement(nbt: NbtElement) {
|
||||||
|
when (nbt) {
|
||||||
|
is NbtInt -> stringWriter.append(nbt.value.toString())
|
||||||
|
is NbtString -> stringWriter.append(escapeString(nbt.value))
|
||||||
|
is NbtFloat -> stringWriter.append(nbt.value).append('F')
|
||||||
|
is NbtDouble -> stringWriter.append(nbt.value).append('D')
|
||||||
|
is NbtByte -> stringWriter.append(nbt.value).append('B')
|
||||||
|
is NbtLong -> stringWriter.append(nbt.value).append('L')
|
||||||
|
is NbtShort -> stringWriter.append(nbt.value).append('S')
|
||||||
|
is NbtCompound -> writeCompound(nbt)
|
||||||
|
is NbtEnd -> {}
|
||||||
|
is AbstractNbtList -> writeArray(nbt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeArray(nbt: AbstractNbtList) {
|
||||||
|
stringWriter.append('[')
|
||||||
|
indent++
|
||||||
|
newLine()
|
||||||
|
nbt.forEachIndexed { index, element ->
|
||||||
|
writeName(index.toString())
|
||||||
|
writeElement(element)
|
||||||
|
if (index != nbt.size() - 1) {
|
||||||
|
stringWriter.append(',')
|
||||||
|
newLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indent--
|
||||||
|
if (nbt.size() != 0)
|
||||||
|
newLine()
|
||||||
|
stringWriter.append(']')
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeCompound(nbt: NbtCompound) {
|
||||||
|
stringWriter.append('{')
|
||||||
|
indent++
|
||||||
|
newLine()
|
||||||
|
val entries = nbt.entrySet().sortedBy { it.key }
|
||||||
|
entries.forEachIndexed { index, it ->
|
||||||
|
writeName(it.key)
|
||||||
|
writeElement(it.value)
|
||||||
|
if (index != entries.lastIndex) {
|
||||||
|
stringWriter.append(',')
|
||||||
|
newLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indent--
|
||||||
|
if (nbt.size != 0)
|
||||||
|
newLine()
|
||||||
|
stringWriter.append('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
fun escapeString(string: String): String {
|
||||||
|
return JsonPrimitive(string).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun escapeName(key: String): String =
|
||||||
|
if (key.matches(SIMPLE_NAME)) key else escapeString(key)
|
||||||
|
|
||||||
|
fun writeName(key: String) {
|
||||||
|
stringWriter.append(escapeName(key))
|
||||||
|
stringWriter.append(':')
|
||||||
|
if (!compact) stringWriter.append(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -104,7 +104,7 @@ data class HypixelPetInfo(
|
|||||||
val exp: Double = 0.0,
|
val exp: Double = 0.0,
|
||||||
val candyUsed: Int = 0,
|
val candyUsed: Int = 0,
|
||||||
val uuid: UUID? = null,
|
val uuid: UUID? = null,
|
||||||
val active: Boolean = false,
|
val active: Boolean? = false,
|
||||||
val heldItem: String? = null,
|
val heldItem: String? = null,
|
||||||
) {
|
) {
|
||||||
val skyblockId get() = SkyblockId("${type.uppercase()};${tier.ordinal}") // TODO: is this ordinal set up correctly?
|
val skyblockId get() = SkyblockId("${type.uppercase()};${tier.ordinal}") // TODO: is this ordinal set up correctly?
|
||||||
|
|||||||
11
src/main/kotlin/util/json/KJsonUtils.kt
Normal file
11
src/main/kotlin/util/json/KJsonUtils.kt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package moe.nea.firmament.util.json
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
|
||||||
|
fun <T : JsonElement> List<T>.asJsonArray(): JsonArray {
|
||||||
|
return JsonArray(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Iterable<String>.toJsonArray(): JsonArray = map { JsonPrimitive(it) }.asJsonArray()
|
||||||
10
src/main/kotlin/util/mc/NbtUtil.kt
Normal file
10
src/main/kotlin/util/mc/NbtUtil.kt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package moe.nea.firmament.util.mc
|
||||||
|
|
||||||
|
import net.minecraft.nbt.NbtElement
|
||||||
|
import net.minecraft.nbt.NbtList
|
||||||
|
|
||||||
|
fun Iterable<NbtElement>.toNbtList() = NbtList().also {
|
||||||
|
for(element in this) {
|
||||||
|
it.add(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,7 +110,7 @@ class SNbtFormatter private constructor() : NbtElementVisitor {
|
|||||||
keys.forEachIndexed { index, key ->
|
keys.forEachIndexed { index, key ->
|
||||||
writeIndent()
|
writeIndent()
|
||||||
val element = compound[key] ?: error("Key '$key' found but not present in compound: $compound")
|
val element = compound[key] ?: error("Key '$key' found but not present in compound: $compound")
|
||||||
val escapedName = if (key.matches(SIMPLE_NAME)) key else NbtString.escape(key)
|
val escapedName = escapeName(key)
|
||||||
result.append(escapedName).append(": ")
|
result.append(escapedName).append(": ")
|
||||||
element.accept(this)
|
element.accept(this)
|
||||||
if (keys.size != index + 1) {
|
if (keys.size != index + 1) {
|
||||||
@@ -134,6 +134,9 @@ class SNbtFormatter private constructor() : NbtElementVisitor {
|
|||||||
|
|
||||||
fun NbtElement.toPrettyString() = prettify(this)
|
fun NbtElement.toPrettyString() = prettify(this)
|
||||||
|
|
||||||
private val SIMPLE_NAME = "[A-Za-z0-9._+-]+".toRegex()
|
fun escapeName(key: String): String =
|
||||||
|
if (key.matches(SIMPLE_NAME)) key else NbtString.escape(key)
|
||||||
|
|
||||||
|
val SIMPLE_NAME = "[A-Za-z0-9._+-]+".toRegex()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ fun OrderedText.reconstitute(): MutableText {
|
|||||||
return base
|
return base
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun StringVisitable.reconstitute(): MutableText {
|
fun StringVisitable.reconstitute(): MutableText {
|
||||||
val base = Text.literal("")
|
val base = Text.literal("")
|
||||||
base.setStyle(Style.EMPTY.withItalic(false))
|
base.setStyle(Style.EMPTY.withItalic(false))
|
||||||
@@ -82,15 +83,47 @@ val Text.unformattedString: String
|
|||||||
|
|
||||||
val Text.directLiteralStringContent: String? get() = (this.content as? PlainTextContent)?.string()
|
val Text.directLiteralStringContent: String? get() = (this.content as? PlainTextContent)?.string()
|
||||||
|
|
||||||
fun Text.getLegacyFormatString() =
|
fun Text.getLegacyFormatString(trimmed: Boolean = false): String =
|
||||||
run {
|
run {
|
||||||
|
var lastCode = "§r"
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
|
fun appendCode(code: String) {
|
||||||
|
if (code != lastCode || !trimmed) {
|
||||||
|
sb.append(code)
|
||||||
|
lastCode = code
|
||||||
|
}
|
||||||
|
}
|
||||||
for (component in iterator()) {
|
for (component in iterator()) {
|
||||||
sb.append(component.style.color?.toChatFormatting()?.toString() ?: "§r")
|
if (component.directLiteralStringContent.isNullOrEmpty() && component.siblings.isEmpty()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
appendCode(component.style.let { style ->
|
||||||
|
var color = style.color?.toChatFormatting()?.toString() ?: "§r"
|
||||||
|
if (style.isBold)
|
||||||
|
color += LegacyFormattingCode.BOLD.formattingCode
|
||||||
|
if (style.isItalic)
|
||||||
|
color += LegacyFormattingCode.ITALIC.formattingCode
|
||||||
|
if (style.isUnderlined)
|
||||||
|
color += LegacyFormattingCode.UNDERLINE.formattingCode
|
||||||
|
if (style.isObfuscated)
|
||||||
|
color += LegacyFormattingCode.OBFUSCATED.formattingCode
|
||||||
|
if (style.isStrikethrough)
|
||||||
|
color += LegacyFormattingCode.STRIKETHROUGH.formattingCode
|
||||||
|
color
|
||||||
|
})
|
||||||
sb.append(component.directLiteralStringContent)
|
sb.append(component.directLiteralStringContent)
|
||||||
sb.append("§r")
|
if (!trimmed)
|
||||||
|
appendCode("§r")
|
||||||
}
|
}
|
||||||
sb.toString()
|
sb.toString()
|
||||||
|
}.also {
|
||||||
|
var it = it
|
||||||
|
if (trimmed) {
|
||||||
|
it = it.removeSuffix("§r")
|
||||||
|
if (it.length == 2 && it.startsWith("§"))
|
||||||
|
it = ""
|
||||||
|
}
|
||||||
|
it
|
||||||
}
|
}
|
||||||
|
|
||||||
private val textColorLUT = Formatting.entries
|
private val textColorLUT = Formatting.entries
|
||||||
@@ -127,7 +160,7 @@ fun MutableText.darkGrey() = withColor(Formatting.DARK_GRAY)
|
|||||||
fun MutableText.red() = withColor(Formatting.RED)
|
fun MutableText.red() = withColor(Formatting.RED)
|
||||||
fun MutableText.white() = withColor(Formatting.WHITE)
|
fun MutableText.white() = withColor(Formatting.WHITE)
|
||||||
fun MutableText.bold(): MutableText = styled { it.withBold(true) }
|
fun MutableText.bold(): MutableText = styled { it.withBold(true) }
|
||||||
fun MutableText.hover(text: Text): MutableText = styled {it.withHoverEvent(HoverEvent.ShowText(text))}
|
fun MutableText.hover(text: Text): MutableText = styled { it.withHoverEvent(HoverEvent.ShowText(text)) }
|
||||||
|
|
||||||
|
|
||||||
fun MutableText.clickCommand(command: String): MutableText {
|
fun MutableText.clickCommand(command: String): MutableText {
|
||||||
|
|||||||
560
src/main/resources/legacy_data/enchantments.json
Normal file
560
src/main/resources/legacy_data/enchantments.json
Normal file
@@ -0,0 +1,560 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "protection",
|
||||||
|
"displayName": "Protection",
|
||||||
|
"maxLevel": 4,
|
||||||
|
"minCost": {
|
||||||
|
"a": 11,
|
||||||
|
"b": -10
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 11,
|
||||||
|
"b": 1
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"blast_protection",
|
||||||
|
"fire_protection",
|
||||||
|
"projectile_protection"
|
||||||
|
],
|
||||||
|
"category": "armor",
|
||||||
|
"weight": 10,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "fire_protection",
|
||||||
|
"displayName": "Fire Protection",
|
||||||
|
"maxLevel": 4,
|
||||||
|
"minCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": 2
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": 10
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"blast_protection",
|
||||||
|
"protection",
|
||||||
|
"projectile_protection"
|
||||||
|
],
|
||||||
|
"category": "armor",
|
||||||
|
"weight": 5,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "feather_falling",
|
||||||
|
"displayName": "Feather Falling",
|
||||||
|
"maxLevel": 4,
|
||||||
|
"minCost": {
|
||||||
|
"a": 6,
|
||||||
|
"b": -1
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 6,
|
||||||
|
"b": 5
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "armor_feet",
|
||||||
|
"weight": 5,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "blast_protection",
|
||||||
|
"displayName": "Blast Protection",
|
||||||
|
"maxLevel": 4,
|
||||||
|
"minCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": -3
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": 5
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"fire_protection",
|
||||||
|
"protection",
|
||||||
|
"projectile_protection"
|
||||||
|
],
|
||||||
|
"category": "armor",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "projectile_protection",
|
||||||
|
"displayName": "Projectile Protection",
|
||||||
|
"maxLevel": 4,
|
||||||
|
"minCost": {
|
||||||
|
"a": 6,
|
||||||
|
"b": -3
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 6,
|
||||||
|
"b": 3
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"protection",
|
||||||
|
"blast_protection",
|
||||||
|
"fire_protection"
|
||||||
|
],
|
||||||
|
"category": "armor",
|
||||||
|
"weight": 5,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "respiration",
|
||||||
|
"displayName": "Respiration",
|
||||||
|
"maxLevel": 3,
|
||||||
|
"minCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 0
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 30
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "armor_head",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "aqua_affinity",
|
||||||
|
"displayName": "Aqua Affinity",
|
||||||
|
"maxLevel": 1,
|
||||||
|
"minCost": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 1
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 41
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "armor_head",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "thorns",
|
||||||
|
"displayName": "Thorns",
|
||||||
|
"maxLevel": 3,
|
||||||
|
"minCost": {
|
||||||
|
"a": 20,
|
||||||
|
"b": -10
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "armor_chest",
|
||||||
|
"weight": 1,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "depth_strider",
|
||||||
|
"displayName": "Depth Strider",
|
||||||
|
"maxLevel": 3,
|
||||||
|
"minCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 0
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 15
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"frost_walker"
|
||||||
|
],
|
||||||
|
"category": "armor_feet",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"name": "sharpness",
|
||||||
|
"displayName": "Sharpness",
|
||||||
|
"maxLevel": 5,
|
||||||
|
"minCost": {
|
||||||
|
"a": 11,
|
||||||
|
"b": -10
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 11,
|
||||||
|
"b": 10
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"smite",
|
||||||
|
"bane_of_arthropods"
|
||||||
|
],
|
||||||
|
"category": "weapon",
|
||||||
|
"weight": 10,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"name": "smite",
|
||||||
|
"displayName": "Smite",
|
||||||
|
"maxLevel": 5,
|
||||||
|
"minCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": -3
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": 17
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"sharpness",
|
||||||
|
"bane_of_arthropods"
|
||||||
|
],
|
||||||
|
"category": "weapon",
|
||||||
|
"weight": 5,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"name": "bane_of_arthropods",
|
||||||
|
"displayName": "Bane of Arthropods",
|
||||||
|
"maxLevel": 5,
|
||||||
|
"minCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": -3
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": 17
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"smite",
|
||||||
|
"sharpness"
|
||||||
|
],
|
||||||
|
"category": "weapon",
|
||||||
|
"weight": 5,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 19,
|
||||||
|
"name": "knockback",
|
||||||
|
"displayName": "Knockback",
|
||||||
|
"maxLevel": 2,
|
||||||
|
"minCost": {
|
||||||
|
"a": 20,
|
||||||
|
"b": -15
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "weapon",
|
||||||
|
"weight": 5,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"name": "fire_aspect",
|
||||||
|
"displayName": "Fire Aspect",
|
||||||
|
"maxLevel": 2,
|
||||||
|
"minCost": {
|
||||||
|
"a": 20,
|
||||||
|
"b": -10
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "weapon",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"name": "looting",
|
||||||
|
"displayName": "Looting",
|
||||||
|
"maxLevel": 3,
|
||||||
|
"minCost": {
|
||||||
|
"a": 9,
|
||||||
|
"b": 6
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "weapon",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 32,
|
||||||
|
"name": "efficiency",
|
||||||
|
"displayName": "Efficiency",
|
||||||
|
"maxLevel": 5,
|
||||||
|
"minCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": -9
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "digger",
|
||||||
|
"weight": 10,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 33,
|
||||||
|
"name": "silk_touch",
|
||||||
|
"displayName": "Silk Touch",
|
||||||
|
"maxLevel": 1,
|
||||||
|
"minCost": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 15
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"fortune"
|
||||||
|
],
|
||||||
|
"category": "digger",
|
||||||
|
"weight": 1,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 34,
|
||||||
|
"name": "unbreaking",
|
||||||
|
"displayName": "Unbreaking",
|
||||||
|
"maxLevel": 3,
|
||||||
|
"minCost": {
|
||||||
|
"a": 8,
|
||||||
|
"b": -3
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "breakable",
|
||||||
|
"weight": 5,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 35,
|
||||||
|
"name": "fortune",
|
||||||
|
"displayName": "Fortune",
|
||||||
|
"maxLevel": 3,
|
||||||
|
"minCost": {
|
||||||
|
"a": 9,
|
||||||
|
"b": 6
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"silk_touch"
|
||||||
|
],
|
||||||
|
"category": "digger",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 48,
|
||||||
|
"name": "power",
|
||||||
|
"displayName": "Power",
|
||||||
|
"maxLevel": 5,
|
||||||
|
"minCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": -9
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 6
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "bow",
|
||||||
|
"weight": 10,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 49,
|
||||||
|
"name": "punch",
|
||||||
|
"displayName": "Punch",
|
||||||
|
"maxLevel": 2,
|
||||||
|
"minCost": {
|
||||||
|
"a": 20,
|
||||||
|
"b": -8
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 17
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "bow",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 50,
|
||||||
|
"name": "flame",
|
||||||
|
"displayName": "Flame",
|
||||||
|
"maxLevel": 1,
|
||||||
|
"minCost": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 20
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 50
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "bow",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 51,
|
||||||
|
"name": "infinity",
|
||||||
|
"displayName": "Infinity",
|
||||||
|
"maxLevel": 1,
|
||||||
|
"minCost": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 20
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 50
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"mending"
|
||||||
|
],
|
||||||
|
"category": "bow",
|
||||||
|
"weight": 1,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 61,
|
||||||
|
"name": "luck_of_the_sea",
|
||||||
|
"displayName": "Luck of the Sea",
|
||||||
|
"maxLevel": 3,
|
||||||
|
"minCost": {
|
||||||
|
"a": 9,
|
||||||
|
"b": 6
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "fishing_rod",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 62,
|
||||||
|
"name": "lure",
|
||||||
|
"displayName": "Lure",
|
||||||
|
"maxLevel": 3,
|
||||||
|
"minCost": {
|
||||||
|
"a": 9,
|
||||||
|
"b": 6
|
||||||
|
},
|
||||||
|
"maxCost": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 51
|
||||||
|
},
|
||||||
|
"exclude": [],
|
||||||
|
"category": "fishing_rod",
|
||||||
|
"weight": 2,
|
||||||
|
"treasureOnly": false,
|
||||||
|
"curse": false,
|
||||||
|
"tradeable": true,
|
||||||
|
"discoverable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
3733
src/main/resources/legacy_data/items.json
Normal file
3733
src/main/resources/legacy_data/items.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user