Skip to content

Commit 707652b

Browse files
committed
feat: add item list
1 parent 6dd14e7 commit 707652b

File tree

12 files changed

+217
-44
lines changed

12 files changed

+217
-44
lines changed

src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockItemIdFocusedStackProvider.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
1313
object SkyblockItemIdFocusedStackProvider : FocusedStackProvider {
1414
override fun provide(screen: Screen?, mouse: Point?): CompoundEventResult<EntryStack<*>> {
1515
if (screen !is HandledScreen<*>) return CompoundEventResult.pass()
16-
screen as AccessorHandledScreen
1716
val focusedSlot = screen.focusedSlot_Firmament ?: return CompoundEventResult.pass()
1817
val item = focusedSlot.stack ?: return CompoundEventResult.pass()
1918
return CompoundEventResult.interruptTrue(SBItemEntryDefinition.getEntry(item))

src/main/kotlin/features/inventory/SlotLocking.kt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import moe.nea.firmament.util.CommonSoundEffects
4242
import moe.nea.firmament.util.MC
4343
import moe.nea.firmament.util.SBData
4444
import moe.nea.firmament.util.SkyBlockIsland
45+
import moe.nea.firmament.util.accessors.castAccessor
4546
import moe.nea.firmament.util.data.Config
4647
import moe.nea.firmament.util.data.ManagedConfig
4748
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
@@ -295,7 +296,7 @@ object SlotLocking {
295296
fun onLockUUID(it: HandledScreenKeyPressedEvent) {
296297
if (!it.matches(TConfig.lockUUID)) return
297298
val inventory = MC.handledScreen ?: return
298-
inventory as AccessorHandledScreen
299+
inventory.castAccessor()
299300

300301
val slot = inventory.focusedSlot_Firmament ?: return
301302
val stack = slot.item ?: return
@@ -330,7 +331,7 @@ object SlotLocking {
330331
@Subscribe
331332
fun onLockSlotKeyRelease(it: HandledScreenKeyReleasedEvent) {
332333
val inventory = MC.handledScreen ?: return
333-
inventory as AccessorHandledScreen
334+
inventory.castAccessor()
334335
val slot = inventory.focusedSlot_Firmament
335336
val storedSlot = storedLockingSlot ?: return
336337

@@ -364,7 +365,7 @@ object SlotLocking {
364365
fun onRenderAllBoundSlots(event: HandledScreenForegroundEvent) {
365366
val boundSlots = currentWorldData?.boundSlots ?: return
366367
fun findByIndex(index: Int) = event.screen.getSlotByIndex(index, true)
367-
val accScreen = event.screen as AccessorHandledScreen
368+
val accScreen = event.screen.castAccessor()
368369
val sx = accScreen.x_Firmament
369370
val sy = accScreen.y_Firmament
370371
val highlitSlots = mutableSetOf<Slot>()
@@ -409,14 +410,20 @@ object SlotLocking {
409410
@Subscribe
410411
fun onRenderCurrentDraggingSlot(event: HandledScreenForegroundEvent) {
411412
val draggingSlot = storedLockingSlot ?: return
412-
val accScreen = event.screen as AccessorHandledScreen
413+
val accScreen = event.screen.castAccessor()
413414
val hoveredSlot = accScreen.focusedSlot_Firmament
414415
?.takeIf { it.container is Inventory }
415416
?.takeIf { it == draggingSlot || it.isHotbar() != draggingSlot.isHotbar() }
416417
val sx = accScreen.x_Firmament
417418
val sy = accScreen.y_Firmament
418419
val (borderX, borderY) = draggingSlot.lineCenter()
419-
event.context.submitOutline(draggingSlot.x + sx, draggingSlot.y + sy, 16, 16, 0xFF00FF00u.toInt()) // TODO: 1.21.10
420+
event.context.submitOutline(
421+
draggingSlot.x + sx,
422+
draggingSlot.y + sy,
423+
16,
424+
16,
425+
0xFF00FF00u.toInt()
426+
) // TODO: 1.21.10
420427
if (hoveredSlot == null) {
421428
event.context.drawLine(
422429
borderX + sx, borderY + sy,
@@ -477,7 +484,7 @@ object SlotLocking {
477484
@Subscribe
478485
fun onLockSlot(it: HandledScreenKeyPressedEvent) {
479486
val inventory = MC.handledScreen ?: return
480-
inventory as AccessorHandledScreen
487+
inventory.castAccessor()
481488

482489
val slot = inventory.focusedSlot_Firmament ?: return
483490
if (slot.container !is Inventory) return

src/main/kotlin/features/inventory/storageoverlay/StorageOverlayCustom.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import net.minecraft.client.input.KeyEvent
1111
import net.minecraft.world.entity.player.Inventory
1212
import net.minecraft.world.inventory.Slot
1313
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
14+
import moe.nea.firmament.util.accessors.castAccessor
1415
import moe.nea.firmament.util.customgui.CustomGui
1516
import moe.nea.firmament.util.focusedItemStack
1617

@@ -42,7 +43,7 @@ class StorageOverlayCustom(
4243
override fun onInit() {
4344
overview.init(Minecraft.getInstance(), screen.width, screen.height)
4445
overview.init()
45-
screen as AccessorHandledScreen
46+
screen.castAccessor()
4647
screen.x_Firmament = overview.measurements.x
4748
screen.y_Firmament = overview.measurements.y
4849
screen.backgroundWidth_Firmament = overview.measurements.totalWidth
@@ -96,7 +97,7 @@ class StorageOverlayCustom(
9697
delta,
9798
(handler as? StorageBackingHandle.Page)?.storagePageSlot,
9899
screen.menu.slots.take(screen.menu.rowCount * 9).drop(9),
99-
Point((screen as AccessorHandledScreen).x_Firmament, screen.y_Firmament)
100+
Point((screen.castAccessor()).x_Firmament, screen.y_Firmament)
100101
)
101102
overview.drawScrollBar(drawContext)
102103
overview.drawControls(drawContext, mouseX, mouseY)
@@ -106,7 +107,7 @@ class StorageOverlayCustom(
106107
val index = slot.containerSlot
107108
if (index in 0..<36) {
108109
val (x, y) = overview.getPlayerInventorySlotPosition(index)
109-
slot.x = x - (screen as AccessorHandledScreen).x_Firmament
110+
slot.x = x - (screen.castAccessor()).x_Firmament
110111
slot.y = y - screen.y_Firmament
111112
} else {
112113
slot.x = -100000
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package moe.nea.firmament.features.items.recipes
2+
3+
import java.util.Optional
4+
import net.minecraft.client.gui.navigation.ScreenRectangle
5+
import net.minecraft.client.gui.screens.Screen
6+
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
7+
import net.minecraft.world.item.ItemStack
8+
import net.minecraft.world.item.Items
9+
import moe.nea.firmament.annotations.Subscribe
10+
import moe.nea.firmament.api.v1.FirmamentAPI
11+
import moe.nea.firmament.events.HandledScreenForegroundEvent
12+
import moe.nea.firmament.events.ReloadRegistrationEvent
13+
import moe.nea.firmament.repo.RepoManager
14+
import moe.nea.firmament.repo.SBItemStack
15+
import moe.nea.firmament.util.MC
16+
import moe.nea.firmament.util.accessors.castAccessor
17+
import moe.nea.firmament.util.skyblockId
18+
19+
object ItemList {
20+
// TODO: add a global toggle for this and RecipeRegistry
21+
22+
fun collectExclusions(screen: Screen): Set<ScreenRectangle> {
23+
val exclusions = mutableSetOf<ScreenRectangle>()
24+
if (screen is AbstractContainerScreen<*>) {
25+
val screenHandler = screen.castAccessor()
26+
exclusions.add(
27+
ScreenRectangle(
28+
screenHandler.x_Firmament,
29+
screenHandler.y_Firmament,
30+
screenHandler.backgroundWidth_Firmament,
31+
screenHandler.backgroundHeight_Firmament
32+
)
33+
)
34+
}
35+
FirmamentAPI.getInstance().extensions
36+
.forEach { extension ->
37+
for (rectangle in extension.getExclusionZones(screen)) {
38+
if (exclusions.any { it.encompasses(rectangle) })
39+
continue
40+
exclusions.add(rectangle)
41+
}
42+
}
43+
44+
return exclusions
45+
}
46+
47+
var reachableItems = listOf<SBItemStack>()
48+
var pageOffset = 0
49+
fun recalculateVisibleItems() {
50+
reachableItems = RepoManager.neuRepo.items
51+
.items.values.map { SBItemStack(it.skyblockId) }
52+
}
53+
54+
@Subscribe
55+
fun onReload(event: ReloadRegistrationEvent) {
56+
event.repo.registerReloadListener { recalculateVisibleItems() }
57+
}
58+
59+
fun coordinates(outer: ScreenRectangle, exclusions: Collection<ScreenRectangle>): Sequence<ScreenRectangle> {
60+
val entryWidth = 18
61+
val columns = outer.width / entryWidth
62+
val rows = outer.height / entryWidth
63+
val lowX = outer.right() - columns * entryWidth
64+
val lowY = outer.top()
65+
return generateSequence(0) { it + 1 }
66+
.map {
67+
val xIndex = it % columns
68+
val yIndex = it / columns
69+
ScreenRectangle(
70+
lowX + xIndex * entryWidth, lowY + yIndex * entryWidth,
71+
entryWidth, entryWidth
72+
)
73+
}
74+
.take(rows * columns)
75+
.filter { candidate -> exclusions.none { it.intersects(candidate) } }
76+
}
77+
78+
var lastRenderPositions: List<Pair<ScreenRectangle, SBItemStack>> = listOf()
79+
var lastHoveredItemStack: Pair<ScreenRectangle, SBItemStack>? = null
80+
81+
fun findStackUnder(mouseX: Int, mouseY: Int): Pair<ScreenRectangle, SBItemStack>? {
82+
val lhis = lastHoveredItemStack
83+
if (lhis != null && lhis.first.containsPoint(mouseX, mouseY))
84+
return lhis
85+
return lastRenderPositions.firstOrNull { it.first.containsPoint(mouseX, mouseY) }
86+
}
87+
88+
@Subscribe
89+
fun onRender(event: HandledScreenForegroundEvent) {
90+
lastHoveredItemStack = null
91+
lastRenderPositions = listOf()
92+
val exclusions = collectExclusions(event.screen)
93+
val potentiallyVisible = reachableItems.subList(pageOffset, reachableItems.size)
94+
val screenWidth = event.screen.width
95+
val rightThird = ScreenRectangle(
96+
screenWidth - screenWidth / 3, 0,
97+
screenWidth / 3, event.screen.height
98+
)
99+
val coords = coordinates(rightThird, exclusions)
100+
101+
lastRenderPositions = coords.zip(potentiallyVisible.asSequence()).toList()
102+
lastRenderPositions.forEach { (pos, stack) ->
103+
val realStack = stack.asLazyImmutableItemStack()
104+
val toRender = realStack ?: ItemStack(Items.PAINTING)
105+
event.context.renderItem(toRender, pos.left() + 1, pos.top() + 1)
106+
if (pos.containsPoint(event.mouseX, event.mouseY)) {
107+
lastHoveredItemStack = pos to stack
108+
event.context.setTooltipForNextFrame(
109+
MC.font,
110+
if (realStack != null)
111+
ItemSlotWidget.getTooltip(realStack)
112+
else
113+
stack.estimateLore(),
114+
Optional.empty(),
115+
event.mouseX, event.mouseY
116+
)
117+
}
118+
}
119+
}
120+
}

src/main/kotlin/features/items/recipes/ItemSlotWidget.kt

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -71,36 +71,37 @@ class ItemSlotWidget(
7171
companion object {
7272
val SHORT_NUM_CUTOFF = 1000
7373
var canUseTooltipEvent = true
74-
}
7574

76-
fun getTooltip(itemStack: ItemStack): List<Component> {
77-
val lore = mutableListOf(itemStack.displayNameAccordingToNbt)
78-
lore.addAll(itemStack.loreAccordingToNbt)
79-
if (canUseTooltipEvent) {
80-
try {
81-
ItemTooltipCallback.EVENT.invoker().getTooltip(
82-
itemStack, Item.TooltipContext.EMPTY,
83-
TooltipFlag.NORMAL, lore
75+
fun getTooltip(itemStack: ItemStack): List<Component> {
76+
val lore = mutableListOf(itemStack.displayNameAccordingToNbt)
77+
lore.addAll(itemStack.loreAccordingToNbt)
78+
if (canUseTooltipEvent) {
79+
try {
80+
ItemTooltipCallback.EVENT.invoker().getTooltip(
81+
itemStack, Item.TooltipContext.EMPTY,
82+
TooltipFlag.NORMAL, lore
83+
)
84+
} catch (ex: Exception) {
85+
canUseTooltipEvent = false
86+
ErrorUtil.softError("Failed to use vanilla tooltips", ex)
87+
}
88+
} else {
89+
ItemTooltipEvent.publish(
90+
ItemTooltipEvent(
91+
itemStack,
92+
Item.TooltipContext.EMPTY,
93+
TooltipFlag.NORMAL,
94+
lore
95+
)
8496
)
85-
} catch (ex: Exception) {
86-
canUseTooltipEvent = false
87-
ErrorUtil.softError("Failed to use vanilla tooltips", ex)
8897
}
89-
} else {
90-
ItemTooltipEvent.publish(
91-
ItemTooltipEvent(
92-
itemStack,
93-
Item.TooltipContext.EMPTY,
94-
TooltipFlag.NORMAL,
95-
lore
96-
)
97-
)
98+
if (itemStack.count >= SHORT_NUM_CUTOFF && lore.isNotEmpty())
99+
lore.add(1, Component.literal("${itemStack.count}x").darkGrey())
100+
return lore
98101
}
99-
if (itemStack.count >= SHORT_NUM_CUTOFF && lore.isNotEmpty())
100-
lore.add(1, Component.literal("${itemStack.count}x").darkGrey())
101-
return lore
102102
}
103103

104+
104105
override fun tick() {
105106
if (SavedKeyBinding.isShiftDown()) return
106107
if (content.size <= 1) return

src/main/kotlin/features/items/recipes/RecipeWidget.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ abstract class RecipeWidget : GuiEventListener, Renderable, NarratableEntry {
2727
this._focused = focused
2828
}
2929

30-
override fun getRectangle(): ScreenRectangle {
31-
return rect.asScreenRectangle()
32-
}
33-
3430
override fun isFocused(): Boolean {
3531
return this._focused
3632
}

src/main/kotlin/features/mining/HotmPresets.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import moe.nea.firmament.util.ClipboardUtils
2424
import moe.nea.firmament.util.MC
2525
import moe.nea.firmament.util.TemplateUtil
2626
import moe.nea.firmament.util.TimeMark
27+
import moe.nea.firmament.util.accessors.castAccessor
2728
import moe.nea.firmament.util.customgui.CustomGui
2829
import moe.nea.firmament.util.customgui.customGui
2930
import moe.nea.firmament.util.mc.CommonTextures
@@ -124,7 +125,7 @@ object HotmPresets {
124125
screen.height / 2 - 100,
125126
300, 200
126127
)
127-
val screen = screen as AccessorHandledScreen
128+
val screen = screen.castAccessor()
128129
screen.x_Firmament = bounds.x
129130
screen.y_Firmament = bounds.y
130131
screen.backgroundWidth_Firmament = bounds.width
@@ -156,7 +157,9 @@ object HotmPresets {
156157
}
157158
}
158159
if (allRows == coveredRows) {
159-
ClipboardUtils.setTextContent(TemplateUtil.encodeTemplate(SHARE_PREFIX, HotmPreset(
160+
ClipboardUtils.setTextContent(
161+
TemplateUtil.encodeTemplate(
162+
SHARE_PREFIX, HotmPreset(
160163
unlockedPerks.map { PerkPreset(it) }
161164
)))
162165
hasAll = true

src/main/kotlin/impl/v1/FirmamentAPIImpl.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package moe.nea.firmament.impl.v1
22

3-
import com.mojang.blaze3d.platform.InputConstants
43
import java.util.Collections
54
import java.util.Optional
65
import net.fabricmc.loader.api.FabricLoader
76
import kotlin.jvm.optionals.getOrNull
7+
import net.minecraft.world.item.ItemStack
88
import moe.nea.firmament.Firmament
99
import moe.nea.firmament.api.v1.FirmamentAPI
1010
import moe.nea.firmament.api.v1.FirmamentExtension
1111
import moe.nea.firmament.api.v1.FirmamentItemWidget
12+
import moe.nea.firmament.features.items.recipes.ItemList
13+
import moe.nea.firmament.repo.ExpensiveItemCacheApi
1214
import moe.nea.firmament.util.MC
1315
import moe.nea.firmament.util.intoOptional
1416

@@ -21,6 +23,7 @@ object FirmamentAPIImpl : FirmamentAPI() {
2123
return Collections.unmodifiableList(_extensions)
2224
}
2325

26+
@OptIn(ExpensiveItemCacheApi::class)
2427
override fun getHoveredItemWidget(): Optional<FirmamentItemWidget> {
2528
val mouse = MC.instance.mouseHandler
2629
val window = MC.window
@@ -30,6 +33,22 @@ object FirmamentAPIImpl : FirmamentAPI() {
3033
?.getChildAt(xpos, ypos)
3134
?.getOrNull()
3235
if (widget is FirmamentItemWidget) return widget.intoOptional()
36+
val itemListStack = ItemList.findStackUnder(xpos.toInt(), ypos.toInt())
37+
if (itemListStack != null)
38+
return object : FirmamentItemWidget {
39+
override fun getPlacement(): FirmamentItemWidget.Placement {
40+
return FirmamentItemWidget.Placement.ITEM_LIST
41+
}
42+
43+
override fun getItemStack(): ItemStack {
44+
return itemListStack.second.asImmutableItemStack()
45+
}
46+
47+
override fun getSkyBlockId(): String {
48+
return itemListStack.second.skyblockId.neuItem
49+
}
50+
51+
}.intoOptional()
3352
return Optional.empty()
3453
}
3554

0 commit comments

Comments
 (0)