aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features/inventory/storageoverlay/StorageOverlay.kt
blob: 333b0fb00888824eb13e86040037177da9e690b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package moe.nea.firmament.features.inventory.storageoverlay

import java.util.SortedMap
import kotlinx.serialization.serializer
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.Items
import net.minecraft.network.packet.c2s.play.CloseHandledScreenC2SPacket
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ScreenChangeEvent
import moe.nea.firmament.events.SlotClickEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.customgui.customGui
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
            }
        }
    }
}