diff --git a/src/main/java/io/github/itzispyder/clickcrystals/mixininterfaces/AccessorMouseHandler.java b/src/main/java/io/github/itzispyder/clickcrystals/mixininterfaces/AccessorMouseHandler.java index 3a9e8ff2..5bd14cba 100644 --- a/src/main/java/io/github/itzispyder/clickcrystals/mixininterfaces/AccessorMouseHandler.java +++ b/src/main/java/io/github/itzispyder/clickcrystals/mixininterfaces/AccessorMouseHandler.java @@ -9,4 +9,6 @@ public interface AccessorMouseHandler { void clickCrystals$middleClick(); void scroll(double amount); + + void setCursorPos(double x, double y); } diff --git a/src/main/java/io/github/itzispyder/clickcrystals/mixins/MixinMouseHandlerHandler.java b/src/main/java/io/github/itzispyder/clickcrystals/mixins/MixinMouseHandlerHandler.java index 661d97f2..e6fd5504 100644 --- a/src/main/java/io/github/itzispyder/clickcrystals/mixins/MixinMouseHandlerHandler.java +++ b/src/main/java/io/github/itzispyder/clickcrystals/mixins/MixinMouseHandlerHandler.java @@ -18,6 +18,8 @@ public abstract class MixinMouseHandlerHandler implements Global, AccessorMouseH @Shadow private double accumulatedDY; @Shadow private double accumulatedDX; + @Shadow private double xpos; + @Shadow private double ypos; @Shadow protected abstract void onButton(long window, MouseButtonInfo input, int action); @Shadow protected abstract void onScroll(long window, double horizontal, double vertical); @@ -75,4 +77,10 @@ public void leftClick() { public void scroll(double amount) { onScroll(mc.getWindow().handle(), 0, amount); } + + @Override + public void setCursorPos(double x, double y) { + this.xpos = x; + this.ypos = y; + } } \ No newline at end of file diff --git a/src/main/java/io/github/itzispyder/clickcrystals/modules/modules/misc/GuiCursor.java b/src/main/java/io/github/itzispyder/clickcrystals/modules/modules/misc/GuiCursor.java index a94323dd..61caf16d 100644 --- a/src/main/java/io/github/itzispyder/clickcrystals/modules/modules/misc/GuiCursor.java +++ b/src/main/java/io/github/itzispyder/clickcrystals/modules/modules/misc/GuiCursor.java @@ -13,6 +13,7 @@ import io.github.itzispyder.clickcrystals.modules.settings.SettingSection; import io.github.itzispyder.clickcrystals.util.minecraft.HotbarUtils; import io.github.itzispyder.clickcrystals.util.minecraft.InvUtils; +import io.github.itzispyder.clickcrystals.mixininterfaces.AccessorMouseHandler; import net.minecraft.client.gui.screens.inventory.InventoryScreen; import net.minecraft.network.protocol.game.ServerboundContainerClickPacket; import net.minecraft.world.InteractionHand; @@ -21,6 +22,11 @@ import net.minecraft.world.item.Items; import org.lwjgl.glfw.GLFW; +import java.awt.Point; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + public class GuiCursor extends Module implements Listener { private final SettingSection scGeneral = getGeneralSection(); @@ -36,7 +42,17 @@ public class GuiCursor extends Module implements Listener { .def(false) .build() ); - private boolean listeningForNextDraw, shiftKeyDown; + public final ModuleSetting closestTotemSlot = scGeneral.add(createBoolSetting() + .name("closest-totem-slot") + .description("Picks the totem slot closest to the center of the inventory instead of the first one found. Requires cursor-action to be HOVER_TOTEM.") + .def(true) + .build() + ); + + private final List totemSlots = new ArrayList<>(); + private final List allSlots = new ArrayList<>(); + private int collectState = -1; // -1 idle, 0 waiting, 1 collecting + private boolean shiftKeyDown; public GuiCursor() { super("gui-cursor", Categories.MISC, "What to do with your cursor when you open inventory gui"); @@ -45,13 +61,17 @@ public GuiCursor() { @Override protected void onEnable() { system.addListener(this); - this.listeningForNextDraw = false; + this.collectState = -1; + this.totemSlots.clear(); + this.allSlots.clear(); } @Override protected void onDisable() { system.removeListener(this); - this.listeningForNextDraw = false; + this.collectState = -1; + this.totemSlots.clear(); + this.allSlots.clear(); } public static void setCursor(int x, int y) { @@ -60,16 +80,21 @@ public static void setCursor(int x, int y) { int w2 = win.getGuiScaledWidth(); int h1 = win.getScreenHeight(); int h2 = win.getGuiScaledHeight(); - double ratW = (double)w2 / (double)w1; - double ratH = (double)h2 / (double)h1; - GLFW.glfwSetCursorPos(win.handle(), x / ratW, y / ratH); + double ratW = (double) w2 / (double) w1; + double ratH = (double) h2 / (double) h1; + double rawX = x / ratW; + double rawY = y / ratH; + GLFW.glfwSetCursorPos(win.handle(), rawX, rawY); + // Update internal mouse state directly for Wayland compatibility, + // where glfwSetCursorPos silently fails in normal cursor mode + ((AccessorMouseHandler) mc.mouseHandler).setCursorPos(rawX, rawY); } public static double getCursorX(double x) { Window win = mc.getWindow(); int w1 = win.getScreenWidth(); int w2 = win.getGuiScaledWidth(); - double ratW = (double)w2 / (double)w1; + double ratW = (double) w2 / (double) w1; return x * ratW; } @@ -77,7 +102,7 @@ public static double getCursorY(double y) { Window win = mc.getWindow(); int h1 = win.getScreenHeight(); int h2 = win.getGuiScaledHeight(); - double ratH = (double)h2 / (double)h1; + double ratH = (double) h2 / (double) h1; return y * ratH; } @@ -87,23 +112,63 @@ public void centerFix() { } public void hoverTotem() { - listeningForNextDraw = true; - system.scheduler.runDelayedTask(() -> listeningForNextDraw = false, 50); + totemSlots.clear(); + allSlots.clear(); + collectState = 0; + system.scheduler.runDelayedTask(() -> { + pickAndMoveCursor(); + totemSlots.clear(); + allSlots.clear(); + collectState = -1; + }, 50); + } + + private void pickAndMoveCursor() { + if (totemSlots.isEmpty()) return; + + boolean useClosest = cursorAction.getVal() == Mode.HOVER_TOTEM + && closestTotemSlot.getVal() + && !allSlots.isEmpty(); + + if (!useClosest) { + Point first = totemSlots.getFirst(); + setCursor(first.x, first.y); + return; + } + + double cx = allSlots.stream().mapToInt(p -> p.x).average().orElse(0); + double cy = allSlots.stream().mapToInt(p -> p.y).average().orElse(0); + + Point chosen = totemSlots.stream() + .min(Comparator.comparingDouble(p -> Math.hypot(p.x - cx, p.y - cy))) + .orElse(totemSlots.getFirst()); + + setCursor(chosen.x, chosen.y); } @EventHandler private void onKey(KeyPressEvent e) { - if (e.getKeycode() == GLFW.GLFW_KEY_LEFT_SHIFT) { + if (e.getKeycode() == GLFW.GLFW_KEY_LEFT_SHIFT) shiftKeyDown = e.getAction().isDown(); - } } @EventHandler private void renderInventoryItem(RenderInventorySlotEvent e) { - if (e.getItem().is(Items.TOTEM_OF_UNDYING) && listeningForNextDraw) { - setCursor(e.getX() + 8, e.getY() + 8); - listeningForNextDraw = false; + if (collectState == -1) return; + + int sx = e.getX() + 8; + int sy = e.getY() + 8; + + if (collectState == 0) { + totemSlots.clear(); + allSlots.clear(); + collectState = 1; } + + allSlots.add(new Point(sx, sy)); + + if (e.getItem().is(Items.TOTEM_OF_UNDYING)) + totemSlots.add(new Point(sx, sy)); } @EventHandler @@ -137,8 +202,7 @@ private void onClick(PacketSendEvent e) { if (mainEmpty) { system.scheduler.runDelayedTask(this::hoverTotem, 50); } - } - else if (slotValid) { + } else if (slotValid) { e.cancel(); InvUtils.quickMove(slot); InvUtils.inv().tick();