From 7de0e8e7e09e3428c17ca9717c21c02469c31b76 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Wed, 16 Oct 2024 19:24:24 +0200 Subject: Add edit backpacks button to /firm storage --- .../storageoverlay/StorageBackingHandle.kt | 7 +- .../inventory/storageoverlay/StorageOverlay.kt | 261 ++++++++++----------- .../storageoverlay/StorageOverlayCustom.kt | 1 + .../storageoverlay/StorageOverlayScreen.kt | 75 +++++- 4 files changed, 210 insertions(+), 134 deletions(-) (limited to 'src/main/kotlin/features') 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(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 - ) { - 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 - ) { - // 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(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 + ) { + 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 + ) { + // 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)) } } -- cgit