From 1b5a0e470e5fca116ee22c9ed9f7130c3718141b Mon Sep 17 00:00:00 2001 From: LambdAurora Date: Wed, 1 Jan 2020 14:54:51 +0100 Subject: [PATCH] :sparkles: Add working in-game chording, WIP controls screen. --- build.gradle | 26 +++- .../lambdacontrols/LambdaControls.java | 2 +- .../lambdacontrols/LambdaInput.java | 35 +++--- .../lambdacontrols/compat/CompatHandler.java | 30 +++++ .../compat/LambdaControlsCompat.java | 23 ++-- .../lambdacontrols/compat/OkZoomerCompat.java | 40 ++++++ .../controller/ButtonBinding.java | 26 ++-- .../controller/InputHandlers.java | 39 +++++- .../controller/InputManager.java | 116 +++++++++++++++--- .../gui/ControlsListWidget.java | 5 +- .../gui/LambdaControlsControlsScreen.java | 23 +--- .../gui/LambdaControlsSettingsScreen.java | 97 +++++++++------ .../assets/lambdacontrols/lang/en_us.json | 1 + .../assets/lambdacontrols/lang/fr_ca.json | 1 + .../assets/lambdacontrols/lang/fr_fr.json | 1 + src/main/resources/fabric.mod.json | 3 +- 16 files changed, 336 insertions(+), 132 deletions(-) create mode 100644 src/main/java/me/lambdaurora/lambdacontrols/compat/CompatHandler.java create mode 100644 src/main/java/me/lambdaurora/lambdacontrols/compat/OkZoomerCompat.java diff --git a/build.gradle b/build.gradle index e9b086d..1127153 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,26 @@ repositories { repositories { maven { url = "https://jitpack.io" } } + // SpruceUI + ivy { + url 'https://github.com/LambdAurora/SpruceUI/releases/download/' + patternLayout { + artifact '[revision]/[module]-[version].[ext]' + } + metadataSources() { + artifact() + } + } + // OkZoomer + ivy { + url 'https://github.com/joaoh1/OkZoomer/releases/download/' + patternLayout { + artifact '[revision]/[module]-[version].[ext]' + } + metadataSources() { + artifact() + } + } } dependencies { @@ -40,6 +60,9 @@ dependencies { include "me.lambdaurora:spruceui:${project.spruceui_version}" //modCompile "io.github.cottonmc:cotton-client-commands:0.4.2+1.14.3-SNAPSHOT" + // Compatibility mods + modCompile "io.github.joaoh1:okzoomer:1.0.3" + implementation "org.jetbrains:annotations:17.0.0" implementation "org.aperlambda:lambdajcommon:1.7.2" implementation "com.electronwill.night-config:core:3.5.3" @@ -48,9 +71,6 @@ dependencies { include "org.aperlambda:lambdajcommon:1.7.2" include "com.electronwill.night-config:core:3.5.3" include "com.electronwill.night-config:toml:3.5.3" - - // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. - // You may need to force-disable transitiveness on them. } processResources { diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java index 5695506..9ccb482 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java @@ -97,7 +97,7 @@ public class LambdaControls implements ClientModInitializer this.hud = new LambdaControlsHud(client, this); - LambdaControlsCompat.init(); + LambdaControlsCompat.init(this); } /** diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaInput.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaInput.java index 562dbfd..47bdfac 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaInput.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaInput.java @@ -59,7 +59,6 @@ import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y; */ public class LambdaInput { - private static final Map BUTTON_STATES = new HashMap<>(); private static final Map BUTTON_COOLDOWNS = new HashMap<>(); private final LambdaControlsConfig config; // Cooldowns @@ -136,11 +135,22 @@ public class LambdaInput this.fetch_axe_input(client, state, true); }); - InputManager.update_bindings(); - InputManager.stream_active_bindings().forEach(binding -> binding.handle(client, InputManager.get_binding_state(binding))); + InputManager.update_bindings(client); if (this.ignore_next_a > 0) this.ignore_next_a--; + + if (client.currentScreen instanceof LambdaControlsControlsScreen && InputManager.STATES.entrySet().parallelStream().map(Map.Entry::getValue).allMatch(ButtonState::is_unpressed)) + { + LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen; + if (controls_screen.focused_binding != null) { + int[] buttons = new int[controls_screen.current_buttons.size()]; + for (int i = 0; i < controls_screen.current_buttons.size(); i++) + buttons[i] = controls_screen.current_buttons.get(i); + controls_screen.focused_binding.set_button(buttons); + controls_screen.focused_binding = null; + } + } } /** @@ -236,31 +246,17 @@ public class LambdaInput if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y) value *= -1.0F; - //InputManager.(axis_as_button(axis, true), value > 0.5F); - //ButtonBinding.set_button_state(axis_as_button(axis, false), value < -0.5F); - int state = value > this.config.get_dead_zone() ? 1 : (value < -this.config.get_dead_zone() ? 2 : 0); this.handle_axe(client, axis, value, abs_value, state); } } - public boolean are_buttons_pressed(int[] buttons) - { - int i = 0; - for (int btn : buttons) { - if (BUTTON_STATES.containsKey(btn) && BUTTON_STATES.get(btn)) - i++; - } - return i == buttons.length; - } - private void handle_button(@NotNull MinecraftClient client, int button, int action, boolean state) { if (client.currentScreen instanceof LambdaControlsControlsScreen && action == 0) { LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen; if (controls_screen.focused_binding != null) { - this.config.set_button_binding(controls_screen.focused_binding, new int[]{button}); - controls_screen.focused_binding = null; + controls_screen.current_buttons.add(button); return; } } @@ -344,8 +340,7 @@ public class LambdaInput axis == ButtonBinding.controller2_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER) || axis == ButtonBinding.controller2_button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER)))) { LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen; if (controls_screen.focused_binding != null) { - this.config.set_button_binding(controls_screen.focused_binding, new int[]{axis_as_button(axis, as_button_state == 1)}); - controls_screen.focused_binding = null; + controls_screen.current_buttons.add(axis_as_button(axis, as_button_state == 1)); return; } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/compat/CompatHandler.java b/src/main/java/me/lambdaurora/lambdacontrols/compat/CompatHandler.java new file mode 100644 index 0000000..6a4f41d --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/compat/CompatHandler.java @@ -0,0 +1,30 @@ +/* + * Copyright © 2020 LambdAurora + * + * This file is part of LambdaControls. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package me.lambdaurora.lambdacontrols.compat; + +import me.lambdaurora.lambdacontrols.LambdaControls; +import org.jetbrains.annotations.NotNull; + +/** + * Represents a compatibility handler for a mod. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.1.0 + */ +public interface CompatHandler +{ + /** + * Handles compatibility of a mod. + * + * @param mod This mod instance. + */ + void handle(@NotNull LambdaControls mod); +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/compat/LambdaControlsCompat.java b/src/main/java/me/lambdaurora/lambdacontrols/compat/LambdaControlsCompat.java index d4ba469..7ab1f93 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/compat/LambdaControlsCompat.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/compat/LambdaControlsCompat.java @@ -9,33 +9,34 @@ package me.lambdaurora.lambdacontrols.compat; -import me.lambdaurora.lambdacontrols.controller.ButtonBinding; +import me.lambdaurora.lambdacontrols.LambdaControls; import me.lambdaurora.lambdacontrols.controller.InputManager; -import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding; import net.fabricmc.loader.api.FabricLoader; import org.aperlambda.lambdacommon.utils.LambdaReflection; -import org.lwjgl.glfw.GLFW; +import org.jetbrains.annotations.NotNull; /** * Represents a compatibility handler. * * @author LambdAurora * @version 1.1.0 + * @since 1.1.0 */ public class LambdaControlsCompat { private static final String OKZOOMER_CLASS_PATH = "io.github.joaoh1.okzoomer.OkZoomer"; - public static void init() + /** + * Initializes compatibility with other mods if needed. + * + * @param mod The mod instance. + */ + public static void init(@NotNull LambdaControls mod) { if (FabricLoader.getInstance().isModLoaded("okzoomer") && LambdaReflection.does_class_exist(OKZOOMER_CLASS_PATH)) { - LambdaReflection.get_class(OKZOOMER_CLASS_PATH).map(clazz -> LambdaReflection.get_first_field_of_type(clazz, FabricKeyBinding.class)) - .ifPresent(field -> field.map(f -> (FabricKeyBinding) LambdaReflection.get_field_value(null, f)) - .ifPresent(zoom_key_binding -> { - ButtonBinding binding = InputManager.register_binding(new ButtonBinding("zoom", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_X}, true)); - binding.set_key_binding(zoom_key_binding); - ButtonBinding.MISC_CATEGORY.register_binding(binding); - })); + mod.log("Adding okzoomer compatibility..."); + new OkZoomerCompat().handle(mod); } + InputManager.load_button_bindings(mod.config); } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/compat/OkZoomerCompat.java b/src/main/java/me/lambdaurora/lambdacontrols/compat/OkZoomerCompat.java new file mode 100644 index 0000000..31a6901 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/compat/OkZoomerCompat.java @@ -0,0 +1,40 @@ +/* + * Copyright © 2020 LambdAurora + * + * This file is part of LambdaControls. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package me.lambdaurora.lambdacontrols.compat; + +import me.lambdaurora.lambdacontrols.LambdaControls; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; +import me.lambdaurora.lambdacontrols.controller.InputManager; +import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding; +import org.aperlambda.lambdacommon.utils.LambdaReflection; +import org.jetbrains.annotations.NotNull; +import org.lwjgl.glfw.GLFW; + +/** + * Represents a compatibility handler for OkZoomer. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.1.0 + */ +public class OkZoomerCompat implements CompatHandler +{ + @Override + public void handle(@NotNull LambdaControls mod) + { + LambdaReflection.get_first_field_of_type(io.github.joaoh1.okzoomer.OkZoomer.class, FabricKeyBinding.class) + .map(field -> (FabricKeyBinding) LambdaReflection.get_field_value(null, field)) + .ifPresent(zoom_key_binding -> { + ButtonBinding binding = InputManager.register_binding(new ButtonBinding("zoom", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_X}, true)); + binding.set_key_binding(zoom_key_binding); + ButtonBinding.MISC_CATEGORY.register_binding(binding); + }); + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonBinding.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonBinding.java index b4f15c8..28c30c3 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonBinding.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonBinding.java @@ -10,12 +10,10 @@ package me.lambdaurora.lambdacontrols.controller; import me.lambdaurora.lambdacontrols.ButtonState; -import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor; 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.client.util.ScreenshotUtils; import org.aperlambda.lambdacommon.utils.Nameable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -60,21 +58,10 @@ public class ButtonBinding implements Nameable public static final ButtonBinding PLAYER_LIST = register_binding(new ButtonBinding("player_list", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_BACK}, false)); public static final ButtonBinding RIGHT = register_binding(new ButtonBinding("right", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, true)}, false)); public static final ButtonBinding SCREENSHOT = register_binding(new ButtonBinding("screenshot", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_A}, - Collections.singletonList((client, button, action) -> { - if (action == ButtonState.PRESS) - ScreenshotUtils.saveScreenshot(client.runDirectory, client.getWindow().getFramebufferWidth(), client.getWindow().getFramebufferHeight(), client.getFramebuffer(), - text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text))); - return true; - }), true)); + Collections.singletonList(InputHandlers::handle_screenshot), true)); public static final ButtonBinding SMOOTH_CAMERA = register_binding(new ButtonBinding("toggle_smooth_camera", new int[]{-1}, true)); public static final ButtonBinding SNEAK = register_binding(new ButtonBinding("sneak", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB}, - Arrays.asList(PressAction.DEFAULT_ACTION, (client, button, action) -> { - if (client.player != null && !client.player.abilities.flying) { - button.as_key_binding().filter(binding -> action == ButtonState.PRESS).ifPresent(binding -> ((KeyBindingAccessor) binding).handle_press_state(!binding.isPressed())); - return true; - } - return false; - }), true)); + Arrays.asList(PressAction.DEFAULT_ACTION, InputHandlers::handle_toggle_sneak), true)); public static final ButtonBinding SPRINT = register_binding(new ButtonBinding("sprint", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB}, false)); public static final ButtonBinding SWAP_HANDS = register_binding(new ButtonBinding("swap_hands", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_X}, true)); public static final ButtonBinding TOGGLE_PERSPECTIVE = register_binding(new ButtonBinding("toggle_perspective", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_Y}, true)); @@ -91,7 +78,7 @@ public class ButtonBinding implements Nameable public ButtonBinding(@NotNull String key, int[] default_button, @NotNull List actions, boolean has_cooldown) { - this.button = this.default_button = default_button; + this.set_button(this.default_button = default_button); this.key = key; this.actions.addAll(actions); this.has_cooldown = has_cooldown; @@ -120,6 +107,9 @@ public class ButtonBinding implements Nameable public void set_button(int[] button) { this.button = button; + + if (InputManager.has_binding(this)) + InputManager.sort_bindings(); } /** @@ -214,8 +204,10 @@ public class ButtonBinding implements Nameable { if (state == ButtonState.REPEAT && this.has_cooldown && this.cooldown != 0) return; - if (this.has_cooldown && state.is_pressed()) + if (this.has_cooldown && state.is_pressed()) { this.cooldown = 5; + + } for (int i = this.actions.size() - 1; i >= 0; i--) { if (this.actions.get(i).press(client, this, state)) break; diff --git a/src/main/java/me/lambdaurora/lambdacontrols/controller/InputHandlers.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/InputHandlers.java index 1fad394..1961c9c 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/controller/InputHandlers.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/InputHandlers.java @@ -11,9 +11,11 @@ package me.lambdaurora.lambdacontrols.controller; import me.lambdaurora.lambdacontrols.ButtonState; import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor; +import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen; import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; +import net.minecraft.client.util.ScreenshotUtils; import net.minecraft.item.ItemGroup; import org.jetbrains.annotations.NotNull; @@ -26,9 +28,14 @@ import org.jetbrains.annotations.NotNull; */ public class InputHandlers { - private InputHandlers() {} + private static int hotbar_cooldown = 0; - public static PressAction handle_hotbar(boolean right) { + private InputHandlers() + { + } + + public static PressAction handle_hotbar(boolean right) + { return (client, button, action) -> { if (action == ButtonState.RELEASE) return false; @@ -55,7 +62,8 @@ public class InputHandlers }; } - public static boolean handle_pause_game(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, @NotNull ButtonState action) { + public static boolean handle_pause_game(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, @NotNull ButtonState action) + { if (action == ButtonState.PRESS) { // If in game, then pause the game. if (client.currentScreen == null) @@ -67,4 +75,29 @@ public class InputHandlers } return true; } + + /** + * Handles the screenshot action. + * + * @param client The client instance. + * @param binding The binding which fired the action. + * @param action The action done on the binding. + * @return True if handled, else false. + */ + public static boolean handle_screenshot(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, @NotNull ButtonState action) + { + if (action == ButtonState.PRESS) + 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 handle_toggle_sneak(@NotNull MinecraftClient client, @NotNull ButtonBinding button, @NotNull ButtonState action) + { + if (client.player != null && !client.player.abilities.flying) { + button.as_key_binding().filter(binding -> action == ButtonState.PRESS).ifPresent(binding -> ((KeyBindingAccessor) binding).handle_press_state(!binding.isPressed())); + return true; + } + return false; + } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/controller/InputManager.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/InputManager.java index 88b2458..b008138 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/controller/InputManager.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/InputManager.java @@ -11,11 +11,13 @@ package me.lambdaurora.lambdacontrols.controller; import me.lambdaurora.lambdacontrols.ButtonState; import me.lambdaurora.lambdacontrols.LambdaControlsConfig; +import net.minecraft.client.MinecraftClient; import org.aperlambda.lambdacommon.Identifier; import org.jetbrains.annotations.NotNull; import java.util.*; import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -31,34 +33,86 @@ public class InputManager private static final List CATEGORIES = new ArrayList<>(); public static final Map STATES = new HashMap<>(); + /** + * Returns whether the specified binding is registered or not. + * + * @param binding The binding to check. + * @return True if the binding is registered, else false. + */ + public static boolean has_binding(@NotNull ButtonBinding binding) + { + return BINDINGS.contains(binding); + } + + /** + * Returns whether the specified binding is registered or not. + * + * @param name The name of the binding to check. + * @return True if the binding is registered, else false. + */ + public static boolean has_binding(@NotNull String name) + { + return BINDINGS.parallelStream().map(ButtonBinding::get_name).anyMatch(binding -> binding.equalsIgnoreCase(name)); + } + + /** + * Returns whether the specified binding is registered or not. + * + * @param identifier The identifier of the binding to check. + * @return True if the binding is registered, else false. + */ + public static boolean has_binding(@NotNull Identifier identifier) + { + return has_binding(identifier.get_namespace() + "." + identifier.get_name()); + } + + /** + * Registers a button binding. + * + * @param binding The binding to register. + * @return The registered binding. + */ public static @NotNull ButtonBinding register_binding(@NotNull ButtonBinding binding) { - if (BINDINGS.contains(binding)) + if (has_binding(binding)) throw new IllegalStateException("Cannot register twice a button binding in the registry."); BINDINGS.add(binding); return binding; } - public static ButtonBinding register_binding(@NotNull Identifier binding_id, int[] default_button, @NotNull List actions, boolean has_cooldown) + public static @NotNull ButtonBinding register_binding(@NotNull Identifier binding_id, int[] default_button, @NotNull List actions, boolean has_cooldown) { return register_binding(new ButtonBinding(binding_id.get_namespace() + "." + binding_id.get_name(), default_button, actions, has_cooldown)); } - public static ButtonBinding register_binding(@NotNull Identifier binding_id, int[] default_button, boolean has_cooldown) + public static @NotNull ButtonBinding register_binding(@NotNull Identifier binding_id, int[] default_button, boolean has_cooldown) { return register_binding(binding_id, default_button, Collections.emptyList(), has_cooldown); } - public static ButtonBinding register_binding(@NotNull net.minecraft.util.Identifier binding_id, int[] default_button, @NotNull List actions, boolean has_cooldown) + public static @NotNull ButtonBinding register_binding(@NotNull net.minecraft.util.Identifier binding_id, int[] default_button, @NotNull List actions, boolean has_cooldown) { return register_binding(new Identifier(binding_id.getNamespace(), binding_id.getPath()), default_button, actions, has_cooldown); } - public static ButtonBinding register_binding(@NotNull net.minecraft.util.Identifier binding_id, int[] default_button, boolean has_cooldown) + public static @NotNull ButtonBinding register_binding(@NotNull net.minecraft.util.Identifier binding_id, int[] default_button, boolean has_cooldown) { return register_binding(binding_id, default_button, Collections.emptyList(), has_cooldown); } + /** + * Sorts bindings to get bindings with the higher button counts first. + */ + public static void sort_bindings() + { + synchronized (BINDINGS) { + List sorted_bindings = BINDINGS.stream().sorted(Collections.reverseOrder(Comparator.comparingInt(binding -> binding.get_button().length))) + .collect(Collectors.toList()); + BINDINGS.clear(); + BINDINGS.addAll(sorted_bindings); + } + } + /** * Registers a category of button bindings. * @@ -95,7 +149,8 @@ public class InputManager */ public static void load_button_bindings(@NotNull LambdaControlsConfig config) { - BINDINGS.forEach(config::load_button_binding); + List load_queue = new ArrayList<>(BINDINGS); + load_queue.forEach(config::load_button_binding); } /** @@ -156,6 +211,18 @@ public class InputManager return count == buttons1.length; } + /** + * Returns whether the button set contains the specified button or not. + * + * @param buttons The button set. + * @param button The button to check. + * @return True if the button set contains the specified button, else false. + */ + public static boolean contains_button(int[] buttons, int button) + { + return Arrays.stream(buttons).anyMatch(btn -> btn == button); + } + /** * Updates the button states. */ @@ -169,9 +236,29 @@ public class InputManager }); } - public static void update_bindings() { - BINDINGS.forEach(binding -> binding.pressed = get_binding_state(binding).is_pressed()); - BINDINGS.forEach(ButtonBinding::update); + public static void update_bindings(@NotNull MinecraftClient client) + { + List skip_buttons = new ArrayList<>(); + Map states = new HashMap<>(); + for (ButtonBinding binding : BINDINGS) { + ButtonState binding_state = get_binding_state(binding); + if (skip_buttons.stream().anyMatch(btn -> contains_button(binding.get_button(), btn))) { + if (binding.pressed) + binding_state = ButtonState.RELEASE; + else + binding_state = ButtonState.NONE; + } + binding.pressed = binding_state.is_pressed(); + binding.update(); + if (binding.pressed) + Arrays.stream(binding.get_button()).forEach(skip_buttons::add); + states.put(binding, binding_state); + } + + states.forEach((binding, state) -> { + if (state != ButtonState.NONE) + binding.handle(client, state); + }); } public static @NotNull Stream stream_bindings() @@ -179,17 +266,6 @@ public class InputManager return BINDINGS.stream(); } - public static @NotNull Stream stream_active_bindings() - { - return BINDINGS.stream().filter(binding -> { - for (int btn : binding.get_button()) { - if (InputManager.STATES.getOrDefault(btn, ButtonState.NONE) == ButtonState.NONE) - return false; - } - return true; - }); - } - public static @NotNull Stream stream_categories() { return CATEGORIES.stream(); diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/ControlsListWidget.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/ControlsListWidget.java index ca31c1d..ee69f4e 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/ControlsListWidget.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/ControlsListWidget.java @@ -80,7 +80,10 @@ public class ControlsListWidget extends ElementListWidget gui.focused_binding = binding) + this.edit_button = new ControllerButtonWidget(0, 0, 110, this.binding, btn -> { + gui.focused_binding = binding; + gui.current_buttons.clear(); + }) { protected String getNarrationMessage() { diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsControlsScreen.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsControlsScreen.java index ba89a49..29f8be1 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsControlsScreen.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsControlsScreen.java @@ -9,19 +9,19 @@ package me.lambdaurora.lambdacontrols.gui; -import me.lambdaurora.lambdacontrols.controller.ButtonBinding; import me.lambdaurora.lambdacontrols.LambdaControls; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; import me.lambdaurora.lambdacontrols.controller.InputManager; import me.lambdaurora.spruceui.SpruceButtonWidget; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.options.ControlsOptionsScreen; import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.options.BooleanOption; -import net.minecraft.client.options.Option; import net.minecraft.client.resource.language.I18n; import net.minecraft.text.TranslatableText; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.function.Predicate; @@ -32,12 +32,11 @@ public class LambdaControlsControlsScreen extends Screen { private final Screen parent; final LambdaControls mod; - private final Option inverts_right_x_axis; - private final Option inverts_right_y_axis; private final boolean hide_settings; private ControlsListWidget bindings_list_widget; private ButtonWidget reset_button; public ButtonBinding focused_binding; + public List current_buttons = new ArrayList<>(); public LambdaControlsControlsScreen(@NotNull Screen parent, boolean hide_settings) { @@ -45,18 +44,6 @@ public class LambdaControlsControlsScreen extends Screen this.parent = parent; this.mod = LambdaControls.get(); this.hide_settings = hide_settings; - this.inverts_right_x_axis = new BooleanOption("lambdacontrols.menu.invert_right_x_axis", game_options -> this.mod.config.does_invert_right_x_axis(), - (game_options, new_value) -> { - synchronized (this.mod.config) { - this.mod.config.set_invert_right_x_axis(new_value); - } - }); - this.inverts_right_y_axis = new BooleanOption("lambdacontrols.menu.invert_right_y_axis", game_options -> this.mod.config.does_invert_right_y_axis(), - (game_options, new_value) -> { - synchronized (this.mod.config) { - this.mod.config.set_invert_right_y_axis(new_value); - } - }); } @Override @@ -69,8 +56,6 @@ public class LambdaControlsControlsScreen extends Screen @Override protected void init() { - //this.addButton(this.inverts_right_x_axis.createButton(this.minecraft.options, this.width / 2 - 155, 18, 150)); - //this.addButton(this.inverts_right_y_axis.createButton(this.minecraft.options, this.width / 2 - 155 + 160, 18, 150)); this.addButton(new SpruceButtonWidget(this.width / 2 - 155, 18, this.hide_settings ? 310 : 150, 20, I18n.translate("lambdacontrols.menu.keyboard_controls"), btn -> this.minecraft.openScreen(new ControlsOptionsScreen(this, this.minecraft.options)))); if (!this.hide_settings) diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java index 4d4093b..2447fb5 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java @@ -21,6 +21,7 @@ import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.options.ControlsOptionsScreen; import net.minecraft.client.gui.widget.ButtonListWidget; import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.options.BooleanOption; import net.minecraft.client.options.CyclingOption; import net.minecraft.client.options.GameOptions; import net.minecraft.client.options.Option; @@ -36,23 +37,28 @@ import org.lwjgl.glfw.GLFW; */ public class LambdaControlsSettingsScreen extends Screen { - public static final String GAMEPAD_TOOL_URL = "http://generalarcade.com/gamepadtool/"; - final LambdaControls mod; - private final Screen parent; - private final boolean hide_controls; - private final Option auto_switch_mode_option; - private final Option controller_option; - private final Option second_controller_option; - private final Option controller_type_option; - private final Option dead_zone_option; - private final Option hud_enable_option; - private final Option hud_side_option; - private final Option mouse_speed_option; - private final Option rotation_speed_option; - private final Option reset_option; - private final String controller_mappings_url_text = I18n.translate("lambdacontrols.controller.mappings.2", Formatting.GOLD.toString(), GAMEPAD_TOOL_URL, Formatting.RESET.toString()); - private ButtonListWidget list; - private SpruceLabelWidget gamepad_tool_url_label; + public static final String GAMEPAD_TOOL_URL = "http://generalarcade.com/gamepadtool/"; + final LambdaControls mod; + private final Screen parent; + private final boolean hide_controls; + // General options + private final Option auto_switch_mode_option; + private final Option rotation_speed_option; + private final Option mouse_speed_option; + private final Option reset_option; + // Controller options + private final Option controller_option; + private final Option second_controller_option; + private final Option controller_type_option; + private final Option dead_zone_option; + private final Option inverts_right_x_axis; + private final Option inverts_right_y_axis; + // Hud options + private final Option hud_enable_option; + private final Option hud_side_option; + private final String controller_mappings_url_text = I18n.translate("lambdacontrols.controller.mappings.2", Formatting.GOLD.toString(), GAMEPAD_TOOL_URL, Formatting.RESET.toString()); + private ButtonListWidget list; + private SpruceLabelWidget gamepad_tool_url_label; public LambdaControlsSettingsScreen(Screen parent, @NotNull GameOptions options, boolean hide_controls) { @@ -60,8 +66,29 @@ public class LambdaControlsSettingsScreen extends Screen this.mod = LambdaControls.get(); this.parent = parent; this.hide_controls = hide_controls; + // General options this.auto_switch_mode_option = new SpruceBooleanOption("lambdacontrols.menu.auto_switch_mode", game_options -> this.mod.config.has_auto_switch_mode(), (game_options, new_value) -> this.mod.config.set_auto_switch_mode(new_value), new TranslatableText("lambdacontrols.tooltip.auto_switch_mode")); + this.rotation_speed_option = new SpruceDoubleOption("lambdacontrols.menu.rotation_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_rotation_speed(), + (game_options, new_value) -> { + synchronized (this.mod.config) { + this.mod.config.set_rotation_speed(new_value); + } + }, (game_options, option) -> option.getDisplayPrefix() + option.get(options), + new TranslatableText("lambdacontrols.tooltip.rotation_speed")); + this.mouse_speed_option = new SpruceDoubleOption("lambdacontrols.menu.mouse_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_mouse_speed(), + (game_options, new_value) -> { + synchronized (this.mod.config) { + this.mod.config.set_mouse_speed(new_value); + } + }, (game_options, option) -> option.getDisplayPrefix() + option.get(options), + new TranslatableText("lambdacontrols.tooltip.mouse_speed")); + this.reset_option = new SpruceResetOption(btn -> { + this.mod.config.reset(); + MinecraftClient client = MinecraftClient.getInstance(); + this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); + }); + // Controller options this.controller_option = new CyclingOption("lambdacontrols.menu.controller", (game_options, amount) -> { int current_id = this.mod.config.get_controller().get_id(); current_id += amount; @@ -98,12 +125,6 @@ public class LambdaControlsSettingsScreen extends Screen (game_options, amount) -> this.mod.config.set_controller_type(this.mod.config.get_controller_type().next()), (game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_controller_type().get_translated_name(), new TranslatableText("lambdacontrols.tooltip.controller_type")); - this.hud_enable_option = new SpruceBooleanOption("lambdacontrols.menu.hud_enable", (game_options) -> this.mod.config.is_hud_enabled(), - (game_options, new_value) -> this.mod.config.set_hud_enabled(new_value), new TranslatableText("lambdacontrols.tooltip.hud_enable")); - this.hud_side_option = new SpruceCyclingOption("lambdacontrols.menu.hud_side", - (game_options, amount) -> this.mod.config.set_hud_side(this.mod.config.get_hud_side().next()), - (game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_hud_side().get_translated_name(), - new TranslatableText("lambdacontrols.tooltip.hud_side")); this.dead_zone_option = new SpruceDoubleOption("lambdacontrols.menu.dead_zone", 0.05, 1.0, 0.05F, game_options -> this.mod.config.get_dead_zone(), (game_options, new_value) -> { synchronized (this.mod.config) { @@ -113,25 +134,25 @@ public class LambdaControlsSettingsScreen extends Screen String value = String.valueOf(option.get(options)); return option.getDisplayPrefix() + value.substring(0, Math.min(value.length(), 5)); }, new TranslatableText("lambdacontrols.tooltip.dead_zone")); - this.rotation_speed_option = new SpruceDoubleOption("lambdacontrols.menu.rotation_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_rotation_speed(), + this.inverts_right_x_axis = new SpruceBooleanOption("lambdacontrols.menu.invert_right_x_axis", game_options -> this.mod.config.does_invert_right_x_axis(), (game_options, new_value) -> { synchronized (this.mod.config) { - this.mod.config.set_rotation_speed(new_value); + this.mod.config.set_invert_right_x_axis(new_value); } - }, (game_options, option) -> option.getDisplayPrefix() + option.get(options), - new TranslatableText("lambdacontrols.tooltip.rotation_speed")); - this.mouse_speed_option = new SpruceDoubleOption("lambdacontrols.menu.mouse_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_mouse_speed(), + }, null); + this.inverts_right_y_axis = new SpruceBooleanOption("lambdacontrols.menu.invert_right_y_axis", game_options -> this.mod.config.does_invert_right_y_axis(), (game_options, new_value) -> { synchronized (this.mod.config) { - this.mod.config.set_mouse_speed(new_value); + this.mod.config.set_invert_right_y_axis(new_value); } - }, (game_options, option) -> option.getDisplayPrefix() + option.get(options), - new TranslatableText("lambdacontrols.tooltip.mouse_speed")); - this.reset_option = new SpruceResetOption(btn -> { - this.mod.config.reset(); - MinecraftClient client = MinecraftClient.getInstance(); - this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); - }); + }, null); + // HUD options + this.hud_enable_option = new SpruceBooleanOption("lambdacontrols.menu.hud_enable", (game_options) -> this.mod.config.is_hud_enabled(), + (game_options, new_value) -> this.mod.config.set_hud_enabled(new_value), new TranslatableText("lambdacontrols.tooltip.hud_enable")); + this.hud_side_option = new SpruceCyclingOption("lambdacontrols.menu.hud_side", + (game_options, amount) -> this.mod.config.set_hud_side(this.mod.config.get_hud_side().next()), + (game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_hud_side().get_translated_name(), + new TranslatableText("lambdacontrols.tooltip.hud_side")); } @Override @@ -178,14 +199,18 @@ public class LambdaControlsSettingsScreen extends Screen })); this.list = new ButtonListWidget(this.minecraft, this.width, this.height, 43, this.height - 29 - this.get_text_height(), 25); + // General options this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.general", true, null)); this.list.addOptionEntry(this.rotation_speed_option, this.mouse_speed_option); this.list.addSingleOptionEntry(this.auto_switch_mode_option); + // Controller options this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.controller", true, null)); this.list.addSingleOptionEntry(this.controller_option); this.list.addSingleOptionEntry(this.second_controller_option); this.list.addOptionEntry(this.controller_type_option, this.dead_zone_option); + this.list.addOptionEntry(this.inverts_right_x_axis, this.inverts_right_y_axis); this.list.addSingleOptionEntry(new ReloadControllerMappingsOption()); + // HUD options this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.hud", true, null)); this.list.addOptionEntry(this.hud_enable_option, this.hud_side_option); this.children.add(this.list); diff --git a/src/main/resources/assets/lambdacontrols/lang/en_us.json b/src/main/resources/assets/lambdacontrols/lang/en_us.json index b6ad570..8f1d1a4 100644 --- a/src/main/resources/assets/lambdacontrols/lang/en_us.json +++ b/src/main/resources/assets/lambdacontrols/lang/en_us.json @@ -29,6 +29,7 @@ "lambdacontrols.action.toggle_perspective": "Toggle Perspective", "lambdacontrols.action.toggle_smooth_camera": "Toggle Cinematic Camera", "lambdacontrols.action.use": "Use", + "lambdacontrols.action.zoom": "Zoom", "lambdacontrols.button.a": "A", "lambdacontrols.button.b": "B", "lambdacontrols.button.x": "X", diff --git a/src/main/resources/assets/lambdacontrols/lang/fr_ca.json b/src/main/resources/assets/lambdacontrols/lang/fr_ca.json index 88b3218..48a7ab6 100644 --- a/src/main/resources/assets/lambdacontrols/lang/fr_ca.json +++ b/src/main/resources/assets/lambdacontrols/lang/fr_ca.json @@ -29,6 +29,7 @@ "lambdacontrols.action.toggle_perspective": "Changer de point de vue", "lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique", "lambdacontrols.action.use": "Utiliser", + "lambdacontrols.action.zoom": "Zoom", "lambdacontrols.button.a": "A", "lambdacontrols.button.b": "B", "lambdacontrols.button.x": "X", diff --git a/src/main/resources/assets/lambdacontrols/lang/fr_fr.json b/src/main/resources/assets/lambdacontrols/lang/fr_fr.json index 88b3218..48a7ab6 100644 --- a/src/main/resources/assets/lambdacontrols/lang/fr_fr.json +++ b/src/main/resources/assets/lambdacontrols/lang/fr_fr.json @@ -29,6 +29,7 @@ "lambdacontrols.action.toggle_perspective": "Changer de point de vue", "lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique", "lambdacontrols.action.use": "Utiliser", + "lambdacontrols.action.zoom": "Zoom", "lambdacontrols.button.a": "A", "lambdacontrols.button.b": "B", "lambdacontrols.button.x": "X", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 369cfd5..3a90b6e 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -33,7 +33,8 @@ "spruceui": ">=1.0.1" }, "recommends": { - "modmenu": ">=1.8.0+build.16" + "modmenu": ">=1.8.0+build.16", + "okzoomer": ">=1.0.3" }, "suggests": { "flamingo": "*"