Add working in-game chording, WIP controls screen.

This commit is contained in:
LambdAurora
2020-01-01 14:54:51 +01:00
parent e6da7adf89
commit 1b5a0e470e
16 changed files with 336 additions and 132 deletions

View File

@@ -24,6 +24,26 @@ repositories {
repositories { repositories {
maven { url = "https://jitpack.io" } 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 { dependencies {
@@ -40,6 +60,9 @@ dependencies {
include "me.lambdaurora:spruceui:${project.spruceui_version}" include "me.lambdaurora:spruceui:${project.spruceui_version}"
//modCompile "io.github.cottonmc:cotton-client-commands:0.4.2+1.14.3-SNAPSHOT" //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.jetbrains:annotations:17.0.0"
implementation "org.aperlambda:lambdajcommon:1.7.2" implementation "org.aperlambda:lambdajcommon:1.7.2"
implementation "com.electronwill.night-config:core:3.5.3" implementation "com.electronwill.night-config:core:3.5.3"
@@ -48,9 +71,6 @@ dependencies {
include "org.aperlambda:lambdajcommon:1.7.2" include "org.aperlambda:lambdajcommon:1.7.2"
include "com.electronwill.night-config:core:3.5.3" include "com.electronwill.night-config:core:3.5.3"
include "com.electronwill.night-config:toml: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 { processResources {

View File

@@ -97,7 +97,7 @@ public class LambdaControls implements ClientModInitializer
this.hud = new LambdaControlsHud(client, this); this.hud = new LambdaControlsHud(client, this);
LambdaControlsCompat.init(); LambdaControlsCompat.init(this);
} }
/** /**

View File

@@ -59,7 +59,6 @@ import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y;
*/ */
public class LambdaInput public class LambdaInput
{ {
private static final Map<Integer, Boolean> BUTTON_STATES = new HashMap<>();
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>(); private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
private final LambdaControlsConfig config; private final LambdaControlsConfig config;
// Cooldowns // Cooldowns
@@ -136,11 +135,22 @@ public class LambdaInput
this.fetch_axe_input(client, state, true); this.fetch_axe_input(client, state, true);
}); });
InputManager.update_bindings(); InputManager.update_bindings(client);
InputManager.stream_active_bindings().forEach(binding -> binding.handle(client, InputManager.get_binding_state(binding)));
if (this.ignore_next_a > 0) if (this.ignore_next_a > 0)
this.ignore_next_a--; 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) if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y)
value *= -1.0F; 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); 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); 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) private void handle_button(@NotNull MinecraftClient client, int button, int action, boolean state)
{ {
if (client.currentScreen instanceof LambdaControlsControlsScreen && action == 0) { if (client.currentScreen instanceof LambdaControlsControlsScreen && action == 0) {
LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen; LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
if (controls_screen.focused_binding != null) { if (controls_screen.focused_binding != null) {
this.config.set_button_binding(controls_screen.focused_binding, new int[]{button}); controls_screen.current_buttons.add(button);
controls_screen.focused_binding = null;
return; 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)))) { 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; LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
if (controls_screen.focused_binding != null) { 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.current_buttons.add(axis_as_button(axis, as_button_state == 1));
controls_screen.focused_binding = null;
return; return;
} }
} }

View File

@@ -0,0 +1,30 @@
/*
* 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.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);
}

View File

@@ -9,33 +9,34 @@
package me.lambdaurora.lambdacontrols.compat; package me.lambdaurora.lambdacontrols.compat;
import me.lambdaurora.lambdacontrols.controller.ButtonBinding; import me.lambdaurora.lambdacontrols.LambdaControls;
import me.lambdaurora.lambdacontrols.controller.InputManager; import me.lambdaurora.lambdacontrols.controller.InputManager;
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import org.aperlambda.lambdacommon.utils.LambdaReflection; import org.aperlambda.lambdacommon.utils.LambdaReflection;
import org.lwjgl.glfw.GLFW; import org.jetbrains.annotations.NotNull;
/** /**
* Represents a compatibility handler. * Represents a compatibility handler.
* *
* @author LambdAurora * @author LambdAurora
* @version 1.1.0 * @version 1.1.0
* @since 1.1.0
*/ */
public class LambdaControlsCompat public class LambdaControlsCompat
{ {
private static final String OKZOOMER_CLASS_PATH = "io.github.joaoh1.okzoomer.OkZoomer"; 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)) { 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)) mod.log("Adding okzoomer compatibility...");
.ifPresent(field -> field.map(f -> (FabricKeyBinding) LambdaReflection.get_field_value(null, f)) new OkZoomerCompat().handle(mod);
.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)); InputManager.load_button_bindings(mod.config);
binding.set_key_binding(zoom_key_binding);
ButtonBinding.MISC_CATEGORY.register_binding(binding);
}));
}
} }
} }

View File

@@ -0,0 +1,40 @@
/*
* 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.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);
});
}
}

View File

@@ -10,12 +10,10 @@
package me.lambdaurora.lambdacontrols.controller; package me.lambdaurora.lambdacontrols.controller;
import me.lambdaurora.lambdacontrols.ButtonState; import me.lambdaurora.lambdacontrols.ButtonState;
import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor;
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.client.resource.language.I18n;
import net.minecraft.client.util.ScreenshotUtils;
import org.aperlambda.lambdacommon.utils.Nameable; import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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 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 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}, 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) -> { Collections.singletonList(InputHandlers::handle_screenshot), true));
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));
public static final ButtonBinding SMOOTH_CAMERA = register_binding(new ButtonBinding("toggle_smooth_camera", new int[]{-1}, 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}, 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) -> { Arrays.asList(PressAction.DEFAULT_ACTION, InputHandlers::handle_toggle_sneak), true));
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));
public static final ButtonBinding SPRINT = register_binding(new ButtonBinding("sprint", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB}, false)); 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 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)); 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<PressAction> actions, boolean has_cooldown) public ButtonBinding(@NotNull String key, int[] default_button, @NotNull List<PressAction> actions, boolean has_cooldown)
{ {
this.button = this.default_button = default_button; this.set_button(this.default_button = default_button);
this.key = key; this.key = key;
this.actions.addAll(actions); this.actions.addAll(actions);
this.has_cooldown = has_cooldown; this.has_cooldown = has_cooldown;
@@ -120,6 +107,9 @@ public class ButtonBinding implements Nameable
public void set_button(int[] button) public void set_button(int[] button)
{ {
this.button = 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) if (state == ButtonState.REPEAT && this.has_cooldown && this.cooldown != 0)
return; return;
if (this.has_cooldown && state.is_pressed()) if (this.has_cooldown && state.is_pressed()) {
this.cooldown = 5; this.cooldown = 5;
}
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, state))
break; break;

View File

@@ -11,9 +11,11 @@ package me.lambdaurora.lambdacontrols.controller;
import me.lambdaurora.lambdacontrols.ButtonState; import me.lambdaurora.lambdacontrols.ButtonState;
import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor; import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor;
import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen; import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.util.ScreenshotUtils;
import net.minecraft.item.ItemGroup; import net.minecraft.item.ItemGroup;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -26,9 +28,14 @@ import org.jetbrains.annotations.NotNull;
*/ */
public class InputHandlers 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) -> { return (client, button, action) -> {
if (action == ButtonState.RELEASE) if (action == ButtonState.RELEASE)
return false; 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 (action == ButtonState.PRESS) {
// If in game, then pause the game. // If in game, then pause the game.
if (client.currentScreen == null) if (client.currentScreen == null)
@@ -67,4 +75,29 @@ public class InputHandlers
} }
return true; 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;
}
} }

View File

@@ -11,11 +11,13 @@ package me.lambdaurora.lambdacontrols.controller;
import me.lambdaurora.lambdacontrols.ButtonState; import me.lambdaurora.lambdacontrols.ButtonState;
import me.lambdaurora.lambdacontrols.LambdaControlsConfig; import me.lambdaurora.lambdacontrols.LambdaControlsConfig;
import net.minecraft.client.MinecraftClient;
import org.aperlambda.lambdacommon.Identifier; import org.aperlambda.lambdacommon.Identifier;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@@ -31,34 +33,86 @@ public class InputManager
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<>();
/**
* 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) 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."); throw new IllegalStateException("Cannot register twice a button binding in the registry.");
BINDINGS.add(binding); BINDINGS.add(binding);
return binding; return binding;
} }
public static ButtonBinding register_binding(@NotNull Identifier binding_id, int[] default_button, @NotNull List<PressAction> actions, boolean has_cooldown) public static @NotNull ButtonBinding register_binding(@NotNull Identifier binding_id, int[] default_button, @NotNull List<PressAction> actions, boolean has_cooldown)
{ {
return register_binding(new ButtonBinding(binding_id.get_namespace() + "." + binding_id.get_name(), default_button, actions, 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); 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<PressAction> actions, boolean has_cooldown) public static @NotNull ButtonBinding register_binding(@NotNull net.minecraft.util.Identifier binding_id, int[] default_button, @NotNull List<PressAction> actions, boolean has_cooldown)
{ {
return register_binding(new Identifier(binding_id.getNamespace(), binding_id.getPath()), default_button, actions, 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); 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<ButtonBinding> 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. * Registers a category of button bindings.
* *
@@ -95,7 +149,8 @@ public class InputManager
*/ */
public static void load_button_bindings(@NotNull LambdaControlsConfig config) public static void load_button_bindings(@NotNull LambdaControlsConfig config)
{ {
BINDINGS.forEach(config::load_button_binding); List<ButtonBinding> load_queue = new ArrayList<>(BINDINGS);
load_queue.forEach(config::load_button_binding);
} }
/** /**
@@ -156,6 +211,18 @@ public class InputManager
return count == buttons1.length; 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. * Updates the button states.
*/ */
@@ -169,9 +236,29 @@ public class InputManager
}); });
} }
public static void update_bindings() { public static void update_bindings(@NotNull MinecraftClient client)
BINDINGS.forEach(binding -> binding.pressed = get_binding_state(binding).is_pressed()); {
BINDINGS.forEach(ButtonBinding::update); List<Integer> skip_buttons = new ArrayList<>();
Map<ButtonBinding, ButtonState> 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<ButtonBinding> stream_bindings() public static @NotNull Stream<ButtonBinding> stream_bindings()
@@ -179,17 +266,6 @@ public class InputManager
return BINDINGS.stream(); return BINDINGS.stream();
} }
public static @NotNull Stream<ButtonBinding> 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<ButtonCategory> stream_categories() public static @NotNull Stream<ButtonCategory> stream_categories()
{ {
return CATEGORIES.stream(); return CATEGORIES.stream();

View File

@@ -80,7 +80,10 @@ public class ControlsListWidget extends ElementListWidget<ControlsListWidget.Ent
{ {
this.binding = binding; this.binding = binding;
this.binding_name = I18n.translate(this.binding.get_translation_key()); this.binding_name = I18n.translate(this.binding.get_translation_key());
this.edit_button = new ControllerButtonWidget(0, 0, 110, this.binding, btn -> 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() protected String getNarrationMessage()
{ {

View File

@@ -9,19 +9,19 @@
package me.lambdaurora.lambdacontrols.gui; package me.lambdaurora.lambdacontrols.gui;
import me.lambdaurora.lambdacontrols.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.LambdaControls; import me.lambdaurora.lambdacontrols.LambdaControls;
import me.lambdaurora.lambdacontrols.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.controller.InputManager; import me.lambdaurora.lambdacontrols.controller.InputManager;
import me.lambdaurora.spruceui.SpruceButtonWidget; import me.lambdaurora.spruceui.SpruceButtonWidget;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.options.ControlsOptionsScreen; import net.minecraft.client.gui.screen.options.ControlsOptionsScreen;
import net.minecraft.client.gui.widget.ButtonWidget; 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.client.resource.language.I18n;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -32,12 +32,11 @@ public class LambdaControlsControlsScreen extends Screen
{ {
private final Screen parent; private final Screen parent;
final LambdaControls mod; final LambdaControls mod;
private final Option inverts_right_x_axis;
private final Option inverts_right_y_axis;
private final boolean hide_settings; private final boolean hide_settings;
private ControlsListWidget bindings_list_widget; private ControlsListWidget bindings_list_widget;
private ButtonWidget reset_button; private ButtonWidget reset_button;
public ButtonBinding focused_binding; public ButtonBinding focused_binding;
public List<Integer> current_buttons = new ArrayList<>();
public LambdaControlsControlsScreen(@NotNull Screen parent, boolean hide_settings) public LambdaControlsControlsScreen(@NotNull Screen parent, boolean hide_settings)
{ {
@@ -45,18 +44,6 @@ public class LambdaControlsControlsScreen extends Screen
this.parent = parent; this.parent = parent;
this.mod = LambdaControls.get(); this.mod = LambdaControls.get();
this.hide_settings = hide_settings; 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 @Override
@@ -69,8 +56,6 @@ public class LambdaControlsControlsScreen extends Screen
@Override @Override
protected void init() 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"), 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)))); btn -> this.minecraft.openScreen(new ControlsOptionsScreen(this, this.minecraft.options))));
if (!this.hide_settings) if (!this.hide_settings)

View File

@@ -21,6 +21,7 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.options.ControlsOptionsScreen; import net.minecraft.client.gui.screen.options.ControlsOptionsScreen;
import net.minecraft.client.gui.widget.ButtonListWidget; import net.minecraft.client.gui.widget.ButtonListWidget;
import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.options.BooleanOption;
import net.minecraft.client.options.CyclingOption; import net.minecraft.client.options.CyclingOption;
import net.minecraft.client.options.GameOptions; import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.Option; import net.minecraft.client.options.Option;
@@ -40,16 +41,21 @@ public class LambdaControlsSettingsScreen extends Screen
final LambdaControls mod; final LambdaControls mod;
private final Screen parent; private final Screen parent;
private final boolean hide_controls; private final boolean hide_controls;
// General options
private final Option auto_switch_mode_option; 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 controller_option;
private final Option second_controller_option; private final Option second_controller_option;
private final Option controller_type_option; private final Option controller_type_option;
private final Option dead_zone_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_enable_option;
private final Option hud_side_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 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 ButtonListWidget list;
private SpruceLabelWidget gamepad_tool_url_label; private SpruceLabelWidget gamepad_tool_url_label;
@@ -60,8 +66,29 @@ public class LambdaControlsSettingsScreen extends Screen
this.mod = LambdaControls.get(); this.mod = LambdaControls.get();
this.parent = parent; this.parent = parent;
this.hide_controls = hide_controls; 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(), 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")); (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) -> { this.controller_option = new CyclingOption("lambdacontrols.menu.controller", (game_options, amount) -> {
int current_id = this.mod.config.get_controller().get_id(); int current_id = this.mod.config.get_controller().get_id();
current_id += amount; 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, 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(), (game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_controller_type().get_translated_name(),
new TranslatableText("lambdacontrols.tooltip.controller_type")); 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(), 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) -> { (game_options, new_value) -> {
synchronized (this.mod.config) { synchronized (this.mod.config) {
@@ -113,25 +134,25 @@ public class LambdaControlsSettingsScreen extends Screen
String value = String.valueOf(option.get(options)); String value = String.valueOf(option.get(options));
return option.getDisplayPrefix() + value.substring(0, Math.min(value.length(), 5)); return option.getDisplayPrefix() + value.substring(0, Math.min(value.length(), 5));
}, new TranslatableText("lambdacontrols.tooltip.dead_zone")); }, 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) -> { (game_options, new_value) -> {
synchronized (this.mod.config) { 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), }, null);
new TranslatableText("lambdacontrols.tooltip.rotation_speed")); this.inverts_right_y_axis = new SpruceBooleanOption("lambdacontrols.menu.invert_right_y_axis", game_options -> this.mod.config.does_invert_right_y_axis(),
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) -> { (game_options, new_value) -> {
synchronized (this.mod.config) { 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), }, null);
new TranslatableText("lambdacontrols.tooltip.mouse_speed")); // HUD options
this.reset_option = new SpruceResetOption(btn -> { this.hud_enable_option = new SpruceBooleanOption("lambdacontrols.menu.hud_enable", (game_options) -> this.mod.config.is_hud_enabled(),
this.mod.config.reset(); (game_options, new_value) -> this.mod.config.set_hud_enabled(new_value), new TranslatableText("lambdacontrols.tooltip.hud_enable"));
MinecraftClient client = MinecraftClient.getInstance(); this.hud_side_option = new SpruceCyclingOption("lambdacontrols.menu.hud_side",
this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); (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 @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); 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.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.general", true, null));
this.list.addOptionEntry(this.rotation_speed_option, this.mouse_speed_option); this.list.addOptionEntry(this.rotation_speed_option, this.mouse_speed_option);
this.list.addSingleOptionEntry(this.auto_switch_mode_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(new SpruceSeparatorOption("lambdacontrols.menu.title.controller", true, null));
this.list.addSingleOptionEntry(this.controller_option); this.list.addSingleOptionEntry(this.controller_option);
this.list.addSingleOptionEntry(this.second_controller_option); this.list.addSingleOptionEntry(this.second_controller_option);
this.list.addOptionEntry(this.controller_type_option, this.dead_zone_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()); this.list.addSingleOptionEntry(new ReloadControllerMappingsOption());
// HUD options
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.hud", true, null)); this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.hud", true, null));
this.list.addOptionEntry(this.hud_enable_option, this.hud_side_option); this.list.addOptionEntry(this.hud_enable_option, this.hud_side_option);
this.children.add(this.list); this.children.add(this.list);

View File

@@ -29,6 +29,7 @@
"lambdacontrols.action.toggle_perspective": "Toggle Perspective", "lambdacontrols.action.toggle_perspective": "Toggle Perspective",
"lambdacontrols.action.toggle_smooth_camera": "Toggle Cinematic Camera", "lambdacontrols.action.toggle_smooth_camera": "Toggle Cinematic Camera",
"lambdacontrols.action.use": "Use", "lambdacontrols.action.use": "Use",
"lambdacontrols.action.zoom": "Zoom",
"lambdacontrols.button.a": "A", "lambdacontrols.button.a": "A",
"lambdacontrols.button.b": "B", "lambdacontrols.button.b": "B",
"lambdacontrols.button.x": "X", "lambdacontrols.button.x": "X",

View File

@@ -29,6 +29,7 @@
"lambdacontrols.action.toggle_perspective": "Changer de point de vue", "lambdacontrols.action.toggle_perspective": "Changer de point de vue",
"lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique", "lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique",
"lambdacontrols.action.use": "Utiliser", "lambdacontrols.action.use": "Utiliser",
"lambdacontrols.action.zoom": "Zoom",
"lambdacontrols.button.a": "A", "lambdacontrols.button.a": "A",
"lambdacontrols.button.b": "B", "lambdacontrols.button.b": "B",
"lambdacontrols.button.x": "X", "lambdacontrols.button.x": "X",

View File

@@ -29,6 +29,7 @@
"lambdacontrols.action.toggle_perspective": "Changer de point de vue", "lambdacontrols.action.toggle_perspective": "Changer de point de vue",
"lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique", "lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique",
"lambdacontrols.action.use": "Utiliser", "lambdacontrols.action.use": "Utiliser",
"lambdacontrols.action.zoom": "Zoom",
"lambdacontrols.button.a": "A", "lambdacontrols.button.a": "A",
"lambdacontrols.button.b": "B", "lambdacontrols.button.b": "B",
"lambdacontrols.button.x": "X", "lambdacontrols.button.x": "X",

View File

@@ -33,7 +33,8 @@
"spruceui": ">=1.0.1" "spruceui": ">=1.0.1"
}, },
"recommends": { "recommends": {
"modmenu": ">=1.8.0+build.16" "modmenu": ">=1.8.0+build.16",
"okzoomer": ">=1.0.3"
}, },
"suggests": { "suggests": {
"flamingo": "*" "flamingo": "*"