aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-10-16 19:24:24 +0200
committerLinnea Gräf <nea@nea.moe>2024-10-16 19:24:24 +0200
commit7de0e8e7e09e3428c17ca9717c21c02469c31b76 (patch)
treef8c3d32a286521967a27877231c1ee74fa8c87ab
parent854ec336cc6a0a3bb60f33acfac28ed45b491a15 (diff)
downloadfirmament-7de0e8e7e09e3428c17ca9717c21c02469c31b76.tar.gz
firmament-7de0e8e7e09e3428c17ca9717c21c02469c31b76.tar.bz2
firmament-7de0e8e7e09e3428c17ca9717c21c02469c31b76.zip
Add edit backpacks button to /firm storage
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt7
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverlay.kt261
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverlayCustom.kt1
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt75
-rw-r--r--src/main/kotlin/gui/EmptyComponent.kt17
-rw-r--r--src/main/kotlin/util/MoulConfigUtils.kt443
-rw-r--r--src/main/kotlin/util/assertions.kt12
-rw-r--r--src/main/kotlin/util/render/DrawContextExt.kt8
-rw-r--r--src/main/kotlin/util/uuid.kt8
-rw-r--r--src/main/resources/assets/firmament/textures/gui/sprites/storageoverlay/storage_controls.pngbin0 -> 4766 bytes
-rw-r--r--src/main/resources/assets/firmament/textures/gui/sprites/storageoverlay/storage_controls.png.mcmeta10
11 files changed, 507 insertions, 335 deletions
diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt
index 1015578..5c1ac34 100644
--- a/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt
+++ b/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt
@@ -1,7 +1,9 @@
-
+@file:OptIn(ExperimentalContracts::class)
package moe.nea.firmament.features.inventory.storageoverlay
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.contract
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
import net.minecraft.screen.GenericContainerScreenHandler
@@ -39,6 +41,9 @@ sealed interface StorageBackingHandle {
* selection screen.
*/
fun fromScreen(screen: Screen?): StorageBackingHandle? {
+ contract {
+ returnsNotNull() implies (screen != null)
+ }
if (screen == null) return null
if (screen !is GenericContainerScreen) return null
val title = screen.title.unformattedString
diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlay.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlay.kt
index 333b0fb..7de0fff 100644
--- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlay.kt
+++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlay.kt
@@ -1,5 +1,3 @@
-
-
package moe.nea.firmament.features.inventory.storageoverlay
import java.util.SortedMap
@@ -22,133 +20,134 @@ import moe.nea.firmament.util.data.ProfileSpecificDataHolder
object StorageOverlay : FirmamentFeature {
- object Data : ProfileSpecificDataHolder<StorageData>(serializer(), "storage-data", ::StorageData)
-
- override val identifier: String
- get() = "storage-overlay"
-
- object TConfig : ManagedConfig(identifier, Category.INVENTORY) {
- val alwaysReplace by toggle("always-replace") { true }
- val columns by integer("rows", 1, 10) { 3 }
- val scrollSpeed by integer("scroll-speed", 1, 50) { 10 }
- val inverseScroll by toggle("inverse-scroll") { false }
- val padding by integer("padding", 1, 20) { 5 }
- val margin by integer("margin", 1, 60) { 20 }
- }
-
- fun adjustScrollSpeed(amount: Double): Double {
- return amount * TConfig.scrollSpeed * (if (TConfig.inverseScroll) 1 else -1)
- }
-
- override val config: TConfig
- get() = TConfig
-
- var lastStorageOverlay: StorageOverviewScreen? = null
- var skipNextStorageOverlayBackflip = false
- var currentHandler: StorageBackingHandle? = null
-
- @Subscribe
- fun onTick(event: TickEvent) {
- rememberContent(currentHandler ?: return)
- }
-
- @Subscribe
- fun onClick(event: SlotClickEvent) {
- if (lastStorageOverlay != null && event.slot.inventory !is PlayerInventory && event.slot.index < 9
- && event.stack.item != Items.BLACK_STAINED_GLASS_PANE
- ) {
- skipNextStorageOverlayBackflip = true
- }
- }
-
- @Subscribe
- fun onScreenChange(it: ScreenChangeEvent) {
- if (it.old == null && it.new == null) return
- val storageOverlayScreen = it.old as? StorageOverlayScreen
- ?: ((it.old as? HandledScreen<*>)?.customGui as? StorageOverlayCustom)?.overview
- var storageOverviewScreen = it.old as? StorageOverviewScreen
- val screen = it.new as? GenericContainerScreen
- val oldHandler = currentHandler
- currentHandler = StorageBackingHandle.fromScreen(screen)
- rememberContent(currentHandler)
- if (storageOverviewScreen != null && oldHandler is StorageBackingHandle.HasBackingScreen) {
- val player = MC.player
- assert(player != null)
- player?.networkHandler?.sendPacket(CloseHandledScreenC2SPacket(oldHandler.handler.syncId))
- if (player?.currentScreenHandler === oldHandler.handler) {
- player.currentScreenHandler = player.playerScreenHandler
- }
- }
- storageOverviewScreen = storageOverviewScreen ?: lastStorageOverlay
- if (it.new == null && storageOverlayScreen != null && !storageOverlayScreen.isExiting) {
- it.overrideScreen = storageOverlayScreen
- return
- }
- if (storageOverviewScreen != null
- && !storageOverviewScreen.isClosing
- && (currentHandler is StorageBackingHandle.Overview || currentHandler == null)
- ) {
- if (skipNextStorageOverlayBackflip) {
- skipNextStorageOverlayBackflip = false
- } else {
- it.overrideScreen = storageOverviewScreen
- lastStorageOverlay = null
- }
- return
- }
- screen ?: return
- screen.customGui = StorageOverlayCustom(
- currentHandler ?: return,
- screen,
- storageOverlayScreen ?: (if (TConfig.alwaysReplace) StorageOverlayScreen() else return))
- }
-
- fun rememberContent(handler: StorageBackingHandle?) {
- handler ?: return
- // TODO: Make all of these functions work on deltas / updates instead of the entire contents
- val data = Data.data?.storageInventories ?: return
- when (handler) {
- is StorageBackingHandle.Overview -> rememberStorageOverview(handler, data)
- is StorageBackingHandle.Page -> rememberPage(handler, data)
- }
- Data.markDirty()
- }
-
- private fun rememberStorageOverview(
- handler: StorageBackingHandle.Overview,
- data: SortedMap<StoragePageSlot, StorageData.StorageInventory>
- ) {
- for ((index, stack) in handler.handler.stacks.withIndex()) {
- // Ignore unloaded item stacks
- if (stack.isEmpty) continue
- val slot = StoragePageSlot.fromOverviewSlotIndex(index) ?: continue
- val isEmpty = stack.item in StorageOverviewScreen.emptyStorageSlotItems
- if (slot in data) {
- if (isEmpty)
- data.remove(slot)
- continue
- }
- if (!isEmpty) {
- data[slot] = StorageData.StorageInventory(slot.defaultName(), slot, null)
- }
- }
- }
-
- private fun rememberPage(
- handler: StorageBackingHandle.Page,
- data: SortedMap<StoragePageSlot, StorageData.StorageInventory>
- ) {
- // TODO: FIXME: FIXME NOW: Definitely don't copy all of this every tick into persistence
- val newStacks =
- VirtualInventory(handler.handler.stacks.take(handler.handler.rows * 9).drop(9).map { it.copy() })
- data.compute(handler.storagePageSlot) { slot, existingInventory ->
- (existingInventory ?: StorageData.StorageInventory(
- slot.defaultName(),
- slot,
- null
- )).also {
- it.inventory = newStacks
- }
- }
- }
+ object Data : ProfileSpecificDataHolder<StorageData>(serializer(), "storage-data", ::StorageData)
+
+ override val identifier: String
+ get() = "storage-overlay"
+
+ object TConfig : ManagedConfig(identifier, Category.INVENTORY) {
+ val alwaysReplace by toggle("always-replace") { true }
+ val columns by integer("rows", 1, 10) { 3 }
+ val scrollSpeed by integer("scroll-speed", 1, 50) { 10 }
+ val inverseScroll by toggle("inverse-scroll") { false }
+ val padding by integer("padding", 1, 20) { 5 }
+ val margin by integer("margin", 1, 60) { 20 }
+ }
+
+ fun adjustScrollSpeed(amount: Double): Double {
+ return amount * TConfig.scrollSpeed * (if (TConfig.inverseScroll) 1 else -1)
+ }
+
+ override val config: TConfig
+ get() = TConfig
+
+ var lastStorageOverlay: StorageOverviewScreen? = null
+ var skipNextStorageOverlayBackflip = false
+ var currentHandler: StorageBackingHandle? = null
+
+ @Subscribe
+ fun onTick(event: TickEvent) {
+ rememberContent(currentHandler ?: return)
+ }
+
+ @Subscribe
+ fun onClick(event: SlotClickEvent) {
+ if (lastStorageOverlay != null && event.slot.inventory !is PlayerInventory && event.slot.index < 9
+ && event.stack.item != Items.BLACK_STAINED_GLASS_PANE
+ ) {
+ skipNextStorageOverlayBackflip = true
+ }
+ }
+
+ @Subscribe
+ fun onScreenChange(it: ScreenChangeEvent) {
+ if (it.old == null && it.new == null) return
+ val storageOverlayScreen = it.old as? StorageOverlayScreen
+ ?: ((it.old as? HandledScreen<*>)?.customGui as? StorageOverlayCustom)?.overview
+ var storageOverviewScreen = it.old as? StorageOverviewScreen
+ val screen = it.new as? GenericContainerScreen
+ val oldHandler = currentHandler
+ currentHandler = StorageBackingHandle.fromScreen(screen)
+ rememberContent(currentHandler)
+ if (storageOverviewScreen != null && oldHandler is StorageBackingHandle.HasBackingScreen) {
+ val player = MC.player
+ assert(player != null)
+ player?.networkHandler?.sendPacket(CloseHandledScreenC2SPacket(oldHandler.handler.syncId))
+ if (player?.currentScreenHandler === oldHandler.handler) {
+ player.currentScreenHandler = player.playerScreenHandler
+ }
+ }
+ storageOverviewScreen = storageOverviewScreen ?: lastStorageOverlay
+ if (it.new == null && storageOverlayScreen != null && !storageOverlayScreen.isExiting) {
+ it.overrideScreen = storageOverlayScreen
+ return
+ }
+ if (storageOverviewScreen != null
+ && !storageOverviewScreen.isClosing
+ && (currentHandler is StorageBackingHandle.Overview || currentHandler == null)
+ ) {
+ if (skipNextStorageOverlayBackflip) {
+ skipNextStorageOverlayBackflip = false
+ } else {
+ it.overrideScreen = storageOverviewScreen
+ lastStorageOverlay = null
+ }
+ return
+ }
+ screen ?: return
+ if (storageOverlayScreen?.isExiting == true) return
+ screen.customGui = StorageOverlayCustom(
+ currentHandler ?: return,
+ screen,
+ storageOverlayScreen ?: (if (TConfig.alwaysReplace) StorageOverlayScreen() else return))
+ }
+
+ fun rememberContent(handler: StorageBackingHandle?) {
+ handler ?: return
+ // TODO: Make all of these functions work on deltas / updates instead of the entire contents
+ val data = Data.data?.storageInventories ?: return
+ when (handler) {
+ is StorageBackingHandle.Overview -> rememberStorageOverview(handler, data)
+ is StorageBackingHandle.Page -> rememberPage(handler, data)
+ }
+ Data.markDirty()
+ }
+
+ private fun rememberStorageOverview(
+ handler: StorageBackingHandle.Overview,
+ data: SortedMap<StoragePageSlot, StorageData.StorageInventory>
+ ) {
+ for ((index, stack) in handler.handler.stacks.withIndex()) {
+ // Ignore unloaded item stacks
+ if (stack.isEmpty) continue
+ val slot = StoragePageSlot.fromOverviewSlotIndex(index) ?: continue
+ val isEmpty = stack.item in StorageOverviewScreen.emptyStorageSlotItems
+ if (slot in data) {
+ if (isEmpty)
+ data.remove(slot)
+ continue
+ }
+ if (!isEmpty) {
+ data[slot] = StorageData.StorageInventory(slot.defaultName(), slot, null)
+ }
+ }
+ }
+
+ private fun rememberPage(
+ handler: StorageBackingHandle.Page,
+ data: SortedMap<StoragePageSlot, StorageData.StorageInventory>
+ ) {
+ // TODO: FIXME: FIXME NOW: Definitely don't copy all of this every tick into persistence
+ val newStacks =
+ VirtualInventory(handler.handler.stacks.take(handler.handler.rows * 9).drop(9).map { it.copy() })
+ data.compute(handler.storagePageSlot) { slot, existingInventory ->
+ (existingInventory ?: StorageData.StorageInventory(
+ slot.defaultName(),
+ slot,
+ null
+ )).also {
+ it.inventory = newStacks
+ }
+ }
+ }
}
diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayCustom.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayCustom.kt
index fc3c4ef..2be798b 100644
--- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayCustom.kt
+++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayCustom.kt
@@ -80,6 +80,7 @@ class StorageOverlayCustom(
screen.screenHandler.slots.take(screen.screenHandler.rows * 9).drop(9),
Point((screen as AccessorHandledScreen).x_Firmament, screen.y_Firmament))
overview.drawScrollBar(drawContext)
+ overview.drawControls(drawContext, mouseX, mouseY)
}
override fun moveSlot(slot: Slot) {
diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
index 58c894a..f81315d 100644
--- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
+++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
@@ -1,14 +1,26 @@
package moe.nea.firmament.features.inventory.storageoverlay
+import io.github.notenoughupdates.moulconfig.gui.GuiContext
+import io.github.notenoughupdates.moulconfig.gui.MouseEvent
+import io.github.notenoughupdates.moulconfig.gui.component.ColumnComponent
+import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
+import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import me.shedaniel.math.Point
import me.shedaniel.math.Rectangle
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.screen.Screen
+import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.screen.slot.Slot
import net.minecraft.text.Text
import net.minecraft.util.Identifier
+import moe.nea.firmament.gui.EmptyComponent
+import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.MoulConfigUtils.adopt
+import moe.nea.firmament.util.MoulConfigUtils.clickMCComponentInPlace
+import moe.nea.firmament.util.MoulConfigUtils.drawMCComponentInPlace
import moe.nea.firmament.util.assertTrueOr
+import moe.nea.firmament.util.customgui.customGui
class StorageOverlayScreen : Screen(Text.literal("")) {
@@ -24,6 +36,9 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
val MAIN_INVENTORY_Y = 9
val SCROLL_BAR_WIDTH = 8
val SCROLL_BAR_HEIGHT = 16
+ val CONTROL_WIDTH = 70
+ val CONTROL_BACKGROUND_WIDTH = CONTROL_WIDTH + PLAYER_Y_INSET
+ val CONTROL_HEIGHT = 100
}
var isExiting: Boolean = false
@@ -39,6 +54,8 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
val y = height / 2 - (overviewHeight + PLAYER_HEIGHT) / 2
val playerX = width / 2 - PLAYER_WIDTH / 2
val playerY = y + overviewHeight - PLAYER_Y_INSET
+ val controlX = x - CONTROL_WIDTH
+ val controlY = y + overviewHeight / 2 - CONTROL_HEIGHT / 2
val totalWidth = overviewWidth
val totalHeight = overviewHeight - PLAYER_Y_INSET + PLAYER_HEIGHT
}
@@ -74,6 +91,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
val slotRowSprite = Identifier.of("firmament:storageoverlay/storage_row")
val scrollbarBackground = Identifier.of("firmament:storageoverlay/scroll_bar_background")
val scrollbarKnob = Identifier.of("firmament:storageoverlay/scroll_bar_knob")
+ val controllerBackground = Identifier.of("firmament:storageoverlay/storage_controls")
override fun close() {
isExiting = true
@@ -86,6 +104,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
drawPages(context, mouseX, mouseY, delta, null, null, Point())
drawScrollBar(context)
drawPlayerInventory(context, mouseX, mouseY, delta)
+ drawControls(context, mouseX, mouseY)
}
fun getScrollbarPercentage(): Float {
@@ -106,6 +125,39 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
)
}
+ fun editPages() {
+ isExiting = true
+ val hs = MC.screen as? HandledScreen<*>
+ if (StorageBackingHandle.fromScreen(hs) is StorageBackingHandle.Overview) {
+ hs.customGui = null
+ } else {
+ MC.sendCommand("storage")
+ }
+ }
+
+ val guiContext = GuiContext(EmptyComponent())
+ private val knobStub = EmptyComponent()
+ val editButton = PanelComponent(ColumnComponent(FirmButtonComponent(TextComponent("Edit"), action = ::editPages)),
+ 8, PanelComponent.DefaultBackgroundRenderer.TRANSPARENT)
+
+ init {
+ guiContext.adopt(editButton)
+ guiContext.adopt(knobStub)
+ }
+
+ fun drawControls(context: DrawContext, mouseX: Int, mouseY: Int) {
+ context.drawGuiTexture(
+ controllerBackground,
+ measurements.controlX,
+ measurements.controlY,
+ CONTROL_BACKGROUND_WIDTH, CONTROL_HEIGHT)
+ context.drawMCComponentInPlace(
+ editButton,
+ measurements.controlX, measurements.controlY,
+ CONTROL_WIDTH, CONTROL_HEIGHT,
+ mouseX, mouseY)
+ }
+
fun drawBackgrounds(context: DrawContext) {
context.drawGuiTexture(upperBackgroundSprite,
measurements.x,
@@ -182,7 +234,10 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
context.disableScissor()
}
- var knobGrabbed = false
+
+ var knobGrabbed: Boolean
+ get() = guiContext.focusedElement == knobStub
+ set(value) = knobStub.setFocus(value)
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
return mouseClicked(mouseX, mouseY, button, null)
@@ -193,6 +248,12 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
knobGrabbed = false
return true
}
+ if (clickMCComponentInPlace(editButton,
+ measurements.controlX, measurements.controlY,
+ CONTROL_WIDTH, CONTROL_HEIGHT,
+ mouseX.toInt(), mouseY.toInt(),
+ MouseEvent.Click(button, false))
+ ) return true
return super.mouseReleased(mouseX, mouseY, button)
}
@@ -226,6 +287,12 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
knobGrabbed = true
return true
}
+ if (clickMCComponentInPlace(editButton,
+ measurements.controlX, measurements.controlY,
+ CONTROL_WIDTH, CONTROL_HEIGHT,
+ mouseX.toInt(), mouseY.toInt(),
+ MouseEvent.Click(button, true))
+ ) return true
return false
}
@@ -309,6 +376,10 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
Rectangle(measurements.playerX,
measurements.playerY,
PLAYER_WIDTH,
- PLAYER_HEIGHT))
+ PLAYER_HEIGHT),
+ Rectangle(measurements.controlX,
+ measurements.controlY,
+ CONTROL_WIDTH,
+ CONTROL_HEIGHT))
}
}
diff --git a/src/main/kotlin/gui/EmptyComponent.kt b/src/main/kotlin/gui/EmptyComponent.kt
new file mode 100644
index 0000000..13efa89
--- /dev/null
+++ b/src/main/kotlin/gui/EmptyComponent.kt
@@ -0,0 +1,17 @@
+package moe.nea.firmament.gui
+
+import io.github.notenoughupdates.moulconfig.gui.GuiComponent
+import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
+
+class EmptyComponent : GuiComponent() {
+ override fun getWidth(): Int {
+ return 0
+ }
+
+ override fun getHeight(): Int {
+ return 0
+ }
+
+ override fun render(context: GuiImmediateContext) {
+ }
+}
diff --git a/src/main/kotlin/util/MoulConfigUtils.kt b/src/main/kotlin/util/MoulConfigUtils.kt
index 00561d1..54528dd 100644
--- a/src/main/kotlin/util/MoulConfigUtils.kt
+++ b/src/main/kotlin/util/MoulConfigUtils.kt
@@ -1,12 +1,15 @@
-
-
package moe.nea.firmament.util
+import io.github.notenoughupdates.moulconfig.common.IMinecraft
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
import io.github.notenoughupdates.moulconfig.gui.CloseEventListener
+import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
import io.github.notenoughupdates.moulconfig.gui.GuiContext
+import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
+import io.github.notenoughupdates.moulconfig.gui.MouseEvent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
+import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
import io.github.notenoughupdates.moulconfig.xml.ChildCount
import io.github.notenoughupdates.moulconfig.xml.XMLContext
import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader
@@ -19,6 +22,7 @@ import me.shedaniel.math.Color
import org.w3c.dom.Element
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
+import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.screen.Screen
import moe.nea.firmament.gui.BarComponent
import moe.nea.firmament.gui.FirmButtonComponent
@@ -26,205 +30,254 @@ import moe.nea.firmament.gui.FirmHoverComponent
import moe.nea.firmament.gui.FixedComponent
import moe.nea.firmament.gui.ImageComponent
import moe.nea.firmament.gui.TickComponent
+import moe.nea.firmament.util.render.isUntranslatedGuiDrawContext
object MoulConfigUtils {
- val firmUrl = "http://firmament.nea.moe/moulconfig"
- val universe = XMLUniverse.getDefaultUniverse().also { uni ->
- uni.registerMapper(java.awt.Color::class.java) {
- if (it.startsWith("#")) {
- val hexString = it.substring(1)
- val hex = hexString.toInt(16)
- if (hexString.length == 6) {
- return@registerMapper java.awt.Color(hex)
- }
- if (hexString.length == 8) {
- return@registerMapper java.awt.Color(hex, true)
- }
- error("Hexcolor $it needs to be exactly 6 or 8 hex digits long")
- }
- return@registerMapper java.awt.Color(it.toInt(), true)
- }
- uni.registerMapper(Color::class.java) {
- val color = uni.mapXMLObject(it, java.awt.Color::class.java)
- Color.ofRGBA(color.red, color.green, color.blue, color.alpha)
- }
- uni.registerLoader(object : XMLGuiLoader.Basic<BarComponent> {
- override fun getName(): QName {
- return QName(firmUrl, "Bar")
- }
-
- override fun createInstance(context: XMLContext<*>, element: Element): BarComponent {
- return BarComponent(
- context.getPropertyFromAttribute(element, QName("progress"), Double::class.java)!!,
- context.getPropertyFromAttribute(element, QName("total"), Double::class.java)!!,
- context.getPropertyFromAttribute(element, QName("fillColor"), Color::class.java)!!.get(),
- context.getPropertyFromAttribute(element, QName("emptyColor"), Color::class.java)!!.get(),
- )
- }
-
- override fun getChildCount(): ChildCount {
- return ChildCount.NONE
- }
-
- override fun getAttributeNames(): Map<String, Boolean> {
- return mapOf("progress" to true, "total" to true, "emptyColor" to true, "fillColor" to true)
- }
- })
- uni.registerLoader(object : XMLGuiLoader.Basic<FirmHoverComponent> {
- override fun createInstance(context: XMLContext<*>, element: Element): FirmHoverComponent {
- return FirmHoverComponent(
- context.getChildFragment(element),
- context.getPropertyFromAttribute(element, QName("lines"), List::class.java) as Supplier<List<String>>,
- context.getPropertyFromAttribute(element, QName("delay"), Duration::class.java, 0.6.seconds),
- )
- }
-
- override fun getName(): QName {
- return QName(firmUrl, "Hover")
- }
-
- override fun getChildCount(): ChildCount {
- return ChildCount.ONE
- }
-
- override fun getAttributeNames(): Map<String, Boolean> {
- return mapOf(
- "lines" to true,
- "delay" to false,
- )
- }
-
- })
- uni.registerLoader(object : XMLGuiLoader.Basic<FirmButtonComponent> {
- override fun getName(): QName {
- return QName(firmUrl, "Button")
- }
-
- override fun createInstance(context: XMLContext<*>, element: Element): FirmButtonComponent {
- return FirmButtonComponent(
- context.getChildFragment(element),
- context.getPropertyFromAttribute(element, QName("enabled"), Boolean::class.java)
- ?: GetSetter.constant(true),
- context.getPropertyFromAttribute(element, QName("noBackground"), Boolean::class.java, false),
- context.getMethodFromAttribute(element, QName("onClick")),
- )
- }
-
- override fun getChildCount(): ChildCount {
- return ChildCount.ONE
- }
-
- override fun getAttributeNames(): Map<String, Boolean> {
- return mapOf("onClick" to true, "enabled" to false, "noBackground" to false)
- }
- })
- uni.registerLoader(object : XMLGuiLoader.Basic<ImageComponent> {
- override fun createInstance(context: XMLContext<*>, element: Element): ImageComponent {
- return ImageComponent(
- context.getPropertyFromAttribute(element, QName("width"), Int::class.java)!!.get(),
- context.getPropertyFromAttribute(element, QName("height"), Int::class.java)!!.get(),
- context.getPropertyFromAttribute(element, QName("resource"), MyResourceLocation::class.java)!!,
- context.getPropertyFromAttribute(element, QName("u1"), Float::class.java, 0f),
- context.getPropertyFromAttribute(element, QName("u2"), Float::class.java, 1f),
- context.getPropertyFromAttribute(element, QName("v1"), Float::class.java, 0f),
- context.getPropertyFromAttribute(element, QName("v2"), Float::class.java, 1f),
- )
- }
-
- override fun getName(): QName {
- return QName(firmUrl, "Image")
- }
-
- override fun getChildCount(): ChildCount {
- return ChildCount.NONE
- }
-
- override fun getAttributeNames(): Map<String, Boolean> {
- return mapOf(
- "width" to true, "height" to true,
- "resource" to true,
- "u1" to false,
- "u2" to false,
- "v1" to false,
- "v2" to false,
- )
- }
- })
- uni.registerLoader(object : XMLGuiLoader.Basic<TickComponent> {
- override fun createInstance(context: XMLContext<*>, element: Element): TickComponent {
- return TickComponent(context.getMethodFromAttribute(element, QName("tick")))
- }
-
- override fun getName(): QName {
- return QName(firmUrl, "Tick")
- }
-
- override fun getChildCount(): ChildCount {
- return ChildCount.NONE
- }
-
- override fun getAttributeNames(): Map<String, Boolean> {
- return mapOf("tick" to true)
- }
- })
- uni.registerLoader(object : XMLGuiLoader.Basic<FixedComponent> {
- override fun createInstance(context: XMLContext<*>, element: Element): FixedComponent {
- return FixedComponent(
- context.getPropertyFromAttribute(element, QName("width"), Int::class.java)
- ?: error("Requires width specified"),
- context.getPropertyFromAttribute(element, QName("height"), Int::class.java)
- ?: error("Requires height specified"),
- context.getChildFragment(element)
- )
- }
-
- override fun getName(): QName {
- return QName(firmUrl, "Fixed")
- }
-
- override fun getChildCount(): ChildCount {
- return ChildCount.ONE
- }
-
- override fun getAttributeNames(): Map<String, Boolean> {
- return mapOf("width" to true, "height" to true)
- }
- })
- }
-
- fun generateXSD(
- file: File,
- namespace: String
- ) {
- val generator = XSDGenerator(universe, namespace)
- generator.writeAll()
- generator.dumpToFile(file)
- }
-
- @JvmStatic
- fun main(args: Array<out String>) {
- generateXSD(File("MoulConfig.xsd"), XMLUniverse.MOULCONFIG_XML_NS)
- generateXSD(File("MoulConfig.Firmament.xsd"), firmUrl)
- File("wrapper.xsd").writeText("""
+ val firmUrl = "http://firmament.nea.moe/moulconfig"
+ val universe = XMLUniverse.getDefaultUniverse().also { uni ->
+ uni.registerMapper(java.awt.Color::class.java) {
+ if (it.startsWith("#")) {
+ val hexString = it.substring(1)
+ val hex = hexString.toInt(16)
+ if (hexString.length == 6) {
+ return@registerMapper java.awt.Color(hex)
+ }
+ if (hexString.length == 8) {
+ return@registerMapper java.awt.Color(hex, true)
+ }
+ error("Hexcolor $it needs to be exactly 6 or 8 hex digits long")
+ }
+ return@registerMapper java.awt.Color(it.toInt(), true)
+ }
+ uni.registerMapper(Color::class.java) {
+ val color = uni.mapXMLObject(it, java.awt.Color::class.java)
+ Color.ofRGBA(color.red, color.green, color.blue, color.alpha)
+ }
+ uni.registerLoader(object : XMLGuiLoader.Basic<BarComponent> {
+ override fun getName(): QName {
+ return QName(firmUrl, "Bar")
+ }
+
+ override fun createInstance(context: XMLContext<*>, element: Element): BarComponent {
+ return BarComponent(
+ context.getPropertyFromAttribute(element, QName("progress"), Double::class.java)!!,
+ context.getPropertyFromAttribute(element, QName("total"), Double::class.java)!!,
+ context.getPropertyFromAttribute(element, QName("fillColor"), Color::class.java)!!.get(),
+ context.getPropertyFromAttribute(element, QName("emptyColor"), Color::class.java)!!.get(),
+ )
+ }
+
+ override fun getChildCount(): ChildCount {
+ return ChildCount.NONE
+ }
+
+ override fun getAttributeNames(): Map<String, Boolean> {
+ return mapOf("progress" to true, "total" to true, "emptyColor" to true, "fillColor" to true)
+ }
+ })
+ uni.registerLoader(object : XMLGuiLoader.Basic<FirmHoverComponent> {
+ override fun createInstance(context: XMLContext<*>, element: Element): FirmHoverComponent {
+ return FirmHoverComponent(
+ context.getChildFragment(element),
+ context.getPropertyFromAttribute(element,
+ QName("lines"),
+ List::class.java) as Supplier<List<String>>,
+ context.getPropertyFromAttribute(element, QName("delay"), Duration::class.java, 0.6.seconds),
+ )
+ }
+
+ override fun getName(): QName {
+ return QName(firmUrl, "Hover")
+ }
+
+ override fun getChildCount(): ChildCount {
+ return ChildCount.ONE
+ }
+
+ override fun getAttributeNames(): Map<String, Boolean> {
+ return mapOf(
+ "lines" to true,
+ "delay" to false,
+ )
+ }
+
+ })
+ uni.registerLoader(object : XMLGuiLoader.Basic<FirmButtonComponent> {
+ override fun getName(): QName {
+ return QName(firmUrl, "Button")
+ }
+
+ override fun createInstance(context: XMLContext<*>, element: Element): FirmButtonComponent {
+ return FirmButtonComponent(
+ context.getChildFragment(element),
+ context.getPropertyFromAttribute(element, QName("enabled"), Boolean::class.java)
+ ?: GetSetter.constant(true),
+ context.getPropertyFromAttribute(element, QName("noBackground"), Boolean::class.java, false),
+ context.getMethodFromAttribute(element, QName("onClick")),
+ )
+ }
+
+ override fun getChildCount(): ChildCount {
+ return ChildCount.ONE
+ }
+
+ override fun getAttributeNames(): Map<String, Boolean> {
+ return mapOf("onClick" to true, "enabled" to false, "noBackground" to false)
+ }
+ })
+ uni.registerLoader(object : XMLGuiLoader.Basic<ImageComponent> {
+ override fun createInstance(context: XMLContext<*>, element: Element): ImageComponent {
+ return ImageComponent(
+ context.getPropertyFromAttribute(element, QName("width"), Int::class.java)!!.get(),
+ context.getPropertyFromAttribute(element, QName("height"), Int::class.java)!!.get(),
+ context.getPropertyFromAttribute(element, QName("resource"), MyResourceLocation::class.java)!!,
+ context.getPropertyFromAttribute(element, QName("u1"), Float::class.java, 0f),
+ context.getPropertyFromAttribute(element, QName("u2"), Float::class.java, 1f),
+ context.getPropertyFromAttribute(element, QName("v1"), Float::class.java, 0f),
+ context.getPropertyFromAttribute(element, QName("v2"), Float::class.java, 1f),
+ )
+ }
+
+ override fun getName(): QName {
+ return QName(firmUrl, "Image")
+ }
+
+ override fun getChildCount(): ChildCount {
+ return ChildCount.NONE
+ }
+
+ override fun getAttributeNames(): Map<String, Boolean> {
+ return mapOf(
+ "width" to true, "height" to true,
+ "resource" to true,
+ "u1" to false,
+ "u2" to false,
+ "v1" to false,
+ "v2" to false,
+ )
+ }
+ })
+ uni.registerLoader(object : XMLGuiLoader.Basic<TickComponent> {
+ override fun createInstance(context: XMLContext<*>, element: Element): TickComponent {
+ return TickComponent(context.getMethodFromAttribute(element, QName("tick")))
+ }
+
+ override fun getName(): QName {
+ return QName(firmUrl, "Tick")
+ }
+
+ override fun getChildCount(): ChildCount {
+ return ChildCount.NONE
+ }
+
+ override fun getAttributeNames(): Map<String, Boolean> {
+ return mapOf("tick" to true)
+ }
+ })
+ uni.registerLoader(object : XMLGuiLoader.Basic<FixedComponent> {
+ override fun createInstance(context: XMLContext<*>, element: Element): FixedComponent {
+ return FixedComponent(
+ context.getPropertyFromAttribute(element, QName("width"), Int::class.java)
+ ?: error("Requires width specified"),
+ context.getPropertyFromAttribute(element, QName("height"), Int::class.java)
+ ?: error("Requires height specified"),
+ context.getChildFragment(element)
+ )
+ }
+
+ override fun getName(): QName {
+ return QName(firmUrl, "Fixed")
+ }
+
+ override fun getChildCount(): ChildCount {
+ return ChildCount.ONE
+ }
+
+ override fun getAttributeNames(): Map<String, Boolean> {
+ return mapOf("width" to true, "height" to true)
+ }
+ })
+ }
+
+ fun generateXSD(
+ file: File,
+ namespace: String
+ ) {
+ val generator = XSDGenerator(universe, namespace)
+ generator.writeAll()
+ generator.dumpToFile(file)
+ }
+
+ @JvmStatic
+ fun main(args: Array<out String>) {
+ generateXSD(File("MoulConfig.xsd"), XMLUniverse.MOULCONFIG_XML_NS)
+ generateXSD(File("MoulConfig.Firmament.xsd"), firmUrl)
+ File("wrapper.xsd").writeText("""
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://notenoughupdates.org/moulconfig" schemaLocation="MoulConfig.xsd"/>
<xs:import namespace="http://firmament.nea.moe/moulconfig" schemaLocation="MoulConfig.Firmament.xsd"/>
</xs:schema>
""".trimIndent())
- }
-
- fun loadScreen(name: String, bindTo: Any, parent: Screen?): Screen {
- return object : GuiComponentWrapper(loadGui(name, bindTo)) {
- override fun close() {
- if (context.onBeforeClose() == CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE) {
- client!!.setScreen(parent)
- }
- }
- }
- }
-
- fun loadGui(name: String, bindTo: Any): GuiContext {
- return GuiContext(universe.load(bindTo, MyResourceLocation("firmament", "gui/$name.xml")))
- }
+ }
+
+ fun loadScreen(name: String, bindTo: Any, parent: Screen?): Screen {
+ return object : GuiComponentWrapper(loadGui(name, bindTo)) {
+ override fun close() {
+ if (context.onBeforeClose() == CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE) {
+ client!!.setScreen(parent)
+ }
+ }
+ }
+ }
+
+ // TODO: move this utility into moulconfig (also rework guicontext into an interface so i can make this mesh better into vanilla)
+ fun GuiContext.adopt(element: GuiComponent) = element.foldRecursive(Unit, { comp, unit -> comp.context = this })
+
+ fun clickMCComponentInPlace(
+ component: GuiComponent,
+ x: Int,
+ y: Int,
+ w: Int,
+ h: Int,
+ mouseX: Int, mouseY: Int,
+ mouseEvent: MouseEvent
+ ): Boolean {
+ val immContext = createInPlaceFullContext(null, mouseX, mouseY)
+ return component.mouseEvent(mouseEvent, immContext.translated(x, y, w, h))
+ }
+
+ fun createInPlaceFullContext(drawContext: DrawContext?, mouseX: Int, mouseY: Int): GuiImmediateContext {
+ assert(drawContext?.isUntranslatedGuiDrawContext() != false)
+ val context = drawContext?.let(::ModernRenderContext)
+ ?: IMinecraft.instance.provideTopLevelRenderContext()
+ val immContext = GuiImmediateContext(context,
+ 0, 0, 0, 0,
+ mouseX, mouseY,
+ mouseX, mouseY,
+ mouseX.toFloat(),
+ mouseY.toFloat())
+ return immContext
+ }
+
+ fun DrawContext.drawMCComponentInPlace(
+ component: GuiComponent,
+ x: Int,
+ y: Int,
+ w: Int,
+ h: Int,
+ mouseX: Int,
+ mouseY: Int
+ ) {
+ val immContext = createInPlaceFullContext(this, mouseX, mouseY)
+ matrices.push()
+ matrices.translate(x.toFloat(), y.toFloat(), 0F)
+ component.render(immContext.translated(x, y, w, h))
+ matrices.pop()
+ }
+
+
+ fun loadGui(name: String, bindTo: Any): GuiContext {
+ return GuiContext(universe.load(bindTo, MyResourceLocation("firmament", "gui/$name.xml")))
+ }
}
diff --git a/src/main/kotlin/util/assertions.kt b/src/main/kotlin/util/assertions.kt
index 6f2ed19..86982be 100644
--- a/src/main/kotlin/util/assertions.kt
+++ b/src/main/kotlin/util/assertions.kt
@@ -1,11 +1,18 @@
-
+@file:OptIn(ExperimentalContracts::class)
package moe.nea.firmament.util
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
+
/**
* Less aggressive version of `require(obj != null)`, which fails in devenv but continues at runtime.
*/
inline fun <T : Any> assertNotNullOr(obj: T?, message: String? = null, block: () -> T): T {
+ contract {
+ callsInPlace(block, InvocationKind.AT_MOST_ONCE)
+ }
if (message == null)
assert(obj != null)
else
@@ -18,6 +25,9 @@ inline fun <T : Any> assertNotNullOr(obj: T?, message: String? = null, block: ()
* Less aggressive version of `require(condition)`, which fails in devenv but continues at runtime.
*/
inline fun assertTrueOr(condition: Boolean, block: () -> Unit) {
+ contract {
+ callsInPlace(block, InvocationKind.AT_MOST_ONCE)
+ }
assert(condition)
if (!condition) block()
}
diff --git a/src/main/kotlin/util/render/DrawContextExt.kt b/src/main/kotlin/util/render/DrawContextExt.kt
new file mode 100644
index 0000000..48698b4
--- /dev/null
+++ b/src/main/kotlin/util/render/DrawContextExt.kt
@@ -0,0 +1,8 @@
+package moe.nea.firmament.util.render
+
+import org.joml.Matrix4f
+import net.minecraft.client.gui.DrawContext
+
+fun DrawContext.isUntranslatedGuiDrawContext(): Boolean {
+ return (matrices.peek().positionMatrix.properties() and Matrix4f.PROPERTY_IDENTITY.toInt()) != 0
+}
diff --git a/src/main/kotlin/util/uuid.kt b/src/main/kotlin/util/uuid.kt
index 4aa0749..cccfdd2 100644
--- a/src/main/kotlin/util/uuid.kt
+++ b/src/main/kotlin/util/uuid.kt
@@ -1,12 +1,10 @@
-
-
package moe.nea.firmament.util
import java.math.BigInteger
import java.util.UUID
fun parseDashlessUUID(dashlessUuid: String): UUID {
- val most = BigInteger(dashlessUuid.substring(0, 16), 16)
- val least = BigInteger(dashlessUuid.substring(16, 32), 16)
- return UUID(most.toLong(), least.toLong())
+ val most = BigInteger(dashlessUuid.substring(0, 16), 16)
+ val least = BigInteger(dashlessUuid.substring(16, 32), 16)
+ return UUID(most.toLong(), least.toLong())
}
diff --git a/src/main/resources/assets/firmament/textures/gui/sprites/storageoverlay/storage_controls.png b/src/main/resources/assets/firmament/textures/gui/sprites/storageoverlay/storage_controls.png
new file mode 100644
index 0000000..97dd0ea
--- /dev/null
+++ b/src/main/resources/assets/firmament/textures/gui/sprites/storageoverlay/storage_controls.png
Binary files differ
diff --git a/src/main/resources/assets/firmament/textures/gui/sprites/storageoverlay/storage_controls.png.mcmeta b/src/main/resources/assets/firmament/textures/gui/sprites/storageoverlay/storage_controls.png.mcmeta
new file mode 100644
index 0000000..5964a6f
--- /dev/null
+++ b/src/main/resources/assets/firmament/textures/gui/sprites/storageoverlay/storage_controls.png.mcmeta
@@ -0,0 +1,10 @@
+{
+ "gui": {
+ "scaling": {
+ "type": "nine_slice",
+ "width": 91,
+ "height": 184,
+ "border": 7
+ }
+ }
+}