diff --git a/LICENSE b/LICENSE index 1041c79..f87002f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright © 2019 LambdAurora +Copyright © 2020 LambdAurora Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/build.gradle b/build.gradle index 914bfb9..e9b086d 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,8 @@ dependencies { modApi "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modCompile "io.github.prospector:modmenu:1.8.0+build.16" + modCompile "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" implementation "org.jetbrains:annotations:17.0.0" diff --git a/gradle.properties b/gradle.properties index 5ffc95c..c6185f0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,11 +8,12 @@ org.gradle.jvmargs=-Xmx1G loader_version=0.7.2+build.174 # Mod Properties - mod_version = 1.0.2 + mod_version = 1.1.0-test1 maven_group = me.lambdaurora archives_base_name = lambdacontrols # Dependencies # currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api - fabric_version=0.4.25+build.282-1.15 + fabric_version=0.4.26+build.283-1.15 + spruceui_version=1.0.1 diff --git a/src/main/java/me/lambdaurora/lambdacontrols/ButtonBinding.java b/src/main/java/me/lambdaurora/lambdacontrols/ButtonBinding.java deleted file mode 100644 index 93ab6b8..0000000 --- a/src/main/java/me/lambdaurora/lambdacontrols/ButtonBinding.java +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright © 2019 LambdAurora - * - * This file is part of LambdaControls. - * - * Licensed under the MIT license. For more information, - * see the LICENSE file. - */ - -package me.lambdaurora.lambdacontrols; - -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.Identifier; -import org.aperlambda.lambdacommon.utils.Identifiable; -import org.aperlambda.lambdacommon.utils.Nameable; -import org.jetbrains.annotations.NotNull; -import org.lwjgl.glfw.GLFW; - -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Stream; - -/** - * Represents a button binding. - * - * @author LambdAurora - */ -public class ButtonBinding implements Nameable -{ - private static final List BINDINGS = new ArrayList<>(); - private static final List CATEGORIES = new ArrayList<>(); - public static final PressAction DEFAULT_ACTION = (client, button, action) -> { - if (action == 2) - return false; - button.as_key_binding().ifPresent(key_binding -> ((KeyBindingAccessor) key_binding).handle_press_state(button.is_button_down())); - return true; - }; - public static final Category MOVEMENT_CATEGORY; - public static final Category GAMEPLAY_CATEGORY; - public static final Category INVENTORY_CATEGORY; - public static final Category MULTIPLAYER_CATEGORY; - public static final Category MISC_CATEGORY; - public static final ButtonBinding ATTACK = new ButtonBinding("attack", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true)); - public static final ButtonBinding BACK = new ButtonBinding("back", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, false)); - public static final ButtonBinding CHAT = new ButtonBinding("chat", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT); - public static final ButtonBinding DROP_ITEM = new ButtonBinding("drop_item", GLFW.GLFW_GAMEPAD_BUTTON_B); - public static final ButtonBinding FORWARD = new ButtonBinding("forward", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, true)); - public static final ButtonBinding INVENTORY = new ButtonBinding("inventory", GLFW.GLFW_GAMEPAD_BUTTON_Y); - public static final ButtonBinding JUMP = new ButtonBinding("jump", GLFW.GLFW_GAMEPAD_BUTTON_A); - public static final ButtonBinding LEFT = new ButtonBinding("left", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, false)); - public static final ButtonBinding PAUSE_GAME = new ButtonBinding("pause_game", GLFW.GLFW_GAMEPAD_BUTTON_START); - public static final ButtonBinding PICK_BLOCK = new ButtonBinding("pick_block", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT); - public static final ButtonBinding PLAYER_LIST = new ButtonBinding("player_list", GLFW.GLFW_GAMEPAD_BUTTON_BACK); - public static final ButtonBinding RIGHT = new ButtonBinding("right", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, true)); - public static final ButtonBinding SCREENSHOT = new ButtonBinding("screenshot", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN, - Collections.singletonList((client, button, action) -> { - if (action == 0) - ScreenshotUtils.saveScreenshot(client.runDirectory, client.getWindow().getFramebufferWidth(), client.getWindow().getFramebufferHeight(), client.getFramebuffer(), - text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text))); - return true; - })); - public static final ButtonBinding SMOOTH_CAMERA = new ButtonBinding("toggle_smooth_camera", -1); - public static final ButtonBinding SNEAK = new ButtonBinding("sneak", GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, - Arrays.asList(DEFAULT_ACTION, (client, button, action) -> { - if (client.player != null && !client.player.abilities.flying) { - button.as_key_binding().filter(binding -> action == 0).ifPresent(binding -> ((KeyBindingAccessor) binding).handle_press_state(!binding.isPressed())); - return true; - } - return false; - })); - public static final ButtonBinding SPRINT = new ButtonBinding("sprint", GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB); - public static final ButtonBinding SWAP_HANDS = new ButtonBinding("swap_hands", GLFW.GLFW_GAMEPAD_BUTTON_X); - public static final ButtonBinding TOGGLE_PERSPECTIVE = new ButtonBinding("toggle_perspective", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP); - public static final ButtonBinding USE = new ButtonBinding("use", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true)); - - private int button; - private int default_button; - private String key; - private KeyBinding minecraft_key_binding = null; - private List actions = new ArrayList<>(Collections.singletonList(DEFAULT_ACTION)); - private boolean pressed = false; - - protected ButtonBinding(@NotNull String key, int default_button, @NotNull List actions) - { - this.default_button = this.button = default_button; - this.key = key; - this.actions.addAll(actions); - BINDINGS.add(this); - } - - protected ButtonBinding(@NotNull String key, int default_button) - { - this(key, default_button, Collections.emptyList()); - } - - /** - * Returns the button bound. - * - * @return The bound button. - */ - public int get_button() - { - return this.button; - } - - /** - * Sets the bound button. - * - * @param button The bound button. - */ - public void set_button(int button) - { - this.button = button; - } - - /** - * Returns whether the bound button is the specified button or not. - * - * @param button The button to check. - * @return True if the bound button is the specified button, else false. - */ - public boolean is_button(int button) - { - return this.button == button; - } - - /** - * Returns whether this button is down or not. - * - * @return True if the button is down, else false. - */ - public boolean is_button_down() - { - return this.pressed; - } - - /** - * Returns whether this button binding is bound or not. - * - * @return True if this button binding is bound, else false. - */ - public boolean is_not_bound() - { - return this.button == -1; - } - - /** - * Gets the default button assigned to this binding. - * - * @return The default button. - */ - public int get_default_button() - { - return this.default_button; - } - - /** - * Returns whether the assigned button is the default button. - * - * @return True if the assigned button is the default button, else false. - */ - public boolean is_default() - { - return this.button == this.default_button; - } - - @Override - public @NotNull String get_name() - { - return this.key; - } - - /** - * Returns the translation key of this button binding. - * - * @return The translation key. - */ - public @NotNull String get_translation_key() - { - return "lambdacontrols.action." + this.get_name(); - } - - /** - * Returns the key binding equivalent of this button binding. - * - * @return The key binding equivalent. - */ - public @NotNull Optional as_key_binding() - { - return Optional.ofNullable(this.minecraft_key_binding); - } - - /** - * Returns the specified axis as a button. - * - * @param axis The axis. - * @param positive True if the axis part is positive, else false. - * @return The axis as a button. - */ - public static int axis_as_button(int axis, boolean positive) - { - return positive ? 100 + axis : 200 + axis; - } - - /** - * Returns the second Joycon's specified button code. - * - * @param button The raw button code. - * @return The second Joycon's button code. - */ - public static int joycon2_button(int button) - { - return 300 + button; - } - - public static void init(@NotNull GameOptions options) - { - ATTACK.minecraft_key_binding = options.keyAttack; - BACK.minecraft_key_binding = options.keyBack; - CHAT.minecraft_key_binding = options.keyChat; - DROP_ITEM.minecraft_key_binding = options.keyDrop; - FORWARD.minecraft_key_binding = options.keyForward; - INVENTORY.minecraft_key_binding = options.keyInventory; - JUMP.minecraft_key_binding = options.keyJump; - LEFT.minecraft_key_binding = options.keyLeft; - PICK_BLOCK.minecraft_key_binding = options.keyPickItem; - PLAYER_LIST.minecraft_key_binding = options.keyPlayerList; - RIGHT.minecraft_key_binding = options.keyRight; - SCREENSHOT.minecraft_key_binding = options.keyScreenshot; - SMOOTH_CAMERA.minecraft_key_binding = options.keySmoothCamera; - SNEAK.minecraft_key_binding = options.keySneak; - SPRINT.minecraft_key_binding = options.keySprint; - SWAP_HANDS.minecraft_key_binding = options.keySwapHands; - TOGGLE_PERSPECTIVE.minecraft_key_binding = options.keyTogglePerspective; - USE.minecraft_key_binding = options.keyUse; - } - - public static void load_from_config(@NotNull LambdaControlsConfig config) - { - BINDINGS.forEach(config::load_button_binding); - } - - public static void set_button_state(int button, boolean state) - { - BINDINGS.parallelStream().filter(binding -> Objects.equals(binding.button, button)) - .forEach(binding -> binding.pressed = state); - } - - public static void handle_button(@NotNull MinecraftClient client, int button, int action) - { - BINDINGS.parallelStream().filter(binding -> binding.button == button) - .forEach(binding -> { - for (int i = binding.actions.size() - 1; i >= 0; i--) { - if (binding.actions.get(i).press(client, binding, action)) - break; - } - }); - } - - /** - * Returns whether the button has duplicated bindings. - * - * @param button The button to check. - * @return True if the button has duplicated bindings, else false. - */ - public static boolean has_duplicates(int button) - { - return BINDINGS.parallelStream().filter(binding -> binding.button == button).count() > 1; - } - - /** - * Returns the localized name of the specified button. - * - * @param button The button. - * @return The localized name of the button. - */ - public static @NotNull String get_localized_button_name(int button) - { - switch (button) { - case -1: - return I18n.translate("key.keyboard.unknown"); - case GLFW.GLFW_GAMEPAD_BUTTON_A: - return I18n.translate("lambdacontrols.button.a"); - case GLFW.GLFW_GAMEPAD_BUTTON_B: - return I18n.translate("lambdacontrols.button.b"); - case GLFW.GLFW_GAMEPAD_BUTTON_X: - return I18n.translate("lambdacontrols.button.x"); - case GLFW.GLFW_GAMEPAD_BUTTON_Y: - return I18n.translate("lambdacontrols.button.y"); - case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: - return I18n.translate("lambdacontrols.button.left_bumper"); - case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: - return I18n.translate("lambdacontrols.button.right_bumper"); - case GLFW.GLFW_GAMEPAD_BUTTON_BACK: - return I18n.translate("lambdacontrols.button.back"); - case GLFW.GLFW_GAMEPAD_BUTTON_START: - return I18n.translate("lambdacontrols.button.start"); - case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE: - return I18n.translate("lambdacontrols.button.guide"); - case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB: - return I18n.translate("lambdacontrols.button.left_thumb"); - case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: - return I18n.translate("lambdacontrols.button.right_thumb"); - case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP: - return I18n.translate("lambdacontrols.button.dpad_up"); - case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: - return I18n.translate("lambdacontrols.button.dpad_right"); - case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN: - return I18n.translate("lambdacontrols.button.dpad_down"); - case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT: - return I18n.translate("lambdacontrols.button.dpad_left"); - case 100: - return I18n.translate("lambdacontrols.axis.left_x+"); - case 101: - return I18n.translate("lambdacontrols.axis.left_y+"); - case 102: - return I18n.translate("lambdacontrols.axis.right_x+"); - case 103: - return I18n.translate("lambdacontrols.axis.right_y+"); - case 104: - return I18n.translate("lambdacontrols.axis.left_trigger"); - case 105: - return I18n.translate("lambdacontrols.axis.right_trigger"); - case 200: - return I18n.translate("lambdacontrols.axis.left_x-"); - case 201: - return I18n.translate("lambdacontrols.axis.left_y-"); - case 202: - return I18n.translate("lambdacontrols.axis.right_x-"); - case 203: - return I18n.translate("lambdacontrols.axis.right_y-"); - default: - return I18n.translate("lambdacontrols.button.unknown", button); - } - } - - public static @NotNull Stream stream() - { - return BINDINGS.stream(); - } - - public static @NotNull Stream stream_categories() - { - return CATEGORIES.stream(); - } - - static { - MOVEMENT_CATEGORY = register_default_category("key.categories.movement", category -> category.register_all_bindings( - FORWARD, - BACK, - LEFT, - RIGHT, - JUMP, - SNEAK, - SPRINT)); - GAMEPLAY_CATEGORY = register_default_category("key.categories.gameplay", category -> category.register_all_bindings( - ATTACK, - PICK_BLOCK, - USE - )); - INVENTORY_CATEGORY = register_default_category("key.categories.inventory", category -> category.register_all_bindings( - DROP_ITEM, - INVENTORY, - SWAP_HANDS - )); - MULTIPLAYER_CATEGORY = register_default_category("key.categories.multiplayer", - category -> category.register_all_bindings(CHAT, PLAYER_LIST)); - MISC_CATEGORY = register_default_category("key.categories.misc", category -> category.register_all_bindings( - SCREENSHOT, - //SMOOTH_CAMERA, - TOGGLE_PERSPECTIVE - )); - } - - public static ButtonBinding register(@NotNull Identifier binding_id, int default_button, @NotNull List actions) - { - return new ButtonBinding(binding_id.get_namespace() + "." + binding_id.get_name(), default_button, actions); - } - - public static ButtonBinding register(@NotNull Identifier binding_id, int default_button) - { - return register(binding_id, default_button, Collections.emptyList()); - } - - public static ButtonBinding register(@NotNull net.minecraft.util.Identifier binding_id, int default_button, @NotNull List actions) - { - return register(new Identifier(binding_id.getNamespace(), binding_id.getPath()), default_button, actions); - } - - public static ButtonBinding register(@NotNull net.minecraft.util.Identifier binding_id, int default_button) - { - return register(binding_id, default_button, Collections.emptyList()); - } - - /** - * Registers a category of button bindings. - * - * @param category The category to register. - * @return The registered category. - */ - public static Category register_category(@NotNull Category category) - { - CATEGORIES.add(category); - return category; - } - - public static Category register_category(@NotNull Identifier identifier, int priority) - { - return register_category(new Category(identifier, priority)); - } - - public static Category register_category(@NotNull Identifier identifier) - { - return register_category(new Category(identifier)); - } - - private static Category register_default_category(@NotNull String key, @NotNull Consumer key_adder) - { - Category category = register_category(new Identifier("minecraft", key), CATEGORIES.size()); - key_adder.accept(category); - return category; - } - - public static class Category implements Identifiable - { - private final List bindings = new ArrayList<>(); - private final Identifier id; - private int priority; - - public Category(@NotNull Identifier id, int priority) - { - this.id = id; - this.priority = priority; - } - - public Category(@NotNull Identifier id) - { - this(id, 100); - } - - public void register_binding(@NotNull ButtonBinding binding) - { - if (this.bindings.contains(binding)) - throw new IllegalStateException("Cannot register twice a button binding in the same category."); - this.bindings.add(binding); - } - - public void register_all_bindings(@NotNull ButtonBinding... bindings) - { - this.register_all_bindings(Arrays.asList(bindings)); - } - - public void register_all_bindings(@NotNull List bindings) - { - bindings.forEach(this::register_binding); - } - - /** - * Gets the bindings assigned to this category. - * - * @return The bindings assigned to this category. - */ - public @NotNull List get_bindings() - { - return Collections.unmodifiableList(this.bindings); - } - - /** - * Gets the translated name of this category. - *

- * The translation key should be `modid.identifier_name`. - * - * @return The translated name. - */ - public @NotNull String get_translated_name() - { - if (this.id.get_namespace().equals("minecraft")) - return I18n.translate(this.id.get_name()); - else - return I18n.translate(this.id.get_namespace() + "." + this.id.get_name()); - } - - /** - * Gets the priority display of this category. - * It will defines in which order the categories will display on the controls screen. - * - * @return The priority of this category. - */ - public int get_priority() - { - return this.priority; - } - - @Override - public @NotNull Identifier get_identifier() - { - return this.id; - } - } - - @FunctionalInterface - public static interface PressAction - { - /** - * Handles when there is a press action on the button. - * - * @param client The client instance. - * @param action The action done. - */ - boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, int action); - } -} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/ButtonState.java b/src/main/java/me/lambdaurora/lambdacontrols/ButtonState.java new file mode 100644 index 0000000..6002805 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/ButtonState.java @@ -0,0 +1,52 @@ +/* + * 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; + +/** + * Represents a button state. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.1.0 + */ +public enum ButtonState +{ + NONE(0), + PRESS(1), + RELEASE(2), + REPEAT(3); + + public final int id; + + ButtonState(int id) + { + this.id = id; + } + + /** + * Returns whether this state is a pressed state. + * + * @return True if this state is a pressed state, else false. + */ + public boolean is_pressed() + { + return this == PRESS || this == REPEAT; + } + + /** + * Returns whether this state is an unpressed state. + * + * @return True if this state is an unpressed state, else false. + */ + public boolean is_unpressed() + { + return this == RELEASE || this == NONE; + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/ControllerType.java b/src/main/java/me/lambdaurora/lambdacontrols/ControllerType.java index 833bba7..a51245e 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/ControllerType.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/ControllerType.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -18,6 +18,10 @@ import java.util.Optional; /** * Represents a controller type. + * + * @author LambdAurora + * @version 1.0.0 + * @since 1.0.0 */ public enum ControllerType implements Nameable { diff --git a/src/main/java/me/lambdaurora/lambdacontrols/ControlsMode.java b/src/main/java/me/lambdaurora/lambdacontrols/ControlsMode.java index 6c96bf3..1a867c0 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/ControlsMode.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/ControlsMode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -18,6 +18,10 @@ import java.util.Optional; /** * Represents the controls mode. + * + * @author LambdAurora + * @version 1.0.0 + * @since 1.0.0 */ public enum ControlsMode implements Nameable { diff --git a/src/main/java/me/lambdaurora/lambdacontrols/HudSide.java b/src/main/java/me/lambdaurora/lambdacontrols/HudSide.java index 19d1c3f..be3328b 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/HudSide.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/HudSide.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -18,6 +18,10 @@ import java.util.Optional; /** * Represents the hud side which is the side where the movements buttons are. + * + * @author LambdAurora + * @version 1.0.0 + * @since 1.0.0 */ public enum HudSide implements Nameable { diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java index d169f36..5695506 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -10,9 +10,14 @@ package me.lambdaurora.lambdacontrols; import com.mojang.blaze3d.platform.GlStateManager; +import me.lambdaurora.lambdacontrols.compat.LambdaControlsCompat; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; +import me.lambdaurora.lambdacontrols.controller.Controller; +import me.lambdaurora.lambdacontrols.gui.LambdaControlsHud; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding; import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawableHelper; @@ -24,28 +29,35 @@ import net.minecraft.text.TranslatableText; import net.minecraft.util.Identifier; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.aperlambda.lambdacommon.utils.Pair; import org.jetbrains.annotations.NotNull; import org.lwjgl.glfw.GLFW; /** * Represents the LambdaControls mod. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.0.0 */ public class LambdaControls implements ClientModInitializer { private static LambdaControls INSTANCE; - public static final FabricKeyBinding BINDING_LOOK_UP = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_up"), + public static final String MODID = "lambdacontrols"; + public static final FabricKeyBinding BINDING_LOOK_UP = FabricKeyBinding.Builder.create(new Identifier(MODID, "look_up"), InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement").build(); - public static final FabricKeyBinding BINDING_LOOK_RIGHT = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_right"), + public static final FabricKeyBinding BINDING_LOOK_RIGHT = FabricKeyBinding.Builder.create(new Identifier(MODID, "look_right"), InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement").build(); - public static final FabricKeyBinding BINDING_LOOK_DOWN = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_down"), + public static final FabricKeyBinding BINDING_LOOK_DOWN = FabricKeyBinding.Builder.create(new Identifier(MODID, "look_down"), InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement").build(); - public static final FabricKeyBinding BINDING_LOOK_LEFT = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_left"), + public static final FabricKeyBinding BINDING_LOOK_LEFT = FabricKeyBinding.Builder.create(new Identifier(MODID, "look_left"), InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement").build(); - public static final Identifier CONTROLLER_BUTTONS = new Identifier("lambdacontrols", "textures/gui/controller_buttons.png"); - public static final Identifier CONTROLLER_AXIS = new Identifier("lambdacontrols", "textures/gui/controller_axis.png"); + public static final Identifier CONTROLLER_BUTTONS = new Identifier(MODID, "textures/gui/controller_buttons.png"); + public static final Identifier CONTROLLER_AXIS = new Identifier(MODID, "textures/gui/controller_axis.png"); public final Logger logger = LogManager.getLogger("LambdaControls"); public final LambdaControlsConfig config = new LambdaControlsConfig(this); public final LambdaInput input = new LambdaInput(this); + private LambdaControlsHud hud; private ControlsMode previous_controls_mode; @Override @@ -53,12 +65,13 @@ public class LambdaControls implements ClientModInitializer { INSTANCE = this; this.log("Initializing LambdaControls..."); - this.config.load(); KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_UP); KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_RIGHT); KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_DOWN); KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_LEFT); + + HudRenderCallback.EVENT.register(delta -> this.hud.render()); } /** @@ -66,8 +79,9 @@ public class LambdaControls implements ClientModInitializer */ public void on_mc_init(@NotNull MinecraftClient client) { - Controller.update_mappings(); ButtonBinding.init(client.options); + this.config.load(); + Controller.update_mappings(); GLFW.glfwSetJoystickCallback((jid, event) -> { if (event == GLFW.GLFW_CONNECTED) { Controller controller = Controller.by_id(jid); @@ -80,6 +94,10 @@ public class LambdaControls implements ClientModInitializer this.switch_controls_mode(); }); + + this.hud = new LambdaControlsHud(client, this); + + LambdaControlsCompat.init(); } /** @@ -128,6 +146,16 @@ public class LambdaControls implements ClientModInitializer this.logger.info("[LambdaControls] " + info); } + /** + * Prints a warning to the terminal. + * + * @param warning The warning to print. + */ + public void warn(String warning) + { + this.logger.info("[LambdaControls] " + warning); + } + /** * Gets the LambdaControls instance. * @@ -138,15 +166,39 @@ public class LambdaControls implements ClientModInitializer return INSTANCE; } - public static int draw_button(int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client) + public static Pair draw_button(int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client) { return draw_button(x, y, button.get_button(), client); } - public static int draw_button(int x, int y, int button, @NotNull MinecraftClient client) + public static Pair draw_button(int x, int y, int[] buttons, @NotNull MinecraftClient client) { + int height = 0; + int length = 0; + int current_x = x; + for (int i = 0; i < buttons.length; i++) { + int btn = buttons[i]; + Pair size = draw_button(current_x, y, btn, client); + if (size.get_key() > height) + height = size.get_value(); + length += size.get_key(); + if (i + 1 < buttons.length) { + length += 2; + current_x = x + length; + } + } + return Pair.of(length, height); + } + + public static Pair draw_button(int x, int y, int button, @NotNull MinecraftClient client) + { + boolean second = false; if (button == -1) - return 0; + return Pair.of(0, 0); + else if (button >= 500) { + button -= 1000; + second = true; + } int controller_type = get().config.get_controller_type().get_id(); boolean axis = false; @@ -218,11 +270,11 @@ public class LambdaControls implements ClientModInitializer client.getTextureManager().bindTexture(axis ? LambdaControls.CONTROLLER_AXIS : LambdaControls.CONTROLLER_BUTTONS); GlStateManager.disableDepthTest(); - GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.color4f(1.0F, second ? 0.0F : 1.0F, 1.0F, 1.0F); DrawableHelper.blit(x, y, (float) button_offset, (float) (controller_type * (axis ? 18 : 15)), axis ? 18 : 15, axis ? 18 : 15, 256, 256); GlStateManager.enableDepthTest(); - return axis ? 18 : 15; + return axis ? Pair.of(18, 18) : Pair.of(15, 15); } public static int draw_button_tip(int x, int y, @NotNull ButtonBinding button, boolean display, @NotNull MinecraftClient client) @@ -230,10 +282,10 @@ public class LambdaControls implements ClientModInitializer return draw_button_tip(x, y, button.get_button(), button.get_translation_key(), display, client); } - public static int draw_button_tip(int x, int y, int button, @NotNull String action, boolean display, @NotNull MinecraftClient client) + public static int draw_button_tip(int x, int y, int[] button, @NotNull String action, boolean display, @NotNull MinecraftClient client) { if (display) { - int button_width = draw_button(x, y, button, client); + int button_width = draw_button(x, y, button, client).get_key(); String translated_action = I18n.translate(action); int text_y = (15 - client.textRenderer.fontHeight) / 2; diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java index 0cf716b..70e668e 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -10,55 +10,78 @@ package me.lambdaurora.lambdacontrols; import com.electronwill.nightconfig.core.file.FileConfig; -import net.minecraft.client.options.KeyBinding; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; +import me.lambdaurora.lambdacontrols.controller.Controller; +import me.lambdaurora.lambdacontrols.controller.InputManager; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; -import java.util.HashMap; -import java.util.Map; +import java.util.Arrays; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_X; +import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y; /** * Represents LambdaControls configuration. */ public class LambdaControlsConfig { - private final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/config.toml").build(); - private final Map keybinding_mappings = new HashMap<>(); - private final LambdaControls mod; - private ControlsMode controls_mode; - private ControllerType controller_type; + private static final ControlsMode DEFAULT_CONTROLS_MODE = ControlsMode.DEFAULT; + private static final boolean DEFAULT_AUTO_SWITCH_MODE = false; + private static final boolean DEFAULT_HUD_ENABLE = true; + private static final HudSide DEFAULT_HUD_SIDE = HudSide.LEFT; + private static final ControllerType DEFAULT_CONTROLLER_TYPE = ControllerType.DEFAULT; + private static final double DEFAULT_DEAD_ZONE = 0.25; + private static final double DEFAULT_ROTATION_SPEED = 40.0; + private static final double DEFAULT_MOUSE_SPEED = 25.0; + + private static final Pattern BUTTON_BINDING_PATTERN = Pattern.compile("(-?\\d+)\\+?"); + + private final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/config.toml").build(); + private final LambdaControls mod; + private ControlsMode controls_mode; + private ControllerType controller_type; // HUD settings. - private boolean hud_enable; - private HudSide hud_side; + private boolean hud_enable; + private HudSide hud_side; // Controller settings - private double dead_zone; - private double rotation_speed; - private double mouse_speed; + private double dead_zone; + private double rotation_speed; + private double mouse_speed; public LambdaControlsConfig(@NotNull LambdaControls mod) { this.mod = mod; } + /** + * Loads the configuration + */ public void load() { - this.keybinding_mappings.clear(); this.config.load(); + this.check_and_fix(); this.mod.log("Configuration loaded."); - this.controls_mode = ControlsMode.by_id(this.config.getOrElse("controls", "default")).orElse(ControlsMode.DEFAULT); + this.controls_mode = ControlsMode.by_id(this.config.getOrElse("controls", DEFAULT_CONTROLS_MODE.get_name())).orElse(DEFAULT_CONTROLS_MODE); // HUD settings. - this.hud_enable = this.config.getOrElse("hud.enable", true); - this.hud_side = HudSide.by_id(this.config.getOrElse("hud.side", "left")).orElse(HudSide.LEFT); + this.hud_enable = this.config.getOrElse("hud.enable", DEFAULT_HUD_ENABLE); + this.hud_side = HudSide.by_id(this.config.getOrElse("hud.side", DEFAULT_HUD_SIDE.get_name())).orElse(DEFAULT_HUD_SIDE); // Controller settings. - this.controller_type = ControllerType.by_id(this.config.getOrElse("controller.type", "default")).orElse(ControllerType.DEFAULT); - this.dead_zone = this.config.getOrElse("controller.dead_zone", 0.25); - this.rotation_speed = this.config.getOrElse("controller.rotation_speed", 40.0); - this.mouse_speed = this.config.getOrElse("controller.mouse_speed", 25.0); + this.controller_type = ControllerType.by_id(this.config.getOrElse("controller.type", DEFAULT_CONTROLLER_TYPE.get_name())).orElse(DEFAULT_CONTROLLER_TYPE); + this.dead_zone = this.config.getOrElse("controller.dead_zone", DEFAULT_DEAD_ZONE); + this.rotation_speed = this.config.getOrElse("controller.rotation_speed", DEFAULT_ROTATION_SPEED); + this.mouse_speed = this.config.getOrElse("controller.mouse_speed", DEFAULT_MOUSE_SPEED); // Controller controls. - ButtonBinding.load_from_config(this); + InputManager.load_button_bindings(this); } + /** + * Saves the configuration. + */ public void save() { this.config.set("controller.dead_zone", this.dead_zone); @@ -68,6 +91,35 @@ public class LambdaControlsConfig this.mod.log("Configuration saved."); } + public void check_and_fix() + { + InputManager.stream_bindings().forEach(binding -> { + String path = "controller.controls." + binding.get_name(); + Object raw = this.config.getRaw(path); + if (raw instanceof Number) { + this.mod.warn("Invalid data at \"" + path + "\", fixing..."); + this.config.set(path, "0;" + raw); + } + }); + } + + /** + * Resets the configuration to default values. + */ + public void reset() + { + this.set_controls_mode(DEFAULT_CONTROLS_MODE); + this.set_auto_switch_mode(DEFAULT_AUTO_SWITCH_MODE); + this.set_hud_enabled(DEFAULT_HUD_ENABLE); + this.set_hud_side(DEFAULT_HUD_SIDE); + this.set_controller_type(DEFAULT_CONTROLLER_TYPE); + this.set_dead_zone(DEFAULT_DEAD_ZONE); + this.set_rotation_speed(DEFAULT_ROTATION_SPEED); + this.set_mouse_speed(DEFAULT_MOUSE_SPEED); + + InputManager.stream_bindings().forEach(binding -> this.set_button_binding(binding, binding.get_default_button())); + } + /** * Gets the controls mode from the configuration. * @@ -96,7 +148,7 @@ public class LambdaControlsConfig */ public boolean has_auto_switch_mode() { - return this.config.getOrElse("auto_switch_mode", false); + return this.config.getOrElse("auto_switch_mode", DEFAULT_AUTO_SWITCH_MODE); } /** @@ -177,6 +229,34 @@ public class LambdaControlsConfig this.config.set("controller.id", controller.get_id()); } + /** + * Gets the second controller (for Joy-Con supports). + * + * @return The second controller. + */ + public @NotNull Optional get_second_controller() + { + Object raw = this.config.getRaw("controller.id2"); + if (raw instanceof Number) { + if ((int) raw == -1) + return Optional.empty(); + return Optional.of(Controller.by_id((Integer) raw)); + } else if (raw instanceof String) { + return Optional.of(Controller.by_guid((String) raw).orElse(Controller.by_id(GLFW.GLFW_JOYSTICK_1))); + } + return Optional.empty(); + } + + /** + * Sets the second controller. + * + * @param controller The second controller. + */ + public void set_second_controller(@Nullable Controller controller) + { + this.config.set("controller.id2", controller == null ? -1 : controller.get_id()); + } + /** * Gets the controller's type. * @@ -318,21 +398,6 @@ public class LambdaControlsConfig return this.does_invert_right_y_axis() ? -1.0 : 1.0; } - /** - * Returns the keybindings. - * - * @return The keybindings. - */ - public @NotNull Map get_keybindings() - { - return this.keybinding_mappings; - } - - public Optional get_keybind(@NotNull String id) - { - return Optional.ofNullable(this.keybinding_mappings.get(id)); - } - /** * Loads the button binding from configuration. * @@ -340,7 +405,43 @@ public class LambdaControlsConfig */ public void load_button_binding(@NotNull ButtonBinding button) { - button.set_button(this.config.getOrElse("controller.controls." + button.get_name(), button.get_button())); + button.set_button(button.get_default_button()); + String button_code = this.config.getOrElse("controller.controls." + button.get_name(), button.get_button_code()); + + Matcher matcher = BUTTON_BINDING_PATTERN.matcher(button_code); + + try { + int[] buttons = new int[1]; + int count = 0; + while (matcher.find()) { + count++; + if (count > buttons.length) + buttons = Arrays.copyOf(buttons, count); + String current; + if (!this.check_validity(button, button_code, current = matcher.group(1))) + return; + buttons[count - 1] = Integer.parseInt(current); + } + if (count == 0) { + this.mod.warn("Malformed config value \"" + button_code + "\" for binding \"" + button.get_name() + "\"."); + this.set_button_binding(button, new int[]{-1}); + } + + button.set_button(buttons); + } catch (Exception e) { + this.mod.warn("Malformed config value \"" + button_code + "\" for binding \"" + button.get_name() + "\"."); + this.config.set("controller.controls." + button.get_name(), button.get_button_code()); + } + } + + private boolean check_validity(@NotNull ButtonBinding binding, @NotNull String input, String group) + { + if (group == null) { + this.mod.warn("Malformed config value \"" + input + "\" for binding \"" + binding.get_name() + "\"."); + this.config.set("controller.controls." + binding.get_name(), binding.get_button_code()); + return false; + } + return true; } /** @@ -349,51 +450,48 @@ public class LambdaControlsConfig * @param binding The button binding. * @param button The button. */ - public void set_button_binding(@NotNull ButtonBinding binding, int button) + public void set_button_binding(@NotNull ButtonBinding binding, int[] button) { binding.set_button(button); - this.config.set("controller.controls." + binding.get_name(), button); + this.config.set("controller.controls." + binding.get_name(), binding.get_button_code()); } public boolean is_back_button(int btn, boolean is_btn, int state) { if (!is_btn && state == 0) return false; - return ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(btn, state == 1)); + return ButtonBinding.axis_as_button(GLFW_GAMEPAD_AXIS_LEFT_Y, false) == ButtonBinding.axis_as_button(btn, state == 1); } public boolean is_forward_button(int btn, boolean is_btn, int state) { if (!is_btn && state == 0) return false; - return ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(btn, state == 1)); + return ButtonBinding.axis_as_button(GLFW_GAMEPAD_AXIS_LEFT_Y, true) == ButtonBinding.axis_as_button(btn, state == 1); } public boolean is_left_button(int btn, boolean is_btn, int state) { if (!is_btn && state == 0) return false; - return ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(btn, state == 1)); + return ButtonBinding.axis_as_button(GLFW_GAMEPAD_AXIS_LEFT_X, false) == ButtonBinding.axis_as_button(btn, state == 1); } public boolean is_right_button(int btn, boolean is_btn, int state) { if (!is_btn && state == 0) return false; - return ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(btn, state == 1)); + return ButtonBinding.axis_as_button(GLFW_GAMEPAD_AXIS_LEFT_X, true) == ButtonBinding.axis_as_button(btn, state == 1); } /** * Returns whether the specified axis is an axis used for movements. * - * @param i The axis index. + * @param axis The axis index. * @return True if the axis is used for movements, else false. */ - public boolean is_movement_axis(int i) + public boolean is_movement_axis(int axis) { - return ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(i, false)) - || ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(i, false)) - || ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(i, false)) - || ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(i, false)); + return axis == GLFW_GAMEPAD_AXIS_LEFT_Y || axis == GLFW_GAMEPAD_AXIS_LEFT_X; } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsModMenu.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsModMenu.java new file mode 100644 index 0000000..97dbae4 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsModMenu.java @@ -0,0 +1,39 @@ +/* + * 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; + +import io.github.prospector.modmenu.api.ModMenuApi; +import me.lambdaurora.lambdacontrols.gui.LambdaControlsSettingsScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; + +import java.util.function.Function; + +/** + * Represents the API implementation of ModMenu for LambdaControls. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.1.0 + */ +public class LambdaControlsModMenu implements ModMenuApi +{ + @Override + public String getModId() + { + return LambdaControls.MODID; + } + + @Override + public Function getConfigScreenFactory() + { + return screen -> new LambdaControlsSettingsScreen(screen, MinecraftClient.getInstance().options, false); + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaInput.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaInput.java index 6b8ab1f..562dbfd 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaInput.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaInput.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -9,21 +9,21 @@ package me.lambdaurora.lambdacontrols; -import me.lambdaurora.lambdacontrols.gui.LabelWidget; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; +import me.lambdaurora.lambdacontrols.controller.Controller; +import me.lambdaurora.lambdacontrols.controller.InputManager; import me.lambdaurora.lambdacontrols.gui.LambdaControlsControlsScreen; import me.lambdaurora.lambdacontrols.gui.TouchscreenOverlay; import me.lambdaurora.lambdacontrols.mixin.EntryListWidgetAccessor; import me.lambdaurora.lambdacontrols.util.AbstractContainerScreenAccessor; -import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor; -import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor; import me.lambdaurora.lambdacontrols.util.MouseAccessor; +import me.lambdaurora.spruceui.SpruceLabelWidget; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.Element; import net.minecraft.client.gui.ParentElement; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.advancement.AdvancementsScreen; import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen; -import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget; import net.minecraft.client.gui.screen.world.WorldListWidget; @@ -32,7 +32,6 @@ import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.container.Slot; import net.minecraft.container.SlotActionType; -import net.minecraft.item.ItemGroup; import net.minecraft.util.math.MathHelper; import org.aperlambda.lambdacommon.utils.Pair; import org.jetbrains.annotations.NotNull; @@ -47,25 +46,26 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import static me.lambdaurora.lambdacontrols.ButtonBinding.*; +import static me.lambdaurora.lambdacontrols.controller.ButtonBinding.axis_as_button; import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X; import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y; /** * Represents the LambdaControls' input handler. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.0.0 */ public class LambdaInput { private static final Map BUTTON_STATES = new HashMap<>(); private static final Map BUTTON_COOLDOWNS = new HashMap<>(); - private static final Map AXIS_STATES = new HashMap<>(); - private static final Map AXIS_COOLDOWNS = new HashMap<>(); private final LambdaControlsConfig config; // Cooldowns private int action_gui_cooldown = 0; private int ignore_next_a = 0; // Sneak state. - private boolean sneak = false; private double prev_target_yaw = 0.0; private double prev_target_pitch = 0.0; private double target_yaw = 0.0; @@ -115,19 +115,29 @@ public class LambdaInput public void on_controller_tick(@NotNull MinecraftClient client) { BUTTON_COOLDOWNS.entrySet().stream().filter(entry -> entry.getValue() > 0).forEach(entry -> BUTTON_COOLDOWNS.put(entry.getKey(), entry.getValue() - 1)); - AXIS_COOLDOWNS.entrySet().stream().filter(entry -> entry.getValue() > 0).forEach(entry -> AXIS_COOLDOWNS.put(entry.getKey(), entry.getValue() - 1)); // Decreases the cooldown for GUI actions. if (this.action_gui_cooldown > 0) --this.action_gui_cooldown; this.prev_target_mouse_x = this.target_mouse_x; this.prev_target_mouse_y = this.target_mouse_y; + InputManager.update_states(); + Controller controller = this.config.get_controller(); if (controller.is_connected()) { GLFWGamepadState state = controller.get_state(); - this.fetch_button_input(client, state); - this.fetch_axe_input(client, state); + this.fetch_button_input(client, state, false); + this.fetch_axe_input(client, state, false); } + this.config.get_second_controller().filter(Controller::is_connected) + .ifPresent(joycon -> { + GLFWGamepadState state = joycon.get_state(); + this.fetch_button_input(client, state, true); + this.fetch_axe_input(client, state, true); + }); + + InputManager.update_bindings(); + InputManager.stream_active_bindings().forEach(binding -> binding.handle(client, InputManager.get_binding_state(binding))); if (this.ignore_next_a > 0) this.ignore_next_a--; @@ -189,66 +199,73 @@ public class LambdaInput } } - private void fetch_button_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state) + private void fetch_button_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state, boolean left_joycon) { ByteBuffer buffer = gamepad_state.buttons(); - for (int btn = 0; btn < buffer.limit(); btn++) { + for (int i = 0; i < buffer.limit(); i++) { + int btn = left_joycon ? ButtonBinding.controller2_button(i) : i; boolean btn_state = buffer.get() == (byte) 1; - boolean previous_state = BUTTON_STATES.getOrDefault(btn, false); + ButtonState current_state = ButtonState.NONE; + ButtonState previous_state = InputManager.STATES.getOrDefault(btn, ButtonState.NONE); - ButtonBinding.set_button_state(btn, btn_state); - - if (btn_state != previous_state) { + if (btn_state != previous_state.is_pressed()) { + current_state = btn_state ? ButtonState.PRESS : ButtonState.RELEASE; this.handle_button(client, btn, btn_state ? 0 : 1, btn_state); if (btn_state) BUTTON_COOLDOWNS.put(btn, 5); } else if (btn_state) { + current_state = ButtonState.REPEAT; if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) { BUTTON_COOLDOWNS.put(btn, 5); this.handle_button(client, btn, 2, true); } } - BUTTON_STATES.put(btn, btn_state); + InputManager.STATES.put(btn, current_state); } } - private void fetch_axe_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state) + private void fetch_axe_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state, boolean left_joycon) { FloatBuffer buffer = gamepad_state.axes(); for (int i = 0; i < buffer.limit(); i++) { + int axis = left_joycon ? ButtonBinding.controller2_button(i) : i; float value = buffer.get(); float abs_value = Math.abs(value); if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y) value *= -1.0F; - ButtonBinding.set_button_state(axis_as_button(i, true), value > 0.5F); - ButtonBinding.set_button_state(axis_as_button(i, false), value < -0.5F); + //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, i, 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) { 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, button); + this.config.set_button_binding(controls_screen.focused_binding, new int[]{button}); controls_screen.focused_binding = null; return; } } if (action == 0 || action == 2) { - // Handles RB and LB buttons. - if (button == GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER || button == GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) { - this.handle_rb_lb(client, button == GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER); - return; - } - if (client.currentScreen != null && is_screen_interactive(client.currentScreen) && (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)) { @@ -267,19 +284,7 @@ public class LambdaInput } } - if (action == 0) { - // Handles when the player presses the Start button. - if (PAUSE_GAME.is_button(button)) { - // If in game, then pause the game. - if (client.currentScreen == null) - client.openPauseMenu(false); - else if (client.currentScreen instanceof AbstractContainerScreen) // If the current screen is a container then close it. - client.player.closeContainer(); - else// Else just close the current screen. - client.currentScreen.onClose(); - return; - } - + if (action == 1) { if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null) { if (this.action_gui_cooldown == 0) { Element focused = client.currentScreen.getFocused(); @@ -327,11 +332,6 @@ public class LambdaInput client.currentScreen.mouseReleased(mouse_x, mouse_y, GLFW.GLFW_MOUSE_BUTTON_1); } this.action_gui_cooldown = 5; - return; - } - - if (client.currentScreen == null) { - ButtonBinding.handle_button(client, button, action); } } @@ -340,10 +340,11 @@ public class LambdaInput int as_button_state = value > 0.5F ? 1 : (value < -0.5F ? 2 : 0); if (client.currentScreen instanceof LambdaControlsControlsScreen && as_button_state != 0 - && !(as_button_state == 2 && (axis == GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER))) { + && !(as_button_state == 2 && (axis == GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == 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; if (controls_screen.focused_binding != null) { - this.config.set_button_binding(controls_screen.focused_binding, axis_as_button(axis, as_button_state == 1)); + this.config.set_button_binding(controls_screen.focused_binding, new int[]{axis_as_button(axis, as_button_state == 1)}); controls_screen.focused_binding = null; return; } @@ -352,38 +353,36 @@ public class LambdaInput double dead_zone = this.config.get_dead_zone(); if (client.currentScreen == null) { { - int axis_minus = axis + 100; boolean current_plus_state = as_button_state == 1; boolean current_minus_state = as_button_state == 2; - boolean previous_plus_state = AXIS_STATES.getOrDefault(axis, false); - boolean previous_minus_state = AXIS_STATES.getOrDefault(axis_minus, false); + ButtonState previous_plus_state = InputManager.STATES.getOrDefault(axis_as_button(axis, true), ButtonState.NONE); + ButtonState previous_minus_state = InputManager.STATES.getOrDefault(axis_as_button(axis, false), ButtonState.NONE); - if (current_plus_state != previous_plus_state) { - ButtonBinding.handle_button(client, ButtonBinding.axis_as_button(axis, true), 0); + if (current_plus_state != previous_plus_state.is_pressed()) { + InputManager.STATES.put(axis_as_button(axis, true), current_plus_state ? ButtonState.PRESS : ButtonState.RELEASE); if (current_plus_state) - AXIS_COOLDOWNS.put(axis, 5); + BUTTON_COOLDOWNS.put(axis_as_button(axis, true), 5); } else if (current_plus_state) { - if (AXIS_COOLDOWNS.getOrDefault(axis, 0) == 0) { - AXIS_COOLDOWNS.put(axis, 5); + InputManager.STATES.put(axis_as_button(axis, true), ButtonState.REPEAT); + if (BUTTON_COOLDOWNS.getOrDefault(axis_as_button(axis, true), 0) == 0) { + BUTTON_COOLDOWNS.put(axis_as_button(axis, true), 5); } } - if (current_minus_state != previous_minus_state) { - ButtonBinding.handle_button(client, ButtonBinding.axis_as_button(axis, false), 0); + if (current_minus_state != previous_minus_state.is_pressed()) { + InputManager.STATES.put(axis_as_button(axis, false), current_minus_state ? ButtonState.PRESS : ButtonState.RELEASE); if (current_minus_state) - AXIS_COOLDOWNS.put(axis_minus, 5); + BUTTON_COOLDOWNS.put(axis_as_button(axis, false), 5); } else if (current_minus_state) { - if (AXIS_COOLDOWNS.getOrDefault(axis_minus, 0) == 0) { - AXIS_COOLDOWNS.put(axis_minus, 5); + InputManager.STATES.put(axis_as_button(axis, false), ButtonState.REPEAT); + if (BUTTON_COOLDOWNS.getOrDefault(axis_as_button(axis, false), 0) == 0) { + BUTTON_COOLDOWNS.put(axis_as_button(axis, false), 5); } } - - AXIS_STATES.put(axis, current_plus_state); - AXIS_STATES.put(axis_minus, current_minus_state); } // Handles the look direction. - this.handle_look(client, axis, abs_value, state); + this.handle_look(client, axis, (float) (abs_value / (1.0 - this.config.get_dead_zone())), state); } else { boolean allow_mouse_control = true; @@ -422,8 +421,8 @@ public class LambdaInput if (Math.abs(prev_x_axis) < dead_zone && Math.abs(prev_y_axis) < dead_zone) { double mouse_x = client.mouse.getX(); double mouse_y = client.mouse.getY(); - prev_target_mouse_x = target_mouse_x = (int) mouse_x; - prev_target_mouse_y = target_mouse_y = (int) mouse_y; + this.prev_target_mouse_x = this.target_mouse_x = (int) mouse_x; + this.prev_target_mouse_y = this.target_mouse_y = (int) mouse_y; } if (Math.abs(movement_x) >= dead_zone) @@ -440,7 +439,7 @@ public class LambdaInput this.mouse_speed_y = 0.F; } - if (Math.abs(this.mouse_speed_x) > .05F || Math.abs(this.mouse_speed_y) > .05F) { + if (Math.abs(this.mouse_speed_x) >= .05F || Math.abs(this.mouse_speed_y) >= .05F) { this.target_mouse_x += this.mouse_speed_x * this.config.get_mouse_speed(); this.target_mouse_x = MathHelper.clamp(this.target_mouse_x, 0, client.getWindow().getWidth()); this.target_mouse_y += this.mouse_speed_y * this.config.get_mouse_speed(); @@ -455,32 +454,6 @@ public class LambdaInput } } - /** - * Handles the press on RB on LB. - * - * @param client The client's instance. - * @param right True if RB is pressed, else false. - */ - private void handle_rb_lb(@NotNull MinecraftClient client, boolean right) - { - // When ingame - if (client.currentScreen == null && client.player != null) { - if (right) - client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 8 ? 0 : client.player.inventory.selectedSlot + 1; - else - client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 0 ? 8 : client.player.inventory.selectedSlot - 1; - } else if (client.currentScreen instanceof CreativeInventoryScreen) { - CreativeInventoryScreenAccessor creative_inventory = (CreativeInventoryScreenAccessor) client.currentScreen; - int current_selected_tab = creative_inventory.get_selected_tab(); - int next_tab = current_selected_tab + (right ? 1 : -1); - if (next_tab < 0) - next_tab = ItemGroup.GROUPS.length - 1; - else if (next_tab >= ItemGroup.GROUPS.length) - next_tab = 0; - creative_inventory.set_selected_tab(ItemGroup.GROUPS[next_tab]); - } - } - private boolean handle_a_button(@NotNull Screen screen, @NotNull Element focused) { if (focused instanceof AbstractPressableButtonWidget) { @@ -488,8 +461,8 @@ public class LambdaInput button_widget.playDownSound(MinecraftClient.getInstance().getSoundManager()); button_widget.onPress(); return true; - } else if (focused instanceof LabelWidget) { - ((LabelWidget) focused).on_press(); + } else if (focused instanceof SpruceLabelWidget) { + ((SpruceLabelWidget) focused).on_press(); return true; } else if (focused instanceof WorldListWidget) { WorldListWidget list = (WorldListWidget) focused; @@ -557,7 +530,7 @@ public class LambdaInput { // Handles the look direction. if (client.player != null) { - double pow_value = Math.pow(value, 2.0); + double pow_value = Math.pow(value, 4.0); if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) { if (state == 2) { this.target_pitch = client.player.pitch - this.config.get_right_y_axis_sign() * (this.config.get_rotation_speed() * pow_value) * 0.33D; diff --git a/src/main/java/me/lambdaurora/lambdacontrols/compat/LambdaControlsCompat.java b/src/main/java/me/lambdaurora/lambdacontrols/compat/LambdaControlsCompat.java new file mode 100644 index 0000000..d4ba469 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/compat/LambdaControlsCompat.java @@ -0,0 +1,41 @@ +/* + * 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.controller.ButtonBinding; +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; + +/** + * Represents a compatibility handler. + * + * @author LambdAurora + * @version 1.1.0 + */ +public class LambdaControlsCompat +{ + private static final String OKZOOMER_CLASS_PATH = "io.github.joaoh1.okzoomer.OkZoomer"; + + public static void init() + { + 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); + })); + } + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonBinding.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonBinding.java new file mode 100644 index 0000000..b4f15c8 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonBinding.java @@ -0,0 +1,391 @@ +/* + * 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.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; +import org.lwjgl.glfw.GLFW; + +import java.util.*; +import java.util.stream.Collectors; + +import static me.lambdaurora.lambdacontrols.controller.InputManager.register_binding; +import static me.lambdaurora.lambdacontrols.controller.InputManager.register_default_category; + +/** + * Represents a button binding. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.0.0 + */ +public class ButtonBinding implements Nameable +{ + public static final ButtonCategory MOVEMENT_CATEGORY; + public static final ButtonCategory GAMEPLAY_CATEGORY; + public static final ButtonCategory INVENTORY_CATEGORY; + public static final ButtonCategory MULTIPLAYER_CATEGORY; + public static final ButtonCategory MISC_CATEGORY; + + public static final ButtonBinding ATTACK = register_binding(new ButtonBinding("attack", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true)}, false)); + public static final ButtonBinding BACK = register_binding(new ButtonBinding("back", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, false)}, false)); + public static final ButtonBinding CHAT = register_binding(new ButtonBinding("chat", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT}, true)); + public static final ButtonBinding DROP_ITEM = register_binding(new ButtonBinding("drop_item", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_B}, true)); + public static final ButtonBinding FORWARD = register_binding(new ButtonBinding("forward", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, true)}, false)); + public static final ButtonBinding HOTBAR_LEFT = register_binding(new ButtonBinding("hotbar_left", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER}, + Collections.singletonList(InputHandlers.handle_hotbar(false)), true)); + public static final ButtonBinding HOTBAR_RIGHT = register_binding(new ButtonBinding("hotbar_right", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER}, + Collections.singletonList(InputHandlers.handle_hotbar(true)), true)); + public static final ButtonBinding INVENTORY = register_binding(new ButtonBinding("inventory", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_Y}, true)); + public static final ButtonBinding JUMP = register_binding(new ButtonBinding("jump", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_A}, false)); + public static final ButtonBinding LEFT = register_binding(new ButtonBinding("left", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, false)}, false)); + public static final ButtonBinding PAUSE_GAME = register_binding(new ButtonBinding("pause_game", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_START}, + Collections.singletonList(InputHandlers::handle_pause_game), true)); + public static final ButtonBinding PICK_BLOCK = register_binding(new ButtonBinding("pick_block", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT}, true)); + 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)); + 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)); + 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)); + public static final ButtonBinding USE = register_binding(new ButtonBinding("use", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true)}, false)); + + private int[] button; + private int[] default_button; + private String key; + private KeyBinding minecraft_key_binding = null; + private List actions = new ArrayList<>(Collections.singletonList(PressAction.DEFAULT_ACTION)); + private boolean has_cooldown; + private int cooldown = 0; + boolean pressed = false; + + public ButtonBinding(@NotNull String key, int[] default_button, @NotNull List actions, boolean has_cooldown) + { + this.button = this.default_button = default_button; + this.key = key; + this.actions.addAll(actions); + this.has_cooldown = has_cooldown; + } + + public ButtonBinding(@NotNull String key, int[] default_button, boolean has_cooldown) + { + this(key, default_button, Collections.emptyList(), has_cooldown); + } + + /** + * Returns the button bound. + * + * @return The bound button. + */ + public int[] get_button() + { + return this.button; + } + + /** + * Sets the bound button. + * + * @param button The bound button. + */ + public void set_button(int[] button) + { + this.button = button; + } + + /** + * Returns whether the bound button is the specified button or not. + * + * @param button The button to check. + * @return True if the bound button is the specified button, else false. + */ + public boolean is_button(int[] button) + { + return InputManager.are_buttons_equivalent(button, this.button); + } + + /** + * Returns whether this button is down or not. + * + * @return True if the button is down, else false. + */ + public boolean is_button_down() + { + return this.pressed; + } + + /** + * Returns whether this button binding is bound or not. + * + * @return True if this button binding is bound, else false. + */ + public boolean is_not_bound() + { + return this.button[0] == -1; + } + + /** + * Gets the default button assigned to this binding. + * + * @return The default button. + */ + public int[] get_default_button() + { + return this.default_button; + } + + /** + * Returns whether the assigned button is the default button. + * + * @return True if the assigned button is the default button, else false. + */ + public boolean is_default() + { + return this.button.length == this.default_button.length && InputManager.are_buttons_equivalent(this.button, this.default_button); + } + + /** + * Returns the button code. + * + * @return The button code. + */ + public @NotNull String get_button_code() + { + return Arrays.stream(this.button) + .mapToObj(btn -> Integer.valueOf(btn).toString()) + .collect(Collectors.joining("+")); + } + + /** + * Sets the key binding to emulate with this button binding. + * + * @param key_binding The optional key binding. + */ + public void set_key_binding(@Nullable KeyBinding key_binding) + { + this.minecraft_key_binding = key_binding; + } + + /** + * Updates the button binding cooldown. + */ + public void update() + { + if (this.has_cooldown && this.cooldown > 0) + this.cooldown--; + } + + /** + * Handles the button binding. + * + * @param client The client instance. + * @param state The state. + */ + public void handle(@NotNull MinecraftClient client, @NotNull ButtonState state) + { + if (state == ButtonState.REPEAT && this.has_cooldown && this.cooldown != 0) + return; + 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; + } + } + + @Override + public @NotNull String get_name() + { + return this.key; + } + + /** + * Returns the translation key of this button binding. + * + * @return The translation key. + */ + public @NotNull String get_translation_key() + { + return "lambdacontrols.action." + this.get_name(); + } + + /** + * Returns the key binding equivalent of this button binding. + * + * @return The key binding equivalent. + */ + public @NotNull Optional as_key_binding() + { + return Optional.ofNullable(this.minecraft_key_binding); + } + + /** + * Returns the specified axis as a button. + * + * @param axis The axis. + * @param positive True if the axis part is positive, else false. + * @return The axis as a button. + */ + public static int axis_as_button(int axis, boolean positive) + { + return positive ? 100 + axis : 200 + axis; + } + + /** + * Returns the second Joycon's specified button code. + * + * @param button The raw button code. + * @return The second Joycon's button code. + */ + public static int controller2_button(int button) + { + return 500 + button; + } + + public static void init(@NotNull GameOptions options) + { + ATTACK.minecraft_key_binding = options.keyAttack; + BACK.minecraft_key_binding = options.keyBack; + CHAT.minecraft_key_binding = options.keyChat; + DROP_ITEM.minecraft_key_binding = options.keyDrop; + FORWARD.minecraft_key_binding = options.keyForward; + INVENTORY.minecraft_key_binding = options.keyInventory; + JUMP.minecraft_key_binding = options.keyJump; + LEFT.minecraft_key_binding = options.keyLeft; + PICK_BLOCK.minecraft_key_binding = options.keyPickItem; + PLAYER_LIST.minecraft_key_binding = options.keyPlayerList; + RIGHT.minecraft_key_binding = options.keyRight; + SCREENSHOT.minecraft_key_binding = options.keyScreenshot; + SMOOTH_CAMERA.minecraft_key_binding = options.keySmoothCamera; + SNEAK.minecraft_key_binding = options.keySneak; + SPRINT.minecraft_key_binding = options.keySprint; + SWAP_HANDS.minecraft_key_binding = options.keySwapHands; + TOGGLE_PERSPECTIVE.minecraft_key_binding = options.keyTogglePerspective; + USE.minecraft_key_binding = options.keyUse; + } + + /** + * Returns the localized name of the specified button. + * + * @param button The button. + * @return The localized name of the button. + */ + public static @NotNull String get_localized_button_name(int button) + { + switch (button % 500) { + case -1: + return I18n.translate("key.keyboard.unknown"); + case GLFW.GLFW_GAMEPAD_BUTTON_A: + return I18n.translate("lambdacontrols.button.a"); + case GLFW.GLFW_GAMEPAD_BUTTON_B: + return I18n.translate("lambdacontrols.button.b"); + case GLFW.GLFW_GAMEPAD_BUTTON_X: + return I18n.translate("lambdacontrols.button.x"); + case GLFW.GLFW_GAMEPAD_BUTTON_Y: + return I18n.translate("lambdacontrols.button.y"); + case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: + return I18n.translate("lambdacontrols.button.left_bumper"); + case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: + return I18n.translate("lambdacontrols.button.right_bumper"); + case GLFW.GLFW_GAMEPAD_BUTTON_BACK: + return I18n.translate("lambdacontrols.button.back"); + case GLFW.GLFW_GAMEPAD_BUTTON_START: + return I18n.translate("lambdacontrols.button.start"); + case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE: + return I18n.translate("lambdacontrols.button.guide"); + case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB: + return I18n.translate("lambdacontrols.button.left_thumb"); + case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: + return I18n.translate("lambdacontrols.button.right_thumb"); + case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP: + return I18n.translate("lambdacontrols.button.dpad_up"); + case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: + return I18n.translate("lambdacontrols.button.dpad_right"); + case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN: + return I18n.translate("lambdacontrols.button.dpad_down"); + case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT: + return I18n.translate("lambdacontrols.button.dpad_left"); + case 100: + return I18n.translate("lambdacontrols.axis.left_x+"); + case 101: + return I18n.translate("lambdacontrols.axis.left_y+"); + case 102: + return I18n.translate("lambdacontrols.axis.right_x+"); + case 103: + return I18n.translate("lambdacontrols.axis.right_y+"); + case 104: + return I18n.translate("lambdacontrols.axis.left_trigger"); + case 105: + return I18n.translate("lambdacontrols.axis.right_trigger"); + case 200: + return I18n.translate("lambdacontrols.axis.left_x-"); + case 201: + return I18n.translate("lambdacontrols.axis.left_y-"); + case 202: + return I18n.translate("lambdacontrols.axis.right_x-"); + case 203: + return I18n.translate("lambdacontrols.axis.right_y-"); + default: + return I18n.translate("lambdacontrols.button.unknown", button); + } + } + + static { + MOVEMENT_CATEGORY = register_default_category("key.categories.movement", category -> category.register_all_bindings( + ButtonBinding.FORWARD, + ButtonBinding.BACK, + ButtonBinding.LEFT, + ButtonBinding.RIGHT, + ButtonBinding.JUMP, + ButtonBinding.SNEAK, + ButtonBinding.SPRINT)); + GAMEPLAY_CATEGORY = register_default_category("key.categories.gameplay", category -> category.register_all_bindings( + ButtonBinding.ATTACK, + ButtonBinding.PICK_BLOCK, + ButtonBinding.USE + )); + INVENTORY_CATEGORY = register_default_category("key.categories.inventory", category -> category.register_all_bindings( + ButtonBinding.DROP_ITEM, + ButtonBinding.HOTBAR_LEFT, + ButtonBinding.HOTBAR_RIGHT, + ButtonBinding.INVENTORY, + ButtonBinding.SWAP_HANDS + )); + MULTIPLAYER_CATEGORY = register_default_category("key.categories.multiplayer", + category -> category.register_all_bindings(ButtonBinding.CHAT, ButtonBinding.PLAYER_LIST)); + MISC_CATEGORY = register_default_category("key.categories.misc", category -> category.register_all_bindings( + ButtonBinding.SCREENSHOT, + //SMOOTH_CAMERA, + ButtonBinding.TOGGLE_PERSPECTIVE + )); + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonCategory.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonCategory.java new file mode 100644 index 0000000..6d686eb --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/ButtonCategory.java @@ -0,0 +1,104 @@ +/* + * 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.controller; + +import net.minecraft.client.resource.language.I18n; +import org.aperlambda.lambdacommon.Identifier; +import org.aperlambda.lambdacommon.utils.Identifiable; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Represents a button binding category + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.1.0 + */ +public class ButtonCategory implements Identifiable +{ + private final List bindings = new ArrayList<>(); + private final Identifier id; + private int priority; + + public ButtonCategory(@NotNull Identifier id, int priority) + { + this.id = id; + this.priority = priority; + } + + public ButtonCategory(@NotNull Identifier id) + { + this(id, 100); + } + + public void register_binding(@NotNull ButtonBinding binding) + { + if (this.bindings.contains(binding)) + throw new IllegalStateException("Cannot register twice a button binding in the same category."); + this.bindings.add(binding); + } + + public void register_all_bindings(@NotNull ButtonBinding... bindings) + { + this.register_all_bindings(Arrays.asList(bindings)); + } + + public void register_all_bindings(@NotNull List bindings) + { + bindings.forEach(this::register_binding); + } + + /** + * Gets the bindings assigned to this category. + * + * @return The bindings assigned to this category. + */ + public @NotNull List get_bindings() + { + return Collections.unmodifiableList(this.bindings); + } + + /** + * Gets the translated name of this category. + *

+ * The translation key should be `modid.identifier_name`. + * + * @return The translated name. + */ + public @NotNull String get_translated_name() + { + if (this.id.get_namespace().equals("minecraft")) + return I18n.translate(this.id.get_name()); + else + return I18n.translate(this.id.get_namespace() + "." + this.id.get_name()); + } + + /** + * Gets the priority display of this category. + * It will defines in which order the categories will display on the controls screen. + * + * @return The priority of this category. + */ + public int get_priority() + { + return this.priority; + } + + @Override + public @NotNull Identifier get_identifier() + { + return this.id; + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/Controller.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/Controller.java similarity index 95% rename from src/main/java/me/lambdaurora/lambdacontrols/Controller.java rename to src/main/java/me/lambdaurora/lambdacontrols/controller/Controller.java index 9bcb685..5c0ee8d 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/Controller.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/Controller.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -7,8 +7,9 @@ * see the LICENSE file. */ -package me.lambdaurora.lambdacontrols; +package me.lambdaurora.lambdacontrols.controller; +import me.lambdaurora.lambdacontrols.LambdaControls; import org.aperlambda.lambdacommon.utils.Nameable; import org.jetbrains.annotations.NotNull; import org.lwjgl.glfw.GLFW; @@ -31,6 +32,10 @@ import static org.lwjgl.BufferUtils.createByteBuffer; /** * Represents a controller. + * + * @author LambdAurora + * @version 1.0.0 + * @since 1.0.0 */ public class Controller implements Nameable { diff --git a/src/main/java/me/lambdaurora/lambdacontrols/controller/InputHandlers.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/InputHandlers.java new file mode 100644 index 0000000..1fad394 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/InputHandlers.java @@ -0,0 +1,70 @@ +/* + * 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.controller; + +import me.lambdaurora.lambdacontrols.ButtonState; +import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen; +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; +import net.minecraft.item.ItemGroup; +import org.jetbrains.annotations.NotNull; + +/** + * Represents some input handlers. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.1.0 + */ +public class InputHandlers +{ + private InputHandlers() {} + + public static PressAction handle_hotbar(boolean right) { + return (client, button, action) -> { + if (action == ButtonState.RELEASE) + return false; + + // When ingame + if (client.currentScreen == null && client.player != null) { + if (right) + client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 8 ? 0 : client.player.inventory.selectedSlot + 1; + else + client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 0 ? 8 : client.player.inventory.selectedSlot - 1; + return true; + } else if (client.currentScreen instanceof CreativeInventoryScreen) { + CreativeInventoryScreenAccessor creative_inventory = (CreativeInventoryScreenAccessor) client.currentScreen; + int current_selected_tab = creative_inventory.get_selected_tab(); + int next_tab = current_selected_tab + (right ? 1 : -1); + if (next_tab < 0) + next_tab = ItemGroup.GROUPS.length - 1; + else if (next_tab >= ItemGroup.GROUPS.length) + next_tab = 0; + creative_inventory.set_selected_tab(ItemGroup.GROUPS[next_tab]); + return true; + } + return false; + }; + } + + 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) + client.openPauseMenu(false); + else if (client.currentScreen instanceof AbstractContainerScreen && client.player != null) // If the current screen is a container then close it. + client.player.closeContainer(); + else // Else just close the current screen. + client.currentScreen.onClose(); + } + return true; + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/controller/InputManager.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/InputManager.java new file mode 100644 index 0000000..88b2458 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/InputManager.java @@ -0,0 +1,197 @@ +/* + * 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.controller; + +import me.lambdaurora.lambdacontrols.ButtonState; +import me.lambdaurora.lambdacontrols.LambdaControlsConfig; +import org.aperlambda.lambdacommon.Identifier; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Stream; + +/** + * Represents an input manager for controllers. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.1.0 + */ +public class InputManager +{ + private static final List BINDINGS = new ArrayList<>(); + private static final List CATEGORIES = new ArrayList<>(); + public static final Map STATES = new HashMap<>(); + + public static @NotNull ButtonBinding register_binding(@NotNull ButtonBinding binding) + { + if (BINDINGS.contains(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) + { + 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) + { + 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) + { + 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) + { + return register_binding(binding_id, default_button, Collections.emptyList(), has_cooldown); + } + + /** + * Registers a category of button bindings. + * + * @param category The category to register. + * @return The registered category. + */ + public static ButtonCategory register_category(@NotNull ButtonCategory category) + { + CATEGORIES.add(category); + return category; + } + + public static ButtonCategory register_category(@NotNull Identifier identifier, int priority) + { + return register_category(new ButtonCategory(identifier, priority)); + } + + public static ButtonCategory register_category(@NotNull Identifier identifier) + { + return register_category(new ButtonCategory(identifier)); + } + + protected static ButtonCategory register_default_category(@NotNull String key, @NotNull Consumer key_adder) + { + ButtonCategory category = register_category(new Identifier("minecraft", key), CATEGORIES.size()); + key_adder.accept(category); + return category; + } + + /** + * Loads the button bindings from configuration. + * + * @param config The configuration instance. + */ + public static void load_button_bindings(@NotNull LambdaControlsConfig config) + { + BINDINGS.forEach(config::load_button_binding); + } + + /** + * Returns the binding state. + * + * @param binding The binding. + * @return The current state of the binding. + */ + public static @NotNull ButtonState get_binding_state(@NotNull ButtonBinding binding) + { + ButtonState state = ButtonState.REPEAT; + for (int btn : binding.get_button()) { + ButtonState btn_state = InputManager.STATES.getOrDefault(btn, ButtonState.NONE); + if (btn_state == ButtonState.PRESS) + state = ButtonState.PRESS; + else if (btn_state == ButtonState.RELEASE) { + state = ButtonState.RELEASE; + break; + } else if (btn_state == ButtonState.NONE) { + state = ButtonState.NONE; + break; + } + } + return state; + } + + /** + * Returns whether the button has duplicated bindings. + * + * @param button The button to check. + * @return True if the button has duplicated bindings, else false. + */ + public static boolean has_duplicated_bindings(int[] button) + { + return BINDINGS.parallelStream().filter(binding -> are_buttons_equivalent(binding.get_button(), button)).count() > 1; + } + + /** + * Returns whether the specified buttons are equivalent or not. + * + * @param buttons1 First set of buttons. + * @param buttons2 Second set of buttons. + * @return True if the two sets of buttons are equivalent, else false. + */ + public static boolean are_buttons_equivalent(int[] buttons1, int[] buttons2) + { + if (buttons1.length != buttons2.length) + return false; + int count = 0; + for (int btn : buttons1) { + for (int btn2 : buttons2) { + if (btn == btn2) { + count++; + break; + } + } + } + return count == buttons1.length; + } + + /** + * Updates the button states. + */ + public static void update_states() + { + STATES.forEach((btn, state) -> { + if (state == ButtonState.PRESS) + STATES.put(btn, ButtonState.REPEAT); + else if (state == ButtonState.RELEASE) + STATES.put(btn, ButtonState.NONE); + }); + } + + public static void update_bindings() { + BINDINGS.forEach(binding -> binding.pressed = get_binding_state(binding).is_pressed()); + BINDINGS.forEach(ButtonBinding::update); + } + + public static @NotNull Stream stream_bindings() + { + 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/controller/PressAction.java b/src/main/java/me/lambdaurora/lambdacontrols/controller/PressAction.java new file mode 100644 index 0000000..c0c8041 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/controller/PressAction.java @@ -0,0 +1,41 @@ +/* + * 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.controller; + +import me.lambdaurora.lambdacontrols.ButtonState; +import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor; +import net.minecraft.client.MinecraftClient; +import org.jetbrains.annotations.NotNull; + +/** + * Represents a press action callback. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.0.0 + */ +@FunctionalInterface +public interface PressAction +{ + PressAction DEFAULT_ACTION = (client, button, action) -> { + if (action == ButtonState.REPEAT || client.currentScreen != null) + return false; + button.as_key_binding().ifPresent(key_binding -> ((KeyBindingAccessor) key_binding).handle_press_state(button.is_button_down())); + return true; + }; + + /** + * Handles when there is a press action. + * + * @param client The client instance. + * @param action The action done. + */ + boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, @NotNull ButtonState action); +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/AbstractIconButtonWidget.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/AbstractIconButtonWidget.java deleted file mode 100644 index 65c35cb..0000000 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/AbstractIconButtonWidget.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2019 LambdAurora - * - * This file is part of LambdaControls. - * - * Licensed under the MIT license. For more information, - * see the LICENSE file. - */ - -package me.lambdaurora.lambdacontrols.gui; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.util.math.MathHelper; -import org.jetbrains.annotations.NotNull; - -/** - * Represents a button with an icon instead of text. - */ -public abstract class AbstractIconButtonWidget extends ButtonWidget -{ - private int icon_size = 0; - - public AbstractIconButtonWidget(int x, int y, int width, int height, @NotNull String message, @NotNull PressAction on_press) - { - super(x, y, width, height, message, on_press); - } - - protected abstract int render_icon(int mouse_x, int mouse_y, float delta, int x, int y); - - @Override - public void renderButton(int mouse_x, int mouse_y, float delta) - { - MinecraftClient client = MinecraftClient.getInstance(); - client.getTextureManager().bindTexture(WIDGETS_LOCATION); - RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha); - int i = this.getYImage(this.isHovered()); - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA); - this.blit(this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height); - this.blit(this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height); - this.renderBg(client, mouse_x, mouse_y); - - this.icon_size = this.render_icon(mouse_x, mouse_y, delta, this.x + 4, this.y + (this.height / 2 - this.icon_size / 2)); - - if (!this.getMessage().isEmpty()) { - int j = this.active ? 16777215 : 10526880; - this.drawCenteredString(client.textRenderer, this.getMessage(), this.x + 8 + this.icon_size + (this.width - 8 - this.icon_size - 6) / 2, - this.y + (this.height - 8) / 2, j | MathHelper.ceil(this.alpha * 255.0F) << 24); - } - } -} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/ControllerButtonWidget.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/ControllerButtonWidget.java index 206c5d2..a055c2c 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/ControllerButtonWidget.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/ControllerButtonWidget.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -9,8 +9,9 @@ package me.lambdaurora.lambdacontrols.gui; -import me.lambdaurora.lambdacontrols.ButtonBinding; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; import me.lambdaurora.lambdacontrols.LambdaControls; +import me.lambdaurora.spruceui.AbstractIconButtonWidget; import net.minecraft.client.MinecraftClient; import org.jetbrains.annotations.NotNull; @@ -23,18 +24,18 @@ public class ControllerButtonWidget extends AbstractIconButtonWidget public ControllerButtonWidget(int x, int y, int width, @NotNull ButtonBinding button_binding, @NotNull PressAction on_press) { - super(x, y, width, 20, ButtonBinding.get_localized_button_name(button_binding.get_button()), on_press); + super(x, y, width, 20, ButtonBinding.get_localized_button_name(button_binding.get_button()[0]), on_press); this.binding = button_binding; } public void update() { - this.setMessage(ButtonBinding.get_localized_button_name(binding.get_button())); + this.setMessage(ButtonBinding.get_localized_button_name(binding.get_button()[0])); } @Override protected int render_icon(int mouse_x, int mouse_y, float delta, int x, int y) { - return LambdaControls.draw_button(x, y, this.binding, MinecraftClient.getInstance()); + return LambdaControls.draw_button(x, y, this.binding, MinecraftClient.getInstance()).get_value(); } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/ControlsListWidget.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/ControlsListWidget.java index 2ba5dc5..ca31c1d 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/ControlsListWidget.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/ControlsListWidget.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -9,7 +9,9 @@ package me.lambdaurora.lambdacontrols.gui; -import me.lambdaurora.lambdacontrols.ButtonBinding; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; +import me.lambdaurora.lambdacontrols.controller.ButtonCategory; +import me.lambdaurora.lambdacontrols.controller.InputManager; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; @@ -39,8 +41,8 @@ public class ControlsListWidget extends ElementListWidget { this.addEntry(new CategoryEntry(category)); @@ -78,7 +80,7 @@ public class ControlsListWidget extends ElementListWidget gui.focused_binding = binding) + this.edit_button = new ControllerButtonWidget(0, 0, 110, this.binding, btn -> gui.focused_binding = binding) { protected String getNarrationMessage() { @@ -114,16 +116,16 @@ public class ControlsListWidget extends ElementListWidget " + Formatting.YELLOW + this.edit_button.getMessage() + Formatting.WHITE + " <"); - } else if (!this.binding.is_not_bound() && ButtonBinding.has_duplicates(this.binding.get_button())) { + } else if (!this.binding.is_not_bound() && InputManager.has_duplicated_bindings(this.binding.get_button())) { this.edit_button.setMessage(Formatting.RED + this.edit_button.getMessage()); } else if (this.binding.is_not_bound()) { - this.edit_button.setMessage(Formatting.GOLD + edit_button.getMessage()); + this.edit_button.setMessage(Formatting.GOLD + this.edit_button.getMessage()); } this.edit_button.render(mouse_x, mouse_y, delta); @@ -148,7 +150,7 @@ public class ControlsListWidget extends ElementListWidget - * - * This file is part of LambdaControls. - * - * Licensed under the MIT license. For more information, - * see the LICENSE file. - */ - -package me.lambdaurora.lambdacontrols.gui; - -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.DrawableHelper; -import net.minecraft.client.gui.Element; -import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.util.math.Matrix4f; -import net.minecraft.client.util.math.MatrixStack; -import org.jetbrains.annotations.NotNull; -import org.lwjgl.glfw.GLFW; - -import java.util.List; -import java.util.function.Consumer; - -/** - * Represents a label widget. - */ -// @TODO move this to a GUI library. -public class LabelWidget extends DrawableHelper implements Element, Drawable -{ - public static final Consumer DEFAULT_ACTION = label -> { - }; - - private final MinecraftClient client = MinecraftClient.getInstance(); - private final Consumer press_action; - private final int x; - private final int y; - private final int max_width; - //private final int max_height; - private String text; - private String tooltip_text; - public boolean visible; - private int width; - private int height; - private boolean centered; - protected boolean hovered; - protected boolean focused; - - public LabelWidget(int x, int y, @NotNull String text, int max_width, @NotNull Consumer press_action, boolean centered) - { - this.visible = true; - this.x = x; - this.y = y; - this.max_width = max_width; - this.press_action = press_action; - this.centered = centered; - this.set_text(text); - } - - public LabelWidget(int x, int y, @NotNull String text, int max_width, @NotNull Consumer press_action) - { - this(x, y, text, max_width, press_action, false); - } - - public LabelWidget(int x, int y, @NotNull String text, int max_width, boolean centered) - { - this(x, y, text, max_width, DEFAULT_ACTION, centered); - } - - public LabelWidget(int x, int y, @NotNull String text, int max_width) - { - this(x, y, text, max_width, DEFAULT_ACTION); - } - - /** - * Sets the text of this label. - * - * @param text The text to set. - */ - public void set_text(@NotNull String text) - { - int width = this.client.textRenderer.getStringWidth(text); - while (width > this.max_width) { - text = text.substring(0, text.length() - 1); - width = this.client.textRenderer.getStringWidth(text); - } - - this.text = text; - this.width = width; - this.height = this.client.textRenderer.fontHeight; - } - - /** - * Sets the tooltip text of this label. - * - * @param text The tooltip text. - */ - public void set_tooltip_text(String text) - { - this.tooltip_text = text; - } - - /** - * Gets the width of this label widget. - * - * @return The width of this label widget. - */ - public int get_width() - { - return this.width; - } - - /** - * Gets the height of this label widget. - * - * @return The height of this label widget. - */ - public int get_height() - { - return this.height; - } - - /** - * Fires the press event on this label widget. - */ - public void on_press() - { - this.press_action.accept(this); - } - - @Override - public void render(int mouse_x, int mouse_y, float delta) - { - if (this.visible) { - int x = this.centered ? this.x - this.client.textRenderer.getStringWidth(this.text) / 2 : this.x; - this.hovered = mouse_x >= x && mouse_y >= this.y && mouse_x < x + this.width && mouse_y < this.y + this.height; - this.drawString(this.client.textRenderer, this.text, x, this.y, 10526880); - - if (this.tooltip_text != null && !this.tooltip_text.isEmpty()) { - List wrapped_tooltip_text = this.client.textRenderer.wrapStringToWidthAsList(this.tooltip_text, Math.max(this.width / 2, 200)); - if (this.hovered) - this.render_tooltip(wrapped_tooltip_text, mouse_x, mouse_y); - else if (this.focused) - this.render_tooltip(wrapped_tooltip_text, this.x - 12, this.y); - } - } - } - - @Override - public boolean mouseClicked(double mouse_x, double mouse_y, int button) - { - if (this.visible && button == GLFW.GLFW_MOUSE_BUTTON_1) { - if (this.hovered) { - this.on_press(); - return true; - } - } - return false; - } - - @Override - public boolean changeFocus(boolean down) - { - if (this.visible) { - this.focused = !this.focused; - return this.focused; - } else { - return false; - } - } - - public void render_tooltip(List text, int x, int y) - { - if (!text.isEmpty()) { - RenderSystem.disableRescaleNormal(); - RenderSystem.disableDepthTest(); - int i = 0; - - for (String string : text) { - int j = this.client.textRenderer.getStringWidth(string); - if (j > i) { - i = j; - } - } - - int k = x + 12; - int l = y - 12; - int n = 8; - if (text.size() > 1) { - n += 2 + (text.size() - 1) * 10; - } - - if (k + i > this.client.getWindow().getScaledWidth()) { - k -= 28 + i; - } - - if (l + n + 6 > this.client.getWindow().getScaledHeight()) { - l = this.client.getWindow().getScaledHeight() - n - 6; - } - - this.setBlitOffset(300); - this.client.getItemRenderer().zOffset = 300.0F; - this.fillGradient(k - 3, l - 4, k + i + 3, l - 3, -267386864, -267386864); - this.fillGradient(k - 3, l + n + 3, k + i + 3, l + n + 4, -267386864, -267386864); - this.fillGradient(k - 3, l - 3, k + i + 3, l + n + 3, -267386864, -267386864); - this.fillGradient(k - 4, l - 3, k - 3, l + n + 3, -267386864, -267386864); - this.fillGradient(k + i + 3, l - 3, k + i + 4, l + n + 3, -267386864, -267386864); - this.fillGradient(k - 3, l - 3 + 1, k - 3 + 1, l + n + 3 - 1, 1347420415, 1344798847); - this.fillGradient(k + i + 2, l - 3 + 1, k + i + 3, l + n + 3 - 1, 1347420415, 1344798847); - this.fillGradient(k - 3, l - 3, k + i + 3, l - 3 + 1, 1347420415, 1347420415); - this.fillGradient(k - 3, l + n + 2, k + i + 3, l + n + 3, 1344798847, 1344798847); - MatrixStack matrix_stack = new MatrixStack(); - VertexConsumerProvider.Immediate immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer()); - matrix_stack.translate(0.0D, 0.0D, this.client.getItemRenderer().zOffset); - Matrix4f matrix4f = matrix_stack.peek().getModel(); - - for (int r = 0; r < text.size(); ++r) { - String string2 = text.get(r); - if (string2 != null) { - this.client.textRenderer.draw(string2, (float) k, (float) l, -1, true, matrix4f, immediate, false, 0, 15728880); - } - - if (r == 0) { - l += 2; - } - - l += 10; - } - - immediate.draw(); - this.setBlitOffset(0); - this.client.getItemRenderer().zOffset = 0.0F; - RenderSystem.enableDepthTest(); - RenderSystem.enableRescaleNormal(); - } - } -} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsControlsScreen.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsControlsScreen.java index 841a239..ba89a49 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsControlsScreen.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsControlsScreen.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -9,9 +9,12 @@ package me.lambdaurora.lambdacontrols.gui; -import me.lambdaurora.lambdacontrols.ButtonBinding; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; import me.lambdaurora.lambdacontrols.LambdaControls; +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; @@ -27,19 +30,21 @@ import java.util.function.Predicate; */ public class LambdaControlsControlsScreen extends Screen { - private final LambdaControlsSettingsScreen parent; - final LambdaControls mod; - private final Option inverts_right_x_axis; - private final Option inverts_right_y_axis; - private ControlsListWidget bindings_list_widget; - private ButtonWidget reset_button; - public ButtonBinding focused_binding; + 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; - protected LambdaControlsControlsScreen(@NotNull LambdaControlsSettingsScreen parent) + public LambdaControlsControlsScreen(@NotNull Screen parent, boolean hide_settings) { super(new TranslatableText("lambdacontrols.menu.title.controller_controls")); this.parent = parent; - this.mod = parent.mod; + 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) { @@ -64,12 +69,17 @@ 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(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) + this.addButton(new SpruceButtonWidget(this.width / 2 - 155 + 160, 18, 150, 20, I18n.translate("menu.options"), + btn -> this.minecraft.openScreen(new LambdaControlsSettingsScreen(this, this.minecraft.options, true)))); this.bindings_list_widget = new ControlsListWidget(this, this.minecraft); this.children.add(this.bindings_list_widget); this.reset_button = this.addButton(new ButtonWidget(this.width / 2 - 155, this.height - 29, 150, 20, I18n.translate("controls.resetAll"), - btn -> ButtonBinding.stream().forEach(binding -> this.mod.config.set_button_binding(binding, binding.get_default_button())))); + btn -> InputManager.stream_bindings().forEach(binding -> this.mod.config.set_button_binding(binding, binding.get_default_button())))); this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, 20, I18n.translate("gui.done"), btn -> this.minecraft.openScreen(this.parent))); } @@ -87,7 +97,7 @@ public class LambdaControlsControlsScreen extends Screen this.renderBackground(); this.bindings_list_widget.render(mouse_x, mouse_y, delta); this.drawCenteredString(this.font, this.title.asFormattedString(), this.width / 2, 8, 16777215); - this.reset_button.active = ButtonBinding.stream().anyMatch(this.not(ButtonBinding::is_default)); + this.reset_button.active = InputManager.stream_bindings().anyMatch(this.not(ButtonBinding::is_default)); super.render(mouse_x, mouse_y, delta); } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsHud.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsHud.java index f751831..a6683ad 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsHud.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsHud.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -9,7 +9,8 @@ package me.lambdaurora.lambdacontrols.gui; -import me.lambdaurora.lambdacontrols.ButtonBinding; +import me.lambdaurora.lambdacontrols.HudSide; +import me.lambdaurora.lambdacontrols.controller.ButtonBinding; import me.lambdaurora.lambdacontrols.ControlsMode; import me.lambdaurora.lambdacontrols.LambdaControls; import net.minecraft.client.MinecraftClient; @@ -19,11 +20,17 @@ import org.jetbrains.annotations.NotNull; /** * Represents the LambdaControls HUD. + * + * @author LambdAurora + * @version 1.1.0 + * @since 1.0.0 */ public class LambdaControlsHud extends DrawableHelper { private final MinecraftClient client; private final LambdaControls mod; + private int width_bottom = 0; + private int width_top = 0; public LambdaControlsHud(@NotNull MinecraftClient client, @NotNull LambdaControls mod) { @@ -37,12 +44,12 @@ public class LambdaControlsHud extends DrawableHelper public void render() { if (this.mod.config.get_controls_mode() == ControlsMode.CONTROLLER && this.mod.config.is_hud_enabled() && this.client.currentScreen == null && !this.client.options.hudHidden) { - int x = 10, y = bottom(10); - x += this.draw_button_tip(x, y, ButtonBinding.INVENTORY, true) + 10; - this.draw_button_tip(x, y, ButtonBinding.SWAP_HANDS, true); - x = 10; - x += this.draw_button_tip(x, (y -= 20), ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty()) + 10; - this.draw_button_tip(x, y, ButtonBinding.ATTACK.get_button(), + int x = this.mod.config.get_hud_side() == HudSide.LEFT ? 10 : client.getWindow().getScaledWidth() - 10 - this.width_bottom, y = bottom(10); + x += (this.width_bottom = this.draw_button_tip(x, y, ButtonBinding.INVENTORY, true) + 10); + this.width_bottom += this.draw_button_tip(x, y, ButtonBinding.SWAP_HANDS, true); + x = this.mod.config.get_hud_side() == HudSide.LEFT ? 10 : client.getWindow().getScaledWidth() - 10 - this.width_top; + x += (this.width_top = this.draw_button_tip(x, (y -= 20), ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty()) + 10); + this.width_top += this.draw_button_tip(x, y, ButtonBinding.ATTACK.get_button(), this.client.crosshairTarget.getType() == HitResult.Type.BLOCK ? "lambdacontrols.action.hit" : ButtonBinding.ATTACK.get_translation_key(), this.client.crosshairTarget.getType() != HitResult.Type.MISS); } @@ -58,7 +65,7 @@ public class LambdaControlsHud extends DrawableHelper return LambdaControls.draw_button_tip(x, y, button, display, this.client); } - private int draw_button_tip(int x, int y, int button, @NotNull String action, boolean display) + private int draw_button_tip(int x, int y, int[] button, @NotNull String action, boolean display) { return LambdaControls.draw_button_tip(x, y, button, action, display, this.client); } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java index 69ee635..4d4093b 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -9,14 +9,21 @@ package me.lambdaurora.lambdacontrols.gui; -import me.lambdaurora.lambdacontrols.Controller; import me.lambdaurora.lambdacontrols.ControlsMode; import me.lambdaurora.lambdacontrols.LambdaControls; +import me.lambdaurora.lambdacontrols.controller.Controller; +import me.lambdaurora.spruceui.SpruceButtonWidget; +import me.lambdaurora.spruceui.SpruceLabelWidget; +import me.lambdaurora.spruceui.Tooltip; +import me.lambdaurora.spruceui.option.*; +import net.minecraft.client.MinecraftClient; 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.*; +import net.minecraft.client.options.CyclingOption; +import net.minecraft.client.options.GameOptions; +import net.minecraft.client.options.Option; import net.minecraft.client.resource.language.I18n; import net.minecraft.text.TranslatableText; import net.minecraft.util.Formatting; @@ -29,30 +36,32 @@ 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 GameOptions options; - private final Option auto_switch_mode_option; - private final Option 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 String controller_mappings_url_text = I18n.translate("lambdacontrols.controller.mappings.2", Formatting.GOLD.toString(), GAMEPAD_TOOL_URL, Formatting.RESET.toString()); - private ButtonListWidget list; - private LabelWidget 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; + 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 LambdaControlsSettingsScreen(Screen parent, @NotNull GameOptions options) + public LambdaControlsSettingsScreen(Screen parent, @NotNull GameOptions options, boolean hide_controls) { super(new TranslatableText("lambdacontrols.title.settings")); this.mod = LambdaControls.get(); this.parent = parent; - this.options = options; - this.auto_switch_mode_option = new BooleanOption("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)); + this.hide_controls = hide_controls; + 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.controller_option = new CyclingOption("lambdacontrols.menu.controller", (game_options, amount) -> { int current_id = this.mod.config.get_controller().get_id(); current_id += amount; @@ -68,15 +77,34 @@ public class LambdaControlsSettingsScreen extends Screen else return option.getDisplayPrefix() + controller_name; }); - this.controller_type_option = new CyclingOption("lambdacontrols.menu.controller_type", + this.second_controller_option = new SpruceCyclingOption("lambdacontrols.menu.controller2", + (game_options, amount) -> { + int current_id = this.mod.config.get_second_controller().map(Controller::get_id).orElse(-1); + current_id += amount; + if (current_id > GLFW.GLFW_JOYSTICK_LAST) + current_id = -1; + this.mod.config.set_second_controller(current_id == -1 ? null : Controller.by_id(current_id)); + }, (game_options, option) -> this.mod.config.get_second_controller().map(controller -> { + String controller_name = controller.get_name(); + if (!controller.is_connected()) + return option.getDisplayPrefix() + Formatting.RED + controller_name; + else if (!controller.is_gamepad()) + return option.getDisplayPrefix() + Formatting.GOLD + controller_name; + else + return option.getDisplayPrefix() + controller_name; + }).orElse(option.getDisplayPrefix() + Formatting.RED + I18n.translate("options.off")), + new TranslatableText("lambdacontrols.tooltip.controller2")); + this.controller_type_option = new SpruceCyclingOption("lambdacontrols.menu.controller_type", (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()); - this.hud_enable_option = new BooleanOption("lambdacontrols.menu.hud_enable", (game_options) -> this.mod.config.is_hud_enabled(), - (game_options, new_value) -> this.mod.config.set_hud_enabled(new_value)); - this.hud_side_option = new CyclingOption("lambdacontrols.menu.hud_side", + (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()); - this.dead_zone_option = new DoubleOption("lambdacontrols.menu.dead_zone", 0.05, 1.0, 0.05F, game_options -> this.mod.config.get_dead_zone(), + (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) { this.mod.config.set_dead_zone(new_value); @@ -84,19 +112,26 @@ public class LambdaControlsSettingsScreen extends Screen }, (game_options, option) -> { String value = String.valueOf(option.get(options)); return option.getDisplayPrefix() + value.substring(0, Math.min(value.length(), 5)); - }); - this.rotation_speed_option = new DoubleOption("lambdacontrols.menu.rotation_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_rotation_speed(), + }, 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(), (game_options, new_value) -> { synchronized (this.mod.config) { this.mod.config.set_rotation_speed(new_value); } - }, (game_options, option) -> option.getDisplayPrefix() + option.get(options)); - this.mouse_speed_option = new DoubleOption("lambdacontrols.menu.mouse_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_mouse_speed(), + }, (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)); + }, (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()); + }); } @Override @@ -123,36 +158,45 @@ public class LambdaControlsSettingsScreen extends Screen { super.init(); int button_height = 20; - this.addButton(new ButtonWidget(this.width / 2 - 155, 18, 150, button_height, I18n.translate("lambdacontrols.menu.controls_mode") + ": " + this.mod.config.get_controls_mode().get_translated_name(), + SpruceButtonWidget controls_mode_btn = new SpruceButtonWidget(this.width / 2 - 155, 18, this.hide_controls ? 310 : 150, button_height, + I18n.translate("lambdacontrols.menu.controls_mode") + ": " + this.mod.config.get_controls_mode().get_translated_name(), btn -> { ControlsMode next = this.mod.config.get_controls_mode().next(); btn.setMessage(I18n.translate("lambdacontrols.menu.controls_mode") + ": " + next.get_translated_name()); this.mod.config.set_controls_mode(next); this.mod.config.save(); - })); - this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, 18, 150, button_height, I18n.translate("options.controls"), - btn -> { - if (this.mod.config.get_controls_mode() == ControlsMode.CONTROLLER) - this.minecraft.openScreen(new LambdaControlsControlsScreen(this)); - else - this.minecraft.openScreen(new ControlsOptionsScreen(this, this.options)); - })); + }); + controls_mode_btn.set_tooltip(new TranslatableText("lambdacontrols.tooltip.controls_mode")); + this.addButton(controls_mode_btn); + if (!this.hide_controls) + this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, 18, 150, button_height, I18n.translate("options.controls"), + btn -> { + if (this.mod.config.get_controls_mode() == ControlsMode.CONTROLLER) + this.minecraft.openScreen(new LambdaControlsControlsScreen(this, true)); + else + this.minecraft.openScreen(new ControlsOptionsScreen(this, this.minecraft.options)); + })); this.list = new ButtonListWidget(this.minecraft, this.width, this.height, 43, this.height - 29 - this.get_text_height(), 25); - this.list.addSingleOptionEntry(this.controller_option); - this.list.addOptionEntry(this.controller_type_option, this.dead_zone_option); - this.list.addOptionEntry(this.hud_enable_option, this.hud_side_option); + 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); + 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.addSingleOptionEntry(new ReloadControllerMappingsOption()); + 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); - this.gamepad_tool_url_label = new LabelWidget(this.width / 2, this.height - 29 - (5 + this.font.fontHeight) * 2, this.controller_mappings_url_text, this.width, + this.gamepad_tool_url_label = new SpruceLabelWidget(this.width / 2, this.height - 29 - (5 + this.font.fontHeight) * 2, this.controller_mappings_url_text, this.width, label -> Util.getOperatingSystem().open(GAMEPAD_TOOL_URL), true); - this.gamepad_tool_url_label.set_tooltip_text(I18n.translate("chat.link.open")); + this.gamepad_tool_url_label.set_tooltip(new TranslatableText("chat.link.open")); this.children.add(this.gamepad_tool_url_label); - this.addButton(new ButtonWidget(this.width / 2 - 155, this.height - 29, 300, button_height, I18n.translate("gui.done"), + this.addButton(this.reset_option.createButton(this.minecraft.options, this.width / 2 - 155, this.height - 29, 150)); + this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, button_height, I18n.translate("gui.done"), (buttonWidget) -> this.minecraft.openScreen(this.parent))); } @@ -166,5 +210,7 @@ public class LambdaControlsSettingsScreen extends Screen this.drawCenteredString(this.font, I18n.translate("lambdacontrols.controller.mappings.1", Formatting.GREEN.toString(), Formatting.RESET.toString()), this.width / 2, this.height - 29 - (5 + this.font.fontHeight) * 3, 10526880); this.gamepad_tool_url_label.render(mouse_x, mouse_y, delta); this.drawCenteredString(this.font, I18n.translate("lambdacontrols.controller.mappings.3", Formatting.GREEN.toString(), Formatting.RESET.toString()), this.width / 2, this.height - 29 - (5 + this.font.fontHeight), 10526880); + + Tooltip.render_all(); } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/ReloadControllerMappingsOption.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/ReloadControllerMappingsOption.java index 145f10d..6f29eef 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/ReloadControllerMappingsOption.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/ReloadControllerMappingsOption.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -9,10 +9,10 @@ package me.lambdaurora.lambdacontrols.gui; -import me.lambdaurora.lambdacontrols.Controller; +import me.lambdaurora.lambdacontrols.controller.Controller; +import me.lambdaurora.spruceui.SpruceButtonWidget; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.widget.AbstractButtonWidget; -import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.options.GameOptions; import net.minecraft.client.options.Option; import net.minecraft.client.resource.language.I18n; @@ -36,13 +36,15 @@ public class ReloadControllerMappingsOption extends Option implements Nameable @Override public AbstractButtonWidget createButton(GameOptions options, int x, int y, int width) { - return new ButtonWidget(x, y, width, 20, this.get_name(), btn -> { + SpruceButtonWidget button = new SpruceButtonWidget(x, y, width, 20, this.get_name(), btn -> { MinecraftClient client = MinecraftClient.getInstance(); Controller.update_mappings(); if (client.currentScreen != null) client.currentScreen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, new TranslatableText("lambdacontrols.controller.mappings.updated"), null)); }); + button.set_tooltip(new TranslatableText("lambdacontrols.tooltip.reload_controller_mappings")); + return button; } @Override diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/TouchscreenButtonWidget.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/TouchscreenButtonWidget.java deleted file mode 100644 index 6b5d4e3..0000000 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/TouchscreenButtonWidget.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © 2019 LambdAurora - * - * This file is part of LambdaControls. - * - * Licensed under the MIT license. For more information, - * see the LICENSE file. - */ - -package me.lambdaurora.lambdacontrols.gui; - -import net.minecraft.client.gui.widget.TexturedButtonWidget; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Consumer; - -/** - * Represents a touchscreen button widget. - */ -public class TouchscreenButtonWidget extends TexturedButtonWidget -{ - private final Consumer on_change_state; - - public TouchscreenButtonWidget(int x, int y, int width, int height, int u, int v, int hovered_v_offset, Identifier texture, @NotNull Consumer on_changed_state) - { - super(x, y, width, height, u, v, hovered_v_offset, texture, 256, 256, btn -> on_changed_state.accept(true)); - this.on_change_state = on_changed_state; - } - - @Override - public void onRelease(double mouseX, double mouseY) - { - super.onRelease(mouseX, mouseY); - this.on_change_state.accept(false); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) - { - if (this.active && this.visible) { - if (this.isValidClickButton(button)) { - boolean clicked = this.clicked(mouseX, mouseY); - if (clicked) { - this.onClick(mouseX, mouseY); - return true; - } - } - - return false; - } else - return false; - } - - @Override - protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) - { - super.onDrag(mouseX, mouseY, deltaX, deltaY); - if (this.active && !this.isHovered) - this.on_change_state.accept(false); - } -} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/TouchscreenOverlay.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/TouchscreenOverlay.java index 46286cf..dc86a56 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/TouchscreenOverlay.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/TouchscreenOverlay.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -12,6 +12,7 @@ package me.lambdaurora.lambdacontrols.gui; import me.lambdaurora.lambdacontrols.HudSide; import me.lambdaurora.lambdacontrols.LambdaControls; import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor; +import me.lambdaurora.spruceui.SpruceTexturedButtonWidget; import net.minecraft.client.gui.screen.ChatScreen; import net.minecraft.client.gui.screen.GameMenuScreen; import net.minecraft.client.gui.screen.Screen; @@ -35,18 +36,18 @@ import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y; */ public class TouchscreenOverlay extends Screen { - public static final Identifier WIDGETS_LOCATION = new Identifier("lambdacontrols", "textures/gui/widgets.png"); - private LambdaControls mod; - private TouchscreenButtonWidget jump_button; - private TouchscreenButtonWidget fly_button; - private TouchscreenButtonWidget fly_up_button; - private TouchscreenButtonWidget fly_down_button; - private int fly_button_enable_ticks = 0; - private int forward_button_tick = 0; - private TouchscreenButtonWidget forward_left_button; - private TouchscreenButtonWidget forward_right_button; - private TouchscreenButtonWidget start_sneak_button; - private TouchscreenButtonWidget end_sneak_button; + public static final Identifier WIDGETS_LOCATION = new Identifier("lambdacontrols", "textures/gui/widgets.png"); + private LambdaControls mod; + private SpruceTexturedButtonWidget jump_button; + private SpruceTexturedButtonWidget fly_button; + private SpruceTexturedButtonWidget fly_up_button; + private SpruceTexturedButtonWidget fly_down_button; + private int fly_button_enable_ticks = 0; + private int forward_button_tick = 0; + private SpruceTexturedButtonWidget forward_left_button; + private SpruceTexturedButtonWidget forward_right_button; + private SpruceTexturedButtonWidget start_sneak_button; + private SpruceTexturedButtonWidget end_sneak_button; public TouchscreenOverlay(@NotNull LambdaControls mod) { @@ -108,7 +109,7 @@ public class TouchscreenOverlay extends Screen this.fly_down_button.visible = true; if (old_state_fly != this.fly_button.visible) { this.fly_button_enable_ticks = 5; - this.handle_jump(false); + this.handle_jump(null, false); } else if (this.fly_button_enable_ticks > 0) this.fly_button_enable_ticks--; } else { @@ -122,9 +123,10 @@ public class TouchscreenOverlay extends Screen /** * Handles the jump button. * + * @param btn The pressed button. * @param state The state of the jump button. */ - private void handle_jump(boolean state) + private void handle_jump(ButtonWidget btn, boolean state) { ((KeyBindingAccessor) this.minecraft.options.keyJump).handle_press_state(state); } @@ -182,8 +184,8 @@ public class TouchscreenOverlay extends Screen sneak_button_x = scaled_width - 10 - 40 - 5; } // Swap items hand. - this.addButton(new TouchscreenButtonWidget(swap_hands_x, sneak_button_y, 20, 20, 0, 160, 20, WIDGETS_LOCATION, - state -> { + this.addButton(new SpruceTexturedButtonWidget(swap_hands_x, sneak_button_y, 20, 20, 0, 160, 20, WIDGETS_LOCATION, + (btn, state) -> { if (state) { if (!this.minecraft.player.isSpectator()) { this.minecraft.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_HELD_ITEMS, BlockPos.ORIGIN, Direction.DOWN)); @@ -191,31 +193,31 @@ public class TouchscreenOverlay extends Screen } })); // Drop - this.addButton(new TouchscreenButtonWidget(swap_hands_x, sneak_button_y + 5 + 20, 20, 20, 20, 160, 20, WIDGETS_LOCATION, - state -> ((KeyBindingAccessor) this.minecraft.options.keyDrop).handle_press_state(state))); + this.addButton(new SpruceTexturedButtonWidget(swap_hands_x, sneak_button_y + 5 + 20, 20, 20, 20, 160, 20, WIDGETS_LOCATION, + (btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keyDrop).handle_press_state(state))); // Jump keys - this.addButton(this.jump_button = new TouchscreenButtonWidget(jump_button_x, sneak_button_y, 20, 20, 0, 40, 20, WIDGETS_LOCATION, + this.addButton(this.jump_button = new SpruceTexturedButtonWidget(jump_button_x, sneak_button_y, 20, 20, 0, 40, 20, WIDGETS_LOCATION, this::handle_jump)); - this.addButton(this.fly_button = new TouchscreenButtonWidget(jump_button_x, sneak_button_y, 20, 20, 20, 40, 20, WIDGETS_LOCATION, - state -> { + this.addButton(this.fly_button = new SpruceTexturedButtonWidget(jump_button_x, sneak_button_y, 20, 20, 20, 40, 20, WIDGETS_LOCATION, + (btn, state) -> { if (this.fly_button_enable_ticks == 0) this.minecraft.player.abilities.flying = false; })); - this.addButton(this.fly_up_button = new TouchscreenButtonWidget(jump_button_x, sneak_button_y - 5 - 20, 20, 20, 40, 40, 20, WIDGETS_LOCATION, + this.addButton(this.fly_up_button = new SpruceTexturedButtonWidget(jump_button_x, sneak_button_y - 5 - 20, 20, 20, 40, 40, 20, WIDGETS_LOCATION, this::handle_jump)); - this.addButton(this.fly_down_button = new TouchscreenButtonWidget(jump_button_x, sneak_button_y + 20 + 5, 20, 20, 60, 40, 20, WIDGETS_LOCATION, - state -> ((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(state))); + this.addButton(this.fly_down_button = new SpruceTexturedButtonWidget(jump_button_x, sneak_button_y + 20 + 5, 20, 20, 60, 40, 20, WIDGETS_LOCATION, + (btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(state))); this.update_jump_buttons(); // Movements keys - this.addButton((this.start_sneak_button = new TouchscreenButtonWidget(sneak_button_x, sneak_button_y, 20, 20, 0, 120, 20, WIDGETS_LOCATION, - state -> { + this.addButton((this.start_sneak_button = new SpruceTexturedButtonWidget(sneak_button_x, sneak_button_y, 20, 20, 0, 120, 20, WIDGETS_LOCATION, + (btn, state) -> { if (state) { ((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(true); this.start_sneak_button.visible = false; this.end_sneak_button.visible = true; } }))); - this.addButton((this.end_sneak_button = new TouchscreenButtonWidget(sneak_button_x, sneak_button_y, 20, 20, 20, 120, 20, WIDGETS_LOCATION, - state -> { + this.addButton((this.end_sneak_button = new SpruceTexturedButtonWidget(sneak_button_x, sneak_button_y, 20, 20, 20, 120, 20, WIDGETS_LOCATION, + (btn, state) -> { if (state) { ((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(false); this.end_sneak_button.visible = false; @@ -223,33 +225,33 @@ public class TouchscreenOverlay extends Screen } }))); this.end_sneak_button.visible = false; - this.addButton(this.forward_left_button = new TouchscreenButtonWidget(sneak_button_x - 20 - 5, sneak_button_y - 5 - 20, 20, 20, 80, 80, 20, WIDGETS_LOCATION, - state -> { + this.addButton(this.forward_left_button = new SpruceTexturedButtonWidget(sneak_button_x - 20 - 5, sneak_button_y - 5 - 20, 20, 20, 80, 80, 20, WIDGETS_LOCATION, + (btn, state) -> { ((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state); ((KeyBindingAccessor) this.minecraft.options.keyLeft).handle_press_state(state); this.update_forward_buttons_state(state); })); this.forward_left_button.visible = false; - this.addButton(new TouchscreenButtonWidget(sneak_button_x, sneak_button_y - 5 - 20, 20, 20, 0, 80, 20, WIDGETS_LOCATION, - state -> { + this.addButton(new SpruceTexturedButtonWidget(sneak_button_x, sneak_button_y - 5 - 20, 20, 20, 0, 80, 20, WIDGETS_LOCATION, + (btn, state) -> { ((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state); this.update_forward_buttons_state(state); this.forward_left_button.visible = true; this.forward_right_button.visible = true; })); - this.addButton(this.forward_right_button = new TouchscreenButtonWidget(sneak_button_x + 20 + 5, sneak_button_y - 5 - 20, 20, 20, 100, 80, 20, WIDGETS_LOCATION, - state -> { + this.addButton(this.forward_right_button = new SpruceTexturedButtonWidget(sneak_button_x + 20 + 5, sneak_button_y - 5 - 20, 20, 20, 100, 80, 20, WIDGETS_LOCATION, + (btn, state) -> { ((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state); ((KeyBindingAccessor) this.minecraft.options.keyRight).handle_press_state(state); this.update_forward_buttons_state(state); })); this.forward_right_button.visible = true; - this.addButton(new TouchscreenButtonWidget(sneak_button_x + 20 + 5, sneak_button_y, 20, 20, 20, 80, 20, WIDGETS_LOCATION, - state -> ((KeyBindingAccessor) this.minecraft.options.keyRight).handle_press_state(state))); - this.addButton(new TouchscreenButtonWidget(sneak_button_x, sneak_button_y + 20 + 5, 20, 20, 40, 80, 20, WIDGETS_LOCATION, - state -> ((KeyBindingAccessor) this.minecraft.options.keyBack).handle_press_state(state))); - this.addButton(new TouchscreenButtonWidget(sneak_button_x - 20 - 5, sneak_button_y, 20, 20, 60, 80, 20, WIDGETS_LOCATION, - state -> ((KeyBindingAccessor) this.minecraft.options.keyLeft).handle_press_state(state))); + this.addButton(new SpruceTexturedButtonWidget(sneak_button_x + 20 + 5, sneak_button_y, 20, 20, 20, 80, 20, WIDGETS_LOCATION, + (btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keyRight).handle_press_state(state))); + this.addButton(new SpruceTexturedButtonWidget(sneak_button_x, sneak_button_y + 20 + 5, 20, 20, 40, 80, 20, WIDGETS_LOCATION, + (btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keyBack).handle_press_state(state))); + this.addButton(new SpruceTexturedButtonWidget(sneak_button_x - 20 - 5, sneak_button_y, 20, 20, 60, 80, 20, WIDGETS_LOCATION, + (btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keyLeft).handle_press_state(state))); } @Override diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/AbstractButtonWidgetAccessor.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/AbstractButtonWidgetAccessor.java index 2a7f182..761a9e5 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/AbstractButtonWidgetAccessor.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/AbstractButtonWidgetAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/AbstractContainerScreenMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/AbstractContainerScreenMixin.java index c9e8333..2fb4be0 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/AbstractContainerScreenMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/AbstractContainerScreenMixin.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -60,10 +60,10 @@ public abstract class AbstractContainerScreenMixin implements AbstractContainerS MinecraftClient client = MinecraftClient.getInstance(); int x = 10, y = client.getWindow().getScaledHeight() - 10 - 15; - x += LambdaControls.draw_button_tip(x, y, GLFW.GLFW_GAMEPAD_BUTTON_A, "lambdacontrols.action.pickup_all", true, client) + 10; - x += LambdaControls.draw_button_tip(x, y, GLFW.GLFW_GAMEPAD_BUTTON_B, "lambdacontrols.action.exit", true, client) + 10; - x += LambdaControls.draw_button_tip(x, y, GLFW.GLFW_GAMEPAD_BUTTON_X, "lambdacontrols.action.pickup", true, client) + 10; - LambdaControls.draw_button_tip(x, y, GLFW.GLFW_GAMEPAD_BUTTON_Y, "lambdacontrols.action.quick_move", true, client); + x += LambdaControls.draw_button_tip(x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_A}, "lambdacontrols.action.pickup_all", true, client) + 10; + x += LambdaControls.draw_button_tip(x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_B}, "lambdacontrols.action.exit", true, client) + 10; + x += LambdaControls.draw_button_tip(x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_X}, "lambdacontrols.action.pickup", true, client) + 10; + LambdaControls.draw_button_tip(x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_Y}, "lambdacontrols.action.quick_move", true, client); } } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/ControlsOptionsScreenMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/ControlsOptionsScreenMixin.java new file mode 100644 index 0000000..0e3a7ab --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/ControlsOptionsScreenMixin.java @@ -0,0 +1,46 @@ +/* + * 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.mixin; + +import me.lambdaurora.lambdacontrols.gui.LambdaControlsControlsScreen; +import me.lambdaurora.lambdacontrols.gui.LambdaControlsSettingsScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.options.ControlsOptionsScreen; +import net.minecraft.client.gui.screen.options.GameOptionsScreen; +import net.minecraft.client.gui.widget.AbstractButtonWidget; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.options.GameOptions; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +/** + * Injects the new controls settings button. + */ +@Mixin(ControlsOptionsScreen.class) +public class ControlsOptionsScreenMixin extends GameOptionsScreen +{ + public ControlsOptionsScreenMixin(Screen parent, GameOptions game_options, Text text) + { + super(parent, game_options, text); + } + + @Redirect(method = "init", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/options/ControlsOptionsScreen;addButton(Lnet/minecraft/client/gui/widget/AbstractButtonWidget;)Lnet/minecraft/client/gui/widget/AbstractButtonWidget;", ordinal = 1)) + private AbstractButtonWidget on_init(ControlsOptionsScreen screen, AbstractButtonWidget btn) + { + if (this.parent instanceof LambdaControlsControlsScreen) + return this.addButton(btn); + else + return this.addButton(new ButtonWidget(btn.x, btn.y, btn.getWidth(), ((AbstractButtonWidgetAccessor) btn).get_height(), I18n.translate("menu.options"), + b -> this.minecraft.openScreen(new LambdaControlsSettingsScreen(this, this.gameOptions, true)))); + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/CreativeInventoryScreenMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/CreativeInventoryScreenMixin.java index fbf10f5..b0ff538 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/CreativeInventoryScreenMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/CreativeInventoryScreenMixin.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/EntryListWidgetAccessor.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/EntryListWidgetAccessor.java index 07b92c3..5be0b6c 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/EntryListWidgetAccessor.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/EntryListWidgetAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/GameRendererMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/GameRendererMixin.java index 1fc5069..c071458 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/GameRendererMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/GameRendererMixin.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/InGameHudMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/InGameHudMixin.java index 2a4ca44..af09605 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/InGameHudMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/InGameHudMixin.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/KeyBindingMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/KeyBindingMixin.java index 8831b54..6969121 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/KeyBindingMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/KeyBindingMixin.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/MinecraftClientMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/MinecraftClientMixin.java index e5baca8..c4f6764 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/MinecraftClientMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/MinecraftClientMixin.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/MouseMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/MouseMixin.java index a770bd8..9a19355 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/MouseMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/MouseMixin.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/mixin/SettingsScreenMixin.java b/src/main/java/me/lambdaurora/lambdacontrols/mixin/SettingsScreenMixin.java index f5ce2a7..86c45aa 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/mixin/SettingsScreenMixin.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/mixin/SettingsScreenMixin.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * @@ -9,12 +9,17 @@ package me.lambdaurora.lambdacontrols.mixin; +import me.lambdaurora.lambdacontrols.ControlsMode; +import me.lambdaurora.lambdacontrols.LambdaControls; +import me.lambdaurora.lambdacontrols.gui.LambdaControlsControlsScreen; import me.lambdaurora.lambdacontrols.gui.LambdaControlsSettingsScreen; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.SettingsScreen; +import net.minecraft.client.gui.screen.options.ControlsOptionsScreen; import net.minecraft.client.gui.widget.AbstractButtonWidget; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.options.GameOptions; +import net.minecraft.client.resource.language.I18n; import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -28,10 +33,6 @@ import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(SettingsScreen.class) public class SettingsScreenMixin extends Screen { - @Final - @Shadow - private GameOptions settings; - protected SettingsScreenMixin(Text title) { super(title); @@ -40,7 +41,11 @@ public class SettingsScreenMixin extends Screen @Redirect(method = "init", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/SettingsScreen;addButton(Lnet/minecraft/client/gui/widget/AbstractButtonWidget;)Lnet/minecraft/client/gui/widget/AbstractButtonWidget;", ordinal = 7)) private AbstractButtonWidget on_init(SettingsScreen screen, AbstractButtonWidget btn) { - return this.addButton(new ButtonWidget(btn.x, btn.y, btn.getWidth(), ((AbstractButtonWidgetAccessor) btn).get_height(), btn.getMessage(), - b -> this.minecraft.openScreen(new LambdaControlsSettingsScreen(this, this.settings)))); + if (LambdaControls.get().config.get_controls_mode() == ControlsMode.CONTROLLER) { + return this.addButton(new ButtonWidget(btn.x, btn.y, btn.getWidth(), ((AbstractButtonWidgetAccessor) btn).get_height(), btn.getMessage(), + b -> this.minecraft.openScreen(new LambdaControlsControlsScreen(this, false)))); + } else { + return this.addButton(btn); + } } } diff --git a/src/main/java/me/lambdaurora/lambdacontrols/util/AbstractContainerScreenAccessor.java b/src/main/java/me/lambdaurora/lambdacontrols/util/AbstractContainerScreenAccessor.java index e7b72d5..c751cb5 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/util/AbstractContainerScreenAccessor.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/util/AbstractContainerScreenAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/util/CreativeInventoryScreenAccessor.java b/src/main/java/me/lambdaurora/lambdacontrols/util/CreativeInventoryScreenAccessor.java index cd222a3..48c43df 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/util/CreativeInventoryScreenAccessor.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/util/CreativeInventoryScreenAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/util/KeyBindingAccessor.java b/src/main/java/me/lambdaurora/lambdacontrols/util/KeyBindingAccessor.java index a2cf665..206c31c 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/util/KeyBindingAccessor.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/util/KeyBindingAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/util/MouseAccessor.java b/src/main/java/me/lambdaurora/lambdacontrols/util/MouseAccessor.java index 676105a..c25b730 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/util/MouseAccessor.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/util/MouseAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 LambdAurora + * Copyright © 2020 LambdAurora * * This file is part of LambdaControls. * diff --git a/src/main/resources/assets/lambdacontrols/lang/en_us.json b/src/main/resources/assets/lambdacontrols/lang/en_us.json index 47b98a0..b6ad570 100644 --- a/src/main/resources/assets/lambdacontrols/lang/en_us.json +++ b/src/main/resources/assets/lambdacontrols/lang/en_us.json @@ -10,6 +10,8 @@ "lambdacontrols.action.exit": "Exit", "lambdacontrols.action.forward": "Forward", "lambdacontrols.action.hit": "Hit", + "lambdacontrols.action.hotbar_left": "Hotbar left", + "lambdacontrols.action.hotbar_right": "Hotbar right", "lambdacontrols.action.inventory": "Inventory", "lambdacontrols.action.jump": "Jump", "lambdacontrols.action.left": "Left", @@ -72,6 +74,7 @@ "lambdacontrols.hud_side.right": "right", "lambdacontrols.menu.auto_switch_mode": "Auto switch mode", "lambdacontrols.menu.controller": "Controller", + "lambdacontrols.menu.controller2": "Second controller", "lambdacontrols.menu.controller_type": "Controller type", "lambdacontrols.menu.controls_mode": "Mode", "lambdacontrols.menu.dead_zone": "Dead zone", @@ -79,9 +82,23 @@ "lambdacontrols.menu.hud_side": "HUD side", "lambdacontrols.menu.invert_right_x_axis": "Invert right X", "lambdacontrols.menu.invert_right_y_axis": "Invert right Y", + "lambdacontrols.menu.keyboard_controls": "Keyboard controls...", "lambdacontrols.menu.mouse_speed": "Mouse speed", "lambdacontrols.menu.reload_controller_mappings": "Reload controller mappings", "lambdacontrols.menu.rotation_speed": "Rotation speed", "lambdacontrols.menu.title": "LambdaControls - Settings", - "lambdacontrols.menu.title.controller_controls": "Controller controls" + "lambdacontrols.menu.title.controller": "Controller options", + "lambdacontrols.menu.title.controller_controls": "Controller controls", + "lambdacontrols.menu.title.general": "General options", + "lambdacontrols.menu.title.hud": "HUD options", + "lambdacontrols.tooltip.auto_switch_mode": "Sets whether the controls mode should be switched automatically to Controller if a controller is connected.", + "lambdacontrols.tooltip.controller2": "Defines a second controller to use, which allow Joy-Cons support for example.", + "lambdacontrols.tooltip.controller_type": "Changes the controller type to displays the correct buttons.", + "lambdacontrols.tooltip.controls_mode": "Changes the controls mode.", + "lambdacontrols.tooltip.dead_zone": "Sets the dead zone of the controller's axises.", + "lambdacontrols.tooltip.hud_enable": "Toggles the on-screen controller button indicator.", + "lambdacontrols.tooltip.hud_side": "Changes the position of the HUD.", + "lambdacontrols.tooltip.mouse_speed": "Changes the emulated mouse speed with a controller.", + "lambdacontrols.tooltip.rotation_speed": "Changes the camera rotation speed in controller mode.", + "lambdacontrols.tooltip.reload_controller_mappings": "Reloads the controller mappings file." } \ No newline at end of file diff --git a/src/main/resources/assets/lambdacontrols/lang/fr_ca.json b/src/main/resources/assets/lambdacontrols/lang/fr_ca.json new file mode 100644 index 0000000..88b3218 --- /dev/null +++ b/src/main/resources/assets/lambdacontrols/lang/fr_ca.json @@ -0,0 +1,104 @@ +{ + "key.lambdacontrols.look_down": "Regarder en bas", + "key.lambdacontrols.look_left": "Regarder à gauche", + "key.lambdacontrols.look_right": "Regarder à droite", + "key.lambdacontrols.look_up": "Regarder en haut", + "lambdacontrols.action.attack": "Attaquer", + "lambdacontrols.action.back": "Reculer", + "lambdacontrols.action.chat": "Ouvrir le tchat", + "lambdacontrols.action.drop_item": "Jeter l'objet", + "lambdacontrols.action.exit": "Sortir", + "lambdacontrols.action.forward": "Avancer", + "lambdacontrols.action.hit": "Taper", + "lambdacontrols.action.hotbar_left": "Case à gauche de la barre d'action", + "lambdacontrols.action.hotbar_right": "Case à droite de la barre d'action", + "lambdacontrols.action.inventory": "Inventaire", + "lambdacontrols.action.jump": "Sauter", + "lambdacontrols.action.left": "Aller à gauche", + "lambdacontrols.action.pause_game": "Mettre en pause le jeu", + "lambdacontrols.action.pick_block": "Choisir le bloc", + "lambdacontrols.action.pickup": "Prendre", + "lambdacontrols.action.pickup_all": "Prendre tout", + "lambdacontrols.action.player_list": "Afficher la liste des joueurs", + "lambdacontrols.action.quick_move": "Mouvement rapide", + "lambdacontrols.action.right": "Aller à droite", + "lambdacontrols.action.screenshot": "Prendre une capture d'écran", + "lambdacontrols.action.sneak": "S'accroupir", + "lambdacontrols.action.sprint": "Courir", + "lambdacontrols.action.swap_hands": "Échanger de mains", + "lambdacontrols.action.toggle_perspective": "Changer de point de vue", + "lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique", + "lambdacontrols.action.use": "Utiliser", + "lambdacontrols.button.a": "A", + "lambdacontrols.button.b": "B", + "lambdacontrols.button.x": "X", + "lambdacontrols.button.y": "Y", + "lambdacontrols.button.left_bumper": "Gâchette haute gauche", + "lambdacontrols.button.right_bumper": "Gâchette haute droite", + "lambdacontrols.button.back": "Retour", + "lambdacontrols.button.start": "Touche Menu", + "lambdacontrols.button.guide": "Guide", + "lambdacontrols.button.left_thumb": "Stick gauche", + "lambdacontrols.button.right_thumb": "Stick droit", + "lambdacontrols.button.dpad_up": "D-Pad haut", + "lambdacontrols.button.dpad_right": "D-Pad droit", + "lambdacontrols.button.dpad_down": "D-Pad bas", + "lambdacontrols.button.dpad_left": "D-Pad gauche", + "lambdacontrols.axis.left_x+": "X+ Gauche", + "lambdacontrols.axis.left_y+": "Y+ Gauche", + "lambdacontrols.axis.right_x+": "X+ Droit", + "lambdacontrols.axis.right_y+": "Y+ Droit", + "lambdacontrols.axis.left_trigger": "Gâchette gauche", + "lambdacontrols.axis.right_trigger": "Gâchette droite", + "lambdacontrols.axis.left_x-": "X- Gauche", + "lambdacontrols.axis.left_y-": "Y- Gauche", + "lambdacontrols.axis.right_x-": "X- Droit", + "lambdacontrols.axis.right_y-": "Y- Droit", + "lambdacontrols.button.unknown": "Inconnu (%d)", + "lambdacontrols.controller.connected": "Manette %d connecté.", + "lambdacontrols.controller.disconnected": "Manette %d déconnecté.", + "lambdacontrols.controller.mappings.1": "Pour configurer les correspondances de la manette, veuillez utiliser %sSDL2 Gamepad Tool%s", + "lambdacontrols.controller.mappings.2": "(%s%s%s),", + "lambdacontrols.controller.mappings.3": "et mettez les correspondances dans le fichier `%s.minecraft/config/gamecontrollerdb.txt%s`.", + "lambdacontrols.controller.mappings.updated": "Configuration des manettes mise à jour!", + "lambdacontrols.controller_type.default": "default", + "lambdacontrols.controller_type.dualshock": "DualShock", + "lambdacontrols.controller_type.switch": "Switch", + "lambdacontrols.controller_type.xbox": "Xbox", + "lambdacontrols.controller_type.steam": "Steam", + "lambdacontrols.controller_type.ouya": "OUYA", + "lambdacontrols.controls_mode.default": "Clavier/Souris", + "lambdacontrols.controls_mode.controller": "Manette", + "lambdacontrols.controls_mode.touchscreen": "Tactile", + "lambdacontrols.hud_side.left": "gauche", + "lambdacontrols.hud_side.right": "droit", + "lambdacontrols.menu.auto_switch_mode": "Changement auto de mode", + "lambdacontrols.menu.controller": "Manette", + "lambdacontrols.menu.controller2": "Deuxième manette", + "lambdacontrols.menu.controller_type": "Type de manette", + "lambdacontrols.menu.controls_mode": "Mode", + "lambdacontrols.menu.dead_zone": "Zone morte", + "lambdacontrols.menu.hud_enable": "Activer le HUD", + "lambdacontrols.menu.hud_side": "Côté du HUD", + "lambdacontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)", + "lambdacontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)", + "lambdacontrols.menu.keyboard_controls": "Contrôles clavier...", + "lambdacontrols.menu.mouse_speed": "Vitesse de la souris", + "lambdacontrols.menu.reload_controller_mappings": "Recharge la configuration des manettes", + "lambdacontrols.menu.rotation_speed": "Vitesse de rotation", + "lambdacontrols.menu.title": "LambdaControls - Paramètres", + "lambdacontrols.menu.title.controller": "Options de manettes", + "lambdacontrols.menu.title.controller_controls": "Contrôles de la manette", + "lambdacontrols.menu.title.general": "Options générales", + "lambdacontrols.menu.title.hud": "Options du HUD", + "lambdacontrols.tooltip.auto_switch_mode": "Détermine si le mode de contrôle doit automatiquement changer sur Manette si une manette est connectée et inversement.", + "lambdacontrols.tooltip.controller2": "Défini une deuxième manette, utile dans le cas d'utilisation de Joy-Cons.", + "lambdacontrols.tooltip.controller_type": "Le type de contrôle n'influe que sur les boutons affichés.", + "lambdacontrols.tooltip.controls_mode": "Change le mode de contrôle.", + "lambdacontrols.tooltip.dead_zone": "Détermine la zone morte des axes de la manette.", + "lambdacontrols.tooltip.hud_enable": "Détermine si l'indicateur des buttons de la manette doit être affiché ou non.", + "lambdacontrols.tooltip.hud_side": "Change la position du HUD.", + "lambdacontrols.tooltip.mouse_speed": "Change la vitesse de la souris émulée par la manette.", + "lambdacontrols.tooltip.rotation_speed": "Change la vitesse de rotation de la caméra.", + "lambdacontrols.tooltip.reload_controller_mappings": "Recharge le fichier de configuration des manettes." +} \ No newline at end of file diff --git a/src/main/resources/assets/lambdacontrols/lang/fr_fr.json b/src/main/resources/assets/lambdacontrols/lang/fr_fr.json index 5d0509c..88b3218 100644 --- a/src/main/resources/assets/lambdacontrols/lang/fr_fr.json +++ b/src/main/resources/assets/lambdacontrols/lang/fr_fr.json @@ -10,6 +10,8 @@ "lambdacontrols.action.exit": "Sortir", "lambdacontrols.action.forward": "Avancer", "lambdacontrols.action.hit": "Taper", + "lambdacontrols.action.hotbar_left": "Case à gauche de la barre d'action", + "lambdacontrols.action.hotbar_right": "Case à droite de la barre d'action", "lambdacontrols.action.inventory": "Inventaire", "lambdacontrols.action.jump": "Sauter", "lambdacontrols.action.left": "Aller à gauche", @@ -36,8 +38,8 @@ "lambdacontrols.button.back": "Retour", "lambdacontrols.button.start": "Touche Menu", "lambdacontrols.button.guide": "Guide", - "lambdacontrols.button.left_thumb": "Stick analogique gauche", - "lambdacontrols.button.right_thumb": "Stick analogique droit", + "lambdacontrols.button.left_thumb": "Stick gauche", + "lambdacontrols.button.right_thumb": "Stick droit", "lambdacontrols.button.dpad_up": "D-Pad haut", "lambdacontrols.button.dpad_right": "D-Pad droit", "lambdacontrols.button.dpad_down": "D-Pad bas", @@ -72,16 +74,31 @@ "lambdacontrols.hud_side.right": "droit", "lambdacontrols.menu.auto_switch_mode": "Changement auto de mode", "lambdacontrols.menu.controller": "Manette", + "lambdacontrols.menu.controller2": "Deuxième manette", "lambdacontrols.menu.controller_type": "Type de manette", "lambdacontrols.menu.controls_mode": "Mode", "lambdacontrols.menu.dead_zone": "Zone morte", "lambdacontrols.menu.hud_enable": "Activer le HUD", "lambdacontrols.menu.hud_side": "Côté du HUD", - "lambdacontrols.menu.invert_right_x_axis": "Inverser le joystick droit (X)", - "lambdacontrols.menu.invert_right_y_axis": "Inverser le joystick droit (Y)", + "lambdacontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)", + "lambdacontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)", + "lambdacontrols.menu.keyboard_controls": "Contrôles clavier...", "lambdacontrols.menu.mouse_speed": "Vitesse de la souris", "lambdacontrols.menu.reload_controller_mappings": "Recharge la configuration des manettes", "lambdacontrols.menu.rotation_speed": "Vitesse de rotation", "lambdacontrols.menu.title": "LambdaControls - Paramètres", - "lambdacontrols.menu.title.controller_controls": "Contrôles de la manette" + "lambdacontrols.menu.title.controller": "Options de manettes", + "lambdacontrols.menu.title.controller_controls": "Contrôles de la manette", + "lambdacontrols.menu.title.general": "Options générales", + "lambdacontrols.menu.title.hud": "Options du HUD", + "lambdacontrols.tooltip.auto_switch_mode": "Détermine si le mode de contrôle doit automatiquement changer sur Manette si une manette est connectée et inversement.", + "lambdacontrols.tooltip.controller2": "Défini une deuxième manette, utile dans le cas d'utilisation de Joy-Cons.", + "lambdacontrols.tooltip.controller_type": "Le type de contrôle n'influe que sur les boutons affichés.", + "lambdacontrols.tooltip.controls_mode": "Change le mode de contrôle.", + "lambdacontrols.tooltip.dead_zone": "Détermine la zone morte des axes de la manette.", + "lambdacontrols.tooltip.hud_enable": "Détermine si l'indicateur des buttons de la manette doit être affiché ou non.", + "lambdacontrols.tooltip.hud_side": "Change la position du HUD.", + "lambdacontrols.tooltip.mouse_speed": "Change la vitesse de la souris émulée par la manette.", + "lambdacontrols.tooltip.rotation_speed": "Change la vitesse de rotation de la caméra.", + "lambdacontrols.tooltip.reload_controller_mappings": "Recharge le fichier de configuration des manettes." } \ No newline at end of file diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml index 2ade73b..a462ebc 100644 --- a/src/main/resources/config.toml +++ b/src/main/resources/config.toml @@ -15,6 +15,8 @@ auto_switch_mode = false [controller] # Controller to use. id = 0 + # Second controller to use. + id2 = -1 # Controller's type. type = "default" # Controller's dead zone. @@ -30,36 +32,42 @@ auto_switch_mode = false # Controller controls. [controller.controls] # Attack control. - attack = 105 + attack = "105" # Back control. - back = 201 + back = "201" # Open chat control. - chat = 12 + chat = "12" # Drop item control. - drop_item = 1 + drop_item = "1" # Forward control. - forward = 101 + forward = "101" + # Hot-bar left control. + hotbar_left = "4" + # Hot-bar right control. + hotbar_right = "5" # Inventory control. - inventory = 3 + inventory = "3" # Jump control. - jump = 0 + jump = "0" # Pause game control. - pause_game = 7 + pause_game = "7" # Pick block control. - pick_block = 14 + pick_block = "14" # Show player list control. - player_list = 6 + player_list = "6" # Take screenshot control. - screenshot = 13 + screenshot = "11+0" # Sneak control. - sneak = 10 + sneak = "10" # Sprint control. - sprint = 9 + sprint = "9" # Swap hands control. - swap_hands = 2 + swap_hands = "2" # Toggle perspective control. - toggle_perspective = 11 + toggle_perspective = "11+3" # Toggle smooth camera control. - toggle_smooth_camera = -1 + toggle_smooth_camera = "-1" # Use control. - use = 104 + use = "104" + # Zoom control. + zoom = "11+2" diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index bda75b7..369cfd5 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -18,6 +18,9 @@ "entrypoints": { "client": [ "me.lambdaurora.lambdacontrols.LambdaControls" + ], + "modmenu": [ + "me.lambdaurora.lambdacontrols.LambdaControlsModMenu" ] }, "mixins": [ @@ -26,7 +29,11 @@ "depends": { "fabricloader": ">=0.4.0", "fabric": "*", - "minecraft": "1.15.x" + "minecraft": "1.15.x", + "spruceui": ">=1.0.1" + }, + "recommends": { + "modmenu": ">=1.8.0+build.16" }, "suggests": { "flamingo": "*" diff --git a/src/main/resources/lambdacontrols.mixins.json b/src/main/resources/lambdacontrols.mixins.json index 2c576b2..f5e09ed 100644 --- a/src/main/resources/lambdacontrols.mixins.json +++ b/src/main/resources/lambdacontrols.mixins.json @@ -5,10 +5,10 @@ "client": [ "AbstractButtonWidgetAccessor", "AbstractContainerScreenMixin", + "ControlsOptionsScreenMixin", "CreativeInventoryScreenMixin", "EntryListWidgetAccessor", "GameRendererMixin", - "InGameHudMixin", "KeyBindingMixin", "MinecraftClientMixin", "MouseMixin", @@ -17,4 +17,4 @@ "injectors": { "defaultRequire": 1 } -} \ No newline at end of file +}