From 207161dc5e2ae93648b563bae7b7427ec2524d64 Mon Sep 17 00:00:00 2001
From: Linnea Gräf <nea@nea.moe>
Date: Tue, 11 Mar 2025 17:58:47 +0100
Subject: feat: Add SBRecombobulated

---
 src/main/kotlin/repo/item/SBItemId.kt         |  2 +-
 src/main/kotlin/repo/item/SBItemProperty.kt   |  8 +++--
 src/main/kotlin/repo/item/SBRecombobulator.kt | 38 +++++++++++++++++++++++
 src/main/kotlin/util/skyblock/Rarity.kt       | 43 +++++++++++++++++++++++++++
 src/main/kotlin/util/textutil.kt              | 28 ++++++++++-------
 5 files changed, 106 insertions(+), 13 deletions(-)
 create mode 100644 src/main/kotlin/repo/item/SBRecombobulator.kt

(limited to 'src/main/kotlin')

diff --git a/src/main/kotlin/repo/item/SBItemId.kt b/src/main/kotlin/repo/item/SBItemId.kt
index fcafff0..0a93609 100644
--- a/src/main/kotlin/repo/item/SBItemId.kt
+++ b/src/main/kotlin/repo/item/SBItemId.kt
@@ -16,7 +16,7 @@ object SBItemId : SBItemProperty.State<SkyblockId>() {
 
 	override fun applyToStack(stack: ItemStack, store: SBItemData, value: SkyblockId?): ItemStack {
 		val id = value ?: SkyblockId.NULL
-		return RepoManager.getNEUItem(id).asItemStack(idHint = id)
+		return RepoManager.getNEUItem(id).asItemStack(idHint = id).copy()
 	}
 
 	override val order: Int
diff --git a/src/main/kotlin/repo/item/SBItemProperty.kt b/src/main/kotlin/repo/item/SBItemProperty.kt
index 55b8f01..fd85251 100644
--- a/src/main/kotlin/repo/item/SBItemProperty.kt
+++ b/src/main/kotlin/repo/item/SBItemProperty.kt
@@ -8,7 +8,7 @@ import moe.nea.firmament.util.compatloader.CompatLoader
 /**
  * A property of a skyblock item. Not every skyblock item must have this property, but some should.
  *
- * Access to this class should be limited to [State.bindWith] and [SBItemData.getData].
+ * Access to this class should be limited to [State.bind] and [SBItemData.getData].
  * @see State
  */
 abstract class SBItemProperty<T> {
@@ -36,8 +36,12 @@ abstract class SBItemProperty<T> {
 	 * to change the state of an item, including its rendering as a vanilla [ItemStack].
 	 */
 	abstract class State<T> : SBItemProperty<T>() {
+		/**
+		 * Apply the stored info back to the item stack. If possible [stack] should be modified and returned directly,
+		 * instead of creating a new [ItemStack] instance. Information stored here should be recovered using [fromStack].
+		 */
 		abstract fun applyToStack(stack: ItemStack, store: SBItemData, value: T?): ItemStack
-		fun bindWith(data: T) = BoundState(this, data)
+		fun bind(data: T) = BoundState(this, data)
 	}
 
 	/**
diff --git a/src/main/kotlin/repo/item/SBRecombobulator.kt b/src/main/kotlin/repo/item/SBRecombobulator.kt
new file mode 100644
index 0000000..8798493
--- /dev/null
+++ b/src/main/kotlin/repo/item/SBRecombobulator.kt
@@ -0,0 +1,38 @@
+package moe.nea.firmament.repo.item
+
+import com.google.auto.service.AutoService
+import net.minecraft.item.ItemStack
+import net.minecraft.nbt.NbtInt
+import moe.nea.firmament.repo.set
+import moe.nea.firmament.util.extraAttributes
+import moe.nea.firmament.util.mc.modifyLore
+import moe.nea.firmament.util.modifyExtraAttributes
+import moe.nea.firmament.util.skyblock.Rarity
+
+@AutoService(SBItemProperty::class)
+object SBRecombobulator : SBItemProperty.State<Boolean>() {
+	override fun applyToStack(
+		stack: ItemStack,
+		store: SBItemData,
+		value: Boolean?
+	): ItemStack {
+		if (value != true) return stack
+		stack.modifyLore { lore ->
+			Rarity.recombobulateLore(lore)
+		}
+		stack.modifyExtraAttributes {
+			it["rarity_upgrades"] = NbtInt.of(1)
+		}
+		return stack
+	}
+
+	override fun fromStack(
+		stack: ItemStack,
+		store: SBItemData
+	): Boolean? {
+		return stack.extraAttributes.getInt("rarity_upgrades") > 0
+	}
+
+	override val order: Int
+		get() = -100
+}
diff --git a/src/main/kotlin/util/skyblock/Rarity.kt b/src/main/kotlin/util/skyblock/Rarity.kt
index f244ef6..0b26bda 100644
--- a/src/main/kotlin/util/skyblock/Rarity.kt
+++ b/src/main/kotlin/util/skyblock/Rarity.kt
@@ -13,10 +13,14 @@ import net.minecraft.text.Text
 import net.minecraft.util.Formatting
 import moe.nea.firmament.util.StringUtil.words
 import moe.nea.firmament.util.collections.lastNotNullOfOrNull
+import moe.nea.firmament.util.directLiteralStringContent
 import moe.nea.firmament.util.mc.loreAccordingToNbt
 import moe.nea.firmament.util.petData
+import moe.nea.firmament.util.prepend
+import moe.nea.firmament.util.prependHypixelified
 import moe.nea.firmament.util.removeColorCodes
 import moe.nea.firmament.util.unformattedString
+import moe.nea.firmament.util.withColor
 
 typealias RepoRarity = io.github.moulberry.repo.data.Rarity
 
@@ -52,6 +56,10 @@ enum class Rarity(vararg altNames: String) {
 	val text: Text get() = Text.literal(name).setStyle(Style.EMPTY.withColor(colourMap[this]))
 	val neuRepoRarity: RepoRarity? = RepoRarity.entries.find { it.name == name }
 
+	fun recombobulate(): Rarity = Rarity.entries.getOrElse(ordinal + 1) { this }
+
+	fun colour() = colourMap[this] ?: Formatting.WHITE
+
 	companion object {
 		// TODO: inline those formattings as fields
 		val colourMap = mapOf(
@@ -94,11 +102,46 @@ enum class Rarity(vararg altNames: String) {
 			}
 		}
 
+		fun findLoreIndex(lore: List<Text>): Int {
+			return lore.indexOfLast {
+				it.unformattedString.words().any { fromString(it) != null }
+			}
+		}
+
 		fun fromLore(lore: List<Text>): Rarity? =
 			lore.lastNotNullOfOrNull {
 				it.unformattedString.words()
 					.firstNotNullOfOrNull(::fromString)
 			}
 
+		fun recombobulateLore(lore: List<Text>): List<Text> {
+			val before = fromLore(lore) ?: return lore
+			val rarityIndex = findLoreIndex(lore)
+			if (rarityIndex < 0) return lore
+			val after = before.recombobulate()
+			val col = after.colour()
+			val loreMut = lore.toMutableList()
+			val obfuscatedTag = Text.literal("a")
+				.withColor(col)
+				.styled { it.withObfuscated(true) }
+			val rarityLine = loreMut[rarityIndex].copy()
+				.prependHypixelified(Text.literal(" "))
+				.prepend(obfuscatedTag)
+				.append(Text.literal(" "))
+				.append(obfuscatedTag)
+			(rarityLine.siblings as MutableList<Text>)
+				.replaceAll {
+					var content = it.directLiteralStringContent
+					before.names.forEach {
+						content = content?.replace(it, after.name)
+					}
+					val editedText = (if (content != it.directLiteralStringContent)
+						Text.literal(content) else it.copy())
+					editedText.withColor(col)
+				}
+			loreMut[rarityIndex] = rarityLine
+			return loreMut
+		}
+
 	}
 }
diff --git a/src/main/kotlin/util/textutil.kt b/src/main/kotlin/util/textutil.kt
index 806f61e..a3a3388 100644
--- a/src/main/kotlin/util/textutil.kt
+++ b/src/main/kotlin/util/textutil.kt
@@ -56,21 +56,22 @@ fun OrderedText.reconstitute(): MutableText {
 	return base
 
 }
+
 fun StringVisitable.reconstitute(): MutableText {
 	val base = Text.literal("")
 	base.setStyle(Style.EMPTY.withItalic(false))
 	var lastColorCode = Style.EMPTY
 	val text = StringBuilder()
 	this.visit({ style, string ->
-		if (style != lastColorCode) {
-			if (text.isNotEmpty())
-				base.append(Text.literal(text.toString()).setStyle(lastColorCode))
-			lastColorCode = style
-			text.clear()
-		}
-		text.append(string)
-		Optional.empty<Unit>()
-	}, Style.EMPTY)
+		           if (style != lastColorCode) {
+			           if (text.isNotEmpty())
+				           base.append(Text.literal(text.toString()).setStyle(lastColorCode))
+			           lastColorCode = style
+			           text.clear()
+		           }
+		           text.append(string)
+		           Optional.empty<Unit>()
+	           }, Style.EMPTY)
 	if (text.isNotEmpty())
 		base.append(Text.literal(text.toString()).setStyle(lastColorCode))
 	return base
@@ -127,7 +128,8 @@ fun MutableText.darkGrey() = withColor(Formatting.DARK_GRAY)
 fun MutableText.red() = withColor(Formatting.RED)
 fun MutableText.white() = withColor(Formatting.WHITE)
 fun MutableText.bold(): MutableText = styled { it.withBold(true) }
-fun MutableText.hover(text: Text): MutableText = styled {it.withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, text))}
+fun MutableText.hover(text: Text): MutableText =
+	styled { it.withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, text)) }
 
 
 fun MutableText.clickCommand(command: String): MutableText {
@@ -137,6 +139,12 @@ fun MutableText.clickCommand(command: String): MutableText {
 	}
 }
 
+fun MutableText.prependHypixelified(text: Text): MutableText {
+	if (directLiteralStringContent == "")
+		return prepend(text)
+	return Text.literal("").styled { it.withItalic(false) }.append(this).prepend(text)
+}
+
 fun MutableText.prepend(text: Text): MutableText {
 	siblings.addFirst(text)
 	return this
-- 
cgit