Make storage overlay scrollbar draggable

This commit is contained in:
Linnea Gräf
2024-10-02 16:44:25 +02:00
parent a4eac70118
commit 67dd2f68d6
4 changed files with 554 additions and 504 deletions

View File

@@ -27,143 +27,161 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(HandledScreen.class) @Mixin(HandledScreen.class)
public class PatchHandledScreen<T extends ScreenHandler> extends Screen implements HasCustomGui { public class PatchHandledScreen<T extends ScreenHandler> extends Screen implements HasCustomGui {
@Shadow @Shadow
@Final @Final
protected T handler; protected T handler;
@Shadow @Shadow
protected int x; protected int x;
@Shadow @Shadow
protected int y; protected int y;
@Unique @Unique
public CustomGui override; public CustomGui override;
@Unique @Unique
public boolean hasRememberedSlots = false; public boolean hasRememberedSlots = false;
protected PatchHandledScreen(Text title) { protected PatchHandledScreen(Text title) {
super(title); super(title);
} }
@Nullable @Nullable
@Override @Override
public CustomGui getCustomGui_Firmament() { public CustomGui getCustomGui_Firmament() {
return override; return override;
} }
@Override @Override
public void setCustomGui_Firmament(@Nullable CustomGui gui) { public void setCustomGui_Firmament(@Nullable CustomGui gui) {
this.override = gui; this.override = gui;
} }
public boolean mouseScrolled_firmament(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { public boolean mouseScrolled_firmament(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) {
return override != null && override.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); return override != null && override.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount);
} }
@Inject(method = "init", at = @At("TAIL")) @Inject(method = "init", at = @At("TAIL"))
private void onInit(CallbackInfo ci) { private void onInit(CallbackInfo ci) {
if (override != null) { if (override != null) {
override.onInit(); override.onInit();
} }
} }
@Inject(method = "drawForeground", at = @At("HEAD"), cancellable = true) @Inject(method = "drawForeground", at = @At("HEAD"), cancellable = true)
private void onDrawForeground(DrawContext context, int mouseX, int mouseY, CallbackInfo ci) { private void onDrawForeground(DrawContext context, int mouseX, int mouseY, CallbackInfo ci) {
if (override != null && !override.shouldDrawForeground()) if (override != null && !override.shouldDrawForeground())
ci.cancel(); ci.cancel();
} }
@Unique @Unique
private Slot didBeforeSlotRender; private Slot didBeforeSlotRender;
@WrapOperation( @WrapOperation(
method = "render", method = "render",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/util/collection/DefaultedList;get(I)Ljava/lang/Object;")) target = "Lnet/minecraft/util/collection/DefaultedList;get(I)Ljava/lang/Object;"))
private Object beforeSlotRender(DefaultedList instance, int index, Operation<Object> original, @Local(argsOnly = true) DrawContext context) { private Object beforeSlotRender(DefaultedList instance, int index, Operation<Object> original, @Local(argsOnly = true) DrawContext context) {
var slot = (Slot) original.call(instance, index); var slot = (Slot) original.call(instance, index);
if (override != null) { if (override != null) {
didBeforeSlotRender = slot; didBeforeSlotRender = slot;
override.beforeSlotRender(context, slot); override.beforeSlotRender(context, slot);
} }
return slot; return slot;
} }
@Inject(method = "render", @Inject(method = "render",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;size()I")) at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;size()I"))
private void afterSlotRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { private void afterSlotRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (override != null && didBeforeSlotRender != null) { if (override != null && didBeforeSlotRender != null) {
override.afterSlotRender(context, didBeforeSlotRender); override.afterSlotRender(context, didBeforeSlotRender);
didBeforeSlotRender = null; didBeforeSlotRender = null;
} }
} }
@Inject(method = "isClickOutsideBounds", at = @At("HEAD"), cancellable = true) @Inject(method = "isClickOutsideBounds", at = @At("HEAD"), cancellable = true)
public void onIsClickOutsideBounds(double mouseX, double mouseY, int left, int top, int button, CallbackInfoReturnable<Boolean> cir) { public void onIsClickOutsideBounds(double mouseX, double mouseY, int left, int top, int button, CallbackInfoReturnable<Boolean> cir) {
if (override != null) { if (override != null) {
cir.setReturnValue(override.isClickOutsideBounds(mouseX, mouseY)); cir.setReturnValue(override.isClickOutsideBounds(mouseX, mouseY));
} }
} }
@Inject(method = "isPointWithinBounds", at = @At("HEAD"), cancellable = true) @Inject(method = "isPointWithinBounds", at = @At("HEAD"), cancellable = true)
public void onIsPointWithinBounds(int x, int y, int width, int height, double pointX, double pointY, CallbackInfoReturnable<Boolean> cir) { public void onIsPointWithinBounds(int x, int y, int width, int height, double pointX, double pointY, CallbackInfoReturnable<Boolean> cir) {
if (override != null) { if (override != null) {
cir.setReturnValue(override.isPointWithinBounds(x + this.x, y + this.y, width, height, pointX, pointY)); cir.setReturnValue(override.isPointWithinBounds(x + this.x, y + this.y, width, height, pointX, pointY));
} }
} }
@Inject(method = "isPointOverSlot", at = @At("HEAD"), cancellable = true) @Inject(method = "isPointOverSlot", at = @At("HEAD"), cancellable = true)
public void onIsPointOverSlot(Slot slot, double pointX, double pointY, CallbackInfoReturnable<Boolean> cir) { public void onIsPointOverSlot(Slot slot, double pointX, double pointY, CallbackInfoReturnable<Boolean> cir) {
if (override != null) { if (override != null) {
cir.setReturnValue(override.isPointOverSlot(slot, this.x, this.y, pointX, pointY)); cir.setReturnValue(override.isPointOverSlot(slot, this.x, this.y, pointX, pointY));
} }
} }
@Inject(method = "render", at = @At("HEAD")) @Inject(method = "render", at = @At("HEAD"))
public void moveSlots(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { public void moveSlots(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (override != null) { if (override != null) {
for (Slot slot : handler.slots) { for (Slot slot : handler.slots) {
if (!hasRememberedSlots) { if (!hasRememberedSlots) {
((CoordRememberingSlot) slot).rememberCoords_firmament(); ((CoordRememberingSlot) slot).rememberCoords_firmament();
} }
override.moveSlot(slot); override.moveSlot(slot);
} }
hasRememberedSlots = true; hasRememberedSlots = true;
} else { } else {
if (hasRememberedSlots) { if (hasRememberedSlots) {
for (Slot slot : handler.slots) { for (Slot slot : handler.slots) {
((CoordRememberingSlot) slot).restoreCoords_firmament(); ((CoordRememberingSlot) slot).restoreCoords_firmament();
} }
hasRememberedSlots = false; hasRememberedSlots = false;
} }
} }
} }
@Inject(at = @At("HEAD"), method = "close", cancellable = true) @Inject(at = @At("HEAD"), method = "close", cancellable = true)
private void onVoluntaryExit(CallbackInfo ci) { private void onVoluntaryExit(CallbackInfo ci) {
if (override != null) { if (override != null) {
if (!override.onVoluntaryExit()) if (!override.onVoluntaryExit())
ci.cancel(); ci.cancel();
} }
} }
@WrapWithCondition(method = "renderBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawBackground(Lnet/minecraft/client/gui/DrawContext;FII)V")) @WrapWithCondition(method = "renderBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawBackground(Lnet/minecraft/client/gui/DrawContext;FII)V"))
public boolean preventDrawingBackground(HandledScreen instance, DrawContext drawContext, float delta, int mouseX, int mouseY) { public boolean preventDrawingBackground(HandledScreen instance, DrawContext drawContext, float delta, int mouseX, int mouseY) {
if (override != null) { if (override != null) {
override.render(drawContext, delta, mouseX, mouseY); override.render(drawContext, delta, mouseX, mouseY);
} }
return override == null; return override == null;
} }
@WrapOperation( @WrapOperation(
method = "mouseClicked", method = "mouseClicked",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z")) at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseClicked(DDI)Z"))
public boolean overrideMouseClicks(HandledScreen instance, double mouseX, double mouseY, int button, public boolean overrideMouseClicks(HandledScreen instance, double mouseX, double mouseY, int button,
Operation<Boolean> original) { Operation<Boolean> original) {
if (override != null) { if (override != null) {
if (override.mouseClick(mouseX, mouseY, button)) if (override.mouseClick(mouseX, mouseY, button))
return true; return true;
} }
return original.call(instance, mouseX, mouseY, button); return original.call(instance, mouseX, mouseY, button);
} }
@Inject(method = "mouseDragged", at = @At("HEAD"), cancellable = true)
public void overrideMouseDrags(double mouseX, double mouseY, int button, double deltaX, double deltaY, CallbackInfoReturnable<Boolean> cir) {
if (override != null) {
if (override.mouseDragged(mouseX, mouseY, button, deltaX, deltaY))
cir.setReturnValue(true);
}
}
@Inject(
method = "mouseReleased",
at = @At("HEAD"), cancellable = true)
public void overrideMouseReleases(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
if (override != null) {
if (override.mouseReleased(mouseX, mouseY, button))
cir.setReturnValue(true);
}
}
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.features.inventory.storageoverlay package moe.nea.firmament.features.inventory.storageoverlay
import me.shedaniel.math.Point import me.shedaniel.math.Point
@@ -12,87 +11,95 @@ import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
import moe.nea.firmament.util.customgui.CustomGui import moe.nea.firmament.util.customgui.CustomGui
class StorageOverlayCustom( class StorageOverlayCustom(
val handler: StorageBackingHandle, val handler: StorageBackingHandle,
val screen: GenericContainerScreen, val screen: GenericContainerScreen,
val overview: StorageOverlayScreen, val overview: StorageOverlayScreen,
) : CustomGui() { ) : CustomGui() {
override fun onVoluntaryExit(): Boolean { override fun onVoluntaryExit(): Boolean {
overview.isExiting = true overview.isExiting = true
return super.onVoluntaryExit() return super.onVoluntaryExit()
} }
override fun getBounds(): List<Rectangle> { override fun getBounds(): List<Rectangle> {
return overview.getBounds() return overview.getBounds()
} }
override fun afterSlotRender(context: DrawContext, slot: Slot) { override fun afterSlotRender(context: DrawContext, slot: Slot) {
if (slot.inventory !is PlayerInventory) if (slot.inventory !is PlayerInventory)
context.disableScissor() context.disableScissor()
} }
override fun beforeSlotRender(context: DrawContext, slot: Slot) { override fun beforeSlotRender(context: DrawContext, slot: Slot) {
if (slot.inventory !is PlayerInventory) if (slot.inventory !is PlayerInventory)
overview.createScissors(context) overview.createScissors(context)
} }
override fun onInit() { override fun onInit() {
overview.init(MinecraftClient.getInstance(), screen.width, screen.height) overview.init(MinecraftClient.getInstance(), screen.width, screen.height)
overview.init() overview.init()
screen as AccessorHandledScreen screen as AccessorHandledScreen
screen.x_Firmament = overview.measurements.x screen.x_Firmament = overview.measurements.x
screen.y_Firmament = overview.measurements.y screen.y_Firmament = overview.measurements.y
screen.backgroundWidth_Firmament = overview.measurements.totalWidth screen.backgroundWidth_Firmament = overview.measurements.totalWidth
screen.backgroundHeight_Firmament = overview.measurements.totalHeight screen.backgroundHeight_Firmament = overview.measurements.totalHeight
} }
override fun isPointOverSlot(slot: Slot, xOffset: Int, yOffset: Int, pointX: Double, pointY: Double): Boolean { override fun isPointOverSlot(slot: Slot, xOffset: Int, yOffset: Int, pointX: Double, pointY: Double): Boolean {
if (!super.isPointOverSlot(slot, xOffset, yOffset, pointX, pointY)) if (!super.isPointOverSlot(slot, xOffset, yOffset, pointX, pointY))
return false return false
if (slot.inventory !is PlayerInventory) { if (slot.inventory !is PlayerInventory) {
if (!overview.getScrollPanelInner().contains(pointX, pointY)) if (!overview.getScrollPanelInner().contains(pointX, pointY))
return false return false
} }
return true return true
} }
override fun shouldDrawForeground(): Boolean { override fun shouldDrawForeground(): Boolean {
return false return false
} }
override fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean { override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
return overview.mouseClicked(mouseX, mouseY, button, (handler as? StorageBackingHandle.Page)?.storagePageSlot) return overview.mouseReleased(mouseX, mouseY, button)
} }
override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) { override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
overview.drawBackgrounds(drawContext) return overview.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
overview.drawPages(drawContext, }
mouseX,
mouseY,
delta,
(handler as? StorageBackingHandle.Page)?.storagePageSlot,
screen.screenHandler.slots.take(screen.screenHandler.rows * 9).drop(9),
Point((screen as AccessorHandledScreen).x_Firmament, screen.y_Firmament))
overview.drawScrollBar(drawContext)
}
override fun moveSlot(slot: Slot) { override fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean {
val index = slot.index return overview.mouseClicked(mouseX, mouseY, button, (handler as? StorageBackingHandle.Page)?.storagePageSlot)
if (index in 0..<36) { }
val (x, y) = overview.getPlayerInventorySlotPosition(index)
slot.x = x - (screen as AccessorHandledScreen).x_Firmament
slot.y = y - screen.y_Firmament
} else {
slot.x = -100000
slot.y = -100000
}
}
override fun mouseScrolled( override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
mouseX: Double, overview.drawBackgrounds(drawContext)
mouseY: Double, overview.drawPages(drawContext,
horizontalAmount: Double, mouseX,
verticalAmount: Double mouseY,
): Boolean { delta,
return overview.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount) (handler as? StorageBackingHandle.Page)?.storagePageSlot,
} screen.screenHandler.slots.take(screen.screenHandler.rows * 9).drop(9),
Point((screen as AccessorHandledScreen).x_Firmament, screen.y_Firmament))
overview.drawScrollBar(drawContext)
}
override fun moveSlot(slot: Slot) {
val index = slot.index
if (index in 0..<36) {
val (x, y) = overview.getPlayerInventorySlotPosition(index)
slot.x = x - (screen as AccessorHandledScreen).x_Firmament
slot.y = y - screen.y_Firmament
} else {
slot.x = -100000
slot.y = -100000
}
}
override fun mouseScrolled(
mouseX: Double,
mouseY: Double,
horizontalAmount: Double,
verticalAmount: Double
): Boolean {
return overview.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
}
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.features.inventory.storageoverlay package moe.nea.firmament.features.inventory.storageoverlay
import me.shedaniel.math.Point import me.shedaniel.math.Point
@@ -8,289 +7,308 @@ import net.minecraft.client.gui.screen.Screen
import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.Slot
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.CommandEvent
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.ScreenUtil
import moe.nea.firmament.util.assertTrueOr import moe.nea.firmament.util.assertTrueOr
class StorageOverlayScreen : Screen(Text.literal("")) { class StorageOverlayScreen : Screen(Text.literal("")) {
companion object { companion object {
val PLAYER_WIDTH = 184 val PLAYER_WIDTH = 184
val PLAYER_HEIGHT = 91 val PLAYER_HEIGHT = 91
val PLAYER_Y_INSET = 3 val PLAYER_Y_INSET = 3
val SLOT_SIZE = 18 val SLOT_SIZE = 18
val PADDING = 10 val PADDING = 10
val PAGE_WIDTH = SLOT_SIZE * 9 val PAGE_WIDTH = SLOT_SIZE * 9
val HOTBAR_X = 12 val HOTBAR_X = 12
val HOTBAR_Y = 67 val HOTBAR_Y = 67
val MAIN_INVENTORY_Y = 9 val MAIN_INVENTORY_Y = 9
val SCROLL_BAR_WIDTH = 8 val SCROLL_BAR_WIDTH = 8
val SCROLL_BAR_HEIGHT = 16 val SCROLL_BAR_HEIGHT = 16
} }
var isExiting: Boolean = false var isExiting: Boolean = false
var scroll: Float = 0F var scroll: Float = 0F
var pageWidthCount = StorageOverlay.TConfig.columns var pageWidthCount = StorageOverlay.TConfig.columns
inner class Measurements { inner class Measurements {
val innerScrollPanelWidth = PAGE_WIDTH * pageWidthCount + (pageWidthCount - 1) * PADDING val innerScrollPanelWidth = PAGE_WIDTH * pageWidthCount + (pageWidthCount - 1) * PADDING
val overviewWidth = innerScrollPanelWidth + 3 * PADDING + SCROLL_BAR_WIDTH val overviewWidth = innerScrollPanelWidth + 3 * PADDING + SCROLL_BAR_WIDTH
val x = width / 2 - overviewWidth / 2 val x = width / 2 - overviewWidth / 2
val overviewHeight = minOf(3 * 18 * 6, height - PLAYER_HEIGHT - minOf(80, height / 10)) val overviewHeight = minOf(3 * 18 * 6, height - PLAYER_HEIGHT - minOf(80, height / 10))
val innerScrollPanelHeight = overviewHeight - PADDING * 2 val innerScrollPanelHeight = overviewHeight - PADDING * 2
val y = height / 2 - (overviewHeight + PLAYER_HEIGHT) / 2 val y = height / 2 - (overviewHeight + PLAYER_HEIGHT) / 2
val playerX = width / 2 - PLAYER_WIDTH / 2 val playerX = width / 2 - PLAYER_WIDTH / 2
val playerY = y + overviewHeight - PLAYER_Y_INSET val playerY = y + overviewHeight - PLAYER_Y_INSET
val totalWidth = overviewWidth val totalWidth = overviewWidth
val totalHeight = overviewHeight - PLAYER_Y_INSET + PLAYER_HEIGHT val totalHeight = overviewHeight - PLAYER_Y_INSET + PLAYER_HEIGHT
} }
var measurements = Measurements() var measurements = Measurements()
var lastRenderedInnerHeight = 0 var lastRenderedInnerHeight = 0
public override fun init() { public override fun init() {
super.init() super.init()
pageWidthCount = StorageOverlay.TConfig.columns pageWidthCount = StorageOverlay.TConfig.columns
.coerceAtMost((width - PADDING) / (PAGE_WIDTH + PADDING)) .coerceAtMost((width - PADDING) / (PAGE_WIDTH + PADDING))
.coerceAtLeast(1) .coerceAtLeast(1)
measurements = Measurements() measurements = Measurements()
} scroll = scroll.coerceAtMost(getMaxScroll()).coerceAtLeast(0F)
}
override fun mouseScrolled( override fun mouseScrolled(
mouseX: Double, mouseX: Double,
mouseY: Double, mouseY: Double,
horizontalAmount: Double, horizontalAmount: Double,
verticalAmount: Double verticalAmount: Double
): Boolean { ): Boolean {
scroll = (scroll + StorageOverlay.adjustScrollSpeed(verticalAmount)).toFloat() scroll = (scroll + StorageOverlay.adjustScrollSpeed(verticalAmount)).toFloat()
.coerceAtMost(getMaxScroll()) .coerceAtMost(getMaxScroll())
.coerceAtLeast(0F) .coerceAtLeast(0F)
return true return true
} }
fun getMaxScroll() = lastRenderedInnerHeight.toFloat() - getScrollPanelInner().height fun getMaxScroll() = lastRenderedInnerHeight.toFloat() - getScrollPanelInner().height
val playerInventorySprite = Identifier.of("firmament:storageoverlay/player_inventory") val playerInventorySprite = Identifier.of("firmament:storageoverlay/player_inventory")
val upperBackgroundSprite = Identifier.of("firmament:storageoverlay/upper_background") val upperBackgroundSprite = Identifier.of("firmament:storageoverlay/upper_background")
val slotRowSprite = Identifier.of("firmament:storageoverlay/storage_row") val slotRowSprite = Identifier.of("firmament:storageoverlay/storage_row")
val scrollbarBackground = Identifier.of("firmament:storageoverlay/scroll_bar_background") val scrollbarBackground = Identifier.of("firmament:storageoverlay/scroll_bar_background")
val scrollbarKnob = Identifier.of("firmament:storageoverlay/scroll_bar_knob") val scrollbarKnob = Identifier.of("firmament:storageoverlay/scroll_bar_knob")
override fun close() { override fun close() {
isExiting = true isExiting = true
super.close() super.close()
} }
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
super.render(context, mouseX, mouseY, delta) super.render(context, mouseX, mouseY, delta)
drawBackgrounds(context) drawBackgrounds(context)
drawPages(context, mouseX, mouseY, delta, null, null, Point()) drawPages(context, mouseX, mouseY, delta, null, null, Point())
drawScrollBar(context) drawScrollBar(context)
drawPlayerInventory(context, mouseX, mouseY, delta) drawPlayerInventory(context, mouseX, mouseY, delta)
} }
fun getScrollbarPercentage(): Float { fun getScrollbarPercentage(): Float {
return scroll / getMaxScroll() return scroll / getMaxScroll()
} }
fun drawScrollBar(context: DrawContext) { fun drawScrollBar(context: DrawContext) {
val sbRect = getScrollBarRect() val sbRect = getScrollBarRect()
context.drawGuiTexture( context.drawGuiTexture(
scrollbarBackground, scrollbarBackground,
sbRect.minX, sbRect.minY, sbRect.minX, sbRect.minY,
sbRect.width, sbRect.height, sbRect.width, sbRect.height,
) )
context.drawGuiTexture( context.drawGuiTexture(
scrollbarKnob, scrollbarKnob,
sbRect.minX, sbRect.minY + (getScrollbarPercentage() * (sbRect.height - SCROLL_BAR_HEIGHT)).toInt(), sbRect.minX, sbRect.minY + (getScrollbarPercentage() * (sbRect.height - SCROLL_BAR_HEIGHT)).toInt(),
SCROLL_BAR_WIDTH, SCROLL_BAR_HEIGHT SCROLL_BAR_WIDTH, SCROLL_BAR_HEIGHT
) )
} }
fun drawBackgrounds(context: DrawContext) { fun drawBackgrounds(context: DrawContext) {
context.drawGuiTexture(upperBackgroundSprite, context.drawGuiTexture(upperBackgroundSprite,
measurements.x, measurements.x,
measurements.y, measurements.y,
0, 0,
measurements.overviewWidth, measurements.overviewWidth,
measurements.overviewHeight) measurements.overviewHeight)
context.drawGuiTexture(playerInventorySprite, context.drawGuiTexture(playerInventorySprite,
measurements.playerX, measurements.playerX,
measurements.playerY, measurements.playerY,
0, 0,
PLAYER_WIDTH, PLAYER_WIDTH,
PLAYER_HEIGHT) PLAYER_HEIGHT)
} }
fun getPlayerInventorySlotPosition(int: Int): Pair<Int, Int> { fun getPlayerInventorySlotPosition(int: Int): Pair<Int, Int> {
if (int < 9) { if (int < 9) {
return Pair(measurements.playerX + int * SLOT_SIZE + HOTBAR_X, HOTBAR_Y + measurements.playerY) return Pair(measurements.playerX + int * SLOT_SIZE + HOTBAR_X, HOTBAR_Y + measurements.playerY)
} }
return Pair( return Pair(
measurements.playerX + (int % 9) * SLOT_SIZE + HOTBAR_X, measurements.playerX + (int % 9) * SLOT_SIZE + HOTBAR_X,
measurements.playerY + (int / 9 - 1) * SLOT_SIZE + MAIN_INVENTORY_Y measurements.playerY + (int / 9 - 1) * SLOT_SIZE + MAIN_INVENTORY_Y
) )
} }
fun drawPlayerInventory(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { fun drawPlayerInventory(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
val items = MC.player?.inventory?.main ?: return val items = MC.player?.inventory?.main ?: return
items.withIndex().forEach { (index, item) -> items.withIndex().forEach { (index, item) ->
val (x, y) = getPlayerInventorySlotPosition(index) val (x, y) = getPlayerInventorySlotPosition(index)
context.drawItem(item, x, y, 0) context.drawItem(item, x, y, 0)
context.drawItemInSlot(textRenderer, item, x, y) context.drawItemInSlot(textRenderer, item, x, y)
} }
} }
fun getScrollBarRect(): Rectangle { fun getScrollBarRect(): Rectangle {
return Rectangle(measurements.x + PADDING + measurements.innerScrollPanelWidth + PADDING, return Rectangle(measurements.x + PADDING + measurements.innerScrollPanelWidth + PADDING,
measurements.y + PADDING, measurements.y + PADDING,
SCROLL_BAR_WIDTH, SCROLL_BAR_WIDTH,
measurements.innerScrollPanelHeight) measurements.innerScrollPanelHeight)
} }
fun getScrollPanelInner(): Rectangle { fun getScrollPanelInner(): Rectangle {
return Rectangle(measurements.x + PADDING, return Rectangle(measurements.x + PADDING,
measurements.y + PADDING, measurements.y + PADDING,
measurements.innerScrollPanelWidth, measurements.innerScrollPanelWidth,
measurements.innerScrollPanelHeight) measurements.innerScrollPanelHeight)
} }
fun createScissors(context: DrawContext) { fun createScissors(context: DrawContext) {
val rect = getScrollPanelInner() val rect = getScrollPanelInner()
context.enableScissor( context.enableScissor(
rect.minX, rect.minY, rect.minX, rect.minY,
rect.maxX, rect.maxY rect.maxX, rect.maxY
) )
} }
fun drawPages( fun drawPages(
context: DrawContext, mouseX: Int, mouseY: Int, delta: Float, context: DrawContext, mouseX: Int, mouseY: Int, delta: Float,
excluding: StoragePageSlot?, excluding: StoragePageSlot?,
slots: List<Slot>?, slots: List<Slot>?,
slotOffset: Point slotOffset: Point
) { ) {
createScissors(context) createScissors(context)
val data = StorageOverlay.Data.data ?: StorageData() val data = StorageOverlay.Data.data ?: StorageData()
layoutedForEach(data) { rect, page, inventory -> layoutedForEach(data) { rect, page, inventory ->
drawPage(context, drawPage(context,
rect.x, rect.x,
rect.y, rect.y,
page, inventory, page, inventory,
if (excluding == page) slots else null, if (excluding == page) slots else null,
slotOffset slotOffset
) )
} }
context.disableScissor() context.disableScissor()
} }
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { var knobGrabbed = false
return mouseClicked(mouseX, mouseY, button, null)
}
fun mouseClicked(mouseX: Double, mouseY: Double, button: Int, activePage: StoragePageSlot?): Boolean { override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (getScrollPanelInner().contains(mouseX, mouseY)) { return mouseClicked(mouseX, mouseY, button, null)
val data = StorageOverlay.Data.data ?: StorageData() }
layoutedForEach(data) { rect, page, _ ->
if (rect.contains(mouseX, mouseY) && activePage != page && button == 0) {
page.navigateTo()
return true
}
}
return false
}
val sbRect = getScrollBarRect()
if (sbRect.contains(mouseX, mouseY)) {
// TODO: support dragging of the mouse and such
val percentage = (mouseY - sbRect.getY()) / sbRect.getHeight()
scroll = (getMaxScroll() * percentage).toFloat()
mouseScrolled(0.0, 0.0, 0.0, 0.0)
return true
}
return false
}
private inline fun layoutedForEach( override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
data: StorageData, if (knobGrabbed) {
func: ( knobGrabbed = false
rectangle: Rectangle, return true
page: StoragePageSlot, inventory: StorageData.StorageInventory, }
) -> Unit return super.mouseReleased(mouseX, mouseY, button)
) { }
var yOffset = -scroll.toInt()
var xOffset = 0
var maxHeight = 0
for ((page, inventory) in data.storageInventories.entries) {
val currentHeight = inventory.inventory?.let { it.rows * SLOT_SIZE + 4 + textRenderer.fontHeight }
?: 18
maxHeight = maxOf(maxHeight, currentHeight)
val rect = Rectangle(
measurements.x + PADDING + (PAGE_WIDTH + PADDING) * xOffset,
yOffset + measurements.y + PADDING,
PAGE_WIDTH,
currentHeight
)
func(rect, page, inventory)
xOffset++
if (xOffset >= pageWidthCount) {
yOffset += maxHeight
xOffset = 0
maxHeight = 0
}
}
lastRenderedInnerHeight = maxHeight + yOffset + scroll.toInt()
}
fun drawPage( override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
context: DrawContext, if (knobGrabbed) {
x: Int, val sbRect = getScrollBarRect()
y: Int, val percentage = (mouseY - sbRect.getY()) / sbRect.getHeight()
page: StoragePageSlot, scroll = (getMaxScroll() * percentage).toFloat()
inventory: StorageData.StorageInventory, mouseScrolled(0.0, 0.0, 0.0, 0.0)
slots: List<Slot>?, return true
slotOffset: Point, }
): Int { return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
val inv = inventory.inventory }
if (inv == null) {
context.drawGuiTexture(upperBackgroundSprite, x, y, PAGE_WIDTH, 18)
context.drawText(textRenderer,
Text.literal("TODO: open this page"),
x + 4,
y + 4,
-1,
true)
return 18
}
assertTrueOr(slots == null || slots.size == inv.stacks.size) { return 0 }
val name = page.defaultName()
context.drawText(textRenderer, Text.literal(name), x + 4, y + 2,
if (slots == null) 0xFFFFFFFF.toInt() else 0xFFFFFF00.toInt(), true)
context.drawGuiTexture(slotRowSprite, x, y + 4 + textRenderer.fontHeight, PAGE_WIDTH, inv.rows * SLOT_SIZE)
inv.stacks.forEachIndexed { index, stack ->
val slotX = (index % 9) * SLOT_SIZE + x + 1
val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1
if (slots == null) {
context.drawItem(stack, slotX, slotY)
context.drawItemInSlot(textRenderer, stack, slotX, slotY)
} else {
val slot = slots[index]
slot.x = slotX - slotOffset.x
slot.y = slotY - slotOffset.y
}
}
return inv.rows * SLOT_SIZE + 4 + textRenderer.fontHeight
}
fun getBounds(): List<Rectangle> { fun mouseClicked(mouseX: Double, mouseY: Double, button: Int, activePage: StoragePageSlot?): Boolean {
return listOf( if (getScrollPanelInner().contains(mouseX, mouseY)) {
Rectangle(measurements.x, val data = StorageOverlay.Data.data ?: StorageData()
measurements.y, layoutedForEach(data) { rect, page, _ ->
measurements.overviewWidth, if (rect.contains(mouseX, mouseY) && activePage != page && button == 0) {
measurements.overviewHeight), page.navigateTo()
Rectangle(measurements.playerX, return true
measurements.playerY, }
PLAYER_WIDTH, }
PLAYER_HEIGHT)) return false
} }
val sbRect = getScrollBarRect()
if (sbRect.contains(mouseX, mouseY)) {
val percentage = (mouseY - sbRect.getY()) / sbRect.getHeight()
scroll = (getMaxScroll() * percentage).toFloat()
mouseScrolled(0.0, 0.0, 0.0, 0.0)
knobGrabbed = true
return true
}
return false
}
private inline fun layoutedForEach(
data: StorageData,
func: (
rectangle: Rectangle,
page: StoragePageSlot, inventory: StorageData.StorageInventory,
) -> Unit
) {
var yOffset = -scroll.toInt()
var xOffset = 0
var maxHeight = 0
for ((page, inventory) in data.storageInventories.entries) {
val currentHeight = inventory.inventory?.let { it.rows * SLOT_SIZE + 4 + textRenderer.fontHeight }
?: 18
maxHeight = maxOf(maxHeight, currentHeight)
val rect = Rectangle(
measurements.x + PADDING + (PAGE_WIDTH + PADDING) * xOffset,
yOffset + measurements.y + PADDING,
PAGE_WIDTH,
currentHeight
)
func(rect, page, inventory)
xOffset++
if (xOffset >= pageWidthCount) {
yOffset += maxHeight
xOffset = 0
maxHeight = 0
}
}
lastRenderedInnerHeight = maxHeight + yOffset + scroll.toInt()
}
fun drawPage(
context: DrawContext,
x: Int,
y: Int,
page: StoragePageSlot,
inventory: StorageData.StorageInventory,
slots: List<Slot>?,
slotOffset: Point,
): Int {
val inv = inventory.inventory
if (inv == null) {
context.drawGuiTexture(upperBackgroundSprite, x, y, PAGE_WIDTH, 18)
context.drawText(textRenderer,
Text.literal("TODO: open this page"),
x + 4,
y + 4,
-1,
true)
return 18
}
assertTrueOr(slots == null || slots.size == inv.stacks.size) { return 0 }
val name = page.defaultName()
context.drawText(textRenderer, Text.literal(name), x + 4, y + 2,
if (slots == null) 0xFFFFFFFF.toInt() else 0xFFFFFF00.toInt(), true)
context.drawGuiTexture(slotRowSprite, x, y + 4 + textRenderer.fontHeight, PAGE_WIDTH, inv.rows * SLOT_SIZE)
inv.stacks.forEachIndexed { index, stack ->
val slotX = (index % 9) * SLOT_SIZE + x + 1
val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1
if (slots == null) {
context.drawItem(stack, slotX, slotY)
context.drawItemInSlot(textRenderer, stack, slotX, slotY)
} else {
val slot = slots[index]
slot.x = slotX - slotOffset.x
slot.y = slotY - slotOffset.y
}
}
return inv.rows * SLOT_SIZE + 4 + textRenderer.fontHeight
}
fun getBounds(): List<Rectangle> {
return listOf(
Rectangle(measurements.x,
measurements.y,
measurements.overviewWidth,
measurements.overviewHeight),
Rectangle(measurements.playerX,
measurements.playerY,
PLAYER_WIDTH,
PLAYER_HEIGHT))
}
} }

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.util.customgui package moe.nea.firmament.util.customgui
import me.shedaniel.math.Rectangle import me.shedaniel.math.Rectangle
@@ -9,64 +8,72 @@ import moe.nea.firmament.events.HandledScreenPushREIEvent
abstract class CustomGui { abstract class CustomGui {
abstract fun getBounds(): List<Rectangle> abstract fun getBounds(): List<Rectangle>
open fun moveSlot(slot: Slot) { open fun moveSlot(slot: Slot) {
// TODO: return a Pair maybe? worth an investigation // TODO: return a Pair maybe? worth an investigation
} }
companion object { companion object {
@Subscribe @Subscribe
fun onExclusionZone(event: HandledScreenPushREIEvent) { fun onExclusionZone(event: HandledScreenPushREIEvent) {
val customGui = event.screen.customGui ?: return val customGui = event.screen.customGui ?: return
event.rectangles.addAll(customGui.getBounds()) event.rectangles.addAll(customGui.getBounds())
} }
} }
open fun render( open fun render(
drawContext: DrawContext, drawContext: DrawContext,
delta: Float, delta: Float,
mouseX: Int, mouseX: Int,
mouseY: Int mouseY: Int
) { ) {
} }
open fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean { open fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean {
return false return false
} }
open fun afterSlotRender(context: DrawContext, slot: Slot) {} open fun afterSlotRender(context: DrawContext, slot: Slot) {}
open fun beforeSlotRender(context: DrawContext, slot: Slot) {} open fun beforeSlotRender(context: DrawContext, slot: Slot) {}
open fun mouseScrolled(mouseX: Double, mouseY: Double, horizontalAmount: Double, verticalAmount: Double): Boolean { open fun mouseScrolled(mouseX: Double, mouseY: Double, horizontalAmount: Double, verticalAmount: Double): Boolean {
return false return false
} }
open fun isClickOutsideBounds(mouseX: Double, mouseY: Double): Boolean { open fun isClickOutsideBounds(mouseX: Double, mouseY: Double): Boolean {
return getBounds().none { it.contains(mouseX, mouseY) } return getBounds().none { it.contains(mouseX, mouseY) }
} }
open fun isPointWithinBounds( open fun isPointWithinBounds(
x: Int, x: Int,
y: Int, y: Int,
width: Int, width: Int,
height: Int, height: Int,
pointX: Double, pointX: Double,
pointY: Double, pointY: Double,
): Boolean { ): Boolean {
return getBounds().any { it.contains(pointX, pointY) } && return getBounds().any { it.contains(pointX, pointY) } &&
Rectangle(x, y, width, height).contains(pointX, pointY) Rectangle(x, y, width, height).contains(pointX, pointY)
} }
open fun isPointOverSlot(slot: Slot, xOffset: Int, yOffset: Int, pointX: Double, pointY: Double): Boolean { open fun isPointOverSlot(slot: Slot, xOffset: Int, yOffset: Int, pointX: Double, pointY: Double): Boolean {
return isPointWithinBounds(slot.x + xOffset, slot.y + yOffset, 16, 16, pointX, pointY) return isPointWithinBounds(slot.x + xOffset, slot.y + yOffset, 16, 16, pointX, pointY)
} }
open fun onInit() {} open fun onInit() {}
open fun shouldDrawForeground(): Boolean { open fun shouldDrawForeground(): Boolean {
return true return true
} }
open fun onVoluntaryExit(): Boolean { open fun onVoluntaryExit(): Boolean {
return true return true
} }
open fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
return false
}
open fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
return false
}
} }