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

@@ -11,7 +11,6 @@ package me.lambdaurora.lambdacontrols.client;
import io.github.prospector.modmenu.api.ConfigScreenFactory; import io.github.prospector.modmenu.api.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi; import io.github.prospector.modmenu.api.ModMenuApi;
import me.lambdaurora.lambdacontrols.LambdaControlsConstants;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsSettingsScreen; 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.ingame.HandledScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget; 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.screen.world.WorldListWidget;
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget; import net.minecraft.client.gui.widget.AbstractPressableButtonWidget;
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget;
@@ -63,7 +64,7 @@ import static org.lwjgl.glfw.GLFW.*;
* Represents the LambdaControls' input handler. * Represents the LambdaControls' input handler.
* *
* @author LambdAurora * @author LambdAurora
* @version 1.3.2 * @version 1.4.0
* @since 1.0.0 * @since 1.0.0
*/ */
public class LambdaInput public class LambdaInput
@@ -81,6 +82,7 @@ public class LambdaInput
private int targetMouseY = 0; private int targetMouseY = 0;
private float mouseSpeedX = 0.F; private float mouseSpeedX = 0.F;
private float mouseSpeedY = 0.F; private float mouseSpeedY = 0.F;
private int inventoryInteractionCooldown = 0;
public LambdaInput(@NotNull LambdaControlsClient mod) public LambdaInput(@NotNull LambdaControlsClient mod)
{ {
@@ -157,6 +159,9 @@ public class LambdaInput
screen.focusedBinding = null; 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); ((MouseAccessor) client.mouse).lambdacontrols_onCursorPos(client.getWindow().getHandle(), 0, 0);
INPUT_MANAGER.resetMouseTarget(client); INPUT_MANAGER.resetMouseTarget(client);
} }
this.inventoryInteractionCooldown = 5;
} }
private void fetchButtonInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon) 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 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(); double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
if (action == 0) { 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) { } 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; this.actionGuiCooldown = 5;
} else { } else {
@@ -355,6 +363,9 @@ public class LambdaInput
if (client.interactionManager == null || client.player == null) if (client.interactionManager == null || client.player == null)
return false; return false;
if (this.inventoryInteractionCooldown > 0)
return true;
if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) { if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) {
client.player.closeHandledScreen(); client.player.closeHandledScreen();
return true; return true;
@@ -457,6 +468,17 @@ public class LambdaInput
BUTTON_COOLDOWNS.put(axisAsButton(axis, false), 5); 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(); double deadZone = this.config.getDeadZone();
@@ -481,6 +503,7 @@ public class LambdaInput
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) { if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
CreativeInventoryScreen screen = (CreativeInventoryScreen) client.currentScreen; CreativeInventoryScreen screen = (CreativeInventoryScreen) client.currentScreen;
CreativeInventoryScreenAccessor accessor = (CreativeInventoryScreenAccessor) screen; CreativeInventoryScreenAccessor accessor = (CreativeInventoryScreenAccessor) screen;
// @TODO allow rebinding to left stick
if (accessor.lambdacontrols_hasScrollbar() && absValue >= deadZone) { if (accessor.lambdacontrols_hasScrollbar() && absValue >= deadZone) {
screen.mouseScrolled(0.0, 0.0, -value); screen.mouseScrolled(0.0, 0.0, -value);
} }
@@ -677,7 +700,7 @@ public class LambdaInput
public static boolean isScreenInteractive(@NotNull Screen screen) 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. // 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; private String name;
VirtualMouseSkin(String name) { VirtualMouseSkin(String name)
{
this.name = 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.MinecraftClient;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View File

@@ -18,7 +18,7 @@ import java.util.Optional;
/** /**
* Represents HQM compatibility handler. * Represents HQM compatibility handler.
* * <p>
* This is bad. * This is bad.
* *
* @author LambdAurora * @author LambdAurora

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,6 @@ import me.lambdaurora.lambdacontrols.client.ButtonState;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.options.GameOptions; import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.KeyBinding; import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import org.aperlambda.lambdacommon.Identifier; import org.aperlambda.lambdacommon.Identifier;
@@ -33,7 +32,7 @@ import static org.lwjgl.glfw.GLFW.*;
* Represents a button binding. * Represents a button binding.
* *
* @author LambdAurora * @author LambdAurora
* @version 1.3.0 * @version 1.4.0
* @since 1.0.0 * @since 1.0.0
*/ */
public class ButtonBinding implements Nameable public class ButtonBinding implements Nameable
@@ -45,21 +44,25 @@ public class ButtonBinding implements Nameable
public static final ButtonCategory MISC_CATEGORY; 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 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 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 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) public static final ButtonBinding HOTBAR_LEFT = new Builder("hotbar_left").buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
.action(InputHandlers.handleHotbar(false)).onlyInGame().cooldown(true).register(); .action(InputHandlers.handleHotbar(false)).onlyInGame().cooldown(true).register();
public static final ButtonBinding HOTBAR_RIGHT = new Builder("hotbar_right").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) public static final ButtonBinding HOTBAR_RIGHT = new Builder("hotbar_right").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
.action(InputHandlers.handleHotbar(true)).onlyInGame().cooldown(true).register(); .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 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 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 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 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 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) public static final ButtonBinding SCREENSHOT = new Builder("screenshot").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_A)
.action(InputHandlers::handleScreenshot).cooldown(true).register(); .action(InputHandlers::handleScreenshot).cooldown(true).register();
public static final ButtonBinding SLOT_DOWN = new Builder("slot_down").buttons(GLFW_GAMEPAD_BUTTON_DPAD_DOWN) 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 client The client instance.
* @param state The state. * @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) if (state == ButtonState.REPEAT && this.hasCooldown && this.cooldown != 0)
return; return;
@@ -238,7 +241,7 @@ public class ButtonBinding implements Nameable
} }
for (int i = this.actions.size() - 1; i >= 0; i--) { 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; break;
} }
} }
@@ -254,8 +257,7 @@ public class ButtonBinding implements Nameable
* *
* @return The translation key. * @return The translation key.
*/ */
public @NotNull public @NotNull String getTranslationKey()
String getTranslationKey()
{ {
return "lambdacontrols.action." + this.getName(); return "lambdacontrols.action." + this.getName();
} }
@@ -283,6 +285,18 @@ public class ButtonBinding implements Nameable
return positive ? 100 + axis : 200 + axis; 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. * Returns the second Joycon's specified button code.
* *

View File

@@ -10,6 +10,7 @@
package me.lambdaurora.lambdacontrols.client.controller; package me.lambdaurora.lambdacontrols.client.controller;
import me.lambdaurora.lambdacontrols.client.ButtonState; 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.AdvancementsScreenAccessor;
import me.lambdaurora.lambdacontrols.client.mixin.CreativeInventoryScreenAccessor; import me.lambdaurora.lambdacontrols.client.mixin.CreativeInventoryScreenAccessor;
import me.lambdaurora.lambdacontrols.client.mixin.RecipeBookWidgetAccessor; import me.lambdaurora.lambdacontrols.client.mixin.RecipeBookWidgetAccessor;
@@ -38,7 +39,7 @@ import java.util.stream.Collectors;
* Represents some input handlers. * Represents some input handlers.
* *
* @author LambdAurora * @author LambdAurora
* @version 1.3.0 * @version 1.4.0
* @since 1.1.0 * @since 1.1.0
*/ */
public class InputHandlers public class InputHandlers
@@ -49,7 +50,7 @@ public class InputHandlers
public static PressAction handleHotbar(boolean next) public static PressAction handleHotbar(boolean next)
{ {
return (client, button, action) -> { return (client, button, value, action) -> {
if (action == ButtonState.RELEASE) if (action == ButtonState.RELEASE)
return false; return false;
@@ -74,6 +75,8 @@ public class InputHandlers
RecipeBookWidgetAccessor recipeBook = (RecipeBookWidgetAccessor) ((InventoryScreen) client.currentScreen).getRecipeBookWidget(); RecipeBookWidgetAccessor recipeBook = (RecipeBookWidgetAccessor) ((InventoryScreen) client.currentScreen).getRecipeBookWidget();
List<RecipeGroupButtonWidget> tabs = recipeBook.getTabButtons(); List<RecipeGroupButtonWidget> tabs = recipeBook.getTabButtons();
RecipeGroupButtonWidget currentTab = recipeBook.getCurrentTab(); RecipeGroupButtonWidget currentTab = recipeBook.getCurrentTab();
if (currentTab == null)
return false;
int nextTab = tabs.indexOf(currentTab) + (next ? 1 : -1); int nextTab = tabs.indexOf(currentTab) + (next ? 1 : -1);
if (nextTab < 0) if (nextTab < 0)
nextTab = tabs.size() - 1; nextTab = tabs.size() - 1;
@@ -83,10 +86,13 @@ public class InputHandlers
recipeBook.setCurrentTab(currentTab = tabs.get(nextTab)); recipeBook.setCurrentTab(currentTab = tabs.get(nextTab));
currentTab.setToggled(true); currentTab.setToggled(true);
recipeBook.lambdacontrols_refreshResults(true); recipeBook.lambdacontrols_refreshResults(true);
return true;
} else if (client.currentScreen instanceof AdvancementsScreen) { } else if (client.currentScreen instanceof AdvancementsScreen) {
AdvancementsScreenAccessor screen = (AdvancementsScreenAccessor) client.currentScreen; AdvancementsScreenAccessor screen = (AdvancementsScreenAccessor) client.currentScreen;
List<AdvancementTab> tabs = screen.getTabs().values().stream().distinct().collect(Collectors.toList()); List<AdvancementTab> tabs = screen.getTabs().values().stream().distinct().collect(Collectors.toList());
AdvancementTab tab = screen.getSelectedTab(); AdvancementTab tab = screen.getSelectedTab();
if (tab == null)
return false;
for (int i = 0; i < tabs.size(); i++) { for (int i = 0; i < tabs.size(); i++) {
if (tabs.get(i).equals(tab)) { if (tabs.get(i).equals(tab)) {
int nextTab = i + (next ? 1 : -1); int nextTab = i + (next ? 1 : -1);
@@ -98,12 +104,13 @@ public class InputHandlers
break; break;
} }
} }
return true;
} }
return false; 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 (action == ButtonState.PRESS) {
// If in game, then pause the game. // If in game, then pause the game.
@@ -125,15 +132,15 @@ public class InputHandlers
* @param action The action done on the binding. * @param action The action done on the binding.
* @return True if handled, else false. * @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(), ScreenshotUtils.saveScreenshot(client.runDirectory, client.getWindow().getFramebufferWidth(), client.getWindow().getFramebufferHeight(), client.getFramebuffer(),
text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text))); text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text)));
return true; 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) { if (client.player != null && !client.player.abilities.flying) {
button.asKeyBinding().filter(binding -> action == ButtonState.PRESS).ifPresent(binding -> ((KeyBindingAccessor) binding).lambdacontrols_handlePressState(!binding.isPressed())); 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) public static PressAction handleInventorySlotPad(int direction)
{ {
return (client, binding, action) -> { return (client, binding, value, action) -> {
if (!(client.currentScreen instanceof HandledScreen && action != ButtonState.RELEASE)) if (!(client.currentScreen instanceof HandledScreen && action != ButtonState.RELEASE))
return false; return false;
@@ -235,6 +242,20 @@ public class InputHandlers
return client.currentScreen == null; 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. * 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.client.util.InputUtil;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import org.aperlambda.lambdacommon.Identifier; import org.aperlambda.lambdacommon.Identifier;
import org.aperlambda.lambdacommon.utils.Pair;
import org.aperlambda.lambdacommon.utils.function.PairPredicate; import org.aperlambda.lambdacommon.utils.function.PairPredicate;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@@ -32,7 +33,7 @@ import java.util.stream.Stream;
* Represents an input manager for controllers. * Represents an input manager for controllers.
* *
* @author LambdAurora * @author LambdAurora
* @version 1.3.0 * @version 1.4.0
* @since 1.1.0 * @since 1.1.0
*/ */
public class InputManager public class InputManager
@@ -41,6 +42,7 @@ public class InputManager
private static final List<ButtonBinding> BINDINGS = new ArrayList<>(); private static final List<ButtonBinding> BINDINGS = new ArrayList<>();
private static final List<ButtonCategory> CATEGORIES = new ArrayList<>(); private static final List<ButtonCategory> CATEGORIES = new ArrayList<>();
public static final Map<Integer, ButtonState> STATES = new HashMap<>(); 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 prevTargetMouseX = 0;
private int prevTargetMouseY = 0; private int prevTargetMouseY = 0;
private int targetMouseX = 0; private int targetMouseX = 0;
@@ -244,6 +246,23 @@ public class InputManager
return state; 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. * Returns whether the button has duplicated bindings.
* *
@@ -317,7 +336,7 @@ public class InputManager
public static void updateBindings(@NotNull MinecraftClient client) public static void updateBindings(@NotNull MinecraftClient client)
{ {
List<Integer> skipButtons = new ArrayList<>(); List<Integer> skipButtons = new ArrayList<>();
Map<ButtonBinding, ButtonState> states = new HashMap<>(); Map<ButtonBinding, Pair<ButtonState, Float>> states = new HashMap<>();
for (ButtonBinding binding : BINDINGS) { for (ButtonBinding binding : BINDINGS) {
ButtonState state = binding.isAvailable(client) ? getBindingState(binding) : ButtonState.NONE; ButtonState state = binding.isAvailable(client) ? getBindingState(binding) : ButtonState.NONE;
if (skipButtons.stream().anyMatch(btn -> containsButton(binding.getButton(), btn))) { if (skipButtons.stream().anyMatch(btn -> containsButton(binding.getButton(), btn))) {
@@ -330,12 +349,15 @@ public class InputManager
binding.update(); binding.update();
if (binding.pressed) if (binding.pressed)
Arrays.stream(binding.getButton()).forEach(skipButtons::add); 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) -> { states.forEach((binding, state) -> {
if (state != ButtonState.NONE) { if (state.key != ButtonState.NONE) {
binding.handle(client, state); binding.handle(client, state.value, state.key);
} }
}); });
} }
@@ -363,12 +385,12 @@ public class InputManager
/** /**
* Returns a new key binding instance. * Returns a new key binding instance.
*
* @param id The identifier of the key binding. * @param id The identifier of the key binding.
* @param type The type. * @param type The type.
* @param code The code. * @param code The code.
* @param category The category of the key binding. * @param category The category of the key binding.
* @return The key binding. * @return The key binding.
*
* @see #makeKeyBinding(Identifier, InputUtil.Type, int, String) * @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) 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. * Returns a new key binding instance.
*
* @param id The identifier of the key binding. * @param id The identifier of the key binding.
* @param type The type. * @param type The type.
* @param code The code. * @param code The code.
* @param category The category of the key binding. * @param category The category of the key binding.
* @return The key binding. * @return The key binding.
*
* @see #makeKeyBinding(net.minecraft.util.Identifier, InputUtil.Type, int, String) * @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) 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. * Represents a press action callback.
* *
* @author LambdAurora * @author LambdAurora
* @version 1.1.0 * @version 1.4.0
* @since 1.0.0 * @since 1.0.0
*/ */
@FunctionalInterface @FunctionalInterface
public interface PressAction public interface PressAction
{ {
PressAction DEFAULT_ACTION = (client, button, action) -> { PressAction DEFAULT_ACTION = (client, button, value, action) -> {
if (action == ButtonState.REPEAT || client.currentScreen != null) if (action == ButtonState.REPEAT || client.currentScreen != null)
return false; return false;
button.asKeyBinding().ifPresent(binding -> ((KeyBindingAccessor) binding).lambdacontrols_handlePressState(button.isButtonDown())); button.asKeyBinding().ifPresent(binding -> ((KeyBindingAccessor) binding).lambdacontrols_handlePressState(button.isButtonDown()));
@@ -37,5 +37,5 @@ public interface PressAction
* @param client The client instance. * @param client The client instance.
* @param action The action done. * @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 com.mojang.authlib.GameProfile;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient; import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.MovementHandler;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.input.Input; import net.minecraft.client.input.Input;
import net.minecraft.client.network.AbstractClientPlayerEntity; 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")) @Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isCamera()Z"))
public void onTickMovement(CallbackInfo ci) 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.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.options.GameOptions; import net.minecraft.client.options.GameOptions;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import org.spongepowered.asm.mixin.Mixin; 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.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Mixin; 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.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.asm.mixin.gen.Invoker;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Arrays;
/** /**
* Represents the mixin for the class ContainerScreen. * 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; package me.lambdaurora.lambdacontrols.client.ring;
/** /**
* Represents a key binding ring. * Represents a ring page.
* *
* @author LambdAurora * @author LambdAurora
* @version 1.4.0 * @version 1.4.0
* @since 1.4.0 * @since 1.4.0
*/ */
public class KeyBindingRing public class RingPage
{ {
private RingAction[] actions = new RingAction[8];
} }