WIP: Reforge recipes
This commit is contained in:
@@ -24,6 +24,7 @@ import moe.nea.firmament.compat.rei.recipes.SBEssenceUpgradeRecipe
|
|||||||
import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe
|
import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe
|
||||||
import moe.nea.firmament.compat.rei.recipes.SBKatRecipe
|
import moe.nea.firmament.compat.rei.recipes.SBKatRecipe
|
||||||
import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe
|
import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe
|
||||||
|
import moe.nea.firmament.compat.rei.recipes.SBReforgeRecipe
|
||||||
import moe.nea.firmament.events.HandledScreenPushREIEvent
|
import moe.nea.firmament.events.HandledScreenPushREIEvent
|
||||||
import moe.nea.firmament.features.inventory.CraftingOverlay
|
import moe.nea.firmament.features.inventory.CraftingOverlay
|
||||||
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
|
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
|
||||||
@@ -78,6 +79,7 @@ class FirmamentReiPlugin : REIClientPlugin {
|
|||||||
registry.add(SBForgeRecipe.Category)
|
registry.add(SBForgeRecipe.Category)
|
||||||
registry.add(SBMobDropRecipe.Category)
|
registry.add(SBMobDropRecipe.Category)
|
||||||
registry.add(SBKatRecipe.Category)
|
registry.add(SBKatRecipe.Category)
|
||||||
|
registry.add(SBReforgeRecipe.Category)
|
||||||
registry.add(SBEssenceUpgradeRecipe.Category)
|
registry.add(SBEssenceUpgradeRecipe.Category)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +92,10 @@ class FirmamentReiPlugin : REIClientPlugin {
|
|||||||
registry.registerDisplayGenerator(
|
registry.registerDisplayGenerator(
|
||||||
SBCraftingRecipe.Category.catIdentifier,
|
SBCraftingRecipe.Category.catIdentifier,
|
||||||
SkyblockCraftingRecipeDynamicGenerator)
|
SkyblockCraftingRecipeDynamicGenerator)
|
||||||
|
registry.registerDisplayGenerator(
|
||||||
|
SBReforgeRecipe.catIdentifier,
|
||||||
|
SBReforgeRecipe.DynamicGenerator
|
||||||
|
)
|
||||||
registry.registerDisplayGenerator(
|
registry.registerDisplayGenerator(
|
||||||
SBForgeRecipe.Category.categoryIdentifier,
|
SBForgeRecipe.Category.categoryIdentifier,
|
||||||
SkyblockForgeRecipeDynamicGenerator)
|
SkyblockForgeRecipeDynamicGenerator)
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package moe.nea.firmament.compat.rei.recipes
|
||||||
|
|
||||||
|
import java.util.Optional
|
||||||
|
import me.shedaniel.math.Point
|
||||||
|
import me.shedaniel.math.Rectangle
|
||||||
|
import me.shedaniel.rei.api.client.gui.Renderer
|
||||||
|
import me.shedaniel.rei.api.client.gui.widgets.Widget
|
||||||
|
import me.shedaniel.rei.api.client.gui.widgets.Widgets
|
||||||
|
import me.shedaniel.rei.api.client.registry.display.DisplayCategory
|
||||||
|
import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator
|
||||||
|
import me.shedaniel.rei.api.client.view.ViewSearchBuilder
|
||||||
|
import me.shedaniel.rei.api.common.category.CategoryIdentifier
|
||||||
|
import me.shedaniel.rei.api.common.display.Display
|
||||||
|
import me.shedaniel.rei.api.common.display.DisplaySerializer
|
||||||
|
import me.shedaniel.rei.api.common.entry.EntryIngredient
|
||||||
|
import me.shedaniel.rei.api.common.entry.EntryStack
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import moe.nea.firmament.Firmament
|
||||||
|
import moe.nea.firmament.compat.rei.SBItemEntryDefinition
|
||||||
|
import moe.nea.firmament.repo.Reforge
|
||||||
|
import moe.nea.firmament.repo.ReforgeStore
|
||||||
|
import moe.nea.firmament.repo.RepoItemTypeCache
|
||||||
|
import moe.nea.firmament.repo.RepoManager
|
||||||
|
import moe.nea.firmament.repo.SBItemStack
|
||||||
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.skyblock.ItemType
|
||||||
|
import moe.nea.firmament.util.skyblockId
|
||||||
|
import moe.nea.firmament.util.tr
|
||||||
|
|
||||||
|
class SBReforgeRecipe(
|
||||||
|
val reforge: Reforge,
|
||||||
|
val limitToItem: SkyblockId?,
|
||||||
|
) : Display {
|
||||||
|
companion object {
|
||||||
|
val catIdentifier = CategoryIdentifier.of<SBReforgeRecipe>(Firmament.MOD_ID, "reforge_recipe")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Category : DisplayCategory<SBReforgeRecipe> {
|
||||||
|
override fun getCategoryIdentifier(): CategoryIdentifier<out SBReforgeRecipe> {
|
||||||
|
return catIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTitle(): Text {
|
||||||
|
return tr("firmament.recipecategory.reforge", "Reforge")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getIcon(): Renderer {
|
||||||
|
return SBItemEntryDefinition.getEntry(SkyblockId("REFORGE_ANVIL"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setupDisplay(display: SBReforgeRecipe, bounds: Rectangle): MutableList<Widget> {
|
||||||
|
val list = mutableListOf<Widget>()
|
||||||
|
list.add(Widgets.createRecipeBase(bounds))
|
||||||
|
// TODO: actual layout after christmas, probably
|
||||||
|
list.add(Widgets.createSlot(Point(bounds.minX + 10, bounds.centerY))
|
||||||
|
.markInput().entries(display.inputItems))
|
||||||
|
val stoneSlot = Widgets.createSlot(Point(bounds.minX + 38, bounds.centerY))
|
||||||
|
.markInput()
|
||||||
|
if (display.reforgeStone != null)
|
||||||
|
stoneSlot.entry(display.reforgeStone)
|
||||||
|
list.add(stoneSlot)
|
||||||
|
list.add(Widgets.createSlot(Point(bounds.minX + 38 + 18, bounds.centerY))
|
||||||
|
.markInput().entries(display.outputItems))
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object DynamicGenerator : DynamicDisplayGenerator<SBReforgeRecipe> {
|
||||||
|
fun getRecipesForSBItemStack(item: SBItemStack): Optional<List<SBReforgeRecipe>> {
|
||||||
|
val reforgeRecipes = mutableListOf<SBReforgeRecipe>()
|
||||||
|
for (reforge in ReforgeStore.findEligibleForInternalName(item.skyblockId)) {
|
||||||
|
reforgeRecipes.add(SBReforgeRecipe(reforge, item.skyblockId))
|
||||||
|
}
|
||||||
|
for (reforge in ReforgeStore.findEligibleForItem(item.itemType ?: ItemType.NIL)) {
|
||||||
|
reforgeRecipes.add(SBReforgeRecipe(reforge, item.skyblockId))
|
||||||
|
}
|
||||||
|
if (reforgeRecipes.isEmpty()) return Optional.empty()
|
||||||
|
return Optional.of(reforgeRecipes)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRecipeFor(entry: EntryStack<*>): Optional<List<SBReforgeRecipe>> {
|
||||||
|
if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
|
||||||
|
val item = entry.castValue<SBItemStack>()
|
||||||
|
return getRecipesForSBItemStack(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUsageFor(entry: EntryStack<*>): Optional<List<SBReforgeRecipe>> {
|
||||||
|
if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
|
||||||
|
val item = entry.castValue<SBItemStack>()
|
||||||
|
ReforgeStore.byReforgeStone[item.skyblockId]?.let { stoneReforge ->
|
||||||
|
return Optional.of(listOf(SBReforgeRecipe(stoneReforge, null)))
|
||||||
|
}
|
||||||
|
return getRecipesForSBItemStack(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generate(builder: ViewSearchBuilder): Optional<List<SBReforgeRecipe>> {
|
||||||
|
// TODO: check builder.recipesFor and such and optionally return all reforge recipes
|
||||||
|
return Optional.empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val eligibleItems =
|
||||||
|
if (limitToItem != null) listOfNotNull(RepoManager.getNEUItem(limitToItem))
|
||||||
|
else reforge.eligibleItems.flatMap {
|
||||||
|
when (it) {
|
||||||
|
is Reforge.ReforgeEligibilityFilter.AllowsInternalName ->
|
||||||
|
listOfNotNull(RepoManager.getNEUItem(it.internalName))
|
||||||
|
is Reforge.ReforgeEligibilityFilter.AllowsItemType ->
|
||||||
|
ReforgeStore.resolveItemType(it.itemType)
|
||||||
|
.flatMap {
|
||||||
|
RepoItemTypeCache.byItemType[it] ?: listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
is Reforge.ReforgeEligibilityFilter.AllowsVanillaItemType -> {
|
||||||
|
listOf() // TODO: add filter support for this and potentially rework this to search for the declared item type in repo, instead of remapped item type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val inputItems = eligibleItems.map { SBItemEntryDefinition.getEntry(it.skyblockId) }
|
||||||
|
private val outputItems =
|
||||||
|
inputItems.map { SBItemEntryDefinition.getEntry(it.value.copy(reforge = reforge.reforgeId)) }
|
||||||
|
private val reforgeStone = reforge.reforgeStone?.let(SBItemEntryDefinition::getEntry)
|
||||||
|
private val inputEntries =
|
||||||
|
listOf(EntryIngredient.of(inputItems)) + listOfNotNull(reforgeStone?.let(EntryIngredient::of))
|
||||||
|
private val outputEntries = listOf(EntryIngredient.of(outputItems))
|
||||||
|
|
||||||
|
override fun getInputEntries(): List<EntryIngredient> {
|
||||||
|
return inputEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOutputEntries(): List<EntryIngredient> {
|
||||||
|
return outputEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCategoryIdentifier(): CategoryIdentifier<*> {
|
||||||
|
return catIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDisplayLocation(): Optional<Identifier> {
|
||||||
|
return Optional.empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSerializer(): DisplaySerializer<out Display>? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package moe.nea.firmament.repo
|
package moe.nea.firmament.repo
|
||||||
|
|
||||||
import io.github.moulberry.repo.IReloadable
|
import io.github.moulberry.repo.IReloadable
|
||||||
@@ -6,23 +5,22 @@ import io.github.moulberry.repo.NEURepository
|
|||||||
import io.github.moulberry.repo.data.NEURecipe
|
import io.github.moulberry.repo.data.NEURecipe
|
||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
|
||||||
class BetterRepoRecipeCache(val essenceRecipeProvider: EssenceRecipeProvider) : IReloadable {
|
class BetterRepoRecipeCache(vararg val extraProviders: ExtraRecipeProvider) : IReloadable {
|
||||||
var usages: Map<SkyblockId, Set<NEURecipe>> = mapOf()
|
var usages: Map<SkyblockId, Set<NEURecipe>> = mapOf()
|
||||||
var recipes: Map<SkyblockId, Set<NEURecipe>> = mapOf()
|
var recipes: Map<SkyblockId, Set<NEURecipe>> = mapOf()
|
||||||
|
|
||||||
override fun reload(repository: NEURepository) {
|
override fun reload(repository: NEURepository) {
|
||||||
val usages = mutableMapOf<SkyblockId, MutableSet<NEURecipe>>()
|
val usages = mutableMapOf<SkyblockId, MutableSet<NEURecipe>>()
|
||||||
val recipes = mutableMapOf<SkyblockId, MutableSet<NEURecipe>>()
|
val recipes = mutableMapOf<SkyblockId, MutableSet<NEURecipe>>()
|
||||||
val baseRecipes = repository.items.items.values
|
val baseRecipes = repository.items.items.values
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.flatMap { it.recipes }
|
.flatMap { it.recipes }
|
||||||
val extraRecipes = essenceRecipeProvider.recipes
|
(baseRecipes + extraProviders.flatMap { it.provideExtraRecipes() })
|
||||||
(baseRecipes + extraRecipes)
|
.forEach { recipe ->
|
||||||
.forEach { recipe ->
|
recipe.allInputs.forEach { usages.getOrPut(SkyblockId(it.itemId), ::mutableSetOf).add(recipe) }
|
||||||
recipe.allInputs.forEach { usages.getOrPut(SkyblockId(it.itemId), ::mutableSetOf).add(recipe) }
|
recipe.allOutputs.forEach { recipes.getOrPut(SkyblockId(it.itemId), ::mutableSetOf).add(recipe) }
|
||||||
recipe.allOutputs.forEach { recipes.getOrPut(SkyblockId(it.itemId), ::mutableSetOf).add(recipe) }
|
}
|
||||||
}
|
this.usages = usages
|
||||||
this.usages = usages
|
this.recipes = recipes
|
||||||
this.recipes = recipes
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package moe.nea.firmament.repo
|
package moe.nea.firmament.repo
|
||||||
|
|
||||||
import io.github.moulberry.repo.IReloadable
|
import io.github.moulberry.repo.IReloadable
|
||||||
@@ -7,44 +6,46 @@ import io.github.moulberry.repo.data.NEUIngredient
|
|||||||
import io.github.moulberry.repo.data.NEURecipe
|
import io.github.moulberry.repo.data.NEURecipe
|
||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
|
||||||
class EssenceRecipeProvider : IReloadable {
|
class EssenceRecipeProvider : IReloadable, ExtraRecipeProvider {
|
||||||
data class EssenceUpgradeRecipe(
|
data class EssenceUpgradeRecipe(
|
||||||
val itemId: SkyblockId,
|
val itemId: SkyblockId,
|
||||||
val starCountAfter: Int,
|
val starCountAfter: Int,
|
||||||
val essenceCost: Int,
|
val essenceCost: Int,
|
||||||
val essenceType: String, // TODO: replace with proper type
|
val essenceType: String, // TODO: replace with proper type
|
||||||
val extraItems: List<NEUIngredient>,
|
val extraItems: List<NEUIngredient>,
|
||||||
) : NEURecipe {
|
) : NEURecipe {
|
||||||
val essenceIngredient= NEUIngredient.fromString("${essenceType}:$essenceCost")
|
val essenceIngredient = NEUIngredient.fromString("${essenceType}:$essenceCost")
|
||||||
val allUpgradeComponents = listOf(essenceIngredient) + extraItems
|
val allUpgradeComponents = listOf(essenceIngredient) + extraItems
|
||||||
|
|
||||||
override fun getAllInputs(): Collection<NEUIngredient> {
|
override fun getAllInputs(): Collection<NEUIngredient> {
|
||||||
return listOf(NEUIngredient.fromString(itemId.neuItem + ":1")) + allUpgradeComponents
|
return listOf(NEUIngredient.fromString(itemId.neuItem + ":1")) + allUpgradeComponents
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllOutputs(): Collection<NEUIngredient> {
|
override fun getAllOutputs(): Collection<NEUIngredient> {
|
||||||
return listOf(NEUIngredient.fromString(itemId.neuItem + ":1"))
|
return listOf(NEUIngredient.fromString(itemId.neuItem + ":1"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var recipes = listOf<EssenceUpgradeRecipe>()
|
var recipes = listOf<EssenceUpgradeRecipe>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun reload(repository: NEURepository) {
|
override fun provideExtraRecipes(): Iterable<NEURecipe> = recipes
|
||||||
val recipes = mutableListOf<EssenceUpgradeRecipe>()
|
|
||||||
for ((neuId, costs) in repository.constants.essenceCost.costs) {
|
override fun reload(repository: NEURepository) {
|
||||||
// TODO: add dungeonization costs. this is in repo, but not in the repo parser.
|
val recipes = mutableListOf<EssenceUpgradeRecipe>()
|
||||||
for ((starCountAfter, essenceCost) in costs.essenceCosts.entries) {
|
for ((neuId, costs) in repository.constants.essenceCost.costs) {
|
||||||
val items = costs.itemCosts[starCountAfter] ?: emptyList()
|
// TODO: add dungeonization costs. this is in repo, but not in the repo parser.
|
||||||
recipes.add(
|
for ((starCountAfter, essenceCost) in costs.essenceCosts.entries) {
|
||||||
EssenceUpgradeRecipe(
|
val items = costs.itemCosts[starCountAfter] ?: emptyList()
|
||||||
SkyblockId(neuId),
|
recipes.add(
|
||||||
starCountAfter,
|
EssenceUpgradeRecipe(
|
||||||
essenceCost,
|
SkyblockId(neuId),
|
||||||
"ESSENCE_" + costs.type.uppercase(), // how flimsy
|
starCountAfter,
|
||||||
items.map { NEUIngredient.fromString(it) }))
|
essenceCost,
|
||||||
}
|
"ESSENCE_" + costs.type.uppercase(), // how flimsy
|
||||||
}
|
items.map { NEUIngredient.fromString(it) }))
|
||||||
this.recipes = recipes
|
}
|
||||||
}
|
}
|
||||||
|
this.recipes = recipes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/main/kotlin/repo/ExtraRecipeProvider.kt
Normal file
7
src/main/kotlin/repo/ExtraRecipeProvider.kt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package moe.nea.firmament.repo
|
||||||
|
|
||||||
|
import io.github.moulberry.repo.data.NEURecipe
|
||||||
|
|
||||||
|
interface ExtraRecipeProvider {
|
||||||
|
fun provideExtraRecipes(): Iterable<NEURecipe>
|
||||||
|
}
|
||||||
@@ -24,21 +24,26 @@ import net.minecraft.nbt.NbtCompound
|
|||||||
import net.minecraft.nbt.NbtElement
|
import net.minecraft.nbt.NbtElement
|
||||||
import net.minecraft.nbt.NbtOps
|
import net.minecraft.nbt.NbtOps
|
||||||
import net.minecraft.nbt.NbtString
|
import net.minecraft.nbt.NbtString
|
||||||
|
import net.minecraft.text.Style
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import moe.nea.firmament.Firmament
|
import moe.nea.firmament.Firmament
|
||||||
import moe.nea.firmament.gui.config.HudMeta
|
import moe.nea.firmament.gui.config.HudMeta
|
||||||
import moe.nea.firmament.gui.config.HudPosition
|
import moe.nea.firmament.gui.config.HudPosition
|
||||||
import moe.nea.firmament.gui.hud.MoulConfigHud
|
import moe.nea.firmament.gui.hud.MoulConfigHud
|
||||||
import moe.nea.firmament.repo.RepoManager.initialize
|
import moe.nea.firmament.repo.RepoManager.initialize
|
||||||
|
import moe.nea.firmament.util.LegacyFormattingCode
|
||||||
import moe.nea.firmament.util.LegacyTagParser
|
import moe.nea.firmament.util.LegacyTagParser
|
||||||
import moe.nea.firmament.util.MC
|
import moe.nea.firmament.util.MC
|
||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
import moe.nea.firmament.util.TestUtil
|
import moe.nea.firmament.util.TestUtil
|
||||||
|
import moe.nea.firmament.util.directLiteralStringContent
|
||||||
import moe.nea.firmament.util.mc.FirmamentDataComponentTypes
|
import moe.nea.firmament.util.mc.FirmamentDataComponentTypes
|
||||||
import moe.nea.firmament.util.mc.appendLore
|
import moe.nea.firmament.util.mc.appendLore
|
||||||
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
import moe.nea.firmament.util.mc.modifyLore
|
import moe.nea.firmament.util.mc.modifyLore
|
||||||
import moe.nea.firmament.util.mc.setCustomName
|
import moe.nea.firmament.util.mc.setCustomName
|
||||||
import moe.nea.firmament.util.mc.setSkullOwner
|
import moe.nea.firmament.util.mc.setSkullOwner
|
||||||
|
import moe.nea.firmament.util.transformEachRecursively
|
||||||
|
|
||||||
object ItemCache : IReloadable {
|
object ItemCache : IReloadable {
|
||||||
private val cache: MutableMap<String, ItemStack> = ConcurrentHashMap()
|
private val cache: MutableMap<String, ItemStack> = ConcurrentHashMap()
|
||||||
@@ -70,6 +75,7 @@ object ItemCache : IReloadable {
|
|||||||
|
|
||||||
val ItemStack.isBroken
|
val ItemStack.isBroken
|
||||||
get() = get(FirmamentDataComponentTypes.IS_BROKEN) ?: false
|
get() = get(FirmamentDataComponentTypes.IS_BROKEN) ?: false
|
||||||
|
|
||||||
fun brokenItemStack(neuItem: NEUItem?, idHint: SkyblockId? = null): ItemStack {
|
fun brokenItemStack(neuItem: NEUItem?, idHint: SkyblockId? = null): ItemStack {
|
||||||
return ItemStack(Items.PAINTING).apply {
|
return ItemStack(Items.PAINTING).apply {
|
||||||
setCustomName(Text.literal(neuItem?.displayName ?: idHint?.neuItem ?: "null"))
|
setCustomName(Text.literal(neuItem?.displayName ?: idHint?.neuItem ?: "null"))
|
||||||
@@ -88,6 +94,35 @@ object ItemCache : IReloadable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun un189Lore(lore: String): Text {
|
||||||
|
val base = Text.literal("")
|
||||||
|
base.setStyle(Style.EMPTY.withItalic(false))
|
||||||
|
var lastColorCode = Style.EMPTY
|
||||||
|
var readOffset = 0
|
||||||
|
while (readOffset < lore.length) {
|
||||||
|
var nextCode = lore.indexOf('§', readOffset)
|
||||||
|
if (nextCode < 0) {
|
||||||
|
nextCode = lore.length
|
||||||
|
}
|
||||||
|
val text = lore.substring(readOffset, nextCode)
|
||||||
|
if (text.isNotEmpty()) {
|
||||||
|
base.append(Text.literal(text).setStyle(lastColorCode))
|
||||||
|
}
|
||||||
|
readOffset = nextCode + 2
|
||||||
|
if (nextCode + 1 < lore.length) {
|
||||||
|
val colorCode = lore[nextCode + 1]
|
||||||
|
val formatting = LegacyFormattingCode.byCode[colorCode.lowercaseChar()] ?: LegacyFormattingCode.RESET
|
||||||
|
val modernFormatting = formatting.modern
|
||||||
|
if (modernFormatting.isColor) {
|
||||||
|
lastColorCode = Style.EMPTY.withColor(modernFormatting)
|
||||||
|
} else {
|
||||||
|
lastColorCode = lastColorCode.withFormatting(modernFormatting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
private fun NEUItem.asItemStackNow(): ItemStack {
|
private fun NEUItem.asItemStackNow(): ItemStack {
|
||||||
try {
|
try {
|
||||||
val oldItemTag = get10809CompoundTag()
|
val oldItemTag = get10809CompoundTag()
|
||||||
@@ -95,6 +130,7 @@ object ItemCache : IReloadable {
|
|||||||
?: return brokenItemStack(this)
|
?: return brokenItemStack(this)
|
||||||
val itemInstance =
|
val itemInstance =
|
||||||
ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this)
|
ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this)
|
||||||
|
itemInstance.loreAccordingToNbt = lore.map { un189Lore(it) }
|
||||||
val extraAttributes = oldItemTag.getCompound("tag").getCompound("ExtraAttributes")
|
val extraAttributes = oldItemTag.getCompound("tag").getCompound("ExtraAttributes")
|
||||||
if (extraAttributes != null)
|
if (extraAttributes != null)
|
||||||
itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes))
|
itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes))
|
||||||
@@ -129,12 +165,13 @@ object ItemCache : IReloadable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Text.applyLoreReplacements(loreReplacements: Map<String, String>): Text {
|
fun Text.applyLoreReplacements(loreReplacements: Map<String, String>): Text {
|
||||||
assert(this.siblings.isEmpty())
|
return this.transformEachRecursively {
|
||||||
var string = this.string
|
var string = it.directLiteralStringContent ?: return@transformEachRecursively it
|
||||||
loreReplacements.forEach { (find, replace) ->
|
loreReplacements.forEach { (find, replace) ->
|
||||||
string = string.replace("{$find}", replace)
|
string = string.replace("{$find}", replace)
|
||||||
|
}
|
||||||
|
Text.literal(string).setStyle(it.style)
|
||||||
}
|
}
|
||||||
return Text.literal(string).styled { this.style }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var job: Job? = null
|
var job: Job? = null
|
||||||
|
|||||||
146
src/main/kotlin/repo/Reforge.kt
Normal file
146
src/main/kotlin/repo/Reforge.kt
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package moe.nea.firmament.repo
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.builtins.MapSerializer
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.JsonDecoder
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
import net.minecraft.item.Item
|
||||||
|
import net.minecraft.registry.RegistryKey
|
||||||
|
import net.minecraft.registry.RegistryKeys
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import moe.nea.firmament.util.ReforgeId
|
||||||
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.skyblock.ItemType
|
||||||
|
import moe.nea.firmament.util.skyblock.Rarity
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Reforge(
|
||||||
|
val reforgeName: String,
|
||||||
|
@SerialName("internalName") val reforgeStone: SkyblockId? = null,
|
||||||
|
val nbtModifier: ReforgeId? = null,
|
||||||
|
val requiredRarities: List<Rarity>? = null,
|
||||||
|
val itemTypes: @Serializable(with = ReforgeEligibilityFilter.ItemTypesSerializer::class) List<ReforgeEligibilityFilter>? = null,
|
||||||
|
val allowOn: List<ReforgeEligibilityFilter>? = null,
|
||||||
|
val reforgeCosts: RarityMapped<Double>? = null,
|
||||||
|
val reforgeAbility: RarityMapped<String>? = null,
|
||||||
|
val reforgeStats: RarityMapped<Map<String, Double>>? = null,
|
||||||
|
) {
|
||||||
|
val eligibleItems get() = allowOn ?: itemTypes ?: listOf()
|
||||||
|
|
||||||
|
@Serializable(with = ReforgeEligibilityFilter.Serializer::class)
|
||||||
|
sealed interface ReforgeEligibilityFilter {
|
||||||
|
object ItemTypesSerializer : KSerializer<List<ReforgeEligibilityFilter>> {
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = JsonElement.serializer().descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): List<ReforgeEligibilityFilter> {
|
||||||
|
decoder as JsonDecoder
|
||||||
|
val jsonElement = decoder.decodeJsonElement()
|
||||||
|
if (jsonElement is JsonPrimitive && jsonElement.isString) {
|
||||||
|
return jsonElement.content.split("/").map { AllowsItemType(ItemType.ofName(it)) }
|
||||||
|
}
|
||||||
|
if (jsonElement is JsonArray) {
|
||||||
|
return decoder.json.decodeFromJsonElement(serializer<List<ReforgeEligibilityFilter>>(), jsonElement)
|
||||||
|
}
|
||||||
|
jsonElement as JsonObject
|
||||||
|
val filters = mutableListOf<ReforgeEligibilityFilter>()
|
||||||
|
jsonElement["internalName"]?.let {
|
||||||
|
decoder.json.decodeFromJsonElement(serializer<List<SkyblockId>>(), it).forEach {
|
||||||
|
filters.add(AllowsInternalName(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsonElement["itemId"]?.let {
|
||||||
|
decoder.json.decodeFromJsonElement(serializer<List<String>>(), it).forEach {
|
||||||
|
val ident = Identifier.tryParse(it)
|
||||||
|
if (ident != null)
|
||||||
|
filters.add(AllowsVanillaItemType(RegistryKey.of(RegistryKeys.ITEM, ident)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filters
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: List<ReforgeEligibilityFilter>) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Serializer : KSerializer<ReforgeEligibilityFilter> {
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = serializer<JsonElement>().descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): ReforgeEligibilityFilter {
|
||||||
|
val jsonObject = serializer<JsonObject>().deserialize(decoder)
|
||||||
|
jsonObject["internalName"]?.let {
|
||||||
|
return AllowsInternalName(SkyblockId((it as JsonPrimitive).content))
|
||||||
|
}
|
||||||
|
jsonObject["itemType"]?.let {
|
||||||
|
return AllowsItemType(ItemType.ofName((it as JsonPrimitive).content))
|
||||||
|
}
|
||||||
|
jsonObject["minecraftId"]?.let {
|
||||||
|
return AllowsVanillaItemType(RegistryKey.of(RegistryKeys.ITEM,
|
||||||
|
Identifier.of((it as JsonPrimitive).content)))
|
||||||
|
}
|
||||||
|
error("Unknown item type")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: ReforgeEligibilityFilter) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class AllowsItemType(val itemType: ItemType) : ReforgeEligibilityFilter
|
||||||
|
data class AllowsInternalName(val internalName: SkyblockId) : ReforgeEligibilityFilter
|
||||||
|
data class AllowsVanillaItemType(val minecraftId: RegistryKey<Item>) : ReforgeEligibilityFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val reforgeId get() = nbtModifier ?: ReforgeId(reforgeName.lowercase())
|
||||||
|
|
||||||
|
@Serializable(with = RarityMapped.Serializer::class)
|
||||||
|
sealed interface RarityMapped<T> {
|
||||||
|
class Serializer<T>(
|
||||||
|
val values: KSerializer<T>
|
||||||
|
) : KSerializer<RarityMapped<T>> {
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = JsonElement.serializer().descriptor
|
||||||
|
|
||||||
|
val indirect = MapSerializer(Rarity.serializer(), values)
|
||||||
|
override fun deserialize(decoder: Decoder): RarityMapped<T> {
|
||||||
|
decoder as JsonDecoder
|
||||||
|
val element = decoder.decodeJsonElement()
|
||||||
|
if (element is JsonObject) {
|
||||||
|
return PerRarity(decoder.json.decodeFromJsonElement(indirect, element))
|
||||||
|
} else {
|
||||||
|
return Direct(decoder.json.decodeFromJsonElement(values, element))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: RarityMapped<T>) {
|
||||||
|
when (value) {
|
||||||
|
is Direct<T> ->
|
||||||
|
values.serialize(encoder, value.value)
|
||||||
|
|
||||||
|
is PerRarity<T> ->
|
||||||
|
indirect.serialize(encoder, value.values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Direct<T>(val value: T) : RarityMapped<T>
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PerRarity<T>(val values: Map<Rarity, T>) : RarityMapped<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
124
src/main/kotlin/repo/ReforgeStore.kt
Normal file
124
src/main/kotlin/repo/ReforgeStore.kt
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package moe.nea.firmament.repo
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.mojang.serialization.JsonOps
|
||||||
|
import io.github.moulberry.repo.IReloadable
|
||||||
|
import io.github.moulberry.repo.NEURepoFile
|
||||||
|
import io.github.moulberry.repo.NEURepository
|
||||||
|
import io.github.moulberry.repo.NEURepositoryException
|
||||||
|
import io.github.moulberry.repo.data.NEURecipe
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
import net.minecraft.item.Item
|
||||||
|
import net.minecraft.registry.RegistryKey
|
||||||
|
import moe.nea.firmament.Firmament
|
||||||
|
import moe.nea.firmament.util.ReforgeId
|
||||||
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.json.KJsonOps
|
||||||
|
import moe.nea.firmament.util.skyblock.ItemType
|
||||||
|
|
||||||
|
object ReforgeStore : ExtraRecipeProvider, IReloadable {
|
||||||
|
override fun provideExtraRecipes(): Iterable<NEURecipe> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
var byType: Map<ItemType, List<Reforge>> = mapOf()
|
||||||
|
var byVanilla: Map<RegistryKey<Item>, List<Reforge>> = mapOf()
|
||||||
|
var byInternalName: Map<SkyblockId, List<Reforge>> = mapOf()
|
||||||
|
var modifierLut = mapOf<ReforgeId, Reforge>()
|
||||||
|
var byReforgeStone = mapOf<SkyblockId, Reforge>()
|
||||||
|
var allReforges = listOf<Reforge>()
|
||||||
|
|
||||||
|
fun findEligibleForItem(itemType: ItemType): List<Reforge> {
|
||||||
|
return byType[itemType] ?: listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findEligibleForInternalName(internalName: SkyblockId): List<Reforge> {
|
||||||
|
return byInternalName[internalName] ?: listOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: return byVanillla
|
||||||
|
override fun reload(repo: NEURepository) {
|
||||||
|
val basicReforges =
|
||||||
|
repo.file("constants/reforges.json")
|
||||||
|
?.kJson(serializer<Map<String, Reforge>>())
|
||||||
|
?.values ?: emptyList()
|
||||||
|
val advancedReforges =
|
||||||
|
repo.file("constants/reforgestones.json")
|
||||||
|
?.kJson(serializer<Map<String, Reforge>>())
|
||||||
|
?.values ?: emptyList()
|
||||||
|
val allReforges = (basicReforges + advancedReforges)
|
||||||
|
modifierLut = allReforges.associateBy { it.reforgeId }
|
||||||
|
byReforgeStone = allReforges.filter { it.reforgeStone != null }
|
||||||
|
.associateBy { it.reforgeStone!! }
|
||||||
|
val byType = mutableMapOf<ItemType, MutableList<Reforge>>()
|
||||||
|
val byVanilla = mutableMapOf<RegistryKey<Item>, MutableList<Reforge>>()
|
||||||
|
val byInternalName = mutableMapOf<SkyblockId, MutableList<Reforge>>()
|
||||||
|
this.byType = byType
|
||||||
|
this.byVanilla = byVanilla
|
||||||
|
this.byInternalName = byInternalName
|
||||||
|
for (reforge in allReforges) {
|
||||||
|
for (eligibleItem in reforge.eligibleItems) {
|
||||||
|
when (eligibleItem) {
|
||||||
|
is Reforge.ReforgeEligibilityFilter.AllowsInternalName -> {
|
||||||
|
byInternalName.getOrPut(eligibleItem.internalName, ::mutableListOf).add(reforge)
|
||||||
|
}
|
||||||
|
|
||||||
|
is Reforge.ReforgeEligibilityFilter.AllowsItemType -> {
|
||||||
|
val actualItemTypes = resolveItemType(eligibleItem.itemType)
|
||||||
|
for (itemType in actualItemTypes) {
|
||||||
|
byType.getOrPut(itemType, ::mutableListOf).add(reforge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is Reforge.ReforgeEligibilityFilter.AllowsVanillaItemType -> {
|
||||||
|
byVanilla.getOrPut(eligibleItem.minecraftId, ::mutableListOf).add(reforge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.allReforges = allReforges
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolveItemType(itemType: ItemType): List<ItemType> {
|
||||||
|
if (ItemType.SWORD == itemType) {
|
||||||
|
return listOf(
|
||||||
|
ItemType.SWORD,
|
||||||
|
ItemType.GAUNTLET,
|
||||||
|
ItemType.LONGSWORD,// TODO: check name
|
||||||
|
ItemType.FISHING_WEAPON,// TODO: check name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (itemType == ItemType.ofName("ARMOR")) {
|
||||||
|
return listOf(
|
||||||
|
ItemType.CHESTPLATE,
|
||||||
|
ItemType.LEGGINGS,
|
||||||
|
ItemType.HELMET,
|
||||||
|
ItemType.BOOTS,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (itemType == ItemType.EQUIPMENT) {
|
||||||
|
return listOf(
|
||||||
|
ItemType.CLOAK,
|
||||||
|
ItemType.BRACELET,
|
||||||
|
ItemType.NECKLACE,
|
||||||
|
ItemType.BELT,
|
||||||
|
ItemType.GLOVES,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (itemType == ItemType.ROD) {
|
||||||
|
return listOf(ItemType.FISHING_ROD, ItemType.FISHING_WEAPON)
|
||||||
|
}
|
||||||
|
return listOf(itemType)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> NEURepoFile.kJson(serializer: KSerializer<T>): T {
|
||||||
|
val rawJson = json(JsonElement::class.java)
|
||||||
|
try {
|
||||||
|
val kJsonElement = JsonOps.INSTANCE.convertTo(KJsonOps.INSTANCE, rawJson)
|
||||||
|
return Firmament.json.decodeFromJsonElement(serializer, kJsonElement)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
throw NEURepositoryException(path, "Could not decode kotlin JSON element", ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/main/kotlin/repo/RepoItemTypeCache.kt
Normal file
15
src/main/kotlin/repo/RepoItemTypeCache.kt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package moe.nea.firmament.repo
|
||||||
|
|
||||||
|
import io.github.moulberry.repo.IReloadable
|
||||||
|
import io.github.moulberry.repo.NEURepository
|
||||||
|
import io.github.moulberry.repo.data.NEUItem
|
||||||
|
import moe.nea.firmament.util.skyblock.ItemType
|
||||||
|
|
||||||
|
object RepoItemTypeCache : IReloadable {
|
||||||
|
|
||||||
|
var byItemType: Map<ItemType?, List<NEUItem>> = mapOf()
|
||||||
|
|
||||||
|
override fun reload(repository: NEURepository) {
|
||||||
|
byItemType = repository.items.items.values.groupBy { ItemType.fromEscapeCodeLore(it.lore.lastOrNull() ?: "") }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,13 +53,16 @@ object RepoManager {
|
|||||||
var recentlyFailedToUpdateItemList = false
|
var recentlyFailedToUpdateItemList = false
|
||||||
|
|
||||||
val essenceRecipeProvider = EssenceRecipeProvider()
|
val essenceRecipeProvider = EssenceRecipeProvider()
|
||||||
val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider)
|
val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider, ReforgeStore)
|
||||||
|
|
||||||
fun makeNEURepository(path: Path): NEURepository {
|
fun makeNEURepository(path: Path): NEURepository {
|
||||||
return NEURepository.of(path).apply {
|
return NEURepository.of(path).apply {
|
||||||
registerReloadListener(ItemCache)
|
registerReloadListener(ItemCache)
|
||||||
|
registerReloadListener(RepoItemTypeCache)
|
||||||
registerReloadListener(ExpLadders)
|
registerReloadListener(ExpLadders)
|
||||||
registerReloadListener(ItemNameLookup)
|
registerReloadListener(ItemNameLookup)
|
||||||
|
registerReloadListener(ReforgeStore)
|
||||||
|
registerReloadListener(essenceRecipeProvider)
|
||||||
ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this))
|
ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this))
|
||||||
registerReloadListener {
|
registerReloadListener {
|
||||||
if (TestUtil.isInTest) return@registerReloadListener
|
if (TestUtil.isInTest) return@registerReloadListener
|
||||||
@@ -70,7 +73,6 @@ object RepoManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
registerReloadListener(essenceRecipeProvider)
|
|
||||||
registerReloadListener(recipeCache)
|
registerReloadListener(recipeCache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,16 @@ import net.minecraft.util.Formatting
|
|||||||
import moe.nea.firmament.repo.ItemCache.asItemStack
|
import moe.nea.firmament.repo.ItemCache.asItemStack
|
||||||
import moe.nea.firmament.util.FirmFormatters
|
import moe.nea.firmament.util.FirmFormatters
|
||||||
import moe.nea.firmament.util.LegacyFormattingCode
|
import moe.nea.firmament.util.LegacyFormattingCode
|
||||||
|
import moe.nea.firmament.util.ReforgeId
|
||||||
import moe.nea.firmament.util.SkyblockId
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.getReforgeId
|
||||||
|
import moe.nea.firmament.util.getUpgradeStars
|
||||||
import moe.nea.firmament.util.mc.appendLore
|
import moe.nea.firmament.util.mc.appendLore
|
||||||
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
|
||||||
|
import moe.nea.firmament.util.mc.loreAccordingToNbt
|
||||||
import moe.nea.firmament.util.petData
|
import moe.nea.firmament.util.petData
|
||||||
import moe.nea.firmament.util.skyBlockId
|
import moe.nea.firmament.util.skyBlockId
|
||||||
|
import moe.nea.firmament.util.skyblock.ItemType
|
||||||
import moe.nea.firmament.util.skyblockId
|
import moe.nea.firmament.util.skyblockId
|
||||||
import moe.nea.firmament.util.withColor
|
import moe.nea.firmament.util.withColor
|
||||||
|
|
||||||
@@ -28,8 +33,8 @@ data class SBItemStack constructor(
|
|||||||
private var stackSize: Int,
|
private var stackSize: Int,
|
||||||
private var petData: PetData?,
|
private var petData: PetData?,
|
||||||
val extraLore: List<Text> = emptyList(),
|
val extraLore: List<Text> = emptyList(),
|
||||||
// TODO: grab this star data from nbt if possible
|
|
||||||
val stars: Int = 0,
|
val stars: Int = 0,
|
||||||
|
val reforge: ReforgeId? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getStackSize() = stackSize
|
fun getStackSize() = stackSize
|
||||||
@@ -66,7 +71,9 @@ data class SBItemStack constructor(
|
|||||||
skyblockId,
|
skyblockId,
|
||||||
RepoManager.getNEUItem(skyblockId),
|
RepoManager.getNEUItem(skyblockId),
|
||||||
itemStack.count,
|
itemStack.count,
|
||||||
petData = itemStack.petData?.let { PetData.fromHypixel(it) }
|
petData = itemStack.petData?.let { PetData.fromHypixel(it) },
|
||||||
|
stars = itemStack.getUpgradeStars(),
|
||||||
|
reforge = itemStack.getReforgeId()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +134,15 @@ data class SBItemStack constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun appendReforgeStatsToLore(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
) {
|
||||||
|
val rarity = itemStack.rarity
|
||||||
|
val lore = itemStack.loreAccordingToNbt
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: avoid instantiating the item stack here
|
||||||
|
val itemType: ItemType? get() = ItemType.fromItemStack(asImmutableItemStack())
|
||||||
private var itemStack_: ItemStack? = null
|
private var itemStack_: ItemStack? = null
|
||||||
|
|
||||||
private val itemStack: ItemStack
|
private val itemStack: ItemStack
|
||||||
@@ -141,6 +157,7 @@ data class SBItemStack constructor(
|
|||||||
return@run neuItem.asItemStack(idHint = skyblockId, replacementData)
|
return@run neuItem.asItemStack(idHint = skyblockId, replacementData)
|
||||||
.copyWithCount(stackSize)
|
.copyWithCount(stackSize)
|
||||||
.also { it.appendLore(extraLore) }
|
.also { it.appendLore(extraLore) }
|
||||||
|
.also { if (reforge != null) it.appendLore(listOf(Text.literal("Reforge: $reforge"))) } // TODO: use this for proper rendering
|
||||||
.also { enhanceStatsByStars(it, stars) }
|
.also { enhanceStatsByStars(it, stars) }
|
||||||
}
|
}
|
||||||
if (itemStack_ == null)
|
if (itemStack_ == null)
|
||||||
|
|||||||
@@ -1,35 +1,37 @@
|
|||||||
|
|
||||||
|
|
||||||
package moe.nea.firmament.util
|
package moe.nea.firmament.util
|
||||||
|
|
||||||
import net.minecraft.util.Formatting
|
import net.minecraft.util.Formatting
|
||||||
|
|
||||||
enum class LegacyFormattingCode(val label: String, val char: Char, val index: Int) {
|
enum class LegacyFormattingCode(val label: String, val char: Char, val index: Int) {
|
||||||
BLACK("BLACK", '0', 0),
|
BLACK("BLACK", '0', 0),
|
||||||
DARK_BLUE("DARK_BLUE", '1', 1),
|
DARK_BLUE("DARK_BLUE", '1', 1),
|
||||||
DARK_GREEN("DARK_GREEN", '2', 2),
|
DARK_GREEN("DARK_GREEN", '2', 2),
|
||||||
DARK_AQUA("DARK_AQUA", '3', 3),
|
DARK_AQUA("DARK_AQUA", '3', 3),
|
||||||
DARK_RED("DARK_RED", '4', 4),
|
DARK_RED("DARK_RED", '4', 4),
|
||||||
DARK_PURPLE("DARK_PURPLE", '5', 5),
|
DARK_PURPLE("DARK_PURPLE", '5', 5),
|
||||||
GOLD("GOLD", '6', 6),
|
GOLD("GOLD", '6', 6),
|
||||||
GRAY("GRAY", '7', 7),
|
GRAY("GRAY", '7', 7),
|
||||||
DARK_GRAY("DARK_GRAY", '8', 8),
|
DARK_GRAY("DARK_GRAY", '8', 8),
|
||||||
BLUE("BLUE", '9', 9),
|
BLUE("BLUE", '9', 9),
|
||||||
GREEN("GREEN", 'a', 10),
|
GREEN("GREEN", 'a', 10),
|
||||||
AQUA("AQUA", 'b', 11),
|
AQUA("AQUA", 'b', 11),
|
||||||
RED("RED", 'c', 12),
|
RED("RED", 'c', 12),
|
||||||
LIGHT_PURPLE("LIGHT_PURPLE", 'd', 13),
|
LIGHT_PURPLE("LIGHT_PURPLE", 'd', 13),
|
||||||
YELLOW("YELLOW", 'e', 14),
|
YELLOW("YELLOW", 'e', 14),
|
||||||
WHITE("WHITE", 'f', 15),
|
WHITE("WHITE", 'f', 15),
|
||||||
OBFUSCATED("OBFUSCATED", 'k', -1),
|
OBFUSCATED("OBFUSCATED", 'k', -1),
|
||||||
BOLD("BOLD", 'l', -1),
|
BOLD("BOLD", 'l', -1),
|
||||||
STRIKETHROUGH("STRIKETHROUGH", 'm', -1),
|
STRIKETHROUGH("STRIKETHROUGH", 'm', -1),
|
||||||
UNDERLINE("UNDERLINE", 'n', -1),
|
UNDERLINE("UNDERLINE", 'n', -1),
|
||||||
ITALIC("ITALIC", 'o', -1),
|
ITALIC("ITALIC", 'o', -1),
|
||||||
RESET("RESET", 'r', -1);
|
RESET("RESET", 'r', -1);
|
||||||
|
|
||||||
val modern = Formatting.byCode(char)!!
|
companion object {
|
||||||
|
val byCode = entries.associateBy { it.char }
|
||||||
|
}
|
||||||
|
|
||||||
val formattingCode = "§$char"
|
val modern = Formatting.byCode(char)!!
|
||||||
|
|
||||||
|
val formattingCode = "§$char"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,10 +125,26 @@ val ItemStack.skyblockUUID: UUID?
|
|||||||
private val petDataCache = WeakCache.memoize<ItemStack, Optional<HypixelPetInfo>>("PetInfo") {
|
private val petDataCache = WeakCache.memoize<ItemStack, Optional<HypixelPetInfo>>("PetInfo") {
|
||||||
val jsonString = it.extraAttributes.getString("petInfo")
|
val jsonString = it.extraAttributes.getString("petInfo")
|
||||||
if (jsonString.isNullOrBlank()) return@memoize Optional.empty()
|
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()
|
.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?
|
val ItemStack.petData: HypixelPetInfo?
|
||||||
get() = petDataCache(this).getOrNull()
|
get() = petDataCache(this).getOrNull()
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ value class ItemType private constructor(val name: String) {
|
|||||||
return ItemType(name)
|
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? {
|
fun fromItemStack(itemStack: ItemStack): ItemType? {
|
||||||
if (itemStack.petData != null)
|
if (itemStack.petData != null)
|
||||||
return PET
|
return PET
|
||||||
@@ -26,13 +33,31 @@ value class ItemType private constructor(val name: String) {
|
|||||||
if (type.isEmpty()) return null
|
if (type.isEmpty()) return null
|
||||||
return ofName(type)
|
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 SWORD = ofName("SWORD")
|
||||||
val DRILL = ofName("DRILL")
|
val DRILL = ofName("DRILL")
|
||||||
val PICKAXE = ofName("PICKAXE")
|
val PICKAXE = ofName("PICKAXE")
|
||||||
val GAUNTLET = ofName("GAUNTLET")
|
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).
|
* This one is not really official (it never shows up in game).
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
package moe.nea.firmament.util.skyblock
|
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.item.ItemStack
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import moe.nea.firmament.util.StringUtil.words
|
import moe.nea.firmament.util.StringUtil.words
|
||||||
@@ -10,6 +17,7 @@ import moe.nea.firmament.util.unformattedString
|
|||||||
|
|
||||||
typealias RepoRarity = io.github.moulberry.repo.data.Rarity
|
typealias RepoRarity = io.github.moulberry.repo.data.Rarity
|
||||||
|
|
||||||
|
@Serializable(with = Rarity.Serializer::class)
|
||||||
enum class Rarity(vararg altNames: String) {
|
enum class Rarity(vararg altNames: String) {
|
||||||
COMMON,
|
COMMON,
|
||||||
UNCOMMON,
|
UNCOMMON,
|
||||||
@@ -24,6 +32,19 @@ enum class Rarity(vararg altNames: String) {
|
|||||||
UNKNOWN
|
UNKNOWN
|
||||||
;
|
;
|
||||||
|
|
||||||
|
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 names = setOf(name) + altNames
|
||||||
|
|
||||||
val neuRepoRarity: RepoRarity? = RepoRarity.entries.find { it.name == name }
|
val neuRepoRarity: RepoRarity? = RepoRarity.entries.find { it.name == name }
|
||||||
|
|||||||
96
src/test/resources/testdata/items/hyperion.snbt
vendored
Normal file
96
src/test/resources/testdata/items/hyperion.snbt
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
components: {
|
||||||
|
"minecraft:attribute_modifiers": {
|
||||||
|
modifiers: [
|
||||||
|
],
|
||||||
|
show_in_tooltip: 0b
|
||||||
|
},
|
||||||
|
"minecraft:custom_data": {
|
||||||
|
ability_scroll: [
|
||||||
|
"IMPLOSION_SCROLL",
|
||||||
|
"WITHER_SHIELD_SCROLL",
|
||||||
|
"SHADOW_WARP_SCROLL"
|
||||||
|
],
|
||||||
|
art_of_war_count: 1,
|
||||||
|
champion_combat_xp: 1.3556020889209766E7d,
|
||||||
|
donated_museum: 1b,
|
||||||
|
enchantments: {
|
||||||
|
champion: 10,
|
||||||
|
cleave: 5,
|
||||||
|
critical: 6,
|
||||||
|
cubism: 5,
|
||||||
|
ender_slayer: 6,
|
||||||
|
execute: 5,
|
||||||
|
experience: 3,
|
||||||
|
fire_aspect: 2,
|
||||||
|
first_strike: 4,
|
||||||
|
giant_killer: 6,
|
||||||
|
impaling: 3,
|
||||||
|
lethality: 5,
|
||||||
|
looting: 4,
|
||||||
|
luck: 6,
|
||||||
|
scavenger: 4,
|
||||||
|
smite: 7,
|
||||||
|
syphon: 4,
|
||||||
|
thunderlord: 6,
|
||||||
|
ultimate_wise: 5,
|
||||||
|
vampirism: 5,
|
||||||
|
venomous: 5
|
||||||
|
},
|
||||||
|
hot_potato_count: 15,
|
||||||
|
id: "HYPERION",
|
||||||
|
modifier: "heroic",
|
||||||
|
rarity_upgrades: 1,
|
||||||
|
stats_book: 65934,
|
||||||
|
timestamp: 1658091600000L,
|
||||||
|
upgrade_level: 5,
|
||||||
|
uuid: "a45337aa-9eaa-4e6f-aa27-26a42f8eca95"
|
||||||
|
},
|
||||||
|
"minecraft:custom_name": '{"extra":[{"color":"light_purple","text":"Heroic Hyperion "},{"color":"gold","text":"✪✪✪✪✪"}],"italic":false,"text":""}',
|
||||||
|
"minecraft:enchantment_glint_override": 1b,
|
||||||
|
"minecraft:hide_additional_tooltip": {
|
||||||
|
},
|
||||||
|
"minecraft:lore": [
|
||||||
|
'{"extra":[{"color":"gray","text":"Gear Score: "},{"color":"light_purple","text":"1145 "},{"color":"dark_gray","text":"(4271)"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Damage: "},{"color":"red","text":"+355 "},{"color":"yellow","text":"(+30) "},{"color":"dark_gray","text":"(+1,490.37)"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Strength: "},{"color":"red","text":"+250 "},{"color":"yellow","text":"(+30) "},{"color":"gold","text":"[+5] "},{"color":"blue","text":"(+50) "},{"color":"dark_gray","text":"(+1,064.55)"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Crit Damage: "},{"color":"red","text":"+70% "},{"color":"dark_gray","text":"(+317.1%)"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Bonus Attack Speed: "},{"color":"red","text":"+7% "},{"color":"blue","text":"(+7%) "},{"color":"dark_gray","text":"(+10.5%)"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Intelligence: "},{"color":"green","text":"+588 "},{"color":"blue","text":"(+125) "},{"color":"dark_gray","text":"(+2,505.09)"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Ferocity: "},{"color":"green","text":"+33 "},{"color":"dark_gray","text":"(+45)"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[" ",{"color":"dark_gray","text":"["},{"color":"dark_gray","text":"✎"},{"color":"dark_gray","text":"] "},{"color":"dark_gray","text":"["},{"color":"dark_gray","text":"⚔"},{"color":"dark_gray","text":"]"}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"bold":true,"color":"light_purple","text":""},{"bold":true,"color":"light_purple","text":"Ultimate Wise V"},{"color":"blue","text":", "},{"color":"blue","text":"Champion X"},{"color":"blue","text":", "},{"color":"blue","text":"Cleave V"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"blue","text":"Critical VI"},{"color":"blue","text":", "},{"color":"blue","text":"Cubism V"},{"color":"blue","text":", "},{"color":"blue","text":"Ender Slayer VI"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"blue","text":"Execute V"},{"color":"blue","text":", "},{"color":"blue","text":"Experience III"},{"color":"blue","text":", "},{"color":"blue","text":"Fire Aspect II"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"blue","text":"First Strike IV"},{"color":"blue","text":", "},{"color":"blue","text":"Giant Killer VI"},{"color":"blue","text":", "},{"color":"blue","text":"Impaling III"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"blue","text":"Lethality V"},{"color":"blue","text":", "},{"color":"blue","text":"Looting IV"},{"color":"blue","text":", "},{"color":"blue","text":"Luck VI"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"blue","text":"Scavenger IV"},{"color":"blue","text":", "},{"color":"blue","text":"Smite VII"},{"color":"blue","text":", "},{"color":"blue","text":"Syphon IV"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"blue","text":"Thunderlord VI"},{"color":"blue","text":", "},{"color":"blue","text":"Vampirism V"},{"color":"blue","text":", "},{"color":"blue","text":"Venomous V"}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Deals "},{"color":"red","text":"+50% "},{"color":"gray","text":"damage to Withers."}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Grants "},{"color":"red","text":"+1 "},{"color":"red","text":"❁ Damage "},{"color":"gray","text":"and "},{"color":"green","text":"+2 "},{"color":"aqua","text":"✎"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"aqua","text":"Intelligence "},{"color":"gray","text":"per "},{"color":"red","text":"Catacombs "},{"color":"gray","text":"level."}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"green","text":"Scroll Abilities:"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gold","text":"Ability: Wither Impact "},{"bold":true,"color":"yellow","text":"RIGHT CLICK"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Teleport "},{"color":"green","text":"10 blocks"},{"color":"gray","text":" ahead of you."}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Then implode dealing "},{"color":"red","text":"21,658 "},{"color":"gray","text":"damage"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"to nearby enemies. Also applies the"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"wither shield scroll ability reducing"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"damage taken and granting an"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"absorption shield for "},{"color":"yellow","text":"5 "},{"color":"gray","text":"seconds."}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"dark_gray","text":"Mana Cost: "},{"color":"dark_aqua","text":"150"}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"white","text":"Kills: "},{"color":"gold","text":"65,934"}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"bold":true,"color":"dark_gray","text":"* "},{"color":"dark_gray","text":"Co-op Soulbound "},{"bold":true,"color":"dark_gray","text":"*"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"bold":true,"color":"light_purple","obfuscated":true,"text":"a"},"",{"bold":false,"extra":[" "],"italic":false,"obfuscated":false,"strikethrough":false,"text":"","underlined":false},{"bold":true,"color":"light_purple","text":"MYTHIC DUNGEON SWORD "},{"bold":true,"color":"light_purple","obfuscated":true,"text":"a"}],"italic":false,"text":""}'
|
||||||
|
],
|
||||||
|
"minecraft:unbreakable": {
|
||||||
|
show_in_tooltip: 0b
|
||||||
|
}
|
||||||
|
},
|
||||||
|
count: 1,
|
||||||
|
id: "minecraft:iron_sword"
|
||||||
|
}
|
||||||
105
src/test/resources/testdata/items/implosion-belt.snbt
vendored
Normal file
105
src/test/resources/testdata/items/implosion-belt.snbt
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
components: {
|
||||||
|
"minecraft:attribute_modifiers": {
|
||||||
|
modifiers: [
|
||||||
|
],
|
||||||
|
show_in_tooltip: 0b
|
||||||
|
},
|
||||||
|
"minecraft:custom_data": {
|
||||||
|
attributes: {
|
||||||
|
dominance: 1,
|
||||||
|
experience: 1
|
||||||
|
},
|
||||||
|
id: "IMPLOSION_BELT",
|
||||||
|
timestamp: "12/5/22 5:17 PM",
|
||||||
|
uuid: "5c04f47e-7c6c-4ced-96b1-b8f83187b0a5"
|
||||||
|
},
|
||||||
|
"minecraft:custom_name": '{"extra":[{"color":"dark_purple","text":"Implosion Belt"}],"italic":false,"text":""}',
|
||||||
|
"minecraft:hide_additional_tooltip": {
|
||||||
|
},
|
||||||
|
"minecraft:lore": [
|
||||||
|
'{"extra":[{"color":"gray","text":"Defense: "},{"color":"green","text":"+70"}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"red","text":"Dominance I ✖"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Gain "},{"color":"red","text":"+1.5% "},{"color":"gray","text":"damage when at full health."}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"aqua","text":"Experience I"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Gain "},{"color":"green","text":"+10% "},{"color":"gray","text":"more experience orbs"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"from killing mobs."}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gold","text":"Ability: Consolidated "}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":""},{"color":"gray","text":"Increases all explosion damage dealt by "},{"color":"green","text":"25%"},{"color":"gray","text":"."}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":""},{"color":"dark_gray","text":"This item can be reforged!"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"bold":true,"color":"dark_purple","text":"EPIC BELT"}],"italic":false,"text":""}'
|
||||||
|
],
|
||||||
|
"minecraft:profile": {
|
||||||
|
id: [I;
|
||||||
|
-896440193,
|
||||||
|
-59755884,
|
||||||
|
-1280665573,
|
||||||
|
-1297214643
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: "textures",
|
||||||
|
signature: "",
|
||||||
|
value: "ewogICJ0aW1lc3RhbXAiIDogMTY0MzYwMjI5OTA2MSwKICAicHJvZmlsZUlkIiA6ICI0ZTMwZjUwZTdiYWU0M2YzYWZkMmE3NDUyY2ViZTI5YyIsCiAgInByb2ZpbGVOYW1lIiA6ICJfdG9tYXRvel8iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjFkMmIwMzZkZDY2NGJiOTBjOWQ0NDNjMTk5OGZiNTI2Mzk4YWI0ZGRkZWI3OWI4NDAxYjE2YjlhNGQxMGJhMyIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
count: 1,
|
||||||
|
id: "minecraft:player_head"
|
||||||
|
}{
|
||||||
|
components: {
|
||||||
|
"minecraft:attribute_modifiers": {
|
||||||
|
modifiers: [
|
||||||
|
],
|
||||||
|
show_in_tooltip: 0b
|
||||||
|
},
|
||||||
|
"minecraft:custom_data": {
|
||||||
|
attributes: {
|
||||||
|
dominance: 1,
|
||||||
|
experience: 1
|
||||||
|
},
|
||||||
|
id: "IMPLOSION_BELT",
|
||||||
|
timestamp: "12/5/22 5:17 PM",
|
||||||
|
uuid: "5c04f47e-7c6c-4ced-96b1-b8f83187b0a5"
|
||||||
|
},
|
||||||
|
"minecraft:custom_name": '{"extra":[{"color":"dark_purple","text":"Implosion Belt"}],"italic":false,"text":""}',
|
||||||
|
"minecraft:hide_additional_tooltip": {
|
||||||
|
},
|
||||||
|
"minecraft:lore": [
|
||||||
|
'{"extra":[{"color":"gray","text":"Defense: "},{"color":"green","text":"+70"}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"red","text":"Dominance I ✖"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Gain "},{"color":"red","text":"+1.5% "},{"color":"gray","text":"damage when at full health."}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"aqua","text":"Experience I"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"Gain "},{"color":"green","text":"+10% "},{"color":"gray","text":"more experience orbs"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":"from killing mobs."}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gold","text":"Ability: Consolidated "}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":""},{"color":"gray","text":"Increases all explosion damage dealt by "},{"color":"green","text":"25%"},{"color":"gray","text":"."}],"italic":false,"text":""}',
|
||||||
|
'{"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"color":"gray","text":""},{"color":"dark_gray","text":"This item can be reforged!"}],"italic":false,"text":""}',
|
||||||
|
'{"extra":[{"bold":true,"color":"dark_purple","text":"EPIC BELT"}],"italic":false,"text":""}'
|
||||||
|
],
|
||||||
|
"minecraft:profile": {
|
||||||
|
id: [I;
|
||||||
|
-896440193,
|
||||||
|
-59755884,
|
||||||
|
-1280665573,
|
||||||
|
-1297214643
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: "textures",
|
||||||
|
signature: "",
|
||||||
|
value: "ewogICJ0aW1lc3RhbXAiIDogMTY0MzYwMjI5OTA2MSwKICAicHJvZmlsZUlkIiA6ICI0ZTMwZjUwZTdiYWU0M2YzYWZkMmE3NDUyY2ViZTI5YyIsCiAgInByb2ZpbGVOYW1lIiA6ICJfdG9tYXRvel8iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjFkMmIwMzZkZDY2NGJiOTBjOWQ0NDNjMTk5OGZiNTI2Mzk4YWI0ZGRkZWI3OWI4NDAxYjE2YjlhNGQxMGJhMyIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
count: 1,
|
||||||
|
id: "minecraft:player_head"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user