feat: allow skull:texturehash items for neu buttons

This commit is contained in:
Linnea Gräf
2025-06-07 23:05:41 +02:00
parent 120fba3bbc
commit 1f24736e0c
4 changed files with 109 additions and 82 deletions

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.features.inventory.buttons package moe.nea.firmament.features.inventory.buttons
import com.mojang.brigadier.StringReader import com.mojang.brigadier.StringReader
@@ -18,69 +16,86 @@ import moe.nea.firmament.repo.RepoManager
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.collections.memoize import moe.nea.firmament.util.collections.memoize
import moe.nea.firmament.util.mc.arbitraryUUID
import moe.nea.firmament.util.mc.createSkullItem
import moe.nea.firmament.util.render.drawGuiTexture import moe.nea.firmament.util.render.drawGuiTexture
@Serializable @Serializable
data class InventoryButton( data class InventoryButton(
var x: Int, var x: Int,
var y: Int, var y: Int,
var anchorRight: Boolean, var anchorRight: Boolean,
var anchorBottom: Boolean, var anchorBottom: Boolean,
var icon: String? = "", var icon: String? = "",
var command: String? = "", var command: String? = "",
) { ) {
companion object { companion object {
val itemStackParser by lazy { val itemStackParser by lazy {
ItemStackArgumentType.itemStack(CommandRegistryAccess.of(MC.defaultRegistries, ItemStackArgumentType.itemStack(
FeatureFlags.VANILLA_FEATURES)) CommandRegistryAccess.of(
} MC.defaultRegistries,
val dimensions = Dimension(18, 18) FeatureFlags.VANILLA_FEATURES
val getItemForName = ::getItemForName0.memoize(1024) )
fun getItemForName0(icon: String): ItemStack { )
val repoItem = RepoManager.getNEUItem(SkyblockId(icon)) }
var itemStack = repoItem.asItemStack(idHint = SkyblockId(icon)) val dimensions = Dimension(18, 18)
if (repoItem == null) { val getItemForName = ::getItemForName0.memoize(1024)
val giveSyntaxItem = if (icon.startsWith("/give") || icon.startsWith("give")) fun getItemForName0(icon: String): ItemStack {
icon.split(" ", limit = 3).getOrNull(2) ?: icon val repoItem = RepoManager.getNEUItem(SkyblockId(icon))
else icon var itemStack = repoItem.asItemStack(idHint = SkyblockId(icon))
val componentItem = if (repoItem == null) {
runCatching { when {
itemStackParser.parse(StringReader(giveSyntaxItem)).createStack(1, false) icon.startsWith("skull:") -> {
}.getOrNull() itemStack = createSkullItem(
if (componentItem != null) arbitraryUUID,
itemStack = componentItem "https://textures.minecraft.net/texture/${icon.substring("skull:".length)}"
} )
return itemStack }
}
}
fun render(context: DrawContext) { else -> {
context.drawGuiTexture( val giveSyntaxItem = if (icon.startsWith("/give") || icon.startsWith("give"))
0, icon.split(" ", limit = 3).getOrNull(2) ?: icon
0, else icon
0, val componentItem =
dimensions.width, runCatching {
dimensions.height, itemStackParser.parse(StringReader(giveSyntaxItem)).createStack(1, false)
Identifier.of("firmament:inventory_button_background") }.getOrNull()
) if (componentItem != null)
context.drawItem(getItem(), 1, 1) itemStack = componentItem
} }
}
}
return itemStack
}
}
fun isValid() = !icon.isNullOrBlank() && !command.isNullOrBlank() fun render(context: DrawContext) {
context.drawGuiTexture(
0,
0,
0,
dimensions.width,
dimensions.height,
Identifier.of("firmament:inventory_button_background")
)
context.drawItem(getItem(), 1, 1)
}
fun getPosition(guiRect: Rectangle): Point { fun isValid() = !icon.isNullOrBlank() && !command.isNullOrBlank()
return Point(
(if (anchorRight) guiRect.maxX else guiRect.minX) + x,
(if (anchorBottom) guiRect.maxY else guiRect.minY) + y,
)
}
fun getBounds(guiRect: Rectangle): Rectangle { fun getPosition(guiRect: Rectangle): Point {
return Rectangle(getPosition(guiRect), dimensions) return Point(
} (if (anchorRight) guiRect.maxX else guiRect.minX) + x,
(if (anchorBottom) guiRect.maxY else guiRect.minY) + y,
)
}
fun getItem(): ItemStack { fun getBounds(guiRect: Rectangle): Rectangle {
return getItemForName(icon ?: "") return Rectangle(getPosition(guiRect), dimensions)
} }
fun getItem(): ItemStack {
return getItemForName(icon ?: "")
}
} }

View File

@@ -1,7 +1,9 @@
package moe.nea.firmament.features.inventory.buttons package moe.nea.firmament.features.inventory.buttons
import io.github.notenoughupdates.moulconfig.common.IItemStack import io.github.notenoughupdates.moulconfig.common.IItemStack
import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
import io.github.notenoughupdates.moulconfig.platform.ModernItemStack import io.github.notenoughupdates.moulconfig.platform.ModernItemStack
import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
import io.github.notenoughupdates.moulconfig.xml.Bind import io.github.notenoughupdates.moulconfig.xml.Bind
import me.shedaniel.math.Point import me.shedaniel.math.Point
import me.shedaniel.math.Rectangle import me.shedaniel.math.Rectangle
@@ -57,7 +59,10 @@ class InventoryButtonEditor(
} }
override fun resize(client: MinecraftClient, width: Int, height: Int) { override fun resize(client: MinecraftClient, width: Int, height: Int) {
lastGuiRect.move(MC.window.scaledWidth / 2 - lastGuiRect.width / 2, MC.window.scaledHeight / 2 - lastGuiRect.height / 2) lastGuiRect.move(
MC.window.scaledWidth / 2 - lastGuiRect.width / 2,
MC.window.scaledHeight / 2 - lastGuiRect.height / 2
)
super.resize(client, width, height) super.resize(client, width, height)
} }
@@ -89,14 +94,20 @@ class InventoryButtonEditor(
val movedButtons = mutableListOf<InventoryButton>() val movedButtons = mutableListOf<InventoryButton>()
for (button in buttons) { for (button in buttons) {
if ((!button.anchorBottom && !button.anchorRight && button.x > 0 && button.y > 0)) { if ((!button.anchorBottom && !button.anchorRight && button.x > 0 && button.y > 0)) {
MC.sendChat(tr("firmament.inventory-buttons.button-moved", MC.sendChat(
"One of your imported buttons intersects with the inventory and has been moved to the top left.")) tr(
movedButtons.add(button.copy( "firmament.inventory-buttons.button-moved",
x = 0, "One of your imported buttons intersects with the inventory and has been moved to the top left."
y = -InventoryButton.dimensions.width, )
anchorRight = false, )
anchorBottom = false movedButtons.add(
)) button.copy(
x = 0,
y = -InventoryButton.dimensions.width,
anchorRight = false,
anchorBottom = false
)
)
} else { } else {
newButtons.add(button) newButtons.add(button)
} }
@@ -105,9 +116,11 @@ class InventoryButtonEditor(
val zeroRect = Rectangle(0, 0, 1, 1) val zeroRect = Rectangle(0, 0, 1, 1)
for (movedButton in movedButtons) { for (movedButton in movedButtons) {
fun getPosition(button: InventoryButton, index: Int) = fun getPosition(button: InventoryButton, index: Int) =
button.copy(x = (index % 10) * InventoryButton.dimensions.width, button.copy(
y = (index / 10) * -InventoryButton.dimensions.height, x = (index % 10) * InventoryButton.dimensions.width,
anchorRight = false, anchorBottom = false) y = (index / 10) * -InventoryButton.dimensions.height,
anchorRight = false, anchorBottom = false
)
while (true) { while (true) {
val newPos = getPosition(movedButton, i++) val newPos = getPosition(movedButton, i++)
val newBounds = newPos.getBounds(zeroRect) val newBounds = newPos.getBounds(zeroRect)
@@ -131,7 +144,12 @@ class InventoryButtonEditor(
super.render(context, mouseX, mouseY, delta) super.render(context, mouseX, mouseY, delta)
context.matrices.push() context.matrices.push()
context.matrices.translate(0f, 0f, -10f) context.matrices.translate(0f, 0f, -10f)
context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1) PanelComponent.DefaultBackgroundRenderer.VANILLA
.render(
ModernRenderContext(context),
lastGuiRect.minX, lastGuiRect.minY,
lastGuiRect.width, lastGuiRect.height,
)
context.matrices.pop() context.matrices.pop()
for (button in buttons) { for (button in buttons) {
val buttonPosition = button.getBounds(lastGuiRect) val buttonPosition = button.getBounds(lastGuiRect)
@@ -193,14 +211,6 @@ class InventoryButtonEditor(
) )
fun getCoordsForMouse(mx: Int, my: Int): AnchoredCoords? { fun getCoordsForMouse(mx: Int, my: Int): AnchoredCoords? {
if (lastGuiRect.contains(mx, my) || lastGuiRect.contains(
Point(
mx + InventoryButton.dimensions.width,
my + InventoryButton.dimensions.height,
)
)
) return null
val anchorRight = mx > lastGuiRect.maxX val anchorRight = mx > lastGuiRect.maxX
val anchorBottom = my > lastGuiRect.maxY val anchorBottom = my > lastGuiRect.maxY
var offsetX = mx - if (anchorRight) lastGuiRect.maxX else lastGuiRect.minX var offsetX = mx - if (anchorRight) lastGuiRect.maxX else lastGuiRect.minX
@@ -209,7 +219,10 @@ class InventoryButtonEditor(
offsetX = MathHelper.floor(offsetX / 20F) * 20 offsetX = MathHelper.floor(offsetX / 20F) * 20
offsetY = MathHelper.floor(offsetY / 20F) * 20 offsetY = MathHelper.floor(offsetY / 20F) * 20
} }
return AnchoredCoords(anchorRight, anchorBottom, offsetX, offsetY) val rect = InventoryButton(offsetX, offsetY, anchorRight, anchorBottom).getBounds(lastGuiRect)
if (rect.intersects(lastGuiRect)) return null
val anchoredCoords = AnchoredCoords(anchorRight, anchorBottom, offsetX, offsetY)
return anchoredCoords
} }
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {

View File

@@ -11,7 +11,7 @@ import net.minecraft.item.Items
import moe.nea.firmament.repo.SBItemStack import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.mc.setEncodedSkullOwner import moe.nea.firmament.util.mc.setEncodedSkullOwner
import moe.nea.firmament.util.mc.zeroUUID import moe.nea.firmament.util.mc.arbitraryUUID
object ModifyEquipment : EntityModifier { object ModifyEquipment : EntityModifier {
val names = mapOf( val names = mapOf(
@@ -36,7 +36,7 @@ object ModifyEquipment : EntityModifier {
if (split.size != 2) return SBItemStack(SkyblockId(item)).asImmutableItemStack() if (split.size != 2) return SBItemStack(SkyblockId(item)).asImmutableItemStack()
val (type, data) = split val (type, data) = split
return when (type) { return when (type) {
"SKULL" -> ItemStack(Items.PLAYER_HEAD).also { it.setEncodedSkullOwner(zeroUUID, data) } "SKULL" -> ItemStack(Items.PLAYER_HEAD).also { it.setEncodedSkullOwner(arbitraryUUID, data) }
"LEATHER_LEGGINGS" -> coloredLeatherArmor(Items.LEATHER_LEGGINGS, data) "LEATHER_LEGGINGS" -> coloredLeatherArmor(Items.LEATHER_LEGGINGS, data)
"LEATHER_BOOTS" -> coloredLeatherArmor(Items.LEATHER_BOOTS, data) "LEATHER_BOOTS" -> coloredLeatherArmor(Items.LEATHER_BOOTS, data)
"LEATHER_HELMET" -> coloredLeatherArmor(Items.LEATHER_HELMET, data) "LEATHER_HELMET" -> coloredLeatherArmor(Items.LEATHER_HELMET, data)

View File

@@ -10,7 +10,6 @@ import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlinx.serialization.encodeToString
import net.minecraft.component.DataComponentTypes import net.minecraft.component.DataComponentTypes
import net.minecraft.component.type.ProfileComponent import net.minecraft.component.type.ProfileComponent
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@@ -51,7 +50,7 @@ fun ItemStack.setEncodedSkullOwner(uuid: UUID, encodedData: String) {
this.set(DataComponentTypes.PROFILE, ProfileComponent(gameProfile)) this.set(DataComponentTypes.PROFILE, ProfileComponent(gameProfile))
} }
val zeroUUID = UUID.fromString("d3cb85e2-3075-48a1-b213-a9bfb62360c1") val arbitraryUUID = UUID.fromString("d3cb85e2-3075-48a1-b213-a9bfb62360c1")
fun createSkullItem(uuid: UUID, url: String) = ItemStack(Items.PLAYER_HEAD) fun createSkullItem(uuid: UUID, url: String) = ItemStack(Items.PLAYER_HEAD)
.also { it.setSkullOwner(uuid, url) } .also { it.setSkullOwner(uuid, url) }