mirror of
https://github.com/TeamMidnightDust/MidnightControls.git
synced 2025-12-13 15:25:08 +01:00
✨ Add controller settings GUI and more WIP on controller support.
This commit is contained in:
487
src/main/java/me/lambdaurora/lambdacontrols/ControllerInput.java
Normal file
487
src/main/java/me/lambdaurora/lambdacontrols/ControllerInput.java
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.mixin.AbstractContainerScreenAccessor;
|
||||
import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor;
|
||||
import me.lambdaurora.lambdacontrols.util.LambdaKeyBinding;
|
||||
import me.lambdaurora.lambdacontrols.util.MouseAccessor;
|
||||
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.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.options.KeyBinding;
|
||||
import net.minecraft.container.Slot;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.aperlambda.lambdacommon.utils.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ControllerInput
|
||||
{
|
||||
private static final Map<Integer, Boolean> BUTTON_STATES = new HashMap<>();
|
||||
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
|
||||
private static final Map<Integer, Float> AXE_STATES = new HashMap<>();
|
||||
private final LambdaControls mod;
|
||||
private final LambdaControlsConfig config;
|
||||
private int controller = GLFW.GLFW_JOYSTICK_3;
|
||||
private int action_gui_cooldown = 0;
|
||||
private boolean last_a_state = false;
|
||||
private boolean continuous_sneak = false;
|
||||
private int last_sneak = 0;
|
||||
private double prev_target_yaw = 0.0;
|
||||
private double prev_target_pitch = 0.0;
|
||||
private double target_yaw = 0.0;
|
||||
private double target_pitch = 0.0;
|
||||
private float prev_x_axis = 0.F;
|
||||
private float prev_y_axis = 0.F;
|
||||
private int prev_target_mouse_x = 0;
|
||||
private int prev_target_mouse_y = 0;
|
||||
private int target_mouse_x = 0;
|
||||
private int target_mouse_y = 0;
|
||||
private float mouse_speed_x = 0.F;
|
||||
private float mouse_speed_y = 0.F;
|
||||
|
||||
public ControllerInput(@NotNull LambdaControls mod)
|
||||
{
|
||||
this.mod = mod;
|
||||
this.config = mod.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called every Minecraft tick.
|
||||
*
|
||||
* @param client The client instance.
|
||||
*/
|
||||
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));
|
||||
// Decreases the last_sneak counter which allows to double press to sneak continuously.
|
||||
if (this.last_sneak > 0)
|
||||
--this.last_sneak;
|
||||
// Decreases the cooldown for GUI actions.
|
||||
if (this.action_gui_cooldown > 0)
|
||||
--this.action_gui_cooldown;
|
||||
this.prev_target_yaw = this.target_yaw;
|
||||
this.prev_target_pitch = this.target_pitch;
|
||||
this.fetch_button_input(client);
|
||||
this.fetch_axe_input(client);
|
||||
}
|
||||
|
||||
public void on_pre_render_screen(@NotNull MinecraftClient client, @NotNull Screen screen)
|
||||
{
|
||||
if (this.prev_target_mouse_x != this.target_mouse_x || this.prev_target_mouse_y != this.target_mouse_y) {
|
||||
double mouse_x = prev_target_mouse_x + (this.target_mouse_x - this.prev_target_mouse_x) * client.getTickDelta() + 0.5;
|
||||
double mouse_y = prev_target_mouse_y + (this.target_mouse_y - this.prev_target_mouse_y) * client.getTickDelta() + 0.5;
|
||||
GLFW.glfwSetCursorPos(client.window.getHandle(), mouse_x, mouse_y);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_render(@NotNull MinecraftClient client)
|
||||
{
|
||||
if (client.currentScreen == null && (this.prev_target_yaw != this.target_yaw || this.prev_target_pitch != this.target_pitch)) {
|
||||
float rotation_yaw = (float) (client.player.prevYaw + (this.target_yaw - client.player.prevYaw) * client.getTickDelta());
|
||||
float rotation_pitch = (float) (client.player.prevPitch + (this.target_pitch - client.player.prevPitch) * client.getTickDelta());
|
||||
client.player.yaw = rotation_yaw;
|
||||
client.player.pitch = MathHelper.clamp(rotation_pitch, -90.F, 90.F);
|
||||
if (client.player.isRiding()) {
|
||||
client.player.getVehicle().copyPositionAndRotation(client.player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void on_screen_open(@NotNull MinecraftClient client, int window_width, int window_height)
|
||||
{
|
||||
if (client.currentScreen == null) {
|
||||
this.target_mouse_x = this.prev_target_mouse_x = (int) (window_width / 2.F);
|
||||
this.target_mouse_y = this.prev_target_mouse_y = (int) (window_height / 2.F);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetch_button_input(@NotNull MinecraftClient client)
|
||||
{
|
||||
ByteBuffer buffer = GLFW.glfwGetJoystickButtons(this.controller);
|
||||
if (buffer != null) {
|
||||
for (int i = 0; i < buffer.limit(); i++) {
|
||||
boolean btn_state = buffer.get() == (byte) 1;
|
||||
boolean previous_state = BUTTON_STATES.getOrDefault(i, false);
|
||||
|
||||
if (btn_state != previous_state) {
|
||||
this.handle_button(client, i, btn_state ? 0 : 1, btn_state);
|
||||
if (btn_state)
|
||||
BUTTON_COOLDOWNS.put(i, 5);
|
||||
} else if (btn_state) {
|
||||
if (BUTTON_COOLDOWNS.getOrDefault(i, 0) == 0) {
|
||||
BUTTON_COOLDOWNS.put(i, 5);
|
||||
this.handle_button(client, i, 2, true);
|
||||
}
|
||||
}
|
||||
|
||||
BUTTON_STATES.put(i, btn_state);
|
||||
if (this.config.is_jump_button(i))
|
||||
this.last_a_state = btn_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fetch_axe_input(@NotNull MinecraftClient client)
|
||||
{
|
||||
FloatBuffer buffer = GLFW.glfwGetJoystickAxes(this.controller);
|
||||
if (buffer != null) {
|
||||
for (int i = 0; i < buffer.limit(); i++) {
|
||||
float value = buffer.get();
|
||||
float abs_value = Math.abs(value);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle_button(@NotNull MinecraftClient client, int button, int action, boolean state)
|
||||
{
|
||||
if (action == 0) {
|
||||
// Handles RB and LB buttons.
|
||||
if (this.config.is_hotbar_left_button(button) || this.config.is_hotbar_right_button(button)) {
|
||||
this.handle_rb_lb(client, this.config.is_hotbar_right_button(button));
|
||||
return;
|
||||
}
|
||||
|
||||
// Handles when the player presses the Start button.
|
||||
if (this.config.is_start_button(button)) {
|
||||
// If in game, then pause the game.
|
||||
if (client.currentScreen == null)
|
||||
client.openPauseMenu(false);
|
||||
else // Else just close the current screen.
|
||||
client.currentScreen.onClose();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.config.is_jump_button(button) && client.currentScreen != null) {
|
||||
if (this.action_gui_cooldown == 0) {
|
||||
Element focused = client.currentScreen.getFocused();
|
||||
if (focused != null)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles sneak button and continuous sneak.
|
||||
if (this.config.is_sneak_button(button) && client.player != null) {
|
||||
if (action == 0) {
|
||||
if (this.continuous_sneak) {
|
||||
this.set_sneaking(client, this.continuous_sneak = false);
|
||||
} else if (this.last_sneak > 3) {
|
||||
this.set_sneaking(client, this.continuous_sneak = true);
|
||||
} else {
|
||||
this.set_sneaking(client, true);
|
||||
this.last_sneak = 15;
|
||||
}
|
||||
} else if (action == 1) {
|
||||
if (this.continuous_sneak)
|
||||
return;
|
||||
this.set_sneaking(client, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.config.is_jump_button(button) && client.currentScreen != null) {
|
||||
if (this.last_a_state != state) {
|
||||
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 (state) {
|
||||
client.currentScreen.mouseClicked(mouse_x, mouse_y, GLFW.GLFW_MOUSE_BUTTON_1);
|
||||
} else {
|
||||
client.currentScreen.mouseReleased(mouse_x, mouse_y, GLFW.GLFW_MOUSE_BUTTON_1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (client.currentScreen == null && action != 2) {
|
||||
Optional<KeyBinding> 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)
|
||||
{
|
||||
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));
|
||||
|
||||
// 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 (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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
boolean allow_mouse_control = true;
|
||||
|
||||
if (this.action_gui_cooldown == 0 && this.config.is_movement_axis(axe)) {
|
||||
if (this.config.is_forward_button(axe, 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)) {
|
||||
allow_mouse_control = this.change_focus(client.currentScreen, true);
|
||||
} else if (this.config.is_left_button(axe, 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)) {
|
||||
allow_mouse_control = this.handle_left_right(client.currentScreen, true);
|
||||
}
|
||||
}
|
||||
|
||||
float movement_x = 0.0F;
|
||||
float movement_y = 0.0F;
|
||||
|
||||
if (this.config.is_back_button(axe, false, (value > 0 ? 1 : 2))) {
|
||||
movement_y = abs_value;
|
||||
} else if (this.config.is_forward_button(axe, false, (value > 0 ? 1 : 2))) {
|
||||
movement_y = -abs_value;
|
||||
} else if (this.config.is_left_button(axe, false, (value > 0 ? 1 : 2))) {
|
||||
movement_x = -abs_value;
|
||||
} else if (this.config.is_right_button(axe, false, (value > 0 ? 1 : 2))) {
|
||||
movement_x = abs_value;
|
||||
}
|
||||
|
||||
if (client.currentScreen != null && allow_mouse_control) {
|
||||
boolean moving = Math.abs(movement_y) >= dead_zone || Math.abs(movement_x) >= dead_zone;
|
||||
if (moving) {
|
||||
/*
|
||||
Updates the target mouse position when the initial movement stick movement is detected.
|
||||
It prevents the cursor to jump to the old target mouse position if the user moves the cursor with the mouse.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
if (Math.abs(movement_x) >= dead_zone)
|
||||
this.mouse_speed_x = movement_x;
|
||||
else
|
||||
this.mouse_speed_x = 0.F;
|
||||
|
||||
if (Math.abs(movement_y) >= dead_zone)
|
||||
this.mouse_speed_y = movement_y;
|
||||
else
|
||||
this.mouse_speed_y = 0.F;
|
||||
} else {
|
||||
this.mouse_speed_x = 0.F;
|
||||
this.mouse_speed_y = 0.F;
|
||||
}
|
||||
|
||||
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_rotation_speed();
|
||||
this.target_mouse_x = MathHelper.clamp(this.target_mouse_x, 0, client.window.getWidth());
|
||||
this.target_mouse_y += this.mouse_speed_y * this.config.get_rotation_speed();
|
||||
this.target_mouse_y = MathHelper.clamp(this.target_mouse_y, 0, client.window.getHeight());
|
||||
}
|
||||
|
||||
//this.move_mouse_to_closest_slot(client, client.currentScreen);
|
||||
}
|
||||
|
||||
this.prev_x_axis = movement_x;
|
||||
this.prev_y_axis = movement_y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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 void handle_a_button(@NotNull Element focused)
|
||||
{
|
||||
if (focused instanceof AbstractPressableButtonWidget) {
|
||||
AbstractPressableButtonWidget button_widget = (AbstractPressableButtonWidget) focused;
|
||||
button_widget.playDownSound(MinecraftClient.getInstance().getSoundManager());
|
||||
button_widget.onPress();
|
||||
} else if (focused instanceof WorldListWidget) {
|
||||
WorldListWidget list = (WorldListWidget) focused;
|
||||
list.method_20159().ifPresent(WorldListWidget.LevelItem::play);
|
||||
} else if (focused instanceof ParentElement) {
|
||||
Element child_focused = ((ParentElement) focused).getFocused();
|
||||
if (child_focused != null)
|
||||
this.handle_a_button(child_focused);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the left and right buttons.
|
||||
*
|
||||
* @param screen The current screen.
|
||||
* @param right True if the right button is pressed, else false.
|
||||
*/
|
||||
private boolean handle_left_right(@NotNull Screen screen, boolean right)
|
||||
{
|
||||
Element focused = screen.getFocused();
|
||||
if (focused != null)
|
||||
return this.handle_right_left_element(focused, right);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handle_right_left_element(@NotNull Element element, boolean right)
|
||||
{
|
||||
if (element instanceof SliderWidget) {
|
||||
SliderWidget slider = (SliderWidget) element;
|
||||
slider.keyPressed(right ? 262 : 263, 0, 0);
|
||||
this.action_gui_cooldown = 2; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
|
||||
return false;
|
||||
} else if (element instanceof ParentElement) {
|
||||
ParentElement entry_list = (ParentElement) element;
|
||||
Element focused = entry_list.getFocused();
|
||||
if (focused == null)
|
||||
return true;
|
||||
return this.handle_right_left_element(focused, right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the player is sneaking.
|
||||
*
|
||||
* @param client The client's instance.
|
||||
* @param sneaking True if the player is sneaking, else false.
|
||||
*/
|
||||
private void set_sneaking(@NotNull MinecraftClient client, boolean sneaking)
|
||||
{
|
||||
((LambdaKeyBinding) client.options.keySneak).handle_press_state(sneaking);
|
||||
}
|
||||
|
||||
private boolean change_focus(@NotNull Screen screen, boolean down)
|
||||
{
|
||||
if (!screen.changeFocus(down)) {
|
||||
if (screen.changeFocus(down)) {
|
||||
this.action_gui_cooldown = 5;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
this.action_gui_cooldown = 5;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Inspired from https://github.com/MrCrayfish/Controllable/blob/1.14.X/src/main/java/com/mrcrayfish/controllable/client/ControllerInput.java#L686.
|
||||
private void move_mouse_to_closest_slot(@NotNull MinecraftClient client, @Nullable Screen screen)
|
||||
{
|
||||
// Makes the mouse attracted to slots. This helps with selecting items when using a controller.
|
||||
if (screen instanceof AbstractContainerScreen) {
|
||||
AbstractContainerScreen inventory_screen = (AbstractContainerScreen) screen;
|
||||
AbstractContainerScreenAccessor accessor = (AbstractContainerScreenAccessor) inventory_screen;
|
||||
int gui_left = accessor.get_left();
|
||||
int gui_top = accessor.get_top();
|
||||
int mouse_x = (int) (target_mouse_x * (double) client.window.getScaledWidth() / (double) client.window.getWidth());
|
||||
int mouse_y = (int) (target_mouse_y * (double) client.window.getScaledHeight() / (double) client.window.getHeight());
|
||||
|
||||
// Finds the closest slot in the GUI within 14 pixels.
|
||||
Optional<Pair<Slot, Double>> closest_slot = inventory_screen.getContainer().slotList.parallelStream()
|
||||
.map(slot -> {
|
||||
int pos_x = gui_left + slot.xPosition + 8;
|
||||
int pos_y = gui_top + slot.yPosition + 8;
|
||||
|
||||
// Distance between the slot and the cursor.
|
||||
double distance = Math.sqrt(Math.pow(pos_x - mouse_x, 2) + Math.pow(pos_y - mouse_y, 2));
|
||||
return Pair.of(slot, distance);
|
||||
}).filter(entry -> entry.get_value() <= 14.0)
|
||||
.min(Comparator.comparingDouble(Pair::get_value));
|
||||
|
||||
if (closest_slot.isPresent()) {
|
||||
Slot slot = closest_slot.get().get_key();
|
||||
if (slot.hasStack() || !client.player.inventory.getMainHandStack().isEmpty()) {
|
||||
int slot_center_x_scaled = gui_left + slot.xPosition + 8;
|
||||
int slot_center_y_scaled = gui_top + slot.yPosition + 8;
|
||||
int slot_center_x = (int) (slot_center_x_scaled / ((double) client.window.getScaledWidth() / (double) client.window.getWidth()));
|
||||
int slot_center_y = (int) (slot_center_y_scaled / ((double) client.window.getScaledHeight() / (double) client.window.getHeight()));
|
||||
double delta_x = slot_center_x - target_mouse_x;
|
||||
double delta_y = slot_center_y - target_mouse_y;
|
||||
|
||||
if (mouse_x != slot_center_x_scaled || mouse_y != slot_center_y_scaled) {
|
||||
this.target_mouse_x += delta_x * 0.75;
|
||||
this.target_mouse_y += delta_y * 0.75;
|
||||
} else {
|
||||
mouse_speed_x = 0.F;
|
||||
mouse_speed_y = 0.F;
|
||||
}
|
||||
this.mouse_speed_x *= .75F;
|
||||
this.mouse_speed_y *= .75F;
|
||||
}
|
||||
} else {
|
||||
this.mouse_speed_x *= .1F;
|
||||
this.mouse_speed_y *= .1F;
|
||||
}
|
||||
} else {
|
||||
this.mouse_speed_x = 0.F;
|
||||
this.mouse_speed_y = 0.F;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,19 +9,37 @@
|
||||
|
||||
package me.lambdaurora.lambdacontrols;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.mixin.AbstractContainerScreenAccessor;
|
||||
import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor;
|
||||
import me.lambdaurora.lambdacontrols.util.LambdaKeyBinding;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
|
||||
import net.minecraft.client.gui.screen.world.WorldListWidget;
|
||||
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget;
|
||||
import net.minecraft.client.gui.widget.EntryListWidget;
|
||||
import net.minecraft.client.gui.widget.SliderWidget;
|
||||
import net.minecraft.client.options.GameOptions;
|
||||
import net.minecraft.container.Slot;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
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.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_JOYSTICK_1;
|
||||
|
||||
@@ -32,10 +50,22 @@ import static org.lwjgl.glfw.GLFW.GLFW_JOYSTICK_1;
|
||||
public class LambdaControls implements ClientModInitializer
|
||||
{
|
||||
private static LambdaControls INSTANCE;
|
||||
public final Logger logger = LogManager.getLogger("LambdaControls");
|
||||
public final LambdaControlsConfig config = new LambdaControlsConfig(this);
|
||||
private final Map<Integer, Integer> BUTTON_STATES = new HashMap<>();
|
||||
private int cid = GLFW_JOYSTICK_1;
|
||||
public final Logger logger = LogManager.getLogger("LambdaControls");
|
||||
public final LambdaControlsConfig config = new LambdaControlsConfig(this);
|
||||
public final ControllerInput controller_input = new ControllerInput(this);
|
||||
private final Map<Integer, Integer> BUTTON_STATES = new HashMap<>();
|
||||
private float prev_x_axis = 0.F;
|
||||
private float prev_y_axis = 0.F;
|
||||
private int prev_target_mouse_x = 0;
|
||||
private int prev_target_mouse_y = 0;
|
||||
private int target_mouse_x = 0;
|
||||
private int target_mouse_y = 0;
|
||||
private float mouse_speed_x = 0.F;
|
||||
private float mouse_speed_y = 0.F;
|
||||
private boolean last_a_state = false;
|
||||
private int cid = GLFW_JOYSTICK_1;
|
||||
private boolean allow_controller_mouse = true;
|
||||
private int action_gui_cooldown = 0;
|
||||
|
||||
@Override
|
||||
public void onInitializeClient()
|
||||
@@ -64,82 +94,25 @@ public class LambdaControls implements ClientModInitializer
|
||||
*
|
||||
* @param client The client instance.
|
||||
*/
|
||||
public void on_tick(MinecraftClient client)
|
||||
public void on_tick(@NotNull MinecraftClient client)
|
||||
{
|
||||
GameOptions options = client.options;
|
||||
ByteBuffer btn_buffer = GLFW.glfwGetJoystickButtons(GLFW.GLFW_JOYSTICK_3);
|
||||
if (btn_buffer != null) {
|
||||
for (int i = 0; i < btn_buffer.limit(); i++) {
|
||||
boolean btn_state = btn_buffer.get() == (byte) 1;
|
||||
if (this.config.get_controls_mode() == ControlsMode.CONTROLLER)
|
||||
this.controller_input.on_tick(client);
|
||||
/* Decreases the cooldown for the screen focus change.
|
||||
if (this.action_gui_cooldown > 0)
|
||||
--this.action_gui_cooldown;
|
||||
if (this.action_gui_cooldown == 0)
|
||||
this.allow_controller_mouse = true;
|
||||
|
||||
int current_state = BUTTON_STATES.getOrDefault(i, 0);
|
||||
if (current_state == 0 && btn_state) {
|
||||
BUTTON_STATES.put(i, 1);
|
||||
this.prev_target_mouse_x = this.target_mouse_x;
|
||||
this.prev_target_mouse_y = this.target_mouse_y;
|
||||
if (LambdaControls.get().config.get_controls_mode() == ControlsMode.CONTROLLER)
|
||||
this.on_controller_tick(client);*/
|
||||
}
|
||||
|
||||
int f_i = i;
|
||||
this.config.get_keybind("button_" + i).ifPresent(key_binding -> {
|
||||
((LambdaKeyBinding) key_binding).lambdacontrols_press();
|
||||
if (key_binding == options.keyInventory && client.player != null && client.player.container != client.player.playerContainer) {
|
||||
BUTTON_STATES.put(f_i, 2);
|
||||
}
|
||||
});
|
||||
if (this.config.is_hotbar_left_button(i)) {
|
||||
client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 0 ? 8 : client.player.inventory.selectedSlot - 1;
|
||||
} else if (this.config.is_hotbar_right_button(i)) {
|
||||
client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 8 ? 0 : client.player.inventory.selectedSlot + 1;
|
||||
}
|
||||
} else if (current_state != 0 && !btn_state) {
|
||||
this.config.get_keybind("button_" + i).ifPresent(key_binding -> {
|
||||
if (key_binding == options.keyInventory && current_state == 2 && client.player != null && client.player.container != client.player.playerContainer) {
|
||||
client.player.closeContainer();
|
||||
}
|
||||
((LambdaKeyBinding) key_binding).lambdacontrols_unpress();
|
||||
});
|
||||
|
||||
BUTTON_STATES.put(i, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
FloatBuffer axes_buffer = GLFW.glfwGetJoystickAxes(GLFW.GLFW_JOYSTICK_3);
|
||||
if (axes_buffer != null) {
|
||||
for (int i = 0; i < axes_buffer.limit(); i++) {
|
||||
float value = axes_buffer.get();
|
||||
{
|
||||
int state = value > 0.5F ? 1 : (value < -0.5F ? 2 : 0);
|
||||
this.config.get_keybind("axe_" + i + "+").ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(state == 1));
|
||||
this.config.get_keybind("axe_" + i + "-").ifPresent(key_binding -> ((LambdaKeyBinding) key_binding).handle_press_state(state == 2));
|
||||
}
|
||||
if (this.config.is_look_axis(i) && (value > 0.25F || value < -0.25F)) {
|
||||
int state = value > 0.25F ? 1 : (value < -0.25F ? 2 : 0);
|
||||
float multiplier = 50.f;
|
||||
double x = 0.0D;
|
||||
double y = 0.0D;
|
||||
if (this.config.is_view_down_control(i, state)) {
|
||||
if (this.config.get_view_down_control().endsWith("+"))
|
||||
y = Math.abs(value * multiplier);
|
||||
else
|
||||
y = -Math.abs(value * multiplier);
|
||||
} else if (this.config.is_view_up_control(i, state)) {
|
||||
if (this.config.get_view_up_control().endsWith("+"))
|
||||
y = Math.abs(value * multiplier);
|
||||
else
|
||||
y = -Math.abs(value * multiplier);
|
||||
}
|
||||
if (this.config.is_view_left_control(i, state)) {
|
||||
if (this.config.get_view_left_control().endsWith("+"))
|
||||
x = Math.abs(value * multiplier);
|
||||
else
|
||||
x = -Math.abs(value * multiplier);
|
||||
} else if (this.config.is_view_right_control(i, state)) {
|
||||
if (this.config.get_view_right_control().endsWith("+"))
|
||||
x = Math.abs(value * multiplier);
|
||||
else
|
||||
x = -Math.abs(value * multiplier);
|
||||
}
|
||||
client.player.changeLookDirection(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void on_render(MinecraftClient client)
|
||||
{
|
||||
this.controller_input.on_render(client);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,16 @@ public class LambdaControlsConfig
|
||||
private final LambdaControls mod;
|
||||
private ControlsMode controls_mode;
|
||||
private HudSide hud_side;
|
||||
// Controller settings
|
||||
private double dead_zone;
|
||||
private double rotation_speed;
|
||||
// Controller controls
|
||||
private String back_button;
|
||||
private String forward_button;
|
||||
private String jump_button;
|
||||
private String left_button;
|
||||
private String right_button;
|
||||
private String sneak_button;
|
||||
|
||||
public LambdaControlsConfig(@NotNull LambdaControls mod)
|
||||
{
|
||||
@@ -41,53 +51,59 @@ public class LambdaControlsConfig
|
||||
this.mod.log("Configuration loaded.");
|
||||
this.controls_mode = ControlsMode.by_id(this.config.getOrElse("controls", "default")).orElse(ControlsMode.DEFAULT);
|
||||
this.hud_side = HudSide.by_id(this.config.getOrElse("hud.side", "left")).orElse(HudSide.LEFT);
|
||||
// Controller settings
|
||||
this.dead_zone = this.config.getOrElse("controller.dead_zone", 0.25D);
|
||||
this.rotation_speed = this.config.getOrElse("controller.rotation_speed", 25.D);
|
||||
// Controller controls
|
||||
this.back_button = this.config.getOrElse("controller.controls.back", "none").toLowerCase();
|
||||
this.forward_button = this.config.getOrElse("controller.controls.forward", "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();
|
||||
}
|
||||
|
||||
public void init_keybindings(GameOptions options)
|
||||
{
|
||||
String str = this.config.getOrElse("controller.attack", "none").toLowerCase();
|
||||
String str = this.config.getOrElse("controller.controls.attack", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyAttack);
|
||||
str = this.config.getOrElse("controller.back", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyBack);
|
||||
str = this.config.getOrElse("controller.drop", "none").toLowerCase();
|
||||
if (!this.back_button.equals("none"))
|
||||
this.keybinding_mappings.put(this.back_button, options.keyBack);
|
||||
str = this.config.getOrElse("controller.controls.drop", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyDrop);
|
||||
str = this.config.getOrElse("controller.forward", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyForward);
|
||||
str = this.config.getOrElse("controller.inventory", "none").toLowerCase();
|
||||
if (!this.forward_button.equals("none"))
|
||||
this.keybinding_mappings.put(this.forward_button, options.keyForward);
|
||||
str = this.config.getOrElse("controller.controls.inventory", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyInventory);
|
||||
str = this.config.getOrElse("controller.jump", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyJump);
|
||||
str = this.config.getOrElse("controller.left", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyLeft);
|
||||
str = this.config.getOrElse("controller.right", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyRight);
|
||||
str = this.config.getOrElse("controller.sneak", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keySneak);
|
||||
str = this.config.getOrElse("controller.sprint", "none").toLowerCase();
|
||||
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.use", "none").toLowerCase();
|
||||
str = this.config.getOrElse("controller.controls.use", "none").toLowerCase();
|
||||
if (!str.equals("none"))
|
||||
this.keybinding_mappings.put(str, options.keyUse);
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
this.config.set("controller.dead_zone", this.dead_zone);
|
||||
this.config.set("controller.rotation_speed", this.rotation_speed);
|
||||
this.config.save();
|
||||
this.mod.log("Configuration saved.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the controls mode from the configuration.
|
||||
* Gets the controls mode from the configuration.
|
||||
*
|
||||
* @return The controls mode.
|
||||
*/
|
||||
@@ -108,7 +124,7 @@ public class LambdaControlsConfig
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HUD side from the configuration.
|
||||
* Gets the HUD side from the configuration.
|
||||
*
|
||||
* @return The HUD side.
|
||||
*/
|
||||
@@ -128,6 +144,46 @@ public class LambdaControlsConfig
|
||||
this.config.set("hud.side", hud_side.get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller's dead zone from the configuration.
|
||||
*
|
||||
* @return The controller's dead zone value.
|
||||
*/
|
||||
public double get_dead_zone()
|
||||
{
|
||||
return this.dead_zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller's dead zone in the configuration.
|
||||
*
|
||||
* @param dead_zone The new controller's dead zone value.
|
||||
*/
|
||||
public void set_dead_zone(double dead_zone)
|
||||
{
|
||||
this.dead_zone = dead_zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller's rotation speed.
|
||||
*
|
||||
* @return The rotation speed.
|
||||
*/
|
||||
public double get_rotation_speed()
|
||||
{
|
||||
return this.rotation_speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller's rotation speed.
|
||||
*
|
||||
* @param rotation_speed The rotation speed.
|
||||
*/
|
||||
public void set_rotation_speed(double rotation_speed)
|
||||
{
|
||||
this.rotation_speed = rotation_speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keybindings.
|
||||
*
|
||||
@@ -143,14 +199,49 @@ public class LambdaControlsConfig
|
||||
return Optional.ofNullable(this.keybinding_mappings.get(id));
|
||||
}
|
||||
|
||||
public String get_back_button()
|
||||
{
|
||||
return this.back_button;
|
||||
}
|
||||
|
||||
public String get_forward_button()
|
||||
{
|
||||
return this.forward_button;
|
||||
}
|
||||
|
||||
public String get_hotbar_left_button()
|
||||
{
|
||||
return this.config.getOrElse("controller.hotbar_left", "none").toLowerCase();
|
||||
return this.config.getOrElse("controller.controls.hotbar_left", "none").toLowerCase();
|
||||
}
|
||||
|
||||
public String get_hotbar_right_button()
|
||||
{
|
||||
return this.config.getOrElse("controller.hotbar_right", "none").toLowerCase();
|
||||
return this.config.getOrElse("controller.controls.hotbar_right", "none").toLowerCase();
|
||||
}
|
||||
|
||||
public String get_jump_button()
|
||||
{
|
||||
return this.jump_button;
|
||||
}
|
||||
|
||||
public String get_left_button()
|
||||
{
|
||||
return this.left_button;
|
||||
}
|
||||
|
||||
public String get_right_button()
|
||||
{
|
||||
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 boolean is_hotbar_left_button(int button)
|
||||
@@ -163,24 +254,67 @@ public class LambdaControlsConfig
|
||||
return this.get_hotbar_right_button().equals("button_" + button);
|
||||
}
|
||||
|
||||
public boolean is_back_button(int btn, boolean is_btn, int state)
|
||||
{
|
||||
if (!is_btn && state == 0)
|
||||
return false;
|
||||
return this.get_back_button().equals((is_btn ? "button_" : "axe_") + btn + (is_btn ? "" : (state == 1 ? "+" : "-")));
|
||||
}
|
||||
|
||||
public boolean is_forward_button(int btn, boolean is_btn, int state)
|
||||
{
|
||||
if (!is_btn && state == 0)
|
||||
return false;
|
||||
return this.get_forward_button().equals((is_btn ? "button_" : "axe_") + btn + (is_btn ? "" : (state == 1 ? "+" : "-")));
|
||||
}
|
||||
|
||||
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)
|
||||
return false;
|
||||
return this.get_left_button().equals((is_btn ? "button_" : "axe_") + btn + (is_btn ? "" : (state == 1 ? "+" : "-")));
|
||||
}
|
||||
|
||||
public boolean is_right_button(int btn, boolean is_btn, int state)
|
||||
{
|
||||
if (!is_btn && state == 0)
|
||||
return false;
|
||||
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 String get_view_down_control()
|
||||
{
|
||||
return this.config.getOrElse("controller.view_down", "none").toLowerCase();
|
||||
return this.config.getOrElse("controller.controls.view_down", "none").toLowerCase();
|
||||
}
|
||||
|
||||
public String get_view_left_control()
|
||||
{
|
||||
return this.config.getOrElse("controller.view_left", "none").toLowerCase();
|
||||
return this.config.getOrElse("controller.controls.view_left", "none").toLowerCase();
|
||||
}
|
||||
|
||||
public String get_view_right_control()
|
||||
{
|
||||
return this.config.getOrElse("controller.view_right", "none").toLowerCase();
|
||||
return this.config.getOrElse("controller.controls.view_right", "none").toLowerCase();
|
||||
}
|
||||
|
||||
public String get_view_up_control()
|
||||
{
|
||||
return this.config.getOrElse("controller.view_up", "none").toLowerCase();
|
||||
return this.config.getOrElse("controller.controls.view_up", "none").toLowerCase();
|
||||
}
|
||||
|
||||
public boolean is_view_down_control(int axe, int state)
|
||||
@@ -211,9 +345,27 @@ public class LambdaControlsConfig
|
||||
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.
|
||||
*
|
||||
* @param i The axis index.
|
||||
* @return True if the axis is used for movements, else false.
|
||||
*/
|
||||
public boolean is_movement_axis(int i)
|
||||
{
|
||||
return this.get_forward_button().startsWith("axe_" + i) || this.get_back_button().startsWith("axe_" + i) || this.get_left_button().startsWith("axe_" + i)
|
||||
|| this.get_right_button().startsWith("axe_" + i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.gui;
|
||||
|
||||
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.resource.language.I18n;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents the LambdaControls settings screen.
|
||||
*/
|
||||
public class LambdaControlsSettingsScreen extends Screen
|
||||
{
|
||||
private final LambdaControls mod;
|
||||
private final Screen parent;
|
||||
private final GameOptions options;
|
||||
private final Option dead_zone_option;
|
||||
private final Option rotation_speed_option;
|
||||
|
||||
public LambdaControlsSettingsScreen(Screen parent, @NotNull GameOptions options)
|
||||
{
|
||||
super(new TranslatableText("lambdacontrols.title.settings"));
|
||||
this.mod = LambdaControls.get();
|
||||
this.parent = parent;
|
||||
this.options = options;
|
||||
this.dead_zone_option = new DoubleOption("lambdacontrols.menu.dead_zone", 0.05D, 1.0D, 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);
|
||||
}
|
||||
}, (game_options, option) -> option.getDisplayPrefix() + option.get(options));
|
||||
this.rotation_speed_option = new DoubleOption("lambdacontrols.menu.rotation_speed", 0.0D, 50.0D, 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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
this.mod.config.save();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
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(),
|
||||
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"),
|
||||
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(),
|
||||
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(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, button_height, I18n.translate("gui.done"), (buttonWidget) -> {
|
||||
this.minecraft.openScreen(this.parent);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(int mouseX, int mouseY, float delta)
|
||||
{
|
||||
this.renderBackground();
|
||||
super.render(mouseX, mouseY, delta);
|
||||
}
|
||||
}
|
||||
@@ -102,9 +102,10 @@ public class TouchscreenOverlay extends Screen
|
||||
this.fly_button.visible = true;
|
||||
this.fly_up_button.visible = true;
|
||||
this.fly_down_button.visible = true;
|
||||
if (old_state_fly != this.fly_button.visible)
|
||||
if (old_state_fly != this.fly_button.visible) {
|
||||
this.fly_button_enable_ticks = 5;
|
||||
else if (this.fly_button_enable_ticks > 0)
|
||||
this.handle_jump(false);
|
||||
} else if (this.fly_button_enable_ticks > 0)
|
||||
this.fly_button_enable_ticks--;
|
||||
} else {
|
||||
this.jump_button.visible = true;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.mixin;
|
||||
|
||||
import net.minecraft.client.gui.widget.AbstractButtonWidget;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(AbstractButtonWidget.class)
|
||||
public interface AbstractButtonWidgetAccessor
|
||||
{
|
||||
@Accessor("height")
|
||||
int get_height();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.mixin;
|
||||
|
||||
import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(AbstractContainerScreen.class)
|
||||
public interface AbstractContainerScreenAccessor
|
||||
{
|
||||
@Accessor("left")
|
||||
int get_left();
|
||||
|
||||
@Accessor("top")
|
||||
int get_top();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.mixin;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor;
|
||||
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(CreativeInventoryScreen.class)
|
||||
public abstract class CreativeInventoryScreenMixin implements CreativeInventoryScreenAccessor
|
||||
{
|
||||
@Shadow
|
||||
protected abstract void setSelectedTab(ItemGroup itemGroup);
|
||||
|
||||
@Accessor("selectedTab")
|
||||
public abstract int get_selected_tab();
|
||||
|
||||
@Override
|
||||
public void set_selected_tab(@NotNull ItemGroup group)
|
||||
{
|
||||
this.setSelectedTab(group);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.mixin;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.ControlsMode;
|
||||
import me.lambdaurora.lambdacontrols.LambdaControls;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public class GameRendererMixin
|
||||
{
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftClient client;
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;render(IIF)V"))
|
||||
private void on_render(float tick_delta, long start_time, boolean full_render, CallbackInfo ci)
|
||||
{
|
||||
if (this.client.currentScreen != null && LambdaControls.get().config.get_controls_mode() == ControlsMode.CONTROLLER)
|
||||
LambdaControls.get().controller_input.on_pre_render_screen(this.client, this.client.currentScreen);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,8 @@ public abstract class MinecraftClientMixin
|
||||
@Shadow
|
||||
public boolean skipGameRender;
|
||||
|
||||
@Shadow public Screen currentScreen;
|
||||
@Shadow
|
||||
public Screen currentScreen;
|
||||
|
||||
@Inject(method = "init", at = @At("RETURN"))
|
||||
private void on_init(CallbackInfo ci)
|
||||
@@ -39,11 +40,16 @@ public abstract class MinecraftClientMixin
|
||||
LambdaControls.get().on_mc_init((MinecraftClient) (Object) this);
|
||||
}
|
||||
|
||||
@Inject(method = "handleInputEvents", at = @At("HEAD"))
|
||||
@Inject(method = "render", at = @At("HEAD"))
|
||||
private void on_render(boolean full_render, CallbackInfo ci)
|
||||
{
|
||||
LambdaControls.get().on_render((MinecraftClient) (Object) (this));
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
private void on_handle_input_events(CallbackInfo ci)
|
||||
{
|
||||
if (LambdaControls.get().config.get_controls_mode() == ControlsMode.CONTROLLER)
|
||||
LambdaControls.get().on_tick((MinecraftClient) (Object) this);
|
||||
LambdaControls.get().on_tick((MinecraftClient) (Object) this);
|
||||
}
|
||||
|
||||
@Inject(method = "openScreen", at = @At("RETURN"))
|
||||
@@ -55,6 +61,8 @@ public abstract class MinecraftClientMixin
|
||||
screen.init(((MinecraftClient) (Object) this), this.window.getScaledWidth(), this.window.getScaledHeight());
|
||||
this.skipGameRender = false;
|
||||
this.currentScreen = screen;
|
||||
} else if (screen != null) {
|
||||
mod.controller_input.on_screen_open(((MinecraftClient) (Object) this), this.window.getWidth(), this.window.getHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package me.lambdaurora.lambdacontrols.mixin;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.ControlsMode;
|
||||
import me.lambdaurora.lambdacontrols.LambdaControls;
|
||||
import me.lambdaurora.lambdacontrols.util.MouseAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.Mouse;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -21,17 +22,13 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Mouse.class)
|
||||
public class MouseMixin
|
||||
public abstract class MouseMixin implements MouseAccessor
|
||||
{
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftClient client;
|
||||
protected abstract void onCursorPos(long window, double x, double y);
|
||||
|
||||
@Shadow
|
||||
private double x;
|
||||
|
||||
@Shadow
|
||||
private double y;
|
||||
protected abstract void onMouseButton(long window, int button, int action, int mods);
|
||||
|
||||
@Inject(method = "lockCursor", at = @At("HEAD"), cancellable = true)
|
||||
private void on_mouse_locked(CallbackInfo ci)
|
||||
@@ -39,4 +36,16 @@ public class MouseMixin
|
||||
if (LambdaControls.get().config.get_controls_mode() == ControlsMode.TOUCHSCREEN)
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void on_mouse_button(long window, int button, int action, int mods)
|
||||
{
|
||||
this.onMouseButton(window, button, action, mods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void on_cursor_pos(long window, double x, double y)
|
||||
{
|
||||
this.onCursorPos(window, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.mixin;
|
||||
|
||||
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.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;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(SettingsScreen.class)
|
||||
public class SettingsScreenMixin extends Screen
|
||||
{
|
||||
@Final
|
||||
@Shadow
|
||||
private GameOptions settings;
|
||||
|
||||
protected SettingsScreenMixin(Text title)
|
||||
{
|
||||
super(title);
|
||||
}
|
||||
|
||||
@Inject(method = "init", at = @At("RETURN"))
|
||||
private void on_init(CallbackInfo ci)
|
||||
{
|
||||
this.buttons.stream().filter(button -> button.getMessage().equals(I18n.translate("options.controls")))
|
||||
.findFirst()
|
||||
.ifPresent(btn -> {
|
||||
this.buttons.remove(btn);
|
||||
this.children.remove(btn);
|
||||
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))));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.util;
|
||||
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents an accessor to CreativeInventoryScreen.
|
||||
*/
|
||||
public interface CreativeInventoryScreenAccessor
|
||||
{
|
||||
/**
|
||||
* Gets the selected tab.
|
||||
*
|
||||
* @return The selected tab index.
|
||||
*/
|
||||
int get_selected_tab();
|
||||
|
||||
/**
|
||||
* Sets the selected tab.
|
||||
*
|
||||
* @param group The tab's item group.
|
||||
*/
|
||||
void set_selected_tab(@NotNull ItemGroup group);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.util;
|
||||
|
||||
/**
|
||||
* Represents mouse's extra access.
|
||||
*/
|
||||
public interface MouseAccessor
|
||||
{
|
||||
void on_mouse_button(long window, int button, int action, int mods);
|
||||
|
||||
void on_cursor_pos(long window, double x, double y);
|
||||
}
|
||||
12
src/main/resources/assets/lambdacontrols/lang/en_us.json
Normal file
12
src/main/resources/assets/lambdacontrols/lang/en_us.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"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.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"
|
||||
}
|
||||
@@ -4,29 +4,33 @@
|
||||
controls = "default"
|
||||
|
||||
[hud]
|
||||
# Dertermines where the movements buttons are.
|
||||
side = "left"
|
||||
# Dertermines where the movements buttons are.
|
||||
side = "left"
|
||||
|
||||
# Controller settings
|
||||
[controller]
|
||||
attack = "button_7"
|
||||
back = "axe_1+"
|
||||
drop = "button_2"
|
||||
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_4"
|
||||
use = "button_6"
|
||||
view_down = "axe_3+"
|
||||
view_left = "axe_2-"
|
||||
view_right = "axe_2+"
|
||||
view_up = "axe_3-"
|
||||
dead_zone = 0.25
|
||||
rotation_speed = 25.0
|
||||
# Controller controls
|
||||
[controller.controls]
|
||||
attack = "button_7"
|
||||
back = "axe_1+"
|
||||
drop = "button_2"
|
||||
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-"
|
||||
|
||||
# Colors
|
||||
[colors]
|
||||
|
||||
@@ -3,10 +3,15 @@
|
||||
"package": "me.lambdaurora.lambdacontrols.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"client": [
|
||||
"AbstractButtonWidgetAccessor",
|
||||
"AbstractContainerScreenAccessor",
|
||||
"CreativeInventoryScreenMixin",
|
||||
"GameRendererMixin",
|
||||
"InGameHudMixin",
|
||||
"KeyBindingMixin",
|
||||
"MinecraftClientMixin",
|
||||
"MouseMixin"
|
||||
"MouseMixin",
|
||||
"SettingsScreenMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
||||
Reference in New Issue
Block a user