Add edit backpacks button to /firm storage
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
object Data : ProfileSpecificDataHolder<StorageData>(serializer(), "storage-data", ::StorageData)
|
||||
|
||||
override val identifier: String
|
||||
get() = "storage-overlay"
|
||||
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 }
|
||||
}
|
||||
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)
|
||||
}
|
||||
fun adjustScrollSpeed(amount: Double): Double {
|
||||
return amount * TConfig.scrollSpeed * (if (TConfig.inverseScroll) 1 else -1)
|
||||
}
|
||||
|
||||
override val config: TConfig
|
||||
get() = TConfig
|
||||
override val config: TConfig
|
||||
get() = TConfig
|
||||
|
||||
var lastStorageOverlay: StorageOverviewScreen? = null
|
||||
var skipNextStorageOverlayBackflip = false
|
||||
var currentHandler: StorageBackingHandle? = null
|
||||
var lastStorageOverlay: StorageOverviewScreen? = null
|
||||
var skipNextStorageOverlayBackflip = false
|
||||
var currentHandler: StorageBackingHandle? = null
|
||||
|
||||
@Subscribe
|
||||
fun onTick(event: TickEvent) {
|
||||
rememberContent(currentHandler ?: return)
|
||||
}
|
||||
@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 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))
|
||||
}
|
||||
@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()
|
||||
}
|
||||
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 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
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user