aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features/inventory/storageoverlay/StorageOverlay.kt
blob: 7de0fff25fba16193c3d08beb178ab03e2675a5d (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
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
		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
			}
		}
	}
}