1
0
forked from Clones/Controlify

scrollable vmouse, YACL compat, toggle sneak/sprint, 22w06a, custom name

This commit is contained in:
isXander
2023-02-08 21:54:01 +00:00
parent 6c5108469c
commit ab801e37b4
26 changed files with 400 additions and 91 deletions

View File

@ -151,7 +151,7 @@ public class Controlify {
var minecraft = Minecraft.getInstance();
if (!minecraft.mouseHandler.isMouseGrabbed())
hideMouse(currentInputMode == InputMode.CONTROLLER);
hideMouse(currentInputMode == InputMode.CONTROLLER, true);
if (minecraft.screen != null) {
ScreenProcessorProvider.provide(minecraft.screen).onInputModeChanged(currentInputMode);
}
@ -159,7 +159,7 @@ public class Controlify {
ControlifyEvents.INPUT_MODE_CHANGED.invoker().onInputModeChanged(currentInputMode);
}
public void hideMouse(boolean hide) {
public void hideMouse(boolean hide, boolean moveMouse) {
var minecraft = Minecraft.getInstance();
GLFW.glfwSetInputMode(
minecraft.getWindow().getWindow(),
@ -170,7 +170,7 @@ public class Controlify {
);
if (minecraft.screen != null) {
var mouseHandlerAccessor = (MouseHandlerAccessor) minecraft.mouseHandler;
if (hide && !virtualMouseHandler().isVirtualMouseEnabled()) {
if (hide && !virtualMouseHandler().isVirtualMouseEnabled() && moveMouse) {
// stop mouse hovering over last element before hiding cursor but don't actually move it
// so when the user switches back to mouse it will be in the same place
mouseHandlerAccessor.invokeOnMove(minecraft.getWindow().getWindow(), -50, -50);

View File

@ -11,6 +11,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BooleanSupplier;
public class ControllerBinding {
private final Controller controller;
@ -18,18 +19,22 @@ public class ControllerBinding {
private final IBind defaultBind;
private final ResourceLocation id;
private final Component name, description;
private final KeyMapping override;
private final KeyMappingOverride override;
private static final Map<Controller, Set<Bind>> pressedBinds = new HashMap<>();
public ControllerBinding(Controller controller, IBind defaultBind, ResourceLocation id, KeyMapping override) {
public ControllerBinding(Controller controller, IBind defaultBind, ResourceLocation id, KeyMapping override, BooleanSupplier toggleOverride) {
this.controller = controller;
this.bind = this.defaultBind = defaultBind;
this.id = id;
this.name = Component.translatable("controlify.binding." + id.getNamespace() + "." + id.getPath());
var descKey = "controlify.binding." + id.getNamespace() + "." + id.getPath() + ".desc";
this.description = Language.getInstance().has(descKey) ? Component.translatable(descKey) : Component.empty();
this.override = override;
this.override = override != null ? new KeyMappingOverride(override, toggleOverride) : null;
}
public ControllerBinding(Controller controller, IBind defaultBind, ResourceLocation id) {
this(controller, defaultBind, id, null, () -> false);
}
public float state() {
@ -86,7 +91,7 @@ public class ControllerBinding {
return description;
}
public KeyMapping override() {
public KeyMappingOverride override() {
return override;
}
@ -114,4 +119,7 @@ public class ControllerBinding {
return Set.of((Bind) bind);
}
}
public record KeyMappingOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
}
}

View File

@ -26,45 +26,50 @@ public class ControllerBindings {
OPEN_CHAT,
GUI_PRESS, GUI_BACK,
GUI_NEXT_TAB, GUI_PREV_TAB,
VMOUSE_LCLICK, VMOUSE_RCLICK, VMOUSE_MCLICK, VMOUSE_ESCAPE, VMOUSE_SHIFT, VMOUSE_TOGGLE,
TOGGLE_DEBUG_MENU;
VMOUSE_LCLICK, VMOUSE_RCLICK, VMOUSE_MCLICK, VMOUSE_SCROLL_UP, VMOUSE_SCROLL_DOWN, VMOUSE_ESCAPE, VMOUSE_SHIFT, VMOUSE_TOGGLE,
PICK_BLOCK;
private final Map<ResourceLocation, ControllerBinding> registry = new LinkedHashMap<>();
private final Controller controller;
public ControllerBindings(Controller controller) {
this.controller = controller;
var options = Minecraft.getInstance().options;
register(WALK_FORWARD = new ControllerBinding(controller, Bind.LEFT_STICK_FORWARD, new ResourceLocation("controlify", "walk_forward"), null));
register(WALK_BACKWARD = new ControllerBinding(controller, Bind.LEFT_STICK_BACKWARD, new ResourceLocation("controlify", "walk_backward"), null));
register(WALK_LEFT = new ControllerBinding(controller, Bind.LEFT_STICK_LEFT, new ResourceLocation("controlify", "strafe_left"), null));
register(WALK_RIGHT = new ControllerBinding(controller, Bind.LEFT_STICK_RIGHT, new ResourceLocation("controlify", "strafe_right"), null));
register(JUMP = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "jump"), options.keyJump));
register(SNEAK = new ControllerBinding(controller, Bind.RIGHT_STICK_PRESS, new ResourceLocation("controlify", "sneak"), options.keyShift));
register(ATTACK = new ControllerBinding(controller, Bind.RIGHT_TRIGGER, new ResourceLocation("controlify", "attack"), options.keyAttack));
register(USE = new ControllerBinding(controller, Bind.LEFT_TRIGGER, new ResourceLocation("controlify", "use"), options.keyUse));
register(SPRINT = new ControllerBinding(controller, Bind.LEFT_STICK_PRESS, new ResourceLocation("controlify", "sprint"), options.keySprint));
register(DROP = new ControllerBinding(controller, Bind.DPAD_DOWN, new ResourceLocation("controlify", "drop"), options.keyDrop));
register(NEXT_SLOT = new ControllerBinding(controller, Bind.RIGHT_BUMPER, new ResourceLocation("controlify", "next_slot"), null));
register(PREV_SLOT = new ControllerBinding(controller, Bind.LEFT_BUMPER, new ResourceLocation("controlify", "prev_slot"), null));
register(PAUSE = new ControllerBinding(controller, Bind.START, new ResourceLocation("controlify", "pause"), null));
register(INVENTORY = new ControllerBinding(controller, Bind.Y_BUTTON, new ResourceLocation("controlify", "inventory"), options.keyInventory));
register(CHANGE_PERSPECTIVE = new ControllerBinding(controller, Bind.BACK, new ResourceLocation("controlify", "change_perspective"), options.keyTogglePerspective));
register(OPEN_CHAT = new ControllerBinding(controller, Bind.DPAD_UP, new ResourceLocation("controlify", "open_chat"), options.keyChat));
register(GUI_PRESS = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "gui_press"), null));
register(GUI_BACK = new ControllerBinding(controller, Bind.B_BUTTON, new ResourceLocation("controlify", "gui_back"), null));
register(GUI_NEXT_TAB = new ControllerBinding(controller, Bind.RIGHT_BUMPER, new ResourceLocation("controlify", "gui_next_tab"), null));
register(GUI_PREV_TAB = new ControllerBinding(controller, Bind.LEFT_BUMPER, new ResourceLocation("controlify", "gui_prev_tab"), null));
register(VMOUSE_LCLICK = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "vmouse_lclick"), null));
register(VMOUSE_RCLICK = new ControllerBinding(controller, Bind.X_BUTTON, new ResourceLocation("controlify", "vmouse_rclick"), null));
register(VMOUSE_MCLICK = new ControllerBinding(controller, Bind.Y_BUTTON, new ResourceLocation("controlify", "vmouse_mclick"), null));
register(VMOUSE_ESCAPE = new ControllerBinding(controller, Bind.B_BUTTON, new ResourceLocation("controlify", "vmouse_escape"), null));
register(VMOUSE_SHIFT = new ControllerBinding(controller, Bind.LEFT_STICK_PRESS, new ResourceLocation("controlify", "vmouse_shift"), null));
register(VMOUSE_TOGGLE = new ControllerBinding(controller, Bind.BACK, new ResourceLocation("controlify", "vmouse_toggle"), null));
register(TOGGLE_DEBUG_MENU = new ControllerBinding(controller, new CompoundBind(Bind.BACK, Bind.START), new ResourceLocation("controlify", "toggle_debug_menu"), null));
register(WALK_FORWARD = new ControllerBinding(controller, Bind.LEFT_STICK_FORWARD, new ResourceLocation("controlify", "walk_forward")));
register(WALK_BACKWARD = new ControllerBinding(controller, Bind.LEFT_STICK_BACKWARD, new ResourceLocation("controlify", "walk_backward")));
register(WALK_LEFT = new ControllerBinding(controller, Bind.LEFT_STICK_LEFT, new ResourceLocation("controlify", "strafe_left")));
register(WALK_RIGHT = new ControllerBinding(controller, Bind.LEFT_STICK_RIGHT, new ResourceLocation("controlify", "strafe_right")));
register(JUMP = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "jump"), options.keyJump, () -> false));
register(SNEAK = new ControllerBinding(controller, Bind.RIGHT_STICK_PRESS, new ResourceLocation("controlify", "sneak"), options.keyShift, () -> controller.config().toggleSneak));
register(ATTACK = new ControllerBinding(controller, Bind.RIGHT_TRIGGER, new ResourceLocation("controlify", "attack"), options.keyAttack, () -> false));
register(USE = new ControllerBinding(controller, Bind.LEFT_TRIGGER, new ResourceLocation("controlify", "use"), options.keyUse, () -> false));
register(SPRINT = new ControllerBinding(controller, Bind.LEFT_STICK_PRESS, new ResourceLocation("controlify", "sprint"), options.keySprint, () -> controller.config().toggleSprint));
register(DROP = new ControllerBinding(controller, Bind.DPAD_DOWN, new ResourceLocation("controlify", "drop"), options.keyDrop, () -> false));
register(NEXT_SLOT = new ControllerBinding(controller, Bind.RIGHT_BUMPER, new ResourceLocation("controlify", "next_slot")));
register(PREV_SLOT = new ControllerBinding(controller, Bind.LEFT_BUMPER, new ResourceLocation("controlify", "prev_slot")));
register(PAUSE = new ControllerBinding(controller, Bind.START, new ResourceLocation("controlify", "pause")));
register(INVENTORY = new ControllerBinding(controller, Bind.Y_BUTTON, new ResourceLocation("controlify", "inventory"), options.keyInventory, () -> false));
register(CHANGE_PERSPECTIVE = new ControllerBinding(controller, Bind.BACK, new ResourceLocation("controlify", "change_perspective"), options.keyTogglePerspective, () -> false));
register(OPEN_CHAT = new ControllerBinding(controller, Bind.DPAD_UP, new ResourceLocation("controlify", "open_chat"), options.keyChat, () -> false));
register(GUI_PRESS = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "gui_press")));
register(GUI_BACK = new ControllerBinding(controller, Bind.B_BUTTON, new ResourceLocation("controlify", "gui_back")));
register(GUI_NEXT_TAB = new ControllerBinding(controller, Bind.RIGHT_BUMPER, new ResourceLocation("controlify", "gui_next_tab")));
register(GUI_PREV_TAB = new ControllerBinding(controller, Bind.LEFT_BUMPER, new ResourceLocation("controlify", "gui_prev_tab")));
register(VMOUSE_LCLICK = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "vmouse_lclick")));
register(VMOUSE_RCLICK = new ControllerBinding(controller, Bind.X_BUTTON, new ResourceLocation("controlify", "vmouse_rclick")));
register(VMOUSE_MCLICK = new ControllerBinding(controller, Bind.Y_BUTTON, new ResourceLocation("controlify", "vmouse_mclick")));
register(VMOUSE_SCROLL_UP = new ControllerBinding(controller, Bind.RIGHT_STICK_FORWARD, new ResourceLocation("controlify", "vmouse_scroll_up")));
register(VMOUSE_SCROLL_DOWN = new ControllerBinding(controller, Bind.RIGHT_STICK_BACKWARD, new ResourceLocation("controlify", "vmouse_scroll_down")));
register(VMOUSE_ESCAPE = new ControllerBinding(controller, Bind.B_BUTTON, new ResourceLocation("controlify", "vmouse_escape")));
register(VMOUSE_SHIFT = new ControllerBinding(controller, Bind.LEFT_STICK_PRESS, new ResourceLocation("controlify", "vmouse_shift")));
register(VMOUSE_TOGGLE = new ControllerBinding(controller, Bind.BACK, new ResourceLocation("controlify", "vmouse_toggle")));
register(PICK_BLOCK = new ControllerBinding(controller, Bind.DPAD_LEFT, new ResourceLocation("controlify", "pick_block"), options.keyPickItem, () -> false));
ControlifyEvents.CONTROLLER_BIND_REGISTRY.invoker().onRegisterControllerBinds(this, controller);
ControlifyEvents.CONTROLLER_STATE_UPDATED.register(this::imitateVanillaClick);
ControlifyEvents.CONTROLLER_STATE_UPDATED.register(this::onControllerUpdate);
ControlifyEvents.INPUT_MODE_CHANGED.register(mode -> KeyMapping.releaseAll());
}
@ -97,7 +102,13 @@ public class ControllerBindings {
}
}
private void imitateVanillaClick(Controller controller) {
public void onControllerUpdate(Controller controller) {
if (controller != this.controller) return;
imitateVanillaClick();
}
private void imitateVanillaClick() {
ControllerBinding.clearPressedBinds(controller);
if (Controlify.instance().currentInputMode() != InputMode.CONTROLLER)
@ -106,12 +117,20 @@ public class ControllerBindings {
return;
for (var binding : registry().values()) {
KeyMapping vanillaKey = binding.override();
if (vanillaKey == null) continue;
var override = binding.override();
if (override == null) continue;
var vanillaKeyCode = ((KeyMappingAccessor) vanillaKey).getKey();
var accessor = (KeyMappingAccessor) override.keyMapping();
var vanillaKeyCode = accessor.getKey();
KeyMapping.set(vanillaKeyCode, binding.held());
if (override.toggleable().getAsBoolean()) {
if (binding.justPressed()) {
// must set field directly to avoid ToggleKeyMapping breaking things
accessor.setIsDown(!accessor.getIsDown());
}
} else {
KeyMapping.set(vanillaKeyCode, binding.held());
}
if (binding.justPressed()) KeyMapping.click(vanillaKeyCode);
}
}

View File

@ -19,7 +19,7 @@ import java.util.*;
public class ScreenProcessor<T extends Screen> {
public final T screen;
private int lastMoved = 0;
protected int lastMoved = 0;
public ScreenProcessor(T screen) {
this.screen = screen;
@ -51,6 +51,7 @@ public class ScreenProcessor<T extends Screen> {
setInitialFocus();
var focusTree = getFocusTree();
var focuses = List.copyOf(focusTree);
while (!focusTree.isEmpty()) {
var focused = focusTree.poll();
var processor = ComponentProcessorProvider.provide(focused);
@ -89,8 +90,12 @@ public class ScreenProcessor<T extends Screen> {
ComponentPath path = screen.nextFocusPath(event);
if (path != null) {
accessor.invokeChangeFocus(path);
ComponentProcessorProvider.provide(path.component()).onNavigateTo(this, controller);
lastMoved = 0;
var newFocusTree = getFocusTree();
while (!newFocusTree.isEmpty() && !focuses.contains(newFocusTree.peek())) {
ComponentProcessorProvider.provide(newFocusTree.poll()).onFocusGained(this, controller);
}
}
}
}

View File

@ -1,11 +1,7 @@
package dev.isxander.controlify.compatibility.screen.component;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import dev.isxander.controlify.controller.AxesState;
import dev.isxander.controlify.controller.ButtonState;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState;
import net.minecraft.client.gui.components.events.GuiEventListener;
public interface ComponentProcessor {
ComponentProcessor EMPTY = new ComponentProcessor(){};
@ -18,6 +14,6 @@ public interface ComponentProcessor {
return false;
}
default void onNavigateTo(ScreenProcessor<?> screen, Controller controller) {
default void onFocusGained(ScreenProcessor<?> screen, Controller controller) {
}
}

View File

@ -0,0 +1,26 @@
package dev.isxander.controlify.compatibility.vanilla;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractButton;
public class AbstractButtonComponentProcessor implements ComponentProcessor {
private final AbstractButton button;
public AbstractButtonComponentProcessor(AbstractButton button) {
this.button = button;
}
@Override
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller controller) {
if (controller.bindings().GUI_PRESS.justPressed()) {
button.playDownSound(Minecraft.getInstance().getSoundManager());
button.onPress();
return true;
}
return false;
}
}

View File

@ -64,7 +64,7 @@ public class SliderComponentProcessor implements ComponentProcessor {
}
@Override
public void onNavigateTo(ScreenProcessor<?> screen, Controller controller) {
public void onFocusGained(ScreenProcessor<?> screen, Controller controller) {
System.out.println("navigated!");
this.canChangeValueSetter.accept(false);
}

View File

@ -0,0 +1,33 @@
package dev.isxander.controlify.compatibility.yacl;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.yacl.gui.controllers.cycling.CyclingControllerElement;
public class CyclingControllerElementComponentProcessor implements ComponentProcessor {
private final CyclingControllerElement cyclingController;
private int lastInput = 0;
public CyclingControllerElementComponentProcessor(CyclingControllerElement cyclingController) {
this.cyclingController = cyclingController;
}
@Override
public boolean overrideControllerNavigation(ScreenProcessor<?> screen, Controller controller) {
var rightStickX = controller.state().axes().rightStickX();
var rightStickY = controller.state().axes().rightStickY();
var input = Math.abs(rightStickX) > Math.abs(rightStickY) ? rightStickX : rightStickY;
var inputI = Math.abs(input) < controller.config().buttonActivationThreshold ? 0 : input > 0 ? 1 : -1;
if (inputI != 0 && inputI != lastInput) {
cyclingController.cycleValue(input > 0 ? 1 : -1);
lastInput = inputI;
return true;
}
lastInput = inputI;
return false;
}
}

View File

@ -0,0 +1,37 @@
package dev.isxander.controlify.compatibility.yacl;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.yacl.gui.controllers.slider.SliderControllerElement;
public class SliderControllerElementComponentProcessor implements ComponentProcessor {
private final SliderControllerElement slider;
private int ticksSinceIncrement = 0;
private int lastInput = 0;
public SliderControllerElementComponentProcessor(SliderControllerElement element) {
this.slider = element;
}
@Override
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller controller) {
ticksSinceIncrement++;
var rightStickX = controller.state().axes().rightStickX();
var rightStickY = controller.state().axes().rightStickY();
var input = Math.abs(rightStickX) > Math.abs(rightStickY) ? rightStickX : rightStickY;
if (Math.abs(input) > controller.config().buttonActivationThreshold) {
if (ticksSinceIncrement > controller.config().screenRepeatNavigationDelay || input != lastInput) {
slider.incrementValue(lastInput = input > 0 ? 1 : -1);
ticksSinceIncrement = 0;
return true;
}
} else {
this.lastInput = 0;
}
return false;
}
}

View File

@ -0,0 +1,32 @@
package dev.isxander.controlify.compatibility.yacl;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.yacl.gui.YACLScreen;
public class YACLScreenProcessor extends ScreenProcessor<YACLScreen> {
public YACLScreenProcessor(YACLScreen screen) {
super(screen);
}
@Override
protected void handleComponentNavigation(Controller controller) {
if (controller.bindings().GUI_NEXT_TAB.justPressed()) {
var idx = screen.getCurrentCategoryIdx() + 1;
if (idx >= screen.config.categories().size()) idx = 0;
screen.changeCategory(idx);
}
if (controller.bindings().GUI_PREV_TAB.justPressed()) {
var idx = screen.getCurrentCategoryIdx() - 1;
if (idx < 0) idx = screen.config.categories().size() - 1;
screen.changeCategory(idx);
}
super.handleComponentNavigation(controller);
}
@Override
protected void setInitialFocus() {
screen.setFocused(screen.optionList);
}
}

View File

@ -47,7 +47,7 @@ public class BindButtonController implements Controller<IBind> {
}
public static class BindButtonWidget extends ControllerWidget<BindButtonController> implements ComponentProcessorProvider, ComponentProcessor {
private boolean awaitingControllerInput = false, skipFirstTickInput = false;
private boolean awaitingControllerInput = false;
private final Component awaitingText = Component.translatable("controlify.gui.bind_input_awaiting").withStyle(ChatFormatting.ITALIC);
private final Set<Bind> pressedBinds = new LinkedHashSet<>();

View File

@ -7,11 +7,13 @@ import dev.isxander.controlify.controller.ControllerTheme;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.yacl.api.*;
import dev.isxander.yacl.gui.controllers.ActionController;
import dev.isxander.yacl.gui.controllers.BooleanController;
import dev.isxander.yacl.gui.controllers.TickBoxController;
import dev.isxander.yacl.gui.controllers.cycling.CyclingListController;
import dev.isxander.yacl.gui.controllers.cycling.EnumController;
import dev.isxander.yacl.gui.controllers.slider.FloatSliderController;
import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController;
import dev.isxander.yacl.gui.controllers.string.StringController;
import net.minecraft.ChatFormatting;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
@ -62,14 +64,14 @@ public class YACLHelper {
for (var controller : Controller.CONTROLLERS.values()) {
var category = ConfigCategory.createBuilder();
var customName = controller.config().customName;
category.name(Component.literal(customName == null ? controller.name() : customName));
category.name(Component.literal(controller.name()));
var config = controller.config();
var def = controller.defaultConfig();
var configGroup = OptionGroup.createBuilder()
.name(Component.translatable("controlify.gui.group.config"))
.tooltip(Component.translatable("controlify.gui.group.config.tooltip"))
var basicGroup = OptionGroup.createBuilder()
.name(Component.translatable("controlify.gui.group.basic"))
.tooltip(Component.translatable("controlify.gui.group.basic.tooltip"))
.option(Option.createBuilder(float.class)
.name(Component.translatable("controlify.gui.horizontal_look_sensitivity"))
.tooltip(Component.translatable("controlify.gui.horizontal_look_sensitivity.tooltip"))
@ -82,12 +84,43 @@ public class YACLHelper {
.binding(def.verticalLookSensitivity, () -> config.verticalLookSensitivity, v -> config.verticalLookSensitivity = v)
.controller(opt -> new FloatSliderController(opt, 0.1f, 2f, 0.05f, v -> Component.literal(String.format("%.0f%%", v*100))))
.build())
.option(Option.createBuilder(boolean.class)
.name(Component.translatable("controlify.gui.toggle_sprint"))
.tooltip(Component.translatable("controlify.gui.toggle_sprint.tooltip"))
.binding(def.toggleSprint, () -> config.toggleSprint, v -> config.toggleSprint = v)
.controller(opt -> new BooleanController(opt, v -> Component.translatable("controlify.gui.format.hold_toggle." + (v ? "toggle" : "hold")), false))
.build())
.option(Option.createBuilder(boolean.class)
.name(Component.translatable("controlify.gui.toggle_sneak"))
.tooltip(Component.translatable("controlify.gui.toggle_sneak.tooltip"))
.binding(def.toggleSneak, () -> config.toggleSneak, v -> config.toggleSneak = v)
.controller(opt -> new BooleanController(opt, v -> Component.translatable("controlify.gui.format.hold_toggle." + (v ? "toggle" : "hold")), false))
.build())
.option(Option.createBuilder(float.class)
.name(Component.translatable("controlify.gui.vmouse_sensitivity"))
.tooltip(Component.translatable("controlify.gui.vmouse_sensitivity.tooltip"))
.binding(def.virtualMouseSensitivity, () -> config.virtualMouseSensitivity, v -> config.virtualMouseSensitivity = v)
.controller(opt -> new FloatSliderController(opt, 0.1f, 2f, 0.05f, v -> Component.literal(String.format("%.0f%%", v*100))))
.build())
.option(Option.createBuilder(ControllerTheme.class)
.name(Component.translatable("controlify.gui.controller_theme"))
.tooltip(Component.translatable("controlify.gui.controller_theme.tooltip"))
.binding(controller.type().theme(), () -> config.theme, v -> config.theme = v)
.controller(EnumController::new)
.instant(true)
.build())
.option(Option.createBuilder(String.class)
.name(Component.translatable("controlify.gui.custom_name"))
.tooltip(Component.translatable("controlify.gui.custom_name.tooltip"))
.binding(def.customName == null ? "" : def.customName, () -> config.customName == null ? "" : config.customName, v -> config.customName = (v.equals("") ? null : v))
.controller(StringController::new)
.build());
category.group(basicGroup.build());
var advancedGroup = OptionGroup.createBuilder()
.name(Component.translatable("controlify.gui.group.advanced"))
.tooltip(Component.translatable("controlify.gui.group.advanced.tooltip"))
.collapsed(true)
.option(Option.createBuilder(int.class)
.name(Component.translatable("controlify.gui.screen_repeat_navi_delay"))
.tooltip(Component.translatable("controlify.gui.screen_repeat_navi_delay.tooltip"))
@ -113,15 +146,8 @@ public class YACLHelper {
.tooltip(Component.translatable("controlify.gui.button_activation_threshold.tooltip"))
.binding(def.buttonActivationThreshold, () -> config.buttonActivationThreshold, v -> config.buttonActivationThreshold = v)
.controller(opt -> new FloatSliderController(opt, 0, 1, 0.05f, v -> Component.literal(String.format("%.0f%%", v*100))))
.build())
.option(Option.createBuilder(ControllerTheme.class)
.name(Component.translatable("controlify.gui.controller_theme"))
.tooltip(Component.translatable("controlify.gui.controller_theme.tooltip"))
.binding(controller.type().theme(), () -> config.theme, v -> config.theme = v)
.controller(EnumController::new)
.instant(true)
.build());
category.group(configGroup.build());
category.group(advancedGroup.build());
var controlsGroup = OptionGroup.createBuilder()
.name(Component.translatable("controlify.gui.group.controls"));

View File

@ -178,6 +178,9 @@ public final class Controller {
public ControllerTheme theme = type().theme();
public boolean toggleSprint = true;
public boolean toggleSneak = true;
public String customName = null;
}
}

View File

@ -3,12 +3,15 @@ package dev.isxander.controlify.ingame;
import dev.isxander.controlify.controller.Controller;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.Input;
import net.minecraft.client.player.LocalPlayer;
public class ControllerPlayerMovement extends Input {
private final Controller controller;
private final LocalPlayer player;
public ControllerPlayerMovement(Controller controller) {
public ControllerPlayerMovement(Controller controller, LocalPlayer player) {
this.controller = controller;
this.player = player;
}
@Override
@ -42,6 +45,13 @@ public class ControllerPlayerMovement extends Input {
}
this.jumping = bindings.JUMP.held();
this.shiftKeyDown = bindings.SNEAK.held();
if (player.getAbilities().flying || !controller.config().toggleSneak) {
this.shiftKeyDown = bindings.SNEAK.held();
} else {
if (bindings.SNEAK.justPressed()) {
this.shiftKeyDown = !this.shiftKeyDown;
}
}
}
}

View File

@ -21,7 +21,7 @@ public class InGameInputHandler {
this.controller = controller;
this.minecraft = Minecraft.getInstance();
this.controllerInput = new ControllerPlayerMovement(controller);
this.controllerInput = new ControllerPlayerMovement(controller, minecraft.player);
this.keyboardInput = new KeyboardInput(minecraft.options);
ControlifyEvents.INPUT_MODE_CHANGED.register(mode -> {
@ -45,9 +45,6 @@ public class InGameInputHandler {
if (controller.bindings().PAUSE.justPressed()) {
minecraft.pauseGame(false);
}
if (controller.bindings().TOGGLE_DEBUG_MENU.justPressed()) {
minecraft.options.renderDebug = !minecraft.options.renderDebug;
}
if (minecraft.player != null) {
if (controller.bindings().NEXT_SLOT.justPressed()) {
minecraft.player.getInventory().swapPaint(-1);
@ -73,9 +70,9 @@ public class InGameInputHandler {
var delta = time - deltaTime;
deltaTime = time;
var hsensitivity = controller.config().horizontalLookSensitivity * 8.0 + 2.0;
var hsensitivity = controller.config().horizontalLookSensitivity * 9.6 + 2.0;
var hsensCubed = hsensitivity * hsensitivity * hsensitivity;
var vsensitivity = controller.config().verticalLookSensitivity * 8.0 + 2.0;
var vsensitivity = controller.config().verticalLookSensitivity * 9.6 + 2.0;
var vsensCubed = vsensitivity * vsensitivity * vsensitivity;
var dx = accumulatedDX * delta;
@ -93,7 +90,6 @@ public class InGameInputHandler {
accumulatedDY -= Math.max(dy * 20, accumulatedDY);
}
if (minecraft.player != null)
minecraft.player.turn(dx * hsensCubed, dy * vsensCubed);
}

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.mixins.compat.screen.vanilla;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessor;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessorProvider;
import dev.isxander.controlify.compatibility.vanilla.AbstractButtonComponentProcessor;
import net.minecraft.client.gui.components.AbstractButton;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(AbstractButton.class)
public class AbstractButtonMixin implements ComponentProcessorProvider {
@Unique private final AbstractButtonComponentProcessor controlify$processor
= new AbstractButtonComponentProcessor((AbstractButton) (Object) this);
@Override
public ComponentProcessor componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.mixins.compat.screen.yacl;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessor;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessorProvider;
import dev.isxander.controlify.compatibility.yacl.CyclingControllerElementComponentProcessor;
import dev.isxander.yacl.gui.controllers.cycling.CyclingControllerElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(CyclingControllerElement.class)
public class CyclingControllerElementMixin implements ComponentProcessorProvider {
@Unique private final CyclingControllerElementComponentProcessor controlify$processor
= new CyclingControllerElementComponentProcessor((CyclingControllerElement) (Object) this);
@Override
public ComponentProcessor componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.mixins.compat.screen.yacl;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessor;
import dev.isxander.controlify.compatibility.screen.component.ComponentProcessorProvider;
import dev.isxander.controlify.compatibility.yacl.SliderControllerElementComponentProcessor;
import dev.isxander.yacl.gui.controllers.slider.SliderControllerElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(SliderControllerElement.class)
public class SliderControllerElementMixin implements ComponentProcessorProvider {
@Unique private final SliderControllerElementComponentProcessor controlify$processor
= new SliderControllerElementComponentProcessor((SliderControllerElement) (Object) this);
@Override
public ComponentProcessor componentProcessor() {
return controlify$processor;
}
}

View File

@ -0,0 +1,18 @@
package dev.isxander.controlify.mixins.compat.screen.yacl;
import dev.isxander.controlify.compatibility.screen.ScreenProcessor;
import dev.isxander.controlify.compatibility.screen.ScreenProcessorProvider;
import dev.isxander.controlify.compatibility.yacl.YACLScreenProcessor;
import dev.isxander.yacl.gui.YACLScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(YACLScreen.class)
public class YACLScreenMixin implements ScreenProcessorProvider {
@Unique private final YACLScreenProcessor controlify$processor = new YACLScreenProcessor((YACLScreen) (Object) this);
@Override
public ScreenProcessor<?> screenProcessor() {
return controlify$processor;
}
}

View File

@ -24,6 +24,6 @@ public class ClientPacketListenerMixin {
@Inject(method = "handleLogin", at = @At(value = "FIELD", target = "Lnet/minecraft/client/player/LocalPlayer;input:Lnet/minecraft/client/player/Input;", opcode = Opcodes.ASTORE, shift = At.Shift.AFTER))
private void useControllerInput(ClientboundLoginPacket packet, CallbackInfo ci) {
if (Controlify.instance().currentInputMode() == InputMode.CONTROLLER && minecraft.player != null)
minecraft.player.input = new ControllerPlayerMovement(Controlify.instance().currentController());
minecraft.player.input = new ControllerPlayerMovement(Controlify.instance().currentController(), minecraft.player);
}
}

View File

@ -9,4 +9,10 @@ import org.spongepowered.asm.mixin.gen.Accessor;
public interface KeyMappingAccessor {
@Accessor
InputConstants.Key getKey();
@Accessor
void setIsDown(boolean down);
@Accessor
boolean getIsDown();
}

View File

@ -22,6 +22,9 @@ public class VirtualMouseHandler {
private double targetX, targetY;
private double currentX, currentY;
private double scrollX, scrollY;
private final Minecraft minecraft;
private boolean virtualMouseEnabled;
@ -51,6 +54,8 @@ public class VirtualMouseHandler {
targetX = Mth.clamp(targetX, 0, minecraft.getWindow().getWidth());
targetY = Mth.clamp(targetY, 0, minecraft.getWindow().getHeight());
scrollY += controller.bindings().VMOUSE_SCROLL_UP.state() - controller.bindings().VMOUSE_SCROLL_DOWN.state();
var mouseHandler = (MouseHandlerAccessor) minecraft.mouseHandler;
var keyboardHandler = (KeyboardHandlerAccessor) minecraft.keyboardHandler;
@ -83,12 +88,27 @@ public class VirtualMouseHandler {
public void updateMouse() {
if (!virtualMouseEnabled) return;
if (targetX == currentX && targetY == currentY) return; // don't need to needlessly update mouse position
currentX = Mth.lerp(minecraft.getDeltaFrameTime(), currentX, targetX);
currentY = Mth.lerp(minecraft.getDeltaFrameTime(), currentY, targetY);
if (Math.round(targetX * 100) / 100.0 != Math.round(currentX * 100) / 100.0 || Math.round(targetY * 100) / 100.0 != Math.round(currentY * 100) / 100.0) {
currentX = Mth.lerp(minecraft.getDeltaFrameTime(), currentX, targetX);
currentY = Mth.lerp(minecraft.getDeltaFrameTime(), currentY, targetY);
((MouseHandlerAccessor) minecraft.mouseHandler).invokeOnMove(minecraft.getWindow().getWindow(), currentX, currentY);
((MouseHandlerAccessor) minecraft.mouseHandler).invokeOnMove(minecraft.getWindow().getWindow(), currentX, currentY);
} else {
currentX = targetX;
currentY = targetY;
}
if (Math.abs(scrollX) >= 0.01 || Math.abs(scrollY) >= 0.01) {
var currentScrollY = scrollY * Minecraft.getInstance().getDeltaFrameTime();
scrollY -= currentScrollY;
var currentScrollX = scrollX * Minecraft.getInstance().getDeltaFrameTime();
scrollX -= currentScrollX;
((MouseHandlerAccessor) minecraft.mouseHandler).invokeOnScroll(minecraft.getWindow().getWindow(), currentScrollX, currentScrollY);
} else {
scrollX = scrollY = 0;
}
}
public void onScreenChanged() {
@ -141,7 +161,6 @@ public class VirtualMouseHandler {
public void enableVirtualMouse() {
if (virtualMouseEnabled) return;
setMousePosition();
GLFW.glfwSetInputMode(minecraft.getWindow().getWindow(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
virtualMouseEnabled = true;
@ -152,6 +171,7 @@ public class VirtualMouseHandler {
targetX = currentX = minecraft.mouseHandler.xpos();
targetY = currentY = minecraft.mouseHandler.ypos();
}
setMousePosition();
ControlifyEvents.VIRTUAL_MOUSE_TOGGLED.invoker().onVirtualMouseToggled(true);
}
@ -162,6 +182,7 @@ public class VirtualMouseHandler {
// make sure minecraft doesn't think the mouse is grabbed when it isn't
((MouseHandlerAccessor) minecraft.mouseHandler).setMouseGrabbed(false);
Controlify.instance().hideMouse(true, true);
GLFW.glfwSetInputMode(minecraft.getWindow().getWindow(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
setMousePosition();
virtualMouseEnabled = false;
@ -196,7 +217,7 @@ public class VirtualMouseHandler {
if (screens.contains(screenClass)) {
screens.remove(screenClass);
disableVirtualMouse();
Controlify.instance().hideMouse(true);
Controlify.instance().hideMouse(true, false);
minecraft.getToasts().addToast(SystemToast.multiline(
minecraft,