diff --git a/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java b/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java index 2292635..3cfca4b 100644 --- a/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java +++ b/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java @@ -49,7 +49,7 @@ public class ControllerBindings { JUMP, SNEAK, ATTACK, USE, SPRINT, - DROP, DROP_STACK, + DROP_INGAME, DROP_STACK, DROP_INVENTORY, NEXT_SLOT, PREV_SLOT, PAUSE, INVENTORY, @@ -67,6 +67,7 @@ public class ControllerBindings { RADIAL_MENU, RADIAL_AXIS_UP, RADIAL_AXIS_DOWN, RADIAL_AXIS_LEFT, RADIAL_AXIS_RIGHT, VMOUSE_MOVE_UP, VMOUSE_MOVE_DOWN, VMOUSE_MOVE_LEFT, VMOUSE_MOVE_RIGHT, VMOUSE_LCLICK, VMOUSE_RCLICK, VMOUSE_SHIFT_CLICK, + VMOUSE_SNAP_UP, VMOUSE_SNAP_DOWN, VMOUSE_SNAP_LEFT, VMOUSE_SNAP_RIGHT, VMOUSE_SCROLL_UP, VMOUSE_SCROLL_DOWN, VMOUSE_SHIFT, VMOUSE_TOGGLE, @@ -173,7 +174,7 @@ public class ControllerBindings { .context(BindContexts.INGAME) .vanillaOverride(options.keyUse, () -> false) .build()); - register(DROP = ControllerBindingBuilder.create(controller) + register(DROP_INGAME = ControllerBindingBuilder.create(controller) .identifier("controlify", "drop") .defaultBind(GamepadBinds.DPAD_DOWN) .category(GAMEPLAY_CATEGORY) @@ -187,6 +188,12 @@ public class ControllerBindings { .context(BindContexts.INGAME) .radialCandidate(RadialIcons.getItem(Items.TNT)) .build()); + register(DROP_INVENTORY = ControllerBindingBuilder.create(controller) + .identifier("controlify", "drop_inventory") + .defaultBind(GamepadBinds.Y_BUTTON) + .category(INVENTORY_CATEGORY) + .context(BindContexts.INVENTORY) + .build()); register(NEXT_SLOT = ControllerBindingBuilder.create(controller) .identifier("controlify", "next_slot") .defaultBind(GamepadBinds.RIGHT_BUMPER) @@ -396,6 +403,30 @@ public class ControllerBindings { .category(VMOUSE_CATEGORY) .context(BindContexts.GUI_VMOUSE) .build()); + register(VMOUSE_SNAP_UP = ControllerBindingBuilder.create(controller) + .identifier("controlify", "vmouse_snap_up") + .defaultBind(GamepadBinds.DPAD_UP) + .category(VMOUSE_CATEGORY) + .context(BindContexts.GUI_VMOUSE) + .build()); + register(VMOUSE_SNAP_DOWN = ControllerBindingBuilder.create(controller) + .identifier("controlify", "vmouse_snap_down") + .defaultBind(GamepadBinds.DPAD_DOWN) + .category(VMOUSE_CATEGORY) + .context(BindContexts.GUI_VMOUSE) + .build()); + register(VMOUSE_SNAP_LEFT = ControllerBindingBuilder.create(controller) + .identifier("controlify", "vmouse_snap_left") + .defaultBind(GamepadBinds.DPAD_LEFT) + .category(VMOUSE_CATEGORY) + .context(BindContexts.GUI_VMOUSE) + .build()); + register(VMOUSE_SNAP_RIGHT = ControllerBindingBuilder.create(controller) + .identifier("controlify", "vmouse_snap_right") + .defaultBind(GamepadBinds.DPAD_RIGHT) + .category(VMOUSE_CATEGORY) + .context(BindContexts.GUI_VMOUSE) + .build()); register(VMOUSE_SCROLL_UP = ControllerBindingBuilder.create(controller) .identifier("controlify", "vmouse_scroll_up") .defaultBind(GamepadBinds.RIGHT_STICK_FORWARD) diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java index fb7d8c6..7c32f40 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java @@ -72,7 +72,7 @@ public class InGameInputHandler { if (minecraft.player.drop(true)) { minecraft.player.swing(InteractionHand.MAIN_HAND); } - } else if (dropRepeatHelper.shouldAction(controller.bindings().DROP)) { + } else if (dropRepeatHelper.shouldAction(controller.bindings().DROP_INGAME)) { if (minecraft.player.drop(false)) { dropRepeatHelper.onNavigate(); minecraft.player.swing(InteractionHand.MAIN_HAND); diff --git a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java index ba66ceb..9140bb4 100644 --- a/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java +++ b/src/main/java/dev/isxander/controlify/screenop/compat/vanilla/AbstractContainerScreenProcessor.java @@ -45,11 +45,12 @@ public class AbstractContainerScreenProcessor( - new GuideAction<>(bindings.DROP, ctx -> { - if (ctx.hoveredSlot() != null && ctx.hoveredSlot().hasItem()) + new GuideAction<>(bindings.DROP_INVENTORY, ctx -> { + if (!ctx.holdingItem().isEmpty()) return Optional.of(Component.translatable("controlify.guide.container.drop")); return Optional.empty(); }), @@ -171,7 +172,7 @@ public class AbstractContainerScreenProcessor( new GuideAction<>(bindings.INV_QUICK_MOVE, ctx -> { - if (ctx.hoveredSlot() != null && ctx.hoveredSlot().hasItem()) + if (ctx.hoveredSlot() != null && ctx.hoveredSlot().hasItem() && ctx.holdingItem().isEmpty()) return Optional.of(Component.translatable("controlify.guide.container.quick_move")); return Optional.empty(); }), diff --git a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java b/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java index baa295c..5f03f00 100644 --- a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java +++ b/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java @@ -14,14 +14,18 @@ import dev.isxander.controlify.screenop.ScreenProcessorProvider; import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.mixins.feature.virtualmouse.KeyboardHandlerAccessor; import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; +import dev.isxander.controlify.utils.HoldRepeatHelper; import dev.isxander.controlify.utils.ToastUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.navigation.ScreenAxis; +import net.minecraft.client.gui.navigation.ScreenDirection; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import org.joml.RoundingMode; import org.joml.Vector2d; +import org.joml.Vector2dc; import org.joml.Vector2i; import org.lwjgl.glfw.GLFW; @@ -42,6 +46,8 @@ public class VirtualMouseHandler { private Set snapPoints; private SnapPoint lastSnappedPoint; + private final HoldRepeatHelper holdRepeatHelper = new HoldRepeatHelper(10, 6); + public VirtualMouseHandler() { this.minecraft = Minecraft.getInstance(); @@ -92,6 +98,20 @@ public class VirtualMouseHandler { scrollY += controller.bindings().VMOUSE_SCROLL_UP.state() - controller.bindings().VMOUSE_SCROLL_DOWN.state(); + if (holdRepeatHelper.shouldAction(controller.bindings().VMOUSE_SNAP_UP)) { + snapInDirection(ScreenDirection.UP); + holdRepeatHelper.onNavigate(); + } else if (holdRepeatHelper.shouldAction(controller.bindings().VMOUSE_SNAP_DOWN)) { + snapInDirection(ScreenDirection.DOWN); + holdRepeatHelper.onNavigate(); + } else if (holdRepeatHelper.shouldAction(controller.bindings().VMOUSE_SNAP_LEFT)) { + snapInDirection(ScreenDirection.LEFT); + holdRepeatHelper.onNavigate(); + } else if (holdRepeatHelper.shouldAction(controller.bindings().VMOUSE_SNAP_RIGHT)) { + snapInDirection(ScreenDirection.RIGHT); + holdRepeatHelper.onNavigate(); + } + if (ScreenProcessorProvider.provide(minecraft.screen).virtualMouseBehaviour().isDefaultOr(VirtualMouseBehaviour.ENABLED)) { handleCompatibilityBinds(controller); } @@ -169,14 +189,58 @@ public class VirtualMouseHandler { .orElse(new Pair<>(null, Long.MAX_VALUE)).getFirst(); // retrieve point if (closestSnapPoint != null) { - lastSnappedPoint = closestSnapPoint; - - targetX = currentX = closestSnapPoint.position().x() / scaleFactor.x(); - targetY = currentY = closestSnapPoint.position().y() / scaleFactor.y(); - ((MouseHandlerAccessor) minecraft.mouseHandler).invokeOnMove(minecraft.getWindow().getWindow(), currentX, currentY); + snapToPoint(closestSnapPoint, scaleFactor); } } + private void snapInDirection(ScreenDirection direction) { + var window = minecraft.getWindow(); + var scaleFactor = new Vector2d((double)window.getGuiScaledWidth() / (double)window.getScreenWidth(), (double)window.getGuiScaledHeight() / (double)window.getScreenHeight()); + var target = new Vector2d(targetX, targetY).mul(scaleFactor); + + var closestSnapPoint = snapPoints.stream() + .filter(snapPoint -> !snapPoint.equals(lastSnappedPoint)) // don't snap to the point currently over snapped point + .map(snapPoint -> new Pair<>(snapPoint, new Vector2d(snapPoint.position().x() - target.x(), snapPoint.position().y() - target.y()))) // map with distance to current pos + // filter points that are not in the correct direction + .filter(pair -> { + Vector2d dist = pair.getSecond(); + + double axis = direction.getAxis() == ScreenAxis.HORIZONTAL ? dist.x : dist.y; + double positive = direction.isPositive() ? 1 : -1; + + return axis * positive > 0; + }) + .filter(pair -> { + SnapPoint snapPoint = pair.getFirst(); + Vector2d dist = pair.getSecond(); + + double distance = Math.abs(direction.getAxis() == ScreenAxis.HORIZONTAL ? dist.x : dist.y); + double deviation = Math.abs(direction.getAxis() == ScreenAxis.HORIZONTAL ? dist.y : dist.x); + + pair.getSecond().set(distance, deviation); + + return distance >= snapPoint.range(); + }) + // pick the closest point + .min(Comparator.comparingDouble(pair -> { + Vector2d distDev = pair.getSecond(); + return distDev.x + distDev.y; + })) + .map(Pair::getFirst); + + closestSnapPoint.ifPresent(snapPoint -> { + snapToPoint(snapPoint, scaleFactor); + }); + } + + public void snapToPoint(SnapPoint snapPoint, Vector2dc scaleFactor) { + lastSnappedPoint = snapPoint; + + targetX = currentX = snapPoint.position().x() / scaleFactor.x(); + targetY = currentY = snapPoint.position().y() / scaleFactor.y(); + ((MouseHandlerAccessor) minecraft.mouseHandler).invokeOnMove(minecraft.getWindow().getWindow(), currentX, currentY); + } + public void onScreenChanged() { if (minecraft.screen != null) { if (requiresVirtualMouse()) { diff --git a/src/main/resources/assets/controlify/lang/en_us.json b/src/main/resources/assets/controlify/lang/en_us.json index 3241f21..f84341d 100644 --- a/src/main/resources/assets/controlify/lang/en_us.json +++ b/src/main/resources/assets/controlify/lang/en_us.json @@ -224,6 +224,7 @@ "controlify.binding.controlify.inv_take_half": "Take Half", "controlify.binding.controlify.drop": "Drop Item", "controlify.binding.controlify.drop_stack": "Drop Stack", + "controlify.binding.controlify.drop_inventory": "Drop Item (In Containers)", "controlify.binding.controlify.pick_block": "Pick Block", "controlify.binding.controlify.pick_block_nbt": "Pick Block (with NBT)", "controlify.binding.controlify.take_screenshot": "Take Screenshot",