diff --git a/src/main/java/me/lambdaurora/lambdacontrols/ButtonBinding.java b/src/main/java/me/lambdaurora/lambdacontrols/ButtonBinding.java new file mode 100644 index 0000000..e52bb10 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdacontrols/ButtonBinding.java @@ -0,0 +1,134 @@ +/* + * 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.LambdaKeyBinding; +import net.minecraft.client.options.GameOptions; +import net.minecraft.client.options.KeyBinding; +import org.jetbrains.annotations.NotNull; +import org.lwjgl.glfw.GLFW; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * Represents a button binding. + * + * @author LambdAurora + */ +public class ButtonBinding +{ + private static final List BINDINGS = new ArrayList<>(); + public static final ButtonBinding BACK = new ButtonBinding(axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, false), "back"); + public static final ButtonBinding DROP_ITEM = new ButtonBinding(GLFW.GLFW_GAMEPAD_BUTTON_B, "drop_item"); + public static final ButtonBinding FORWARD = new ButtonBinding(axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, true), "forward"); + public static final ButtonBinding INVENTORY = new ButtonBinding(GLFW.GLFW_GAMEPAD_BUTTON_Y, "inventory"); + public static final ButtonBinding JUMP = new ButtonBinding(GLFW.GLFW_GAMEPAD_BUTTON_A, "jump"); + public static final ButtonBinding PAUSE_GAME = new ButtonBinding(GLFW.GLFW_GAMEPAD_BUTTON_START, "pause_game"); + public static final ButtonBinding SNEAK = new ButtonBinding(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, "sneak"); + public static final ButtonBinding SPRINT = new ButtonBinding(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB, "sprint"); + public static final ButtonBinding SWAP_HANDS = new ButtonBinding(GLFW.GLFW_GAMEPAD_BUTTON_X, "swap_hands"); + + private int button; + private String key; + private KeyBinding minecraft_key_binding = null; + private boolean pressed = false; + + public ButtonBinding(int button, @NotNull String key) + { + this.button = button; + this.key = key; + BINDINGS.add(this); + } + + /** + * Returns the button bound. + * + * @return The bound button. + */ + public int get_button() + { + return this.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 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 ? GLFW.GLFW_GAMEPAD_BUTTON_LAST + GLFW.GLFW_GAMEPAD_AXIS_LAST + axis : GLFW.GLFW_GAMEPAD_BUTTON_LAST + GLFW.GLFW_GAMEPAD_AXIS_LAST * 2 + axis; + } + + public static void init(@NotNull GameOptions options) + { + BACK.minecraft_key_binding = options.keyBack; + 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; + SPRINT.minecraft_key_binding = options.keySprint; + SWAP_HANDS.minecraft_key_binding = options.keySwapHands; + } + + 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(int button, boolean state) + { + BINDINGS.parallelStream().filter(binding -> binding.button == button) + .map(ButtonBinding::as_key_binding) + .forEach(binding -> binding.ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(state))); + } +} diff --git a/src/main/java/me/lambdaurora/lambdacontrols/Controller.java b/src/main/java/me/lambdaurora/lambdacontrols/Controller.java index b57a9dd..dd370bd 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/Controller.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/Controller.java @@ -17,14 +17,12 @@ import org.lwjgl.glfw.GLFWGamepadState; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -94,7 +92,7 @@ public class Controller implements Nameable public @NotNull String get_name() { String name = this.is_gamepad() ? GLFW.glfwGetGamepadName(this.id) : GLFW.glfwGetJoystickName(this.id); - return name == null ? "" : name; + return name == null ? String.valueOf(this.get_id()) : name; } /** @@ -130,7 +128,7 @@ public class Controller implements Nameable { return CONTROLLERS.values().stream().filter(Controller::is_connected) .filter(controller -> controller.get_guid().equals(guid)) - .findFirst(); + .max(Comparator.comparingInt(Controller::get_id)); } private static ByteBuffer resizeBuffer(ByteBuffer buffer, int new_capacity) @@ -178,8 +176,6 @@ public class Controller implements Nameable if (!mappings_file.exists()) return; ByteBuffer buffer = io_resource_to_buffer(mappings_file.getPath(), 1024); - //buffer.rewind(); - System.out.println(buffer); GLFW.glfwUpdateGamepadMappings(buffer); } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/me/lambdaurora/lambdacontrols/ControllerInput.java b/src/main/java/me/lambdaurora/lambdacontrols/ControllerInput.java index 7d9d611..a8102f9 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/ControllerInput.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/ControllerInput.java @@ -18,9 +18,11 @@ 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.*; +import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen; +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; import net.minecraft.client.gui.screen.world.WorldListWidget; -import net.minecraft.client.gui.widget.*; +import net.minecraft.client.gui.widget.AbstractPressableButtonWidget; +import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.client.options.KeyBinding; import net.minecraft.container.Slot; import net.minecraft.container.SlotActionType; @@ -31,7 +33,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWGamepadState; -import org.lwjgl.system.CallbackI; import java.nio.ByteBuffer; import java.nio.FloatBuffer; @@ -40,13 +41,24 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import static me.lambdaurora.lambdacontrols.ButtonBinding.PAUSE_GAME; +import static me.lambdaurora.lambdacontrols.ButtonBinding.SNEAK; +import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X; +import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y; + +/** + * Represents the controller input handler. + */ public class ControllerInput { 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; private int action_gui_cooldown = 0; private boolean continuous_sneak = false; + private int ignore_next_a = 0; private int last_sneak = 0; private double prev_target_yaw = 0.0; private double prev_target_pitch = 0.0; @@ -74,6 +86,7 @@ public class ControllerInput public void on_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 last_sneak counter which allows to double press to sneak continuously. if (this.last_sneak > 0) --this.last_sneak; @@ -91,6 +104,9 @@ public class ControllerInput this.fetch_button_input(client, state); this.fetch_axe_input(client, state); } + + if (this.ignore_next_a > 0) + this.ignore_next_a--; } public void on_pre_render_screen(@NotNull MinecraftClient client, @NotNull Screen screen) @@ -130,22 +146,24 @@ public class ControllerInput private void fetch_button_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state) { ByteBuffer buffer = gamepad_state.buttons(); - for (int i = 0; i < buffer.limit(); i++) { + for (int btn = 0; btn < buffer.limit(); btn++) { boolean btn_state = buffer.get() == (byte) 1; - boolean previous_state = BUTTON_STATES.getOrDefault(i, false); + boolean previous_state = BUTTON_STATES.getOrDefault(btn, false); + + ButtonBinding.set_button_state(btn, btn_state); if (btn_state != previous_state) { - this.handle_button(client, i, btn_state ? 0 : 1, btn_state); + this.handle_button(client, btn, btn_state ? 0 : 1, btn_state); if (btn_state) - BUTTON_COOLDOWNS.put(i, 5); + BUTTON_COOLDOWNS.put(btn, 5); } else if (btn_state) { - if (BUTTON_COOLDOWNS.getOrDefault(i, 0) == 0) { - BUTTON_COOLDOWNS.put(i, 5); - this.handle_button(client, i, 2, true); + if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) { + BUTTON_COOLDOWNS.put(btn, 5); + this.handle_button(client, btn, 2, true); } } - BUTTON_STATES.put(i, btn_state); + BUTTON_STATES.put(btn, btn_state); } } @@ -163,15 +181,17 @@ public class ControllerInput private void handle_button(@NotNull MinecraftClient client, int button, int action, boolean state) { - if (action == 0) { + 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 (action == 0) { // Handles when the player presses the Start button. - if (button == GLFW.GLFW_GAMEPAD_BUTTON_START) { + if (PAUSE_GAME.is_button(button)) { // If in game, then pause the game. if (client.currentScreen == null) client.openPauseMenu(false); @@ -185,10 +205,12 @@ public class ControllerInput if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null) { if (this.action_gui_cooldown == 0) { Element focused = client.currentScreen.getFocused(); - if (focused != null && this.is_screen_interactive(client.currentScreen)) - this.handle_a_button(focused); - this.action_gui_cooldown = 5; // Prevent to press too quickly the focused element, so we have to skip 5 ticks. - return; + if (focused != null && this.is_screen_interactive(client.currentScreen)) { + if (this.handle_a_button(focused)) { + this.action_gui_cooldown = 5; // Prevent to press too quickly the focused element, so we have to skip 5 ticks. + return; + } + } } } @@ -198,6 +220,7 @@ public class ControllerInput Slot slot = ((AbstractContainerScreenAccessor) client.currentScreen).get_slot_at(pos_x, pos_y); if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && slot != null) { client.interactionManager.method_2906(((AbstractContainerScreen) client.currentScreen).getContainer().syncId, slot.id, GLFW.GLFW_MOUSE_BUTTON_1, SlotActionType.PICKUP, client.player); + this.action_gui_cooldown = 5; return; } else if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) { client.player.closeContainer(); @@ -218,7 +241,7 @@ public class ControllerInput } // Handles sneak button and continuous sneak. - if (button == GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB && client.player != null) { + if (SNEAK.is_button(button) && client.player != null) { if (action == 0) { if (this.continuous_sneak) { this.set_sneaking(client, this.continuous_sneak = false); @@ -236,74 +259,91 @@ public class ControllerInput return; } - if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null && !this.is_screen_interactive(client.currentScreen)) { + if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null && !this.is_screen_interactive(client.currentScreen) && this.action_gui_cooldown == 0 && this.ignore_next_a == 0) { double mouse_x = client.mouse.getX() * (double) client.window.getScaledWidth() / (double) client.window.getWidth(); double mouse_y = client.mouse.getY() * (double) client.window.getScaledHeight() / (double) client.window.getHeight(); - if (client.currentScreen instanceof AbstractContainerScreen) { - Slot slot = ((AbstractContainerScreenAccessor) client.currentScreen).get_slot_at(mouse_x, mouse_y); - if (slot != null) - return; - } if (action == 0) { client.currentScreen.mouseClicked(mouse_x, mouse_y, GLFW.GLFW_MOUSE_BUTTON_1); } else if (action == 1) { client.currentScreen.mouseReleased(mouse_x, mouse_y, GLFW.GLFW_MOUSE_BUTTON_1); } + this.action_gui_cooldown = 5; return; } if (client.currentScreen == null && action != 2) { - Optional key_binding = this.config.get_keybind("button_" + button); - key_binding.ifPresent(keyBinding -> ((LambdaKeyBinding) keyBinding).handle_press_state(action != 1)); + ButtonBinding.handle_button(button, state); + //Optional key_binding = this.config.get_keybind("button_" + button); + //key_binding.ifPresent(keyBinding -> ((LambdaKeyBinding) keyBinding).handle_press_state(action != 1)); } } - private void handle_axe(@NotNull MinecraftClient client, int axe, float value, float abs_value, int state) + private void handle_axe(@NotNull MinecraftClient client, int axis, float value, float abs_value, int state) { int as_button_state = value > 0.5F ? 1 : (value < -0.5F ? 2 : 0); double dead_zone = this.config.get_dead_zone(); if (client.currentScreen == null) { - this.config.get_keybind("axe_" + axe + "+").ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(as_button_state == 1)); - this.config.get_keybind("axe_" + axe + "-").ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(as_button_state == 2)); + { + int axis_minus = axis + 10; + 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); + + if (current_plus_state != previous_plus_state) { + this.config.get_keybind("axis_" + axis + "+").ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(current_plus_state)); + if (current_plus_state) + AXIS_COOLDOWNS.put(axis, 5); + } else if (current_plus_state) { + if (AXIS_COOLDOWNS.getOrDefault(axis, 0) == 0) { + AXIS_COOLDOWNS.put(axis, 5); + } + } + + if (current_minus_state != previous_minus_state) { + this.config.get_keybind("axis_" + axis + "-").ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(current_minus_state)); + if (current_minus_state) + AXIS_COOLDOWNS.put(axis_minus, 5); + } else if (current_minus_state) { + if (AXIS_COOLDOWNS.getOrDefault(axis_minus, 0) == 0) { + AXIS_COOLDOWNS.put(axis_minus, 5); + } + } + + AXIS_STATES.put(axis, current_plus_state); + AXIS_STATES.put(axis_minus, current_minus_state); + } // Handles the look direction. - if (this.config.is_look_axis(axe) && client.player != null) { - if (this.config.is_view_down_control(axe, state)) { - if (this.config.get_view_down_control().endsWith("+")) - this.target_pitch = client.player.pitch + (this.config.get_rotation_speed() * (abs_value - dead_zone) / (1.0 - dead_zone)) * 0.33D; - else - this.target_pitch = client.player.pitch - (this.config.get_rotation_speed() * (abs_value + dead_zone) / (1.0 - dead_zone)) * 0.33D; - this.target_pitch = MathHelper.clamp(this.target_pitch, -90.0D, 90.0D); - } else if (this.config.is_view_up_control(axe, state)) { - if (this.config.get_view_up_control().endsWith("+")) - this.target_pitch = client.player.pitch + (this.config.get_rotation_speed() * (abs_value - dead_zone) / (1.0 - dead_zone)) * 0.33D; - else - this.target_pitch = client.player.pitch - (this.config.get_rotation_speed() * (abs_value + dead_zone) / (1.0 - dead_zone)) * 0.33D; - this.target_pitch = MathHelper.clamp(this.target_pitch, -90.0D, 90.0D); + if (client.player != null) { + 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() * (abs_value + dead_zone) / (1.0 - dead_zone)) * 0.33D; + this.target_pitch = MathHelper.clamp(this.target_pitch, -90.0D, 90.0D); + } else if (state == 1) { + this.target_pitch = client.player.pitch + this.config.get_right_y_axis_sign() * (this.config.get_rotation_speed() * (abs_value - dead_zone) / (1.0 - dead_zone)) * 0.33D; + this.target_pitch = MathHelper.clamp(this.target_pitch, -90.0D, 90.0D); + } } - if (this.config.is_view_left_control(axe, state)) { - if (this.config.get_view_left_control().endsWith("+")) - this.target_yaw = client.player.yaw + (this.config.get_rotation_speed() * (abs_value - dead_zone) / (1.0 - dead_zone)) * 0.33D; - else - this.target_yaw = client.player.yaw - (this.config.get_rotation_speed() * (abs_value + dead_zone) / (1.0 - dead_zone)) * 0.33D; - } else if (this.config.is_view_right_control(axe, state)) { - if (this.config.get_view_right_control().endsWith("+")) - this.target_yaw = client.player.yaw + (this.config.get_rotation_speed() * (abs_value - dead_zone) / (1.0 - dead_zone)) * 0.33D; - else - this.target_yaw = client.player.yaw - (this.config.get_rotation_speed() * (abs_value + dead_zone) / (1.0 - dead_zone)) * 0.33D; + if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X) { + if (state == 2) { + this.target_yaw = client.player.yaw - this.config.get_right_x_axis_sign() * (this.config.get_rotation_speed() * (abs_value + dead_zone) / (1.0 - dead_zone)) * 0.33D; + } else if (state == 1) { + this.target_yaw = client.player.yaw + this.config.get_right_x_axis_sign() * (this.config.get_rotation_speed() * (abs_value + dead_zone) / (1.0 - dead_zone)) * 0.33D; + } } } } else { boolean allow_mouse_control = true; - if (this.action_gui_cooldown == 0 && this.config.is_movement_axis(axe) && this.is_screen_interactive(client.currentScreen)) { - if (this.config.is_forward_button(axe, false, as_button_state)) { + if (this.action_gui_cooldown == 0 && this.config.is_movement_axis(axis) && this.is_screen_interactive(client.currentScreen)) { + if (this.config.is_forward_button(axis, false, as_button_state)) { allow_mouse_control = this.change_focus(client.currentScreen, false); - } else if (this.config.is_back_button(axe, false, as_button_state)) { + } else if (this.config.is_back_button(axis, false, as_button_state)) { allow_mouse_control = this.change_focus(client.currentScreen, true); - } else if (this.config.is_left_button(axe, false, as_button_state)) { + } else if (this.config.is_left_button(axis, false, as_button_state)) { allow_mouse_control = this.handle_left_right(client.currentScreen, false); - } else if (this.config.is_right_button(axe, false, as_button_state)) { + } else if (this.config.is_right_button(axis, false, as_button_state)) { allow_mouse_control = this.handle_left_right(client.currentScreen, true); } } @@ -311,13 +351,13 @@ public class ControllerInput float movement_x = 0.0F; float movement_y = 0.0F; - if (this.config.is_back_button(axe, false, (value > 0 ? 1 : 2))) { + if (this.config.is_back_button(axis, false, (value > 0 ? 1 : 2))) { movement_y = abs_value; - } else if (this.config.is_forward_button(axe, false, (value > 0 ? 1 : 2))) { + } else if (this.config.is_forward_button(axis, false, (value > 0 ? 1 : 2))) { movement_y = -abs_value; - } else if (this.config.is_left_button(axe, false, (value > 0 ? 1 : 2))) { + } else if (this.config.is_left_button(axis, false, (value > 0 ? 1 : 2))) { movement_x = -abs_value; - } else if (this.config.is_right_button(axe, false, (value > 0 ? 1 : 2))) { + } else if (this.config.is_right_button(axis, false, (value > 0 ? 1 : 2))) { movement_x = abs_value; } @@ -390,20 +430,23 @@ public class ControllerInput } } - private void handle_a_button(@NotNull Element focused) + private boolean handle_a_button(@NotNull Element focused) { if (focused instanceof AbstractPressableButtonWidget) { AbstractPressableButtonWidget button_widget = (AbstractPressableButtonWidget) focused; button_widget.playDownSound(MinecraftClient.getInstance().getSoundManager()); button_widget.onPress(); + return true; } else if (focused instanceof WorldListWidget) { WorldListWidget list = (WorldListWidget) focused; list.method_20159().ifPresent(WorldListWidget.LevelItem::play); + return true; } else if (focused instanceof ParentElement) { Element child_focused = ((ParentElement) focused).getFocused(); if (child_focused != null) - this.handle_a_button(child_focused); + return this.handle_a_button(child_focused); } + return false; } /** diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java index 0a937f2..5061173 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControls.java @@ -44,6 +44,7 @@ public class LambdaControls implements ClientModInitializer public void on_mc_init(@NotNull MinecraftClient client) { Controller.update_mappings(); + ButtonBinding.init(client.options); this.config.init_keybindings(client.options); GLFW.glfwSetJoystickCallback((jid, event) -> { if (event == GLFW.GLFW_CONNECTED) { diff --git a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java index 998ede7..f1fb390 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/LambdaControlsConfig.java @@ -19,6 +19,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import static org.lwjgl.glfw.GLFW.*; + /** * Represents LambdaControls configuration. */ @@ -34,19 +36,10 @@ public class LambdaControlsConfig private double rotation_speed; private double mouse_speed; // Controller controls - private String b_button; private String back_button; - private String dpad_up; - private String dpad_right; - private String dpad_down; - private String dpad_left; private String forward_button; - private String inventory_button; - private String jump_button; private String left_button; private String right_button; - private String sneak_button; - private String x_button; public LambdaControlsConfig(@NotNull LambdaControls mod) { @@ -65,50 +58,29 @@ public class LambdaControlsConfig this.rotation_speed = this.config.getOrElse("controller.rotation_speed", 40.0); this.mouse_speed = this.config.getOrElse("controller.mouse_speed", 25.0); // Controller controls - this.b_button = this.config.getOrElse("controller.controls.b", "none").toLowerCase(); this.back_button = this.config.getOrElse("controller.controls.back", "none").toLowerCase(); - this.dpad_up = this.config.getOrElse("controller.controls.dpad_up", "none").toLowerCase(); - this.dpad_right = this.config.getOrElse("controller.controls.dpad_right", "none").toLowerCase(); - this.dpad_down = this.config.getOrElse("controller.controls.dpad_down", "none").toLowerCase(); - this.dpad_left = this.config.getOrElse("controller.controls.dpad_left", "none").toLowerCase(); this.forward_button = this.config.getOrElse("controller.controls.forward", "none").toLowerCase(); - this.inventory_button = this.config.getOrElse("controller.controls.inventory", "none").toLowerCase(); - this.jump_button = this.config.getOrElse("controller.controls.jump", "none").toLowerCase(); this.left_button = this.config.getOrElse("controller.controls.left", "none").toLowerCase(); this.right_button = this.config.getOrElse("controller.controls.right", "none").toLowerCase(); - this.sneak_button = this.config.getOrElse("controller.controls.sneak", "none").toLowerCase(); - this.x_button = this.config.getOrElse("controller.controls.x", "none").toLowerCase(); } public void init_keybindings(GameOptions options) { - String str = this.config.getOrElse("controller.controls.attack", "none").toLowerCase(); - if (!str.equals("none")) - this.keybinding_mappings.put(str, options.keyAttack); - if (!this.back_button.equals("none")) - this.keybinding_mappings.put(this.back_button, options.keyBack); - if (!this.b_button.equals("none")) - this.keybinding_mappings.put(this.b_button, options.keyDrop); - if (!this.forward_button.equals("none")) - this.keybinding_mappings.put(this.forward_button, options.keyForward); - if (!this.inventory_button.equals("none")) - this.keybinding_mappings.put(this.inventory_button, options.keyInventory); - if (!this.jump_button.equals("none")) - this.keybinding_mappings.put(this.jump_button, options.keyJump); - if (!this.left_button.equals("none")) - this.keybinding_mappings.put(this.left_button, options.keyLeft); - if (!this.right_button.equals("none")) - this.keybinding_mappings.put(this.right_button, options.keyRight); - if (!this.sneak_button.equals("none")) - this.keybinding_mappings.put(this.sneak_button, options.keySneak); - str = this.config.getOrElse("controller.controls.sprint", "none").toLowerCase(); - if (!str.equals("none")) - this.keybinding_mappings.put(str, options.keySprint); - str = this.config.getOrElse("controller.controls.use", "none").toLowerCase(); - if (!str.equals("none")) - this.keybinding_mappings.put(str, options.keyUse); - if (!this.x_button.equals("none")) - this.keybinding_mappings.put(this.x_button, options.keySwapHands); + this.keybinding_mappings.put("axis_" + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + "+", options.keyAttack); + this.keybinding_mappings.put("axis_" + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + "+", options.keyUse); + this.keybinding_mappings.put("axis_" + GLFW_GAMEPAD_AXIS_LEFT_X + "+", options.keyRight); + this.keybinding_mappings.put("axis_" + GLFW_GAMEPAD_AXIS_LEFT_X + "-", options.keyLeft); + this.keybinding_mappings.put("axis_" + GLFW_GAMEPAD_AXIS_LEFT_Y + "+", options.keyBack); + this.keybinding_mappings.put("axis_" + GLFW_GAMEPAD_AXIS_LEFT_Y + "-", options.keyForward); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_A, options.keyJump); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_B, options.keyDrop); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_X, options.keySwapHands); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_Y, options.keyInventory); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_BACK, options.keyPlayerList); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_GUIDE, options.keyScreenshot); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, options.keySneak); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_LEFT_THUMB, options.keySprint); + this.keybinding_mappings.put("button_" + GLFW_GAMEPAD_BUTTON_DPAD_UP, options.keyTogglePerspective); } public void save() @@ -185,8 +157,7 @@ public class LambdaControlsConfig */ public void set_controller(@NotNull Controller controller) { - String guid = controller.get_guid(); - this.config.set("controller.id", guid.equals("") ? controller.get_id() : guid); + this.config.set("controller.id", controller.get_id()); } /** @@ -249,6 +220,66 @@ public class LambdaControlsConfig this.mouse_speed = mouse_speed; } + /** + * Returns whether the right X axis is inverted or not. + * + * @return True if the right X axis is inverted, else false. + */ + public boolean does_invert_right_x_axis() + { + return this.config.getOrElse("controller.invert_right_x_axis", false); + } + + /** + * Sets whether the right X axis is inverted or not. + * + * @param invert True if the right X axis is inverted, else false. + */ + public void set_invert_right_x_axis(boolean invert) + { + this.config.set("controller.invert_right_x_axis", invert); + } + + /** + * Returns whether the right Y axis is inverted or not. + * + * @return True if the right Y axis is inverted, else false. + */ + public boolean does_invert_right_y_axis() + { + return this.config.getOrElse("controller.invert_right_y_axis", false); + } + + /** + * Sets whether the right Y axis is inverted or not. + * + * @param invert True if the right Y axis is inverted, else false. + */ + public void set_invert_right_y_axis(boolean invert) + { + this.config.set("controller.invert_right_y_axis", invert); + } + + /** + * Gets the right X axis sign. + * + * @return The right X axis sign. + */ + public double get_right_x_axis_sign() + { + return this.does_invert_right_x_axis() ? -1.0 : 1.0; + } + + /** + * Gets the right Y axis sign. + * + * @return The right Y axis sign. + */ + public double get_right_y_axis_sign() + { + return this.does_invert_right_y_axis() ? -1.0 : 1.0; + } + /** * Returns the keybindings. * @@ -264,71 +295,16 @@ public class LambdaControlsConfig return Optional.ofNullable(this.keybinding_mappings.get(id)); } - /** - * Returns the B button mapping. - * - * @return The B button mapping. - */ - public String get_b_button() - { - return this.b_button; - } - public String get_back_button() { return this.back_button; } - public String get_dpad_up() - { - return this.dpad_up; - } - - public String get_dpad_right() - { - return this.dpad_right; - } - - public String get_dpad_down() - { - return this.dpad_down; - } - - public String get_dpad_left() - { - return this.dpad_left; - } - public String get_forward_button() { return this.forward_button; } - public String get_hotbar_left_button() - { - return this.config.getOrElse("controller.controls.hotbar_left", "none").toLowerCase(); - } - - public String get_hotbar_right_button() - { - return this.config.getOrElse("controller.controls.hotbar_right", "none").toLowerCase(); - } - - /** - * Gets the Inventory button mapping. - * - * @return The Inventory button mapping. - */ - public String get_inventory_button() - { - return this.inventory_button; - } - - public String get_jump_button() - { - return this.jump_button; - } - public String get_left_button() { return this.left_button; @@ -339,26 +315,6 @@ public class LambdaControlsConfig return this.right_button; } - public String get_sneak_button() - { - return this.sneak_button; - } - - public String get_start_button() - { - return this.config.getOrElse("controller.controls.start", "none").toLowerCase(); - } - - public String get_x_button() - { - return this.x_button; - } - - public boolean is_b_button(int button) - { - return this.get_b_button().equals("button_" + button); - } - public boolean is_back_button(int btn, boolean is_btn, int state) { if (!is_btn && state == 0) @@ -366,26 +322,6 @@ public class LambdaControlsConfig return this.get_back_button().equals((is_btn ? "button_" : "axe_") + btn + (is_btn ? "" : (state == 1 ? "+" : "-"))); } - public boolean is_dpad_up(int button) - { - return this.get_dpad_up().equals("button_" + button); - } - - public boolean is_dpad_right(int button) - { - return this.get_dpad_right().equals("button_" + button); - } - - public boolean is_dpad_down(int button) - { - return this.get_dpad_down().equals("button_" + button); - } - - public boolean is_dpad_left(int button) - { - return this.get_dpad_left().equals("button_" + button); - } - public boolean is_forward_button(int btn, boolean is_btn, int state) { if (!is_btn && state == 0) @@ -393,26 +329,6 @@ public class LambdaControlsConfig return this.get_forward_button().equals((is_btn ? "button_" : "axe_") + btn + (is_btn ? "" : (state == 1 ? "+" : "-"))); } - public boolean is_hotbar_left_button(int button) - { - return this.get_hotbar_left_button().equals("button_" + button); - } - - public boolean is_hotbar_right_button(int button) - { - return this.get_hotbar_right_button().equals("button_" + button); - } - - public boolean is_inventory_button(int btn) - { - return this.get_inventory_button().equals("button_" + btn); - } - - public boolean is_jump_button(int btn) - { - return this.get_jump_button().equals("button_" + btn); - } - public boolean is_left_button(int btn, boolean is_btn, int state) { if (!is_btn && state == 0) @@ -427,81 +343,6 @@ public class LambdaControlsConfig return this.get_right_button().equals((is_btn ? "button_" : "axe_") + btn + (is_btn ? "" : (state == 1 ? "+" : "-"))); } - public boolean is_sneak_button(int btn) - { - return this.get_sneak_button().equals("button_" + btn); - } - - public boolean is_start_button(int btn) - { - return this.get_start_button().equals("button_" + btn); - } - - public boolean is_x_button(int btn) - { - return this.get_x_button().equals("button_" + btn); - } - - public String get_view_down_control() - { - return this.config.getOrElse("controller.controls.view_down", "none").toLowerCase(); - } - - public String get_view_left_control() - { - return this.config.getOrElse("controller.controls.view_left", "none").toLowerCase(); - } - - public String get_view_right_control() - { - return this.config.getOrElse("controller.controls.view_right", "none").toLowerCase(); - } - - public String get_view_up_control() - { - return this.config.getOrElse("controller.controls.view_up", "none").toLowerCase(); - } - - public boolean is_view_down_control(int axe, int state) - { - if (state == 0) - return false; - return this.get_view_down_control().contains(axe + (state == 1 ? "+" : "-")); - } - - public boolean is_view_left_control(int axe, int state) - { - if (state == 0) - return false; - return this.get_view_left_control().endsWith(axe + (state == 1 ? "+" : "-")); - } - - public boolean is_view_right_control(int axe, int state) - { - if (state == 0) - return false; - return this.get_view_right_control().contains(axe + (state == 1 ? "+" : "-")); - } - - public boolean is_view_up_control(int axe, int state) - { - if (state == 0) - return false; - return this.get_view_up_control().contains(axe + (state == 1 ? "+" : "-")); - } - - /** - * Returns whether the specified axis is an axis used for look direction. - * - * @param i The axis index. - * @return True if the axis is used for look direction, else false. - */ - public boolean is_look_axis(int i) - { - return this.get_view_down_control().startsWith("axe_" + i) || this.get_view_left_control().startsWith("axe_" + i) || this.get_view_right_control().startsWith("axe_" + i) - || this.get_view_up_control().startsWith("axe_" + i); - } - /** * Returns whether the specified axis is an axis used for movements. * diff --git a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java index 10051e5..3d27411 100644 --- a/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java +++ b/src/main/java/me/lambdaurora/lambdacontrols/gui/LambdaControlsSettingsScreen.java @@ -9,18 +9,18 @@ package me.lambdaurora.lambdacontrols.gui; +import me.lambdaurora.lambdacontrols.Controller; import me.lambdaurora.lambdacontrols.ControlsMode; import me.lambdaurora.lambdacontrols.HudSide; import me.lambdaurora.lambdacontrols.LambdaControls; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.controls.ControlsOptionsScreen; import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.options.DoubleOption; -import net.minecraft.client.options.GameOptions; -import net.minecraft.client.options.Option; +import net.minecraft.client.options.*; import net.minecraft.client.resource.language.I18n; import net.minecraft.text.TranslatableText; import org.jetbrains.annotations.NotNull; +import org.lwjgl.glfw.GLFW; /** * Represents the LambdaControls settings screen. @@ -30,9 +30,13 @@ public class LambdaControlsSettingsScreen extends Screen private final LambdaControls mod; private final Screen parent; private final GameOptions options; + private final Option controller_option; private final Option dead_zone_option; private final Option rotation_speed_option; private final Option mouse_speed_option; + private final Option inverts_right_x_axis; + private final Option inverts_right_y_axis; + private int buttons_y = 18; public LambdaControlsSettingsScreen(Screen parent, @NotNull GameOptions options) { @@ -40,6 +44,13 @@ public class LambdaControlsSettingsScreen extends Screen this.mod = LambdaControls.get(); this.parent = parent; this.options = options; + this.controller_option = new CyclingOption("lambdacontrols.menu.controller", (game_options, amount) -> { + int current_id = this.mod.config.get_controller().get_id(); + current_id += amount; + if (current_id > GLFW.GLFW_JOYSTICK_LAST) + current_id = GLFW.GLFW_JOYSTICK_1; + this.mod.config.set_controller(Controller.by_id(current_id)); + }, (game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_controller().get_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, new_value) -> { synchronized (this.mod.config) { @@ -61,6 +72,18 @@ public class LambdaControlsSettingsScreen extends Screen this.mod.config.set_mouse_speed(new_value); } }, (game_options, option) -> option.getDisplayPrefix() + option.get(options)); + this.inverts_right_x_axis = new BooleanOption("lambdacontrols.menu.invert_right_x_axis", game_options -> this.mod.config.does_invert_right_x_axis(), + (game_options, new_value) -> { + synchronized (this.mod.config) { + this.mod.config.set_invert_right_x_axis(new_value); + } + }); + this.inverts_right_y_axis = new BooleanOption("lambdacontrols.menu.invert_right_y_axis", game_options -> this.mod.config.does_invert_right_y_axis(), + (game_options, new_value) -> { + synchronized (this.mod.config) { + this.mod.config.set_invert_right_y_axis(new_value); + } + }); } @Override @@ -81,32 +104,33 @@ public class LambdaControlsSettingsScreen extends Screen protected void init() { super.init(); - int y = 18; int button_height = 20, spacing = 5; - this.addButton(new ButtonWidget(this.width / 2 - 155, y, 150, button_height, I18n.translate("lambdacontrols.menu.controls_mode") + ": " + this.mod.config.get_controls_mode().get_translated_name(), + this.addButton(new ButtonWidget(this.width / 2 - 155, this.buttons_y, 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, y, 150, button_height, I18n.translate("options.controls"), + this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, this.buttons_y, 150, button_height, I18n.translate("options.controls"), btn -> this.minecraft.openScreen(new ControlsOptionsScreen(this, this.options)))); - this.addButton(new ButtonWidget(this.width / 2 - 155, (y += spacing + button_height), 150, button_height, I18n.translate("lambdacontrols.menu.hud_side") + ": " + this.mod.config.get_hud_side().get_translated_name(), + this.addButton(new ButtonWidget(this.width / 2 - 155, (this.buttons_y += spacing + button_height), 150, button_height, I18n.translate("lambdacontrols.menu.hud_side") + ": " + this.mod.config.get_hud_side().get_translated_name(), btn -> { HudSide next = this.mod.config.get_hud_side().next(); btn.setMessage(I18n.translate("lambdacontrols.menu.hud_side") + ": " + next.get_translated_name()); this.mod.config.set_hud_side(next); this.mod.config.save(); })); - this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, y, 150, button_height, I18n.translate("lambdacontrols.menu.controller_controls"), - btn -> this.minecraft.openScreen(new ControlsOptionsScreen(this, this.options)))); - this.addButton(this.dead_zone_option.createButton(this.options, this.width / 2 - 155, (y += spacing + button_height), 150)); - this.addButton(this.rotation_speed_option.createButton(this.options, this.width / 2 - 155 + 160, y, 150)); - this.addButton(this.mouse_speed_option.createButton(this.options, this.width / 2 - 155, (y += spacing + button_height), 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); - })); + this.addButton(this.controller_option.createButton(this.options, this.width / 2 - 155, (this.buttons_y += spacing + button_height), 150)); + this.addButton(this.dead_zone_option.createButton(this.options, this.width / 2 - 155 + 160, this.buttons_y, 150)); + this.addButton(this.rotation_speed_option.createButton(this.options, this.width / 2 - 155, (this.buttons_y += spacing + button_height), 150)); + this.addButton(this.mouse_speed_option.createButton(this.options, this.width / 2 - 155 + 160, this.buttons_y, 150)); + this.addButton(this.inverts_right_x_axis.createButton(this.options, this.width / 2 - 155, (this.buttons_y += spacing + button_height), 150)); + this.addButton(this.inverts_right_y_axis.createButton(this.options, this.width / 2 - 155 + 160, this.buttons_y, 150)); + this.addButton(new ButtonWidget(this.width / 2 - 155, this.height - 29, 300, button_height, I18n.translate("gui.done"), + (buttonWidget) -> this.minecraft.openScreen(this.parent))); + + this.buttons_y += spacing + button_height; } @Override @@ -114,5 +138,8 @@ public class LambdaControlsSettingsScreen extends Screen { this.renderBackground(); super.render(mouseX, mouseY, delta); + this.drawCenteredString(this.font, I18n.translate("lambdacontrols.controller.mappings.1"), this.width / 2, this.buttons_y + (20 - 8) / 2, 10526880); + this.drawCenteredString(this.font, I18n.translate("lambdacontrols.controller.mappings.2"), this.width / 2, this.buttons_y + (20 - 8) / 2 + font.fontHeight + 5, 10526880); + this.drawCenteredString(this.font, I18n.translate("lambdacontrols.controller.mappings.3"), this.width / 2, this.buttons_y + (20 - 8) / 2 + font.fontHeight * 2 + 10, 10526880); } } diff --git a/src/main/resources/assets/lambdacontrols/lang/en_us.json b/src/main/resources/assets/lambdacontrols/lang/en_us.json index a912b80..469a9c8 100644 --- a/src/main/resources/assets/lambdacontrols/lang/en_us.json +++ b/src/main/resources/assets/lambdacontrols/lang/en_us.json @@ -1,15 +1,20 @@ { - "lambdacontrols.menu.controller_controls": "Controller controls...", - "lambdacontrols.menu.controls_mode": "Controls mode", - "lambdacontrols.menu.dead_zone": "Dead zone", - "lambdacontrols.menu.hud_side": "HUD side", - "lambdacontrols.menu.rotation_speed": "Rotation speed", - "lambdacontrols.menu.mouse_speed": "Mouse speed", "lambdacontrols.controller.connected": "Controller %d connected.", "lambdacontrols.controller.disconnected": "Controller %d disconnected.", + "lambdacontrols.controller.mappings.1": "To configure the controller mappings, please use SDL2 Gamepad Tool", + "lambdacontrols.controller.mappings.2": "(http://generalarcade.com/gamepadtool/),", + "lambdacontrols.controller.mappings.3": "and put the mapping in `config/gamecontrollerdb.txt`.", "lambdacontrols.controls_mode.default": "Keyboard/Mouse", "lambdacontrols.controls_mode.controller": "Controller", "lambdacontrols.controls_mode.touchscreen": "Touchscreen", "lambdacontrols.hud_side.left": "left", - "lambdacontrols.hud_side.right": "right" + "lambdacontrols.hud_side.right": "right", + "lambdacontrols.menu.controller": "Controller", + "lambdacontrols.menu.controls_mode": "Controls mode", + "lambdacontrols.menu.dead_zone": "Dead zone", + "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.mouse_speed": "Mouse speed", + "lambdacontrols.menu.rotation_speed": "Rotation speed" } \ No newline at end of file diff --git a/src/main/resources/assets/lambdacontrols/textures/gui/controller_buttons.png b/src/main/resources/assets/lambdacontrols/textures/gui/controller_buttons.png new file mode 100644 index 0000000..3242ac6 Binary files /dev/null and b/src/main/resources/assets/lambdacontrols/textures/gui/controller_buttons.png differ diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml index 3ab35cf..ffa60b1 100644 --- a/src/main/resources/config.toml +++ b/src/main/resources/config.toml @@ -17,35 +17,7 @@ controls = "default" rotation_speed = 40.0 # Mouse speed in GUI. mouse_speed = 30.0 - # Controller controls - [controller.controls] - attack = "button_7" - b = "button_1" - back = "axe_1+" - dpad_up = "button_13" - dpad_right = "button_14" - dpad_down = "button_15" - dpad_left = "button_16" - forward = "axe_1-" - hotbar_left = "button_4" - hotbar_right = "button_5" - inventory = "button_3" - jump = "button_0" - left = "axe_0-" - right = "axe_0+" - sneak = "button_12" - sprint = "button_11" - start = "button_9" - use = "button_6" - view_down = "axe_3+" - view_left = "axe_2-" - view_right = "axe_2+" - view_up = "axe_3-" - x = "button_2" - -# Colors -[colors] -normal = "#ffffffff" -pressed = "#ffa000ff" -background_normal = "#000000aa" -background_pressed = "#555555aa" + # Inverts the right X axis. + invert_right_x_axis = false + # Inverts the right Y axis. + invert_right_y_axis = false