Add analog movements and WIP action ring.

This commit is contained in:
LambdAurora
2020-06-30 19:19:29 +02:00
parent 3f221feb26
commit 155278130f
22 changed files with 461 additions and 70 deletions

View File

@@ -61,12 +61,12 @@ public class LambdaControlsConfig
protected final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/config.toml").build();
private final LambdaControlsClient mod;
private ControlsMode controlsMode;
private ControllerType controllerType;
private ControllerType controllerType;
// Gameplay.
private boolean shouldRenderReacharoundOutline;
private int[] reacharoundOutlineColor;
private boolean shouldRenderReacharoundOutline;
private int[] reacharoundOutlineColor;
// Controller settings
private double deadZone;
private double deadZone;
private double rotationSpeed;
private double mouseSpeed;
private boolean unfocusedInput;

View File

@@ -11,7 +11,6 @@ package me.lambdaurora.lambdacontrols.client;
import io.github.prospector.modmenu.api.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi;
import me.lambdaurora.lambdacontrols.LambdaControlsConstants;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsSettingsScreen;
/**

View File

@@ -32,6 +32,7 @@ import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
import net.minecraft.client.gui.screen.pack.ResourcePackScreen;
import net.minecraft.client.gui.screen.world.WorldListWidget;
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget;
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget;
@@ -63,24 +64,25 @@ import static org.lwjgl.glfw.GLFW.*;
* Represents the LambdaControls' input handler.
*
* @author LambdAurora
* @version 1.3.2
* @version 1.4.0
* @since 1.0.0
*/
public class LambdaInput
{
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
private final LambdaControlsConfig config;
// Cooldowns
private int actionGuiCooldown = 0;
private boolean ignoreNextARelease = false;
private double targetYaw = 0.0;
private double targetPitch = 0.0;
private float prevXAxis = 0.F;
private float prevYAxis = 0.F;
private int targetMouseX = 0;
private int targetMouseY = 0;
private float mouseSpeedX = 0.F;
private float mouseSpeedY = 0.F;
private int actionGuiCooldown = 0;
private boolean ignoreNextARelease = false;
private double targetYaw = 0.0;
private double targetPitch = 0.0;
private float prevXAxis = 0.F;
private float prevYAxis = 0.F;
private int targetMouseX = 0;
private int targetMouseY = 0;
private float mouseSpeedX = 0.F;
private float mouseSpeedY = 0.F;
private int inventoryInteractionCooldown = 0;
public LambdaInput(@NotNull LambdaControlsClient mod)
{
@@ -157,6 +159,9 @@ public class LambdaInput
screen.focusedBinding = null;
}
}
if (this.inventoryInteractionCooldown > 0)
this.inventoryInteractionCooldown--;
}
/**
@@ -214,6 +219,7 @@ public class LambdaInput
((MouseAccessor) client.mouse).lambdacontrols_onCursorPos(client.getWindow().getHandle(), 0, 0);
INPUT_MANAGER.resetMouseTarget(client);
}
this.inventoryInteractionCooldown = 5;
}
private void fetchButtonInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon)
@@ -329,9 +335,11 @@ public class LambdaInput
double mouseX = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
if (action == 0) {
client.currentScreen.mouseClicked(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1);
Screen.wrapScreenError(() -> client.currentScreen.mouseClicked(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1),
"mouseClicked event handler", client.currentScreen.getClass().getCanonicalName());
} else if (action == 1) {
client.currentScreen.mouseReleased(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1);
Screen.wrapScreenError(() -> client.currentScreen.mouseReleased(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1),
"mouseReleased event handler", client.currentScreen.getClass().getCanonicalName());
}
this.actionGuiCooldown = 5;
} else {
@@ -355,6 +363,9 @@ public class LambdaInput
if (client.interactionManager == null || client.player == null)
return false;
if (this.inventoryInteractionCooldown > 0)
return true;
if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) {
client.player.closeHandledScreen();
return true;
@@ -457,6 +468,17 @@ public class LambdaInput
BUTTON_COOLDOWNS.put(axisAsButton(axis, false), 5);
}
}
float axisValue = absValue < this.config.getDeadZone() ? 0.f : (float) (absValue - this.config.getDeadZone());
axisValue /= (1.0 - this.config.getDeadZone());
if (currentPlusState)
InputManager.BUTTON_VALUES.put(axisAsButton(axis, true), axisValue);
else
InputManager.BUTTON_VALUES.put(axisAsButton(axis, true), 0.f);
if (currentMinusState)
InputManager.BUTTON_VALUES.put(axisAsButton(axis, false), axisValue);
else
InputManager.BUTTON_VALUES.put(axisAsButton(axis, false), 0.f);
}
double deadZone = this.config.getDeadZone();
@@ -481,6 +503,7 @@ public class LambdaInput
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
CreativeInventoryScreen screen = (CreativeInventoryScreen) client.currentScreen;
CreativeInventoryScreenAccessor accessor = (CreativeInventoryScreenAccessor) screen;
// @TODO allow rebinding to left stick
if (accessor.lambdacontrols_hasScrollbar() && absValue >= deadZone) {
screen.mouseScrolled(0.0, 0.0, -value);
}
@@ -677,7 +700,7 @@ public class LambdaInput
public static boolean isScreenInteractive(@NotNull Screen screen)
{
return !(screen instanceof AdvancementsScreen || screen instanceof HandledScreen || LambdaControlsCompat.requireMouseOnScreen(screen));
return !(screen instanceof AdvancementsScreen || screen instanceof HandledScreen || screen instanceof ResourcePackScreen || LambdaControlsCompat.requireMouseOnScreen(screen));
}
// Inspired from https://github.com/MrCrayfish/Controllable/blob/1.14.X/src/main/java/com/mrcrayfish/controllable/client/ControllerInput.java#L686.

View File

@@ -31,7 +31,8 @@ public enum VirtualMouseSkin implements Nameable
private String name;
VirtualMouseSkin(String name) {
VirtualMouseSkin(String name)
{
this.name = name;
}

View File

@@ -13,7 +13,6 @@ import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.screen.slot.Slot;
import net.minecraft.util.hit.BlockHitResult;
import org.jetbrains.annotations.NotNull;

View File

@@ -18,7 +18,7 @@ import java.util.Optional;
/**
* Represents HQM compatibility handler.
*
* <p>
* This is bad.
*
* @author LambdAurora
@@ -27,8 +27,8 @@ import java.util.Optional;
*/
public class HQMCompat implements CompatHandler
{
public static final String GUI_BASE_CLASS_PATH = "hardcorequesting.client.interfaces.GuiBase";
private Optional<Class<?>> guiBaseClass;
public static final String GUI_BASE_CLASS_PATH = "hardcorequesting.client.interfaces.GuiBase";
private Optional<Class<?>> guiBaseClass;
@Override
public void handle(@NotNull LambdaControlsClient mod)

View File

@@ -14,7 +14,6 @@ import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.util.hit.BlockHitResult;
import org.aperlambda.lambdacommon.utils.LambdaReflection;
import org.jetbrains.annotations.NotNull;
@@ -132,7 +131,8 @@ public class LambdaControlsCompat
* @param screen The screen.
* @return True if the handle was fired and succeed, else false.
*/
public static boolean handleMenuBack(@NotNull MinecraftClient client, @NotNull Screen screen) {
public static boolean handleMenuBack(@NotNull MinecraftClient client, @NotNull Screen screen)
{
for (CompatHandler handler : HANDLERS) {
if (handler.handleMenuBack(client, screen))
return true;

View File

@@ -36,5 +36,7 @@ public class OkZoomerCompat implements CompatHandler
.category(ButtonBinding.MISC_CATEGORY)
.linkKeybind(OkZoomerClientMod.zoomKeyBinding)
.register();
// @TODO Zoom in and out
}
}

View File

@@ -39,7 +39,7 @@ import static org.lwjgl.glfw.GLFW.*;
* Represents a compatibility handler for REI.
*
* @author LambdAurora
* @version 1.3.2
* @version 1.4.0
* @since 1.2.0
*/
public class ReiCompat implements CompatHandler
@@ -79,7 +79,7 @@ public class ReiCompat implements CompatHandler
InputManager.registerBinding(new ButtonBinding.Builder(new Identifier("rei", "show_usage"))
.buttons(GLFW_GAMEPAD_BUTTON_RIGHT_THUMB)
.filter((client, binding) -> InputHandlers.inInventory(client, binding) || isViewingScreen(client.currentScreen))
.action((client, button, action) -> {
.action((client, button, value, action) -> {
if (action != ButtonState.RELEASE)
return false;
Optional<ContainerScreenOverlay> overlay = ScreenHelper.getOptionalOverlay();
@@ -130,7 +130,7 @@ public class ReiCompat implements CompatHandler
private static PressAction handlePage(boolean next)
{
return (client, button, action) -> {
return (client, button, value, action) -> {
if (action == ButtonState.RELEASE)
return false;
@@ -160,7 +160,7 @@ public class ReiCompat implements CompatHandler
*/
private static PressAction handleTab(boolean next)
{
return (client, button, action) -> {
return (client, button, value, action) -> {
if (action != ButtonState.RELEASE)
return false;

View File

@@ -13,7 +13,6 @@ import me.lambdaurora.lambdacontrols.client.ButtonState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import org.aperlambda.lambdacommon.Identifier;
@@ -33,7 +32,7 @@ import static org.lwjgl.glfw.GLFW.*;
* Represents a button binding.
*
* @author LambdAurora
* @version 1.3.0
* @version 1.4.0
* @since 1.0.0
*/
public class ButtonBinding implements Nameable
@@ -45,21 +44,25 @@ public class ButtonBinding implements Nameable
public static final ButtonCategory MISC_CATEGORY;
public static final ButtonBinding ATTACK = new Builder("attack").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true)).onlyInGame().register();
public static final ButtonBinding BACK = new Builder("back").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false)).onlyInGame().register();
public static final ButtonBinding BACK = new Builder("back").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false))
.action(MovementHandler.HANDLER).onlyInGame().register();
public static final ButtonBinding CHAT = new Builder("chat").buttons(GLFW_GAMEPAD_BUTTON_DPAD_RIGHT).onlyInGame().cooldown(true).register();
public static final ButtonBinding DROP_ITEM = new Builder("drop_item").buttons(GLFW_GAMEPAD_BUTTON_B).onlyInGame().cooldown(true).register();
public static final ButtonBinding FORWARD = new Builder("forward").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true)).onlyInGame().register();
public static final ButtonBinding FORWARD = new Builder("forward").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true))
.action(MovementHandler.HANDLER).onlyInGame().register();
public static final ButtonBinding HOTBAR_LEFT = new Builder("hotbar_left").buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
.action(InputHandlers.handleHotbar(false)).onlyInGame().cooldown(true).register();
public static final ButtonBinding HOTBAR_RIGHT = new Builder("hotbar_right").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
.action(InputHandlers.handleHotbar(true)).onlyInGame().cooldown(true).register();
public static final ButtonBinding INVENTORY = new Builder("inventory").buttons(GLFW_GAMEPAD_BUTTON_Y).onlyInGame().cooldown(true).register();
public static final ButtonBinding JUMP = new Builder("jump").buttons(GLFW_GAMEPAD_BUTTON_A).onlyInGame().register();
public static final ButtonBinding LEFT = new Builder("left").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false)).onlyInGame().register();
public static final ButtonBinding LEFT = new Builder("left").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false))
.action(MovementHandler.HANDLER).onlyInGame().register();
public static final ButtonBinding PAUSE_GAME = new Builder("pause_game").buttons(GLFW_GAMEPAD_BUTTON_START).action(InputHandlers::handlePauseGame).cooldown(true).register();
public static final ButtonBinding PICK_BLOCK = new Builder("pick_block").buttons(GLFW_GAMEPAD_BUTTON_DPAD_LEFT).onlyInGame().cooldown(true).register();
public static final ButtonBinding PLAYER_LIST = new Builder("player_list").buttons(GLFW_GAMEPAD_BUTTON_BACK).onlyInGame().register();
public static final ButtonBinding RIGHT = new Builder("right").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true)).register();
public static final ButtonBinding RIGHT = new Builder("right").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true))
.action(MovementHandler.HANDLER).register();
public static final ButtonBinding SCREENSHOT = new Builder("screenshot").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_A)
.action(InputHandlers::handleScreenshot).cooldown(true).register();
public static final ButtonBinding SLOT_DOWN = new Builder("slot_down").buttons(GLFW_GAMEPAD_BUTTON_DPAD_DOWN)
@@ -229,7 +232,7 @@ public class ButtonBinding implements Nameable
* @param client The client instance.
* @param state The state.
*/
public void handle(@NotNull MinecraftClient client, @NotNull ButtonState state)
public void handle(@NotNull MinecraftClient client, float value, @NotNull ButtonState state)
{
if (state == ButtonState.REPEAT && this.hasCooldown && this.cooldown != 0)
return;
@@ -238,7 +241,7 @@ public class ButtonBinding implements Nameable
}
for (int i = this.actions.size() - 1; i >= 0; i--) {
if (this.actions.get(i).press(client, this, state))
if (this.actions.get(i).press(client, this, value, state))
break;
}
}
@@ -254,8 +257,7 @@ public class ButtonBinding implements Nameable
*
* @return The translation key.
*/
public @NotNull
String getTranslationKey()
public @NotNull String getTranslationKey()
{
return "lambdacontrols.action." + this.getName();
}
@@ -283,6 +285,18 @@ public class ButtonBinding implements Nameable
return positive ? 100 + axis : 200 + axis;
}
/**
* Returns whether the specified button is an axis or not.
*
* @param button The button.
* @return True if the button is an axis, else false.
*/
public static boolean isAxis(int button)
{
button %= 500;
return button >= 100;
}
/**
* Returns the second Joycon's specified button code.
*

View File

@@ -10,6 +10,7 @@
package me.lambdaurora.lambdacontrols.client.controller;
import me.lambdaurora.lambdacontrols.client.ButtonState;
import me.lambdaurora.lambdacontrols.client.LambdaInput;
import me.lambdaurora.lambdacontrols.client.mixin.AdvancementsScreenAccessor;
import me.lambdaurora.lambdacontrols.client.mixin.CreativeInventoryScreenAccessor;
import me.lambdaurora.lambdacontrols.client.mixin.RecipeBookWidgetAccessor;
@@ -38,7 +39,7 @@ import java.util.stream.Collectors;
* Represents some input handlers.
*
* @author LambdAurora
* @version 1.3.0
* @version 1.4.0
* @since 1.1.0
*/
public class InputHandlers
@@ -49,7 +50,7 @@ public class InputHandlers
public static PressAction handleHotbar(boolean next)
{
return (client, button, action) -> {
return (client, button, value, action) -> {
if (action == ButtonState.RELEASE)
return false;
@@ -74,6 +75,8 @@ public class InputHandlers
RecipeBookWidgetAccessor recipeBook = (RecipeBookWidgetAccessor) ((InventoryScreen) client.currentScreen).getRecipeBookWidget();
List<RecipeGroupButtonWidget> tabs = recipeBook.getTabButtons();
RecipeGroupButtonWidget currentTab = recipeBook.getCurrentTab();
if (currentTab == null)
return false;
int nextTab = tabs.indexOf(currentTab) + (next ? 1 : -1);
if (nextTab < 0)
nextTab = tabs.size() - 1;
@@ -83,10 +86,13 @@ public class InputHandlers
recipeBook.setCurrentTab(currentTab = tabs.get(nextTab));
currentTab.setToggled(true);
recipeBook.lambdacontrols_refreshResults(true);
return true;
} else if (client.currentScreen instanceof AdvancementsScreen) {
AdvancementsScreenAccessor screen = (AdvancementsScreenAccessor) client.currentScreen;
List<AdvancementTab> tabs = screen.getTabs().values().stream().distinct().collect(Collectors.toList());
AdvancementTab tab = screen.getSelectedTab();
if (tab == null)
return false;
for (int i = 0; i < tabs.size(); i++) {
if (tabs.get(i).equals(tab)) {
int nextTab = i + (next ? 1 : -1);
@@ -98,12 +104,13 @@ public class InputHandlers
break;
}
}
return true;
}
return false;
};
}
public static boolean handlePauseGame(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, @NotNull ButtonState action)
public static boolean handlePauseGame(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, float value, @NotNull ButtonState action)
{
if (action == ButtonState.PRESS) {
// If in game, then pause the game.
@@ -125,15 +132,15 @@ public class InputHandlers
* @param action The action done on the binding.
* @return True if handled, else false.
*/
public static boolean handleScreenshot(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, @NotNull ButtonState action)
public static boolean handleScreenshot(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, float value, @NotNull ButtonState action)
{
if (action == ButtonState.PRESS)
if (action == ButtonState.RELEASE)
ScreenshotUtils.saveScreenshot(client.runDirectory, client.getWindow().getFramebufferWidth(), client.getWindow().getFramebufferHeight(), client.getFramebuffer(),
text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text)));
return true;
}
public static boolean handleToggleSneak(@NotNull MinecraftClient client, @NotNull ButtonBinding button, @NotNull ButtonState action)
public static boolean handleToggleSneak(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action)
{
if (client.player != null && !client.player.abilities.flying) {
button.asKeyBinding().filter(binding -> action == ButtonState.PRESS).ifPresent(binding -> ((KeyBindingAccessor) binding).lambdacontrols_handlePressState(!binding.isPressed()));
@@ -144,7 +151,7 @@ public class InputHandlers
public static PressAction handleInventorySlotPad(int direction)
{
return (client, binding, action) -> {
return (client, binding, value, action) -> {
if (!(client.currentScreen instanceof HandledScreen && action != ButtonState.RELEASE))
return false;
@@ -235,6 +242,20 @@ public class InputHandlers
return client.currentScreen == null;
}
/**
* Returns whether the client is in a non-interactive screen (which means require mouse input) or not.
*
* @param client The client instance.
* @param binding The affected binding.
* @return True if the client is in a non-interactive screen, else false.
*/
public static boolean inNonInteractiveScreens(@NotNull MinecraftClient client, @NotNull ButtonBinding binding)
{
if (client.currentScreen == null)
return false;
return !LambdaInput.isScreenInteractive(client.currentScreen);
}
/**
* Returns whether the client is in an inventory or not.
*

View File

@@ -19,6 +19,7 @@ import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.util.InputUtil;
import net.minecraft.util.math.MathHelper;
import org.aperlambda.lambdacommon.Identifier;
import org.aperlambda.lambdacommon.utils.Pair;
import org.aperlambda.lambdacommon.utils.function.PairPredicate;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
@@ -32,7 +33,7 @@ import java.util.stream.Stream;
* Represents an input manager for controllers.
*
* @author LambdAurora
* @version 1.3.0
* @version 1.4.0
* @since 1.1.0
*/
public class InputManager
@@ -41,6 +42,7 @@ public class InputManager
private static final List<ButtonBinding> BINDINGS = new ArrayList<>();
private static final List<ButtonCategory> CATEGORIES = new ArrayList<>();
public static final Map<Integer, ButtonState> STATES = new HashMap<>();
public static final Map<Integer, Float> BUTTON_VALUES = new HashMap<>();
private int prevTargetMouseX = 0;
private int prevTargetMouseY = 0;
private int targetMouseX = 0;
@@ -244,6 +246,23 @@ public class InputManager
return state;
}
public static float getBindingValue(@NotNull ButtonBinding binding, @NotNull ButtonState state)
{
if (state.isUnpressed())
return 0.f;
float value = 0.f;
for (int btn : binding.getButton()) {
if (ButtonBinding.isAxis(btn)) {
value = BUTTON_VALUES.getOrDefault(btn, 1.f);
break;
} else {
value = 1.f;
}
}
return value;
}
/**
* Returns whether the button has duplicated bindings.
*
@@ -317,7 +336,7 @@ public class InputManager
public static void updateBindings(@NotNull MinecraftClient client)
{
List<Integer> skipButtons = new ArrayList<>();
Map<ButtonBinding, ButtonState> states = new HashMap<>();
Map<ButtonBinding, Pair<ButtonState, Float>> states = new HashMap<>();
for (ButtonBinding binding : BINDINGS) {
ButtonState state = binding.isAvailable(client) ? getBindingState(binding) : ButtonState.NONE;
if (skipButtons.stream().anyMatch(btn -> containsButton(binding.getButton(), btn))) {
@@ -330,12 +349,15 @@ public class InputManager
binding.update();
if (binding.pressed)
Arrays.stream(binding.getButton()).forEach(skipButtons::add);
states.put(binding, state);
float value = getBindingValue(binding, state);
states.put(binding, Pair.of(state, value));
}
states.forEach((binding, state) -> {
if (state != ButtonState.NONE) {
binding.handle(client, state);
if (state.key != ButtonState.NONE) {
binding.handle(client, state.value, state.key);
}
});
}
@@ -363,12 +385,12 @@ public class InputManager
/**
* Returns a new key binding instance.
* @param id The identifier of the key binding.
* @param type The type.
* @param code The code.
*
* @param id The identifier of the key binding.
* @param type The type.
* @param code The code.
* @param category The category of the key binding.
* @return The key binding.
*
* @see #makeKeyBinding(Identifier, InputUtil.Type, int, String)
*/
public static @NotNull KeyBinding makeKeyBinding(@NotNull net.minecraft.util.Identifier id, InputUtil.Type type, int code, @NotNull String category)
@@ -378,12 +400,12 @@ public class InputManager
/**
* Returns a new key binding instance.
* @param id The identifier of the key binding.
* @param type The type.
* @param code The code.
*
* @param id The identifier of the key binding.
* @param type The type.
* @param code The code.
* @param category The category of the key binding.
* @return The key binding.
*
* @see #makeKeyBinding(net.minecraft.util.Identifier, InputUtil.Type, int, String)
*/
public static @NotNull KeyBinding makeKeyBinding(@NotNull Identifier id, InputUtil.Type type, int code, @NotNull String category)

View File

@@ -0,0 +1,98 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.controller;
import me.lambdaurora.lambdacontrols.client.ButtonState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import org.jetbrains.annotations.NotNull;
/**
* Represents the movement handler.
*
* @author LambdAurora
* @version 1.4.0
* @since 1.4.0
*/
public final class MovementHandler implements PressAction
{
public static final MovementHandler HANDLER = new MovementHandler();
private boolean shouldOverrideMovement = false;
private boolean pressingForward = false;
private boolean pressingBack = false;
private boolean pressingLeft = false;
private boolean pressingRight = false;
private float movementForward = 0.f;
private float movementSideways = 0.f;
private MovementHandler()
{
}
/**
* Applies movement input of this handler to the player's input.
*
* @param player The client player.
*/
public void applyMovement(@NotNull ClientPlayerEntity player)
{
if (!this.shouldOverrideMovement)
return;
player.input.pressingForward = this.pressingForward;
player.input.pressingBack = this.pressingBack;
player.input.pressingLeft = this.pressingLeft;
player.input.pressingRight = this.pressingRight;
player.input.movementForward = this.movementForward;
player.input.movementSideways = this.movementSideways;
this.shouldOverrideMovement = false;
}
@Override
public boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action)
{
if (client.currentScreen != null || client.player == null)
return this.shouldOverrideMovement = false;
int direction = 0;
if (button == ButtonBinding.FORWARD || button == ButtonBinding.LEFT)
direction = 1;
else if (button == ButtonBinding.BACK || button == ButtonBinding.RIGHT)
direction = -1;
if (direction == 0)
return false;
this.shouldOverrideMovement = true;
value = (float) Math.pow(value, 2);
if (button == ButtonBinding.FORWARD || button == ButtonBinding.BACK) {
// Handle forward movement.
this.pressingForward = direction > 0;
this.pressingBack = direction < 0;
this.movementForward = direction * value;
// Slowing down if sneaking.
if (client.player.input.sneaking)
this.movementForward *= 0.3D;
} else {
// Handle sideways movement.
this.pressingLeft = direction > 0;
this.pressingRight = direction < 0;
this.movementSideways = direction * value;
// Slowing down if sneaking.
if (client.player.input.sneaking)
this.movementSideways *= 0.3D;
}
return true;
}
}

View File

@@ -18,13 +18,13 @@ import org.jetbrains.annotations.NotNull;
* Represents a press action callback.
*
* @author LambdAurora
* @version 1.1.0
* @version 1.4.0
* @since 1.0.0
*/
@FunctionalInterface
public interface PressAction
{
PressAction DEFAULT_ACTION = (client, button, action) -> {
PressAction DEFAULT_ACTION = (client, button, value, action) -> {
if (action == ButtonState.REPEAT || client.currentScreen != null)
return false;
button.asKeyBinding().ifPresent(binding -> ((KeyBindingAccessor) binding).lambdacontrols_handlePressState(button.isButtonDown()));
@@ -37,5 +37,5 @@ public interface PressAction
* @param client The client instance.
* @param action The action done.
*/
boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, @NotNull ButtonState action);
boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action);
}

View File

@@ -11,6 +11,7 @@ package me.lambdaurora.lambdacontrols.client.mixin;
import com.mojang.authlib.GameProfile;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.MovementHandler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.input.Input;
import net.minecraft.client.network.AbstractClientPlayerEntity;
@@ -69,6 +70,12 @@ public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity
}
}
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick(Z)V", shift = At.Shift.AFTER))
public void onInputUpdate(CallbackInfo ci)
{
MovementHandler.HANDLER.applyMovement((ClientPlayerEntity) (Object) this);
}
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isCamera()Z"))
public void onTickMovement(CallbackInfo ci)
{

View File

@@ -17,7 +17,6 @@ import net.minecraft.client.gui.screen.options.GameOptionsScreen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import org.spongepowered.asm.mixin.Mixin;

View File

@@ -22,15 +22,12 @@ import net.minecraft.screen.slot.SlotActionType;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Arrays;
/**
* Represents the mixin for the class ContainerScreen.
*/

View File

@@ -0,0 +1,46 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.ring;
import me.lambdaurora.lambdacontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.options.KeyBinding;
import org.jetbrains.annotations.NotNull;
public class KeyBindingRingAction extends RingAction
{
public final KeyBinding binding;
public KeyBindingRingAction(@NotNull KeyBinding binding)
{
this.binding = binding;
}
@Override
public @NotNull String getName()
{
return this.binding.getTranslationKey();
}
@Override
public void onAction(@NotNull RingButtonMode mode)
{
KeyBindingAccessor accessor = (KeyBindingAccessor) this.binding;
switch (mode) {
case PRESS:
case HOLD:
accessor.lambdacontrols_handlePressState(this.activated);
break;
case TOGGLE:
accessor.lambdacontrols_handlePressState(!this.binding.isPressed());
this.activated = !this.binding.isPressed();
break;
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.ring;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Represents a key binding ring.
*
* @author LambdAurora
* @version 1.4.0
* @since 1.4.0
*/
public class LambdaRing
{
private final List<RingPage> pages = new ArrayList<>(Collections.singletonList(new RingPage()));
private int currentPage = 0;
public LambdaRing()
{
}
public @NotNull RingPage getCurrentPage()
{
if (this.currentPage >= this.pages.size())
this.currentPage = this.pages.size() - 1;
else if (this.currentPage < 0)
this.currentPage = 0;
return this.pages.get(this.currentPage);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.ring;
import net.minecraft.text.TranslatableText;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
/**
* Represents a ring action.
*
* @author LambdAurora
* @version 1.4.0
* @since 1.4.0
*/
public abstract class RingAction implements Nameable
{
protected boolean activated = false;
/**
* Gets the translated name of the ring action.
*
* @return The translated name.
*/
public TranslatableText getTranslatedName()
{
return new TranslatableText(this.getName());
}
/**
* Returns whether the action is activated or not.
*
* @return True if the action is activated, else false.
*/
public boolean isActivated()
{
return this.activated;
}
public void activate(@NotNull RingButtonMode mode)
{
this.activated = !this.activated;
this.onAction(mode);
}
public abstract void onAction(@NotNull RingButtonMode mode);
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.ring;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
/**
* Represents the mode of a ring button.
*
* @author LambdAurora
* @version 1.4.0
* @since 1.4.0
*/
public enum RingButtonMode implements Nameable
{
PRESS("press"),
HOLD("hold"),
TOGGLE("toggle");
private String name;
RingButtonMode(@NotNull String name)
{
this.name = name;
}
/**
* Returns the next ring button mode available.
*
* @return The next ring button mode.
*/
public RingButtonMode next()
{
RingButtonMode[] v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
/**
* Gets the translated name of this ring button mode.
*
* @return The translated name of this ring button mode.
*/
public String getTranslatedName()
{
return I18n.translate("lambdacontrols.ring.button_mode." + this.getName());
}
@Override
public @NotNull String getName()
{
return this.name;
}
}

View File

@@ -10,13 +10,15 @@
package me.lambdaurora.lambdacontrols.client.ring;
/**
* Represents a key binding ring.
* Represents a ring page.
*
* @author LambdAurora
* @version 1.4.0
* @since 1.4.0
*/
public class KeyBindingRing
public class RingPage
{
private RingAction[] actions = new RingAction[8];
}