💥 Lot of breaking changes.

This commit is contained in:
LambdAurora
2020-01-04 15:19:02 +01:00
parent 7c06afb4a4
commit 738d094fb1
59 changed files with 554 additions and 328 deletions

87
fabric/build.gradle Normal file
View File

@@ -0,0 +1,87 @@
plugins {
id 'fabric-loom' version '0.2.6-SNAPSHOT'
id 'java-library'
id 'maven-publish'
}
archivesBaseName = project.archives_base_name + "-fabric"
minecraft {
}
repositories {
maven {
name = 'CottonMC'
url = 'http://server.bbkr.space:8081/artifactory/libs-snapshot'
}
repositories {
maven { url = "https://jitpack.io" }
}
// SpruceUI
ivy {
url 'https://github.com/LambdAurora/SpruceUI/releases/download/'
patternLayout {
artifact '[revision]/[module]-[version].[ext]'
}
metadataSources() {
artifact()
}
}
// OkZoomer
ivy {
url 'https://github.com/joaoh1/OkZoomer/releases/download/'
patternLayout {
artifact '[revision]/[module]-[version].[ext]'
}
metadataSources() {
artifact()
}
}
}
dependencies {
//to change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}"
modCompile "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modApi "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modCompile "io.github.prospector:modmenu:1.8.0+build.16"
modCompile "me.lambdaurora:spruceui:${project.spruceui_version}"
include "me.lambdaurora:spruceui:${project.spruceui_version}"
//modCompile "io.github.cottonmc:cotton-client-commands:0.4.2+1.14.3-SNAPSHOT"
// Compatibility mods
modCompile "io.github.joaoh1:okzoomer:1.0.3"
api project(":common")
include project(":common")
include "org.jetbrains:annotations:17.0.0"
include "org.aperlambda:lambdajcommon:1.7.2"
include "com.electronwill.night-config:core:3.5.3"
include "com.electronwill.night-config:toml:3.5.3"
}
processResources {
inputs.property "version", project.version
from(sourceSets.main.resources.srcDirs) {
include "fabric.mod.json"
expand "version": project.version
}
from(sourceSets.main.resources.srcDirs) {
exclude "fabric.mod.json"
}
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
jar {
from "../LICENSE"
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols;
import me.lambdaurora.lambdacontrols.event.PlayerChangeControlsModeCallback;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Represents the LambdaControls mod.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.0.0
*/
public class LambdaControls implements ModInitializer
{
private static LambdaControls INSTANCE;
public static final Identifier CONTROLS_MODE_CHANNEL = new Identifier(LambdaControlsConstants.NAMESPACE, "controls_mode");
public final Logger logger = LogManager.getLogger("LambdaControls");
@Override
public void onInitialize()
{
INSTANCE = this;
this.log("Initializing LambdaControls...");
ServerSidePacketRegistry.INSTANCE.register(CONTROLS_MODE_CHANNEL,
(context, attached_data) -> ControlsMode.by_id(attached_data.readString(32))
.ifPresent(controls_mode -> context.getTaskQueue()
.execute(() -> PlayerChangeControlsModeCallback.EVENT.invoker().apply(context.getPlayer(), controls_mode))));
}
/**
* Prints a message to the terminal.
*
* @param info The message to print.
*/
public void log(String info)
{
this.logger.info("[LambdaControls] " + info);
}
/**
* Prints a warning to the terminal.
*
* @param warning The warning to print.
*/
public void warn(String warning)
{
this.logger.info("[LambdaControls] " + warning);
}
/**
* Gets the LambdaControls instance.
*
* @return The LambdaControls instance.
*/
public static LambdaControls get()
{
return INSTANCE;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client;
/**
* Represents a button state.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public enum ButtonState
{
NONE(0),
PRESS(1),
RELEASE(2),
REPEAT(3);
public final int id;
ButtonState(int id)
{
this.id = id;
}
/**
* Returns whether this state is a pressed state.
*
* @return True if this state is a pressed state, else false.
*/
public boolean is_pressed()
{
return this == PRESS || this == REPEAT;
}
/**
* Returns whether this state is an unpressed state.
*
* @return True if this state is an unpressed state, else false.
*/
public boolean is_unpressed()
{
return this == RELEASE || this == NONE;
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Optional;
/**
* Represents a controller type.
*
* @author LambdAurora
* @version 1.0.0
* @since 1.0.0
*/
public enum ControllerType implements Nameable
{
DEFAULT(0),
DUALSHOCK(1),
SWITCH(2),
XBOX(3),
STEAM(4),
OUYA(5);
private final int id;
ControllerType(int id)
{
this.id = id;
}
/**
* Returns the controller type's identifier.
*
* @return The controller type's identifier.
*/
public int get_id()
{
return this.id;
}
/**
* Returns the next controller type available.
*
* @return The next available controller type.
*/
public ControllerType next()
{
ControllerType[] v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
/**
* Gets the translated name of this controller type.
*
* @return The translated name of this controller type.
*/
public String get_translated_name()
{
return I18n.translate("lambdacontrols.controller_type." + this.get_name());
}
@Override
public @NotNull String get_name()
{
return this.name().toLowerCase();
}
/**
* Gets the controller type from its identifier.
*
* @param id The identifier of the controller type.
* @return The controller type if found, else empty.
*/
public static Optional<ControllerType> by_id(@NotNull String id)
{
return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Optional;
/**
* Represents the hud side which is the side where the movements buttons are.
*
* @author LambdAurora
* @version 1.0.0
* @since 1.0.0
*/
public enum HudSide implements Nameable
{
LEFT,
RIGHT;
/**
* Returns the next side available.
*
* @return The next available side.
*/
public HudSide next()
{
HudSide[] v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
/**
* Gets the translated name of this hud side.
*
* @return The translated name of this hud side.
*/
public String get_translated_name()
{
return I18n.translate("lambdacontrols.hud_side." + this.get_name());
}
@Override
public @NotNull String get_name()
{
return this.name().toLowerCase();
}
/**
* Gets the hud side from its identifier.
*
* @param id The identifier of the hud side.
* @return The hud side if found, else empty.
*/
public static Optional<HudSide> by_id(@NotNull String id)
{
return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client;
import com.mojang.blaze3d.platform.GlStateManager;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.LambdaControls;
import me.lambdaurora.lambdacontrols.LambdaControlsConstants;
import me.lambdaurora.lambdacontrols.client.compat.LambdaControlsCompat;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.client.controller.Controller;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsHud;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.client.util.InputUtil;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import org.aperlambda.lambdacommon.utils.Pair;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
/**
* Represents the LambdaControls client mod.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class LambdaControlsClient extends LambdaControls implements ClientModInitializer
{
private static LambdaControlsClient INSTANCE;
public static final FabricKeyBinding BINDING_LOOK_UP = FabricKeyBinding.Builder.create(new Identifier(LambdaControlsConstants.NAMESPACE, "look_up"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement").build();
public static final FabricKeyBinding BINDING_LOOK_RIGHT = FabricKeyBinding.Builder.create(new Identifier(LambdaControlsConstants.NAMESPACE, "look_right"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement").build();
public static final FabricKeyBinding BINDING_LOOK_DOWN = FabricKeyBinding.Builder.create(new Identifier(LambdaControlsConstants.NAMESPACE, "look_down"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement").build();
public static final FabricKeyBinding BINDING_LOOK_LEFT = FabricKeyBinding.Builder.create(new Identifier(LambdaControlsConstants.NAMESPACE, "look_left"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement").build();
public static final Identifier CONTROLLER_BUTTONS = new Identifier(LambdaControlsConstants.NAMESPACE, "textures/gui/controller_buttons.png");
public static final Identifier CONTROLLER_AXIS = new Identifier(LambdaControlsConstants.NAMESPACE, "textures/gui/controller_axis.png");
public final LambdaControlsConfig config = new LambdaControlsConfig(this);
public final LambdaInput input = new LambdaInput(this);
private LambdaControlsHud hud;
private ControlsMode previous_controls_mode;
@Override
public void onInitializeClient()
{
INSTANCE = this;
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_UP);
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_RIGHT);
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_DOWN);
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_LEFT);
HudRenderCallback.EVENT.register(delta -> this.hud.render());
}
/**
* This method is called when Minecraft is initializing.
*/
public void on_mc_init(@NotNull MinecraftClient client)
{
ButtonBinding.init(client.options);
this.config.load();
Controller.update_mappings();
GLFW.glfwSetJoystickCallback((jid, event) -> {
if (event == GLFW.GLFW_CONNECTED) {
Controller controller = Controller.by_id(jid);
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, new TranslatableText("lambdacontrols.controller.connected", jid),
new LiteralText(controller.get_name())));
} else if (event == GLFW.GLFW_DISCONNECTED) {
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, new TranslatableText("lambdacontrols.controller.disconnected", jid),
null));
}
this.switch_controls_mode();
});
this.hud = new LambdaControlsHud(client, this);
LambdaControlsCompat.init(this);
}
/**
* This method is called every Minecraft tick.
*
* @param client The client instance.
*/
public void on_tick(@NotNull MinecraftClient client)
{
this.input.on_tick(client);
if (this.config.get_controls_mode() == ControlsMode.CONTROLLER)
this.input.on_controller_tick(client);
}
public void on_render(MinecraftClient client)
{
this.input.on_render(client);
}
/**
* Switches the controls mode if the auto switch is enabled.
*/
public void switch_controls_mode()
{
if (this.config.has_auto_switch_mode()) {
if (this.config.get_controller().is_gamepad()) {
this.previous_controls_mode = this.config.get_controls_mode();
this.config.set_controls_mode(ControlsMode.CONTROLLER);
} else {
if (this.previous_controls_mode == null) {
this.previous_controls_mode = ControlsMode.DEFAULT;
}
this.config.set_controls_mode(this.previous_controls_mode);
}
}
}
/**
* Gets the LambdaControls client instance.
*
* @return The LambdaControls client instance.
*/
public static LambdaControlsClient get()
{
return INSTANCE;
}
public static Pair<Integer, Integer> draw_button(int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client)
{
return draw_button(x, y, button.get_button(), client);
}
public static Pair<Integer, Integer> draw_button(int x, int y, int[] buttons, @NotNull MinecraftClient client)
{
int height = 0;
int length = 0;
int current_x = x;
for (int i = 0; i < buttons.length; i++) {
int btn = buttons[i];
Pair<Integer, Integer> size = draw_button(current_x, y, btn, client);
if (size.get_key() > height)
height = size.get_value();
length += size.get_key();
if (i + 1 < buttons.length) {
length += 2;
current_x = x + length;
}
}
return Pair.of(length, height);
}
public static Pair<Integer, Integer> draw_button(int x, int y, int button, @NotNull MinecraftClient client)
{
boolean second = false;
if (button == -1)
return Pair.of(0, 0);
else if (button >= 500) {
button -= 1000;
second = true;
}
int controller_type = get().config.get_controller_type().get_id();
boolean axis = false;
int button_offset = button * 15;
switch (button) {
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER:
button_offset = 7 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER:
button_offset = 8 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_BACK:
button_offset = 4 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_START:
button_offset = 6 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE:
button_offset = 5 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB:
button_offset = 15 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB:
button_offset = 16 * 15;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 100:
button_offset = 0;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 100:
button_offset = 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 100:
button_offset = 2 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 100:
button_offset = 3 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 200:
button_offset = 4 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 200:
button_offset = 5 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 200:
button_offset = 6 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 200:
button_offset = 7 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 100:
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 200:
button_offset = 9 * 15;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 100:
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 200:
button_offset = 10 * 15;
break;
}
client.getTextureManager().bindTexture(axis ? LambdaControlsClient.CONTROLLER_AXIS : LambdaControlsClient.CONTROLLER_BUTTONS);
GlStateManager.disableDepthTest();
GlStateManager.color4f(1.0F, second ? 0.0F : 1.0F, 1.0F, 1.0F);
DrawableHelper.blit(x, y, (float) button_offset, (float) (controller_type * (axis ? 18 : 15)), axis ? 18 : 15, axis ? 18 : 15, 256, 256);
GlStateManager.enableDepthTest();
return axis ? Pair.of(18, 18) : Pair.of(15, 15);
}
public static int draw_button_tip(int x, int y, @NotNull ButtonBinding button, boolean display, @NotNull MinecraftClient client)
{
return draw_button_tip(x, y, button.get_button(), button.get_translation_key(), display, client);
}
public static int draw_button_tip(int x, int y, int[] button, @NotNull String action, boolean display, @NotNull MinecraftClient client)
{
if (display) {
int button_width = draw_button(x, y, button, client).get_key();
String translated_action = I18n.translate(action);
int text_y = (15 - client.textRenderer.fontHeight) / 2;
client.textRenderer.drawWithShadow(translated_action, (float) (x + button_width + 5), (float) (y + text_y), 14737632);
return get_button_tip_width(translated_action, client.textRenderer);
}
return -10;
}
private static int get_button_tip_width(@NotNull String action, @NotNull TextRenderer text_renderer)
{
return 15 + 5 + text_renderer.getStringWidth(action);
}
}

View File

@@ -0,0 +1,500 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client;
import com.electronwill.nightconfig.core.file.FileConfig;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.client.controller.Controller;
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import java.util.Arrays;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_X;
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y;
/**
* Represents LambdaControls configuration.
*/
public class LambdaControlsConfig
{
private static final ControlsMode DEFAULT_CONTROLS_MODE = ControlsMode.DEFAULT;
private static final boolean DEFAULT_AUTO_SWITCH_MODE = false;
private static final boolean DEFAULT_HUD_ENABLE = true;
private static final HudSide DEFAULT_HUD_SIDE = HudSide.LEFT;
private static final ControllerType DEFAULT_CONTROLLER_TYPE = ControllerType.DEFAULT;
private static final double DEFAULT_DEAD_ZONE = 0.25;
private static final double DEFAULT_ROTATION_SPEED = 40.0;
private static final double DEFAULT_MOUSE_SPEED = 25.0;
private static final Pattern BUTTON_BINDING_PATTERN = Pattern.compile("(-?\\d+)\\+?");
protected final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/config.toml").build();
private final LambdaControlsClient mod;
private ControlsMode controls_mode;
private ControllerType controller_type;
// HUD settings.
private boolean hud_enable;
private HudSide hud_side;
// Controller settings
private double dead_zone;
private double rotation_speed;
private double mouse_speed;
public LambdaControlsConfig(@NotNull LambdaControlsClient mod)
{
this.mod = mod;
}
/**
* Loads the configuration
*/
public void load()
{
this.config.load();
this.check_and_fix();
this.mod.log("Configuration loaded.");
this.controls_mode = ControlsMode.by_id(this.config.getOrElse("controls", DEFAULT_CONTROLS_MODE.get_name())).orElse(DEFAULT_CONTROLS_MODE);
// HUD settings.
this.hud_enable = this.config.getOrElse("hud.enable", DEFAULT_HUD_ENABLE);
this.hud_side = HudSide.by_id(this.config.getOrElse("hud.side", DEFAULT_HUD_SIDE.get_name())).orElse(DEFAULT_HUD_SIDE);
// Controller settings.
this.controller_type = ControllerType.by_id(this.config.getOrElse("controller.type", DEFAULT_CONTROLLER_TYPE.get_name())).orElse(DEFAULT_CONTROLLER_TYPE);
this.dead_zone = this.config.getOrElse("controller.dead_zone", DEFAULT_DEAD_ZONE);
this.rotation_speed = this.config.getOrElse("controller.rotation_speed", DEFAULT_ROTATION_SPEED);
this.mouse_speed = this.config.getOrElse("controller.mouse_speed", DEFAULT_MOUSE_SPEED);
// Controller controls.
InputManager.load_button_bindings(this);
}
/**
* Saves the configuration.
*/
public void save()
{
this.config.set("controller.dead_zone", this.dead_zone);
this.config.set("controller.rotation_speed", this.rotation_speed);
this.config.set("controller.mouse_speed", this.mouse_speed);
this.config.save();
this.mod.log("Configuration saved.");
}
public void check_and_fix()
{
InputManager.stream_bindings().forEach(binding -> {
String path = "controller.controls." + binding.get_name();
Object raw = this.config.getRaw(path);
if (raw instanceof Number) {
this.mod.warn("Invalid data at \"" + path + "\", fixing...");
this.config.set(path, String.valueOf(raw));
}
});
}
/**
* Resets the configuration to default values.
*/
public void reset()
{
this.set_controls_mode(DEFAULT_CONTROLS_MODE);
this.set_auto_switch_mode(DEFAULT_AUTO_SWITCH_MODE);
this.set_hud_enabled(DEFAULT_HUD_ENABLE);
this.set_hud_side(DEFAULT_HUD_SIDE);
this.set_controller_type(DEFAULT_CONTROLLER_TYPE);
this.set_dead_zone(DEFAULT_DEAD_ZONE);
this.set_rotation_speed(DEFAULT_ROTATION_SPEED);
this.set_mouse_speed(DEFAULT_MOUSE_SPEED);
// Collect prevents concurrent modification.
InputManager.stream_bindings().collect(Collectors.toList()).forEach(binding -> this.set_button_binding(binding, binding.get_default_button()));
}
/**
* Gets the controls mode from the configuration.
*
* @return The controls mode.
*/
public @NotNull ControlsMode get_controls_mode()
{
return this.controls_mode;
}
/**
* Sets the controls mode in the configuration.
*
* @param controls_mode The controls mode.
*/
public void set_controls_mode(@NotNull ControlsMode controls_mode)
{
this.controls_mode = controls_mode;
this.config.set("controls", controls_mode.get_name());
}
/**
* Returns whether the auto switch mode is enabled or not.
*
* @return True if the auto switch mode is enabled, else false.
*/
public boolean has_auto_switch_mode()
{
return this.config.getOrElse("auto_switch_mode", DEFAULT_AUTO_SWITCH_MODE);
}
/**
* Sets whether the auto switch mode is enabled or not.
*
* @param auto_switch_mode True if the auto switch mode is enabled, else false.
*/
public void set_auto_switch_mode(boolean auto_switch_mode)
{
this.config.set("auto_switch_mode", auto_switch_mode);
}
/**
* Returns whether the HUD is enabled.
*
* @return True if the HUD is enabled, else false.
*/
public boolean is_hud_enabled()
{
return this.hud_enable;
}
/**
* Sets whether the HUD is enabled.
*
* @param enable True if the HUD is enabled, else false.
*/
public void set_hud_enabled(boolean enable)
{
this.hud_enable = enable;
this.config.set("hud.enable", this.hud_enable);
}
/**
* Gets the HUD side from the configuration.
*
* @return The HUD side.
*/
public @NotNull HudSide get_hud_side()
{
return this.hud_side;
}
/**
* Sets the HUD side in the configuration.
*
* @param hud_side The HUD side.
*/
public void set_hud_side(@NotNull HudSide hud_side)
{
this.hud_side = hud_side;
this.config.set("hud.side", hud_side.get_name());
}
/**
* Gets the used controller.
*
* @return The used controller.
*/
public @NotNull Controller get_controller()
{
Object raw = this.config.getRaw("controller.id");
if (raw instanceof Number) {
return Controller.by_id((Integer) raw);
} else if (raw instanceof String) {
return Controller.by_guid((String) raw).orElse(Controller.by_id(GLFW.GLFW_JOYSTICK_1));
}
return Controller.by_id(GLFW.GLFW_JOYSTICK_1);
}
/**
* Sets the used controller.
*
* @param controller The used controller.
*/
public void set_controller(@NotNull Controller controller)
{
this.config.set("controller.id", controller.get_id());
}
/**
* Gets the second controller (for Joy-Con supports).
*
* @return The second controller.
*/
public @NotNull Optional<Controller> get_second_controller()
{
Object raw = this.config.getRaw("controller.id2");
if (raw instanceof Number) {
if ((int) raw == -1)
return Optional.empty();
return Optional.of(Controller.by_id((Integer) raw));
} else if (raw instanceof String) {
return Optional.of(Controller.by_guid((String) raw).orElse(Controller.by_id(GLFW.GLFW_JOYSTICK_1)));
}
return Optional.empty();
}
/**
* Sets the second controller.
*
* @param controller The second controller.
*/
public void set_second_controller(@Nullable Controller controller)
{
this.config.set("controller.id2", controller == null ? -1 : controller.get_id());
}
/**
* Gets the controller's type.
*
* @return The controller's type.
*/
public @NotNull ControllerType get_controller_type()
{
return this.controller_type;
}
/**
* Sets the controller's type.
*
* @param controller_type The controller's type.
*/
public void set_controller_type(@NotNull ControllerType controller_type)
{
this.controller_type = controller_type;
this.config.set("controller.type", controller_type.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;
}
/**
* Gets the controller's mouse speed.
*
* @return The mouse speed.
*/
public double get_mouse_speed()
{
return this.mouse_speed;
}
/**
* Sets the controller's mouse speed.
*
* @param mouse_speed The mouse speed.
*/
public void set_mouse_speed(double mouse_speed)
{
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;
}
/**
* Loads the button binding from configuration.
*
* @param button The button binding.
*/
public void load_button_binding(@NotNull ButtonBinding button)
{
button.set_button(button.get_default_button());
String button_code = this.config.getOrElse("controller.controls." + button.get_name(), button.get_button_code());
Matcher matcher = BUTTON_BINDING_PATTERN.matcher(button_code);
try {
int[] buttons = new int[1];
int count = 0;
while (matcher.find()) {
count++;
if (count > buttons.length)
buttons = Arrays.copyOf(buttons, count);
String current;
if (!this.check_validity(button, button_code, current = matcher.group(1)))
return;
buttons[count - 1] = Integer.parseInt(current);
}
if (count == 0) {
this.mod.warn("Malformed config value \"" + button_code + "\" for binding \"" + button.get_name() + "\".");
this.set_button_binding(button, new int[]{-1});
}
button.set_button(buttons);
} catch (Exception e) {
this.mod.warn("Malformed config value \"" + button_code + "\" for binding \"" + button.get_name() + "\".");
this.config.set("controller.controls." + button.get_name(), button.get_button_code());
}
}
private boolean check_validity(@NotNull ButtonBinding binding, @NotNull String input, String group)
{
if (group == null) {
this.mod.warn("Malformed config value \"" + input + "\" for binding \"" + binding.get_name() + "\".");
this.config.set("controller.controls." + binding.get_name(), binding.get_button_code());
return false;
}
return true;
}
/**
* Sets the button binding in configuration.
*
* @param binding The button binding.
* @param button The button.
*/
public void set_button_binding(@NotNull ButtonBinding binding, int[] button)
{
binding.set_button(button);
this.config.set("controller.controls." + binding.get_name(), binding.get_button_code());
}
public boolean is_back_button(int btn, boolean is_btn, int state)
{
if (!is_btn && state == 0)
return false;
return ButtonBinding.axis_as_button(GLFW_GAMEPAD_AXIS_LEFT_Y, false) == ButtonBinding.axis_as_button(btn, state == 1);
}
public boolean is_forward_button(int btn, boolean is_btn, int state)
{
if (!is_btn && state == 0)
return false;
return ButtonBinding.axis_as_button(GLFW_GAMEPAD_AXIS_LEFT_Y, true) == ButtonBinding.axis_as_button(btn, state == 1);
}
public boolean is_left_button(int btn, boolean is_btn, int state)
{
if (!is_btn && state == 0)
return false;
return ButtonBinding.axis_as_button(GLFW_GAMEPAD_AXIS_LEFT_X, false) == ButtonBinding.axis_as_button(btn, state == 1);
}
public boolean is_right_button(int btn, boolean is_btn, int state)
{
if (!is_btn && state == 0)
return false;
return ButtonBinding.axis_as_button(GLFW_GAMEPAD_AXIS_LEFT_X, true) == ButtonBinding.axis_as_button(btn, state == 1);
}
/**
* Returns whether the specified axis is an axis used for movements.
*
* @param axis The axis index.
* @return True if the axis is used for movements, else false.
*/
public boolean is_movement_axis(int axis)
{
return axis == GLFW_GAMEPAD_AXIS_LEFT_Y || axis == GLFW_GAMEPAD_AXIS_LEFT_X;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client;
import io.github.prospector.modmenu.api.ModMenuApi;
import me.lambdaurora.lambdacontrols.LambdaControlsConstants;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsSettingsScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import java.util.function.Function;
/**
* Represents the API implementation of ModMenu for LambdaControls.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class LambdaControlsModMenu implements ModMenuApi
{
@Override
public String getModId()
{
return LambdaControlsConstants.NAMESPACE;
}
@Override
public Function<Screen, ? extends Screen> getConfigScreenFactory()
{
return screen -> new LambdaControlsSettingsScreen(screen, MinecraftClient.getInstance().options, false);
}
}

View File

@@ -0,0 +1,649 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.client.controller.Controller;
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsControlsScreen;
import me.lambdaurora.lambdacontrols.client.gui.TouchscreenOverlay;
import me.lambdaurora.lambdacontrols.client.mixin.EntryListWidgetAccessor;
import me.lambdaurora.lambdacontrols.client.util.AbstractContainerScreenAccessor;
import me.lambdaurora.lambdacontrols.client.util.MouseAccessor;
import me.lambdaurora.spruceui.SpruceLabelWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.ParentElement;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
import net.minecraft.client.gui.screen.world.WorldListWidget;
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget;
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget;
import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.container.Slot;
import net.minecraft.container.SlotActionType;
import net.minecraft.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 org.lwjgl.glfw.GLFWGamepadState;
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 me.lambdaurora.lambdacontrols.client.controller.ButtonBinding.axis_as_button;
import static org.lwjgl.glfw.GLFW.*;
/**
* Represents the LambdaControls' input handler.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.0.0
*/
public class LambdaInput
{
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
private final LambdaControlsConfig config;
// Cooldowns
private int action_gui_cooldown = 0;
private int ignore_next_a = 0;
// Sneak state.
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 LambdaInput(@NotNull LambdaControlsClient mod)
{
this.config = mod.config;
}
/**
* This method is called every Minecraft tick.
*
* @param client The client instance.
*/
public void on_tick(@NotNull MinecraftClient client)
{
this.prev_target_yaw = this.target_yaw;
this.prev_target_pitch = this.target_pitch;
// Handles the key bindings.
if (LambdaControlsClient.BINDING_LOOK_UP.isPressed()) {
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 2);
} else if (LambdaControlsClient.BINDING_LOOK_DOWN.isPressed()) {
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 1);
}
if (LambdaControlsClient.BINDING_LOOK_LEFT.isPressed()) {
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 2);
} else if (LambdaControlsClient.BINDING_LOOK_RIGHT.isPressed()) {
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 1);
}
}
/**
* This method is called every Minecraft tick for controller input update.
*
* @param client The client instance.
*/
public void on_controller_tick(@NotNull MinecraftClient client)
{
BUTTON_COOLDOWNS.entrySet().stream().filter(entry -> entry.getValue() > 0).forEach(entry -> BUTTON_COOLDOWNS.put(entry.getKey(), entry.getValue() - 1));
// Decreases the cooldown for GUI actions.
if (this.action_gui_cooldown > 0)
--this.action_gui_cooldown;
this.prev_target_mouse_x = this.target_mouse_x;
this.prev_target_mouse_y = this.target_mouse_y;
InputManager.update_states();
Controller controller = this.config.get_controller();
if (controller.is_connected()) {
GLFWGamepadState state = controller.get_state();
this.fetch_button_input(client, state, false);
this.fetch_axe_input(client, state, false);
}
this.config.get_second_controller().filter(Controller::is_connected)
.ifPresent(joycon -> {
GLFWGamepadState state = joycon.get_state();
this.fetch_button_input(client, state, true);
this.fetch_axe_input(client, state, true);
});
boolean allow_input = true;
if (client.currentScreen instanceof LambdaControlsControlsScreen && ((LambdaControlsControlsScreen) client.currentScreen).focused_binding != null)
allow_input = false;
if (allow_input)
InputManager.update_bindings(client);
if (this.ignore_next_a > 0)
this.ignore_next_a--;
if (client.currentScreen instanceof LambdaControlsControlsScreen && InputManager.STATES.entrySet().parallelStream().map(Map.Entry::getValue).allMatch(ButtonState::is_unpressed)) {
LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
if (controls_screen.focused_binding != null && !controls_screen.waiting) {
int[] buttons = new int[controls_screen.current_buttons.size()];
for (int i = 0; i < controls_screen.current_buttons.size(); i++)
buttons[i] = controls_screen.current_buttons.get(i);
controls_screen.focused_binding.set_button(buttons);
controls_screen.focused_binding = null;
}
}
}
/**
* This method is called before the screen is rendered.
*
* @param client The client instance.
* @param screen The screen to render.
*/
public void on_pre_render_screen(@NotNull MinecraftClient client, @NotNull Screen screen)
{
if (!is_screen_interactive(screen)) {
if (this.prev_target_mouse_x != this.target_mouse_x || this.prev_target_mouse_y != this.target_mouse_y) {
double mouse_x = this.prev_target_mouse_x + (this.target_mouse_x - this.prev_target_mouse_x) * client.getTickDelta() + 0.5;
double mouse_y = this.prev_target_mouse_y + (this.target_mouse_y - this.prev_target_mouse_y) * client.getTickDelta() + 0.5;
GLFW.glfwSetCursorPos(client.getWindow().getHandle(), mouse_x, mouse_y);
((MouseAccessor) client.mouse).on_cursor_pos(client.getWindow().getHandle(), mouse_x, mouse_y);
}
}
}
/**
* This method is called when Minecraft renders.
*
* @param client The client instance.
*/
public void on_render(@NotNull MinecraftClient client)
{
if ((client.currentScreen == null || client.currentScreen instanceof TouchscreenOverlay) &&
(this.prev_target_yaw != this.target_yaw || this.prev_target_pitch != this.target_pitch)) {
float delta_yaw = (float) ((this.target_yaw - client.player.prevYaw) * client.getTickDelta());
float delta_pitch = (float) ((this.target_pitch - client.player.prevPitch) * client.getTickDelta());
float rotation_yaw = client.player.prevYaw + delta_yaw;
float rotation_pitch = client.player.prevPitch + delta_pitch;
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);
}
client.getTutorialManager().onUpdateMouse(delta_pitch, delta_yaw);
}
}
/**
* This method is called when a Screen is opened.
*
* @param client The client instance.
* @param window_width The window width.
* @param window_height The window height.
*/
public void on_screen_open(@NotNull MinecraftClient client, int window_width, int window_height)
{
if (client.currentScreen == null) {
this.mouse_speed_x = this.mouse_speed_y = 0.0F;
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, @NotNull GLFWGamepadState gamepad_state, boolean left_joycon)
{
ByteBuffer buffer = gamepad_state.buttons();
for (int i = 0; i < buffer.limit(); i++) {
int btn = left_joycon ? ButtonBinding.controller2_button(i) : i;
boolean btn_state = buffer.get() == (byte) 1;
ButtonState current_state = ButtonState.NONE;
ButtonState previous_state = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
if (btn_state != previous_state.is_pressed()) {
current_state = btn_state ? ButtonState.PRESS : ButtonState.RELEASE;
this.handle_button(client, btn, btn_state ? 0 : 1, btn_state);
if (btn_state)
BUTTON_COOLDOWNS.put(btn, 5);
} else if (btn_state) {
current_state = ButtonState.REPEAT;
if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) {
BUTTON_COOLDOWNS.put(btn, 5);
this.handle_button(client, btn, 2, true);
}
}
InputManager.STATES.put(btn, current_state);
}
}
private void fetch_axe_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state, boolean left_joycon)
{
FloatBuffer buffer = gamepad_state.axes();
for (int i = 0; i < buffer.limit(); i++) {
int axis = left_joycon ? ButtonBinding.controller2_button(i) : i;
float value = buffer.get();
float abs_value = Math.abs(value);
if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y)
value *= -1.0F;
int state = value > this.config.get_dead_zone() ? 1 : (value < -this.config.get_dead_zone() ? 2 : 0);
this.handle_axe(client, axis, value, abs_value, state);
}
}
private void handle_button(@NotNull MinecraftClient client, int button, int action, boolean state)
{
if (client.currentScreen instanceof LambdaControlsControlsScreen) {
LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
if (controls_screen.focused_binding != null) {
if (action == 0 && !controls_screen.current_buttons.contains(button)) {
controls_screen.current_buttons.add(button);
int[] buttons = new int[controls_screen.current_buttons.size()];
for (int i = 0; i < controls_screen.current_buttons.size(); i++)
buttons[i] = controls_screen.current_buttons.get(i);
controls_screen.focused_binding.set_button(buttons);
controls_screen.waiting = false;
}
return;
}
}
if (action == 0 || action == 2) {
if (client.currentScreen != null && is_screen_interactive(client.currentScreen)
&& (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN
|| button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)) {
if (this.action_gui_cooldown == 0) {
if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP) {
this.change_focus(client.currentScreen, false);
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN) {
this.change_focus(client.currentScreen, true);
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT) {
this.handle_left_right(client.currentScreen, false);
} else {
this.handle_left_right(client.currentScreen, true);
}
}
return;
}
}
if (action == 1) {
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null) {
if (this.action_gui_cooldown == 0) {
Element focused = client.currentScreen.getFocused();
if (focused != null && is_screen_interactive(client.currentScreen)) {
if (this.handle_a_button(client.currentScreen, focused)) {
this.action_gui_cooldown = 5; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
return;
}
}
}
}
if (client.currentScreen instanceof AbstractContainerScreen && client.interactionManager != null && client.player != null) {
double pos_x = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double pos_y = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
Slot slot = ((AbstractContainerScreenAccessor) client.currentScreen).get_slot_at(pos_x, pos_y);
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && slot != null) {
client.interactionManager.clickSlot(((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();
return;
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_X && slot != null) {
client.interactionManager.clickSlot(((AbstractContainerScreen) client.currentScreen).getContainer().syncId, slot.id, GLFW.GLFW_MOUSE_BUTTON_2, SlotActionType.PICKUP, client.player);
return;
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_Y && slot != null) {
client.interactionManager.clickSlot(((AbstractContainerScreen) client.currentScreen).getContainer().syncId, slot.id, GLFW.GLFW_MOUSE_BUTTON_1, SlotActionType.QUICK_MOVE, client.player);
return;
}
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) {
if (client.currentScreen != null) {
client.currentScreen.onClose();
return;
}
}
}
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null && !is_screen_interactive(client.currentScreen) && this.action_gui_cooldown == 0 && this.ignore_next_a == 0) {
double mouse_x = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double mouse_y = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
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;
}
}
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);
if (axis == GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER || axis == ButtonBinding.controller2_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER) ||
axis == ButtonBinding.controller2_button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER))
if (as_button_state == 2)
as_button_state = 0;
{
boolean current_plus_state = as_button_state == 1;
boolean current_minus_state = as_button_state == 2;
ButtonState previous_plus_state = InputManager.STATES.getOrDefault(axis_as_button(axis, true), ButtonState.NONE);
ButtonState previous_minus_state = InputManager.STATES.getOrDefault(axis_as_button(axis, false), ButtonState.NONE);
if (current_plus_state != previous_plus_state.is_pressed()) {
InputManager.STATES.put(axis_as_button(axis, true), current_plus_state ? ButtonState.PRESS : ButtonState.RELEASE);
if (current_plus_state)
BUTTON_COOLDOWNS.put(axis_as_button(axis, true), 5);
} else if (current_plus_state) {
InputManager.STATES.put(axis_as_button(axis, true), ButtonState.REPEAT);
if (BUTTON_COOLDOWNS.getOrDefault(axis_as_button(axis, true), 0) == 0) {
BUTTON_COOLDOWNS.put(axis_as_button(axis, true), 5);
}
}
if (current_minus_state != previous_minus_state.is_pressed()) {
InputManager.STATES.put(axis_as_button(axis, false), current_minus_state ? ButtonState.PRESS : ButtonState.RELEASE);
if (current_minus_state)
BUTTON_COOLDOWNS.put(axis_as_button(axis, false), 5);
} else if (current_minus_state) {
InputManager.STATES.put(axis_as_button(axis, false), ButtonState.REPEAT);
if (BUTTON_COOLDOWNS.getOrDefault(axis_as_button(axis, false), 0) == 0) {
BUTTON_COOLDOWNS.put(axis_as_button(axis, false), 5);
}
}
}
if (client.currentScreen instanceof LambdaControlsControlsScreen) {
LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
if (controls_screen.focused_binding != null) {
if (as_button_state != 0 && !controls_screen.current_buttons.contains(axis_as_button(axis, as_button_state == 1))) {
controls_screen.current_buttons.add(axis_as_button(axis, as_button_state == 1));
int[] buttons = new int[controls_screen.current_buttons.size()];
for (int i = 0; i < controls_screen.current_buttons.size(); i++)
buttons[i] = controls_screen.current_buttons.get(i);
controls_screen.focused_binding.set_button(buttons);
controls_screen.waiting = false;
}
return;
}
}
double dead_zone = this.config.get_dead_zone();
if (client.currentScreen == null) {
// Handles the look direction.
this.handle_look(client, axis, (float) (abs_value / (1.0 - this.config.get_dead_zone())), state);
} else {
boolean allow_mouse_control = true;
if (this.action_gui_cooldown == 0 && this.config.is_movement_axis(axis) && 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(axis, false, as_button_state)) {
allow_mouse_control = this.change_focus(client.currentScreen, true);
} 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(axis, 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(axis, false, (value > 0 ? 1 : 2))) {
movement_y = abs_value;
} else if (this.config.is_forward_button(axis, false, (value > 0 ? 1 : 2))) {
movement_y = -abs_value;
} else if (this.config.is_left_button(axis, false, (value > 0 ? 1 : 2))) {
movement_x = -abs_value;
} else if (this.config.is_right_button(axis, 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();
this.prev_target_mouse_x = this.target_mouse_x = (int) mouse_x;
this.prev_target_mouse_y = this.target_mouse_y = (int) mouse_y;
}
if (Math.abs(movement_x) >= dead_zone)
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_mouse_speed();
this.target_mouse_x = MathHelper.clamp(this.target_mouse_x, 0, client.getWindow().getWidth());
this.target_mouse_y += this.mouse_speed_y * this.config.get_mouse_speed();
this.target_mouse_y = MathHelper.clamp(this.target_mouse_y, 0, client.getWindow().getHeight());
}
this.move_mouse_to_closest_slot(client, client.currentScreen);
}
this.prev_x_axis = movement_x;
this.prev_y_axis = movement_y;
}
}
private boolean handle_a_button(@NotNull Screen screen, @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 SpruceLabelWidget) {
((SpruceLabelWidget) focused).on_press();
return true;
} else if (focused instanceof WorldListWidget) {
WorldListWidget list = (WorldListWidget) focused;
list.method_20159().ifPresent(WorldListWidget.Entry::play);
return true;
} else if (focused instanceof MultiplayerServerListWidget) {
MultiplayerServerListWidget list = (MultiplayerServerListWidget) focused;
MultiplayerServerListWidget.Entry entry = list.getSelected();
if (entry instanceof MultiplayerServerListWidget.LanServerEntry || entry instanceof MultiplayerServerListWidget.ServerEntry) {
((MultiplayerScreen) screen).select(entry);
((MultiplayerScreen) screen).connect();
}
} else if (focused instanceof ParentElement) {
Element child_focused = ((ParentElement) focused).getFocused();
if (child_focused != null)
return this.handle_a_button(screen, child_focused);
}
return false;
}
/**
* 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)
if (this.handle_right_left_element(focused, right))
return this.change_focus(screen, 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 AlwaysSelectedEntryListWidget) {
((EntryListWidgetAccessor) element).move_selection(right ? 1 : -1);
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;
}
/**
* Handles the look direction input.
*
* @param client The client isntance.
* @param axis The axis to change.
* @param value The value of the look.
* @param state The state.
*/
public void handle_look(@NotNull MinecraftClient client, int axis, float value, int state)
{
// Handles the look direction.
if (client.player != null) {
double pow_value = Math.pow(value, 4.0);
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
if (state == 2) {
this.target_pitch = client.player.pitch - this.config.get_right_y_axis_sign() * (this.config.get_rotation_speed() * pow_value) * 0.33D;
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() * pow_value) * 0.33D;
this.target_pitch = MathHelper.clamp(this.target_pitch, -90.0D, 90.0D);
}
}
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() * pow_value) * 0.33D;
} else if (state == 1) {
this.target_yaw = client.player.yaw + this.config.get_right_x_axis_sign() * (this.config.get_rotation_speed() * pow_value) * 0.33D;
}
}
}
}
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;
}
}
private static boolean is_screen_interactive(@NotNull Screen screen)
{
return !(screen instanceof AdvancementsScreen || screen instanceof AbstractContainerScreen);
}
// 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_x();
int gui_top = accessor.get_y();
int mouse_x = (int) (target_mouse_x * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
int mouse_y = (int) (target_mouse_y * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().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.getWindow().getScaledWidth() / (double) client.getWindow().getWidth()));
int slot_center_y = (int) (slot_center_y_scaled / ((double) client.getWindow().getScaledHeight() / (double) client.getWindow().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 {
this.mouse_speed_x *= 0.3F;
this.mouse_speed_y *= 0.3F;
}
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 *= .3F;
this.mouse_speed_y *= .3F;
}
} else {
this.mouse_speed_x = 0.F;
this.mouse_speed_y = 0.F;
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.compat;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import org.jetbrains.annotations.NotNull;
/**
* Represents a compatibility handler for a mod.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public interface CompatHandler
{
/**
* Handles compatibility of a mod.
*
* @param mod This mod instance.
*/
void handle(@NotNull LambdaControlsClient mod);
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.compat;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import net.fabricmc.loader.api.FabricLoader;
import org.aperlambda.lambdacommon.utils.LambdaReflection;
import org.jetbrains.annotations.NotNull;
/**
* Represents a compatibility handler.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class LambdaControlsCompat
{
/**
* Initializes compatibility with other mods if needed.
*
* @param mod The mod instance.
*/
public static void init(@NotNull LambdaControlsClient mod)
{
if (FabricLoader.getInstance().isModLoaded("okzoomer") && LambdaReflection.does_class_exist(OkZoomerCompat.OKZOOMER_CLASS_PATH)) {
mod.log("Adding okzoomer compatibility...");
new OkZoomerCompat().handle(mod);
}
InputManager.load_button_bindings(mod.config);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.compat;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
import org.aperlambda.lambdacommon.utils.LambdaReflection;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
/**
* Represents a compatibility handler for OkZoomer.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class OkZoomerCompat implements CompatHandler
{
public static final String OKZOOMER_CLASS_PATH = "io.github.joaoh1.okzoomer.OkZoomer";
@Override
public void handle(@NotNull LambdaControlsClient mod)
{
LambdaReflection.get_first_field_of_type(io.github.joaoh1.okzoomer.OkZoomer.class, FabricKeyBinding.class)
.map(field -> (FabricKeyBinding) LambdaReflection.get_field_value(null, field))
.ifPresent(zoom_key_binding -> {
ButtonBinding binding = InputManager.register_binding(new ButtonBinding("zoom", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_X}, true));
binding.set_key_binding(zoom_key_binding);
ButtonBinding.MISC_CATEGORY.register_binding(binding);
});
}
}

View File

@@ -0,0 +1,383 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.controller;
import me.lambdaurora.lambdacontrols.client.ButtonState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import java.util.*;
import java.util.stream.Collectors;
import static me.lambdaurora.lambdacontrols.client.controller.InputManager.register_binding;
import static me.lambdaurora.lambdacontrols.client.controller.InputManager.register_default_category;
/**
* Represents a button binding.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.0.0
*/
public class ButtonBinding implements Nameable
{
public static final ButtonCategory MOVEMENT_CATEGORY;
public static final ButtonCategory GAMEPLAY_CATEGORY;
public static final ButtonCategory INVENTORY_CATEGORY;
public static final ButtonCategory MULTIPLAYER_CATEGORY;
public static final ButtonCategory MISC_CATEGORY;
public static final ButtonBinding ATTACK = register_binding(new ButtonBinding("attack", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true)}, false));
public static final ButtonBinding BACK = register_binding(new ButtonBinding("back", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, false)}, false));
public static final ButtonBinding CHAT = register_binding(new ButtonBinding("chat", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT}, true));
public static final ButtonBinding DROP_ITEM = register_binding(new ButtonBinding("drop_item", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_B}, true));
public static final ButtonBinding FORWARD = register_binding(new ButtonBinding("forward", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, true)}, false));
public static final ButtonBinding HOTBAR_LEFT = register_binding(new ButtonBinding("hotbar_left", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER},
Collections.singletonList(InputHandlers.handle_hotbar(false)), true));
public static final ButtonBinding HOTBAR_RIGHT = register_binding(new ButtonBinding("hotbar_right", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER},
Collections.singletonList(InputHandlers.handle_hotbar(true)), true));
public static final ButtonBinding INVENTORY = register_binding(new ButtonBinding("inventory", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_Y}, true));
public static final ButtonBinding JUMP = register_binding(new ButtonBinding("jump", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_A}, false));
public static final ButtonBinding LEFT = register_binding(new ButtonBinding("left", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, false)}, false));
public static final ButtonBinding PAUSE_GAME = register_binding(new ButtonBinding("pause_game", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_START},
Collections.singletonList(InputHandlers::handle_pause_game), true));
public static final ButtonBinding PICK_BLOCK = register_binding(new ButtonBinding("pick_block", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT}, true));
public static final ButtonBinding PLAYER_LIST = register_binding(new ButtonBinding("player_list", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_BACK}, false));
public static final ButtonBinding RIGHT = register_binding(new ButtonBinding("right", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, true)}, false));
public static final ButtonBinding SCREENSHOT = register_binding(new ButtonBinding("screenshot", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_A},
Collections.singletonList(InputHandlers::handle_screenshot), true));
public static final ButtonBinding SMOOTH_CAMERA = register_binding(new ButtonBinding("toggle_smooth_camera", new int[]{-1}, true));
public static final ButtonBinding SNEAK = register_binding(new ButtonBinding("sneak", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB},
Arrays.asList(PressAction.DEFAULT_ACTION, InputHandlers::handle_toggle_sneak), true));
public static final ButtonBinding SPRINT = register_binding(new ButtonBinding("sprint", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB}, false));
public static final ButtonBinding SWAP_HANDS = register_binding(new ButtonBinding("swap_hands", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_X}, true));
public static final ButtonBinding TOGGLE_PERSPECTIVE = register_binding(new ButtonBinding("toggle_perspective", new int[]{GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_Y}, true));
public static final ButtonBinding USE = register_binding(new ButtonBinding("use", new int[]{axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true)}, false));
private int[] button;
private int[] default_button;
private String key;
private KeyBinding minecraft_key_binding = null;
private List<PressAction> actions = new ArrayList<>(Collections.singletonList(PressAction.DEFAULT_ACTION));
private boolean has_cooldown;
private int cooldown = 0;
boolean pressed = false;
public ButtonBinding(@NotNull String key, int[] default_button, @NotNull List<PressAction> actions, boolean has_cooldown)
{
this.set_button(this.default_button = default_button);
this.key = key;
this.actions.addAll(actions);
this.has_cooldown = has_cooldown;
}
public ButtonBinding(@NotNull String key, int[] default_button, boolean has_cooldown)
{
this(key, default_button, Collections.emptyList(), has_cooldown);
}
/**
* Returns the button bound.
*
* @return The bound button.
*/
public int[] get_button()
{
return this.button;
}
/**
* Sets the bound button.
*
* @param button The bound button.
*/
public void set_button(int[] button)
{
this.button = button;
if (InputManager.has_binding(this))
InputManager.sort_bindings();
}
/**
* Returns whether the bound button is the specified button or not.
*
* @param button The button to check.
* @return True if the bound button is the specified button, else false.
*/
public boolean is_button(int[] button)
{
return InputManager.are_buttons_equivalent(button, this.button);
}
/**
* Returns whether this button is down or not.
*
* @return True if the button is down, else false.
*/
public boolean is_button_down()
{
return this.pressed;
}
/**
* Returns whether this button binding is bound or not.
*
* @return True if this button binding is bound, else false.
*/
public boolean is_not_bound()
{
return this.button.length == 0 || this.button[0] == -1;
}
/**
* Gets the default button assigned to this binding.
*
* @return The default button.
*/
public int[] get_default_button()
{
return this.default_button;
}
/**
* Returns whether the assigned button is the default button.
*
* @return True if the assigned button is the default button, else false.
*/
public boolean is_default()
{
return this.button.length == this.default_button.length && InputManager.are_buttons_equivalent(this.button, this.default_button);
}
/**
* Returns the button code.
*
* @return The button code.
*/
public @NotNull String get_button_code()
{
return Arrays.stream(this.button)
.mapToObj(btn -> Integer.valueOf(btn).toString())
.collect(Collectors.joining("+"));
}
/**
* Sets the key binding to emulate with this button binding.
*
* @param key_binding The optional key binding.
*/
public void set_key_binding(@Nullable KeyBinding key_binding)
{
this.minecraft_key_binding = key_binding;
}
/**
* Updates the button binding cooldown.
*/
public void update()
{
if (this.has_cooldown && this.cooldown > 0)
this.cooldown--;
}
/**
* Handles the button binding.
*
* @param client The client instance.
* @param state The state.
*/
public void handle(@NotNull MinecraftClient client, @NotNull ButtonState state)
{
if (state == ButtonState.REPEAT && this.has_cooldown && this.cooldown != 0)
return;
if (this.has_cooldown && state.is_pressed()) {
this.cooldown = 5;
}
for (int i = this.actions.size() - 1; i >= 0; i--) {
if (this.actions.get(i).press(client, this, state))
break;
}
}
@Override
public @NotNull String get_name()
{
return this.key;
}
/**
* Returns the translation key of this button binding.
*
* @return The translation key.
*/
public @NotNull String get_translation_key()
{
return "lambdacontrols.action." + this.get_name();
}
/**
* Returns the key binding equivalent of this button binding.
*
* @return The key binding equivalent.
*/
public @NotNull Optional<KeyBinding> as_key_binding()
{
return Optional.ofNullable(this.minecraft_key_binding);
}
/**
* Returns the specified axis as a button.
*
* @param axis The axis.
* @param positive True if the axis part is positive, else false.
* @return The axis as a button.
*/
public static int axis_as_button(int axis, boolean positive)
{
return positive ? 100 + axis : 200 + axis;
}
/**
* Returns the second Joycon's specified button code.
*
* @param button The raw button code.
* @return The second Joycon's button code.
*/
public static int controller2_button(int button)
{
return 500 + button;
}
public static void init(@NotNull GameOptions options)
{
ATTACK.minecraft_key_binding = options.keyAttack;
BACK.minecraft_key_binding = options.keyBack;
CHAT.minecraft_key_binding = options.keyChat;
DROP_ITEM.minecraft_key_binding = options.keyDrop;
FORWARD.minecraft_key_binding = options.keyForward;
INVENTORY.minecraft_key_binding = options.keyInventory;
JUMP.minecraft_key_binding = options.keyJump;
LEFT.minecraft_key_binding = options.keyLeft;
PICK_BLOCK.minecraft_key_binding = options.keyPickItem;
PLAYER_LIST.minecraft_key_binding = options.keyPlayerList;
RIGHT.minecraft_key_binding = options.keyRight;
SCREENSHOT.minecraft_key_binding = options.keyScreenshot;
SMOOTH_CAMERA.minecraft_key_binding = options.keySmoothCamera;
SNEAK.minecraft_key_binding = options.keySneak;
SPRINT.minecraft_key_binding = options.keySprint;
SWAP_HANDS.minecraft_key_binding = options.keySwapHands;
TOGGLE_PERSPECTIVE.minecraft_key_binding = options.keyTogglePerspective;
USE.minecraft_key_binding = options.keyUse;
}
/**
* Returns the localized name of the specified button.
*
* @param button The button.
* @return The localized name of the button.
*/
public static @NotNull String get_localized_button_name(int button)
{
switch (button % 500) {
case -1:
return I18n.translate("key.keyboard.unknown");
case GLFW.GLFW_GAMEPAD_BUTTON_A:
return I18n.translate("lambdacontrols.button.a");
case GLFW.GLFW_GAMEPAD_BUTTON_B:
return I18n.translate("lambdacontrols.button.b");
case GLFW.GLFW_GAMEPAD_BUTTON_X:
return I18n.translate("lambdacontrols.button.x");
case GLFW.GLFW_GAMEPAD_BUTTON_Y:
return I18n.translate("lambdacontrols.button.y");
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER:
return I18n.translate("lambdacontrols.button.left_bumper");
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER:
return I18n.translate("lambdacontrols.button.right_bumper");
case GLFW.GLFW_GAMEPAD_BUTTON_BACK:
return I18n.translate("lambdacontrols.button.back");
case GLFW.GLFW_GAMEPAD_BUTTON_START:
return I18n.translate("lambdacontrols.button.start");
case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE:
return I18n.translate("lambdacontrols.button.guide");
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB:
return I18n.translate("lambdacontrols.button.left_thumb");
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB:
return I18n.translate("lambdacontrols.button.right_thumb");
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP:
return I18n.translate("lambdacontrols.button.dpad_up");
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT:
return I18n.translate("lambdacontrols.button.dpad_right");
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN:
return I18n.translate("lambdacontrols.button.dpad_down");
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT:
return I18n.translate("lambdacontrols.button.dpad_left");
case 100:
return I18n.translate("lambdacontrols.axis.left_x+");
case 101:
return I18n.translate("lambdacontrols.axis.left_y+");
case 102:
return I18n.translate("lambdacontrols.axis.right_x+");
case 103:
return I18n.translate("lambdacontrols.axis.right_y+");
case 104:
return I18n.translate("lambdacontrols.axis.left_trigger");
case 105:
return I18n.translate("lambdacontrols.axis.right_trigger");
case 200:
return I18n.translate("lambdacontrols.axis.left_x-");
case 201:
return I18n.translate("lambdacontrols.axis.left_y-");
case 202:
return I18n.translate("lambdacontrols.axis.right_x-");
case 203:
return I18n.translate("lambdacontrols.axis.right_y-");
default:
return I18n.translate("lambdacontrols.button.unknown", button);
}
}
static {
MOVEMENT_CATEGORY = register_default_category("key.categories.movement", category -> category.register_all_bindings(
ButtonBinding.FORWARD,
ButtonBinding.BACK,
ButtonBinding.LEFT,
ButtonBinding.RIGHT,
ButtonBinding.JUMP,
ButtonBinding.SNEAK,
ButtonBinding.SPRINT));
GAMEPLAY_CATEGORY = register_default_category("key.categories.gameplay", category -> category.register_all_bindings(
ButtonBinding.ATTACK,
ButtonBinding.PICK_BLOCK,
ButtonBinding.USE
));
INVENTORY_CATEGORY = register_default_category("key.categories.inventory", category -> category.register_all_bindings(
ButtonBinding.DROP_ITEM,
ButtonBinding.HOTBAR_LEFT,
ButtonBinding.HOTBAR_RIGHT,
ButtonBinding.INVENTORY,
ButtonBinding.SWAP_HANDS
));
MULTIPLAYER_CATEGORY = register_default_category("key.categories.multiplayer",
category -> category.register_all_bindings(ButtonBinding.CHAT, ButtonBinding.PLAYER_LIST));
MISC_CATEGORY = register_default_category("key.categories.misc", category -> category.register_all_bindings(
ButtonBinding.SCREENSHOT,
//SMOOTH_CAMERA,
ButtonBinding.TOGGLE_PERSPECTIVE
));
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.controller;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.Identifier;
import org.aperlambda.lambdacommon.utils.Identifiable;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Represents a button binding category
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class ButtonCategory implements Identifiable
{
private final List<ButtonBinding> bindings = new ArrayList<>();
private final Identifier id;
private int priority;
public ButtonCategory(@NotNull Identifier id, int priority)
{
this.id = id;
this.priority = priority;
}
public ButtonCategory(@NotNull Identifier id)
{
this(id, 100);
}
public void register_binding(@NotNull ButtonBinding binding)
{
if (this.bindings.contains(binding))
throw new IllegalStateException("Cannot register twice a button binding in the same category.");
this.bindings.add(binding);
}
public void register_all_bindings(@NotNull ButtonBinding... bindings)
{
this.register_all_bindings(Arrays.asList(bindings));
}
public void register_all_bindings(@NotNull List<ButtonBinding> bindings)
{
bindings.forEach(this::register_binding);
}
/**
* Gets the bindings assigned to this category.
*
* @return The bindings assigned to this category.
*/
public @NotNull List<ButtonBinding> get_bindings()
{
return Collections.unmodifiableList(this.bindings);
}
/**
* Gets the translated name of this category.
* <p>
* The translation key should be `modid.identifier_name`.
*
* @return The translated name.
*/
public @NotNull String get_translated_name()
{
if (this.id.get_namespace().equals("minecraft"))
return I18n.translate(this.id.get_name());
else
return I18n.translate(this.id.get_namespace() + "." + this.id.get_name());
}
/**
* Gets the priority display of this category.
* It will defines in which order the categories will display on the controls screen.
*
* @return The priority of this category.
*/
public int get_priority()
{
return this.priority;
}
@Override
public @NotNull Identifier get_identifier()
{
return this.id;
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.controller;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWGamepadState;
import java.io.File;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
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;
import static org.lwjgl.BufferUtils.createByteBuffer;
/**
* Represents a controller.
*
* @author LambdAurora
* @version 1.0.0
* @since 1.0.0
*/
public class Controller implements Nameable
{
private static final Map<Integer, Controller> CONTROLLERS = new HashMap<>();
private final int id;
public Controller(int id)
{
this.id = id;
}
/**
* Gets the identifier of this controller.
*
* @return The identifier of this controller.
*/
public int get_id()
{
return this.id;
}
/**
* Gets the controller's globally unique identifier.
*
* @return The controller's GUID.
*/
public String get_guid()
{
String guid = GLFW.glfwGetJoystickGUID(this.id);
return guid == null ? "" : guid;
}
/**
* Returns whether this controller is connected or not.
*
* @return True if this controller is connected, else false.
*/
public boolean is_connected()
{
return GLFW.glfwJoystickPresent(this.id);
}
/**
* Returns whether this controller is a gamepad or not.
*
* @return True if this controller is a gamepad, else false.
*/
public boolean is_gamepad()
{
return this.is_connected() && GLFW.glfwJoystickIsGamepad(this.id);
}
/**
* Gets the name of the controller.
*
* @return The controller's name.
*/
@Override
public @NotNull String get_name()
{
String name = this.is_gamepad() ? GLFW.glfwGetGamepadName(this.id) : GLFW.glfwGetJoystickName(this.id);
return name == null ? String.valueOf(this.get_id()) : name;
}
/**
* Gets the state of the controller.
*
* @return The state of the controller input.
*/
public GLFWGamepadState get_state()
{
GLFWGamepadState state = GLFWGamepadState.create();
if (this.is_gamepad())
GLFW.glfwGetGamepadState(this.id, state);
return state;
}
public static @NotNull Controller by_id(int id)
{
if (id > GLFW.GLFW_JOYSTICK_LAST) {
LambdaControlsClient.get().log("Controller '" + id + "' doesn't exist.");
id = GLFW.GLFW_JOYSTICK_LAST;
}
Controller controller;
if (CONTROLLERS.containsKey(id))
return CONTROLLERS.get(id);
else {
controller = new Controller(id);
CONTROLLERS.put(id, controller);
return controller;
}
}
public static @NotNull Optional<Controller> by_guid(@NotNull String guid)
{
return CONTROLLERS.values().stream().filter(Controller::is_connected)
.filter(controller -> controller.get_guid().equals(guid))
.max(Comparator.comparingInt(Controller::get_id));
}
/**
* Reads the specified resource and returns the raw data as a ByteBuffer.
*
* @param resource The resource to read.
* @param buffer_size The initial buffer size.
* @return The resource data.
* @throws IOException If an IO error occurs.
*/
private static ByteBuffer io_resource_to_buffer(String resource, int buffer_size) throws IOException
{
ByteBuffer buffer = null;
Path path = Paths.get(resource);
if (Files.isReadable(path)) {
try (SeekableByteChannel fc = Files.newByteChannel(path)) {
buffer = createByteBuffer((int) fc.size() + 2);
while (fc.read(buffer) != -1) ;
buffer.put((byte) 0);
}
}
((Buffer) buffer).flip(); // Force Java 8 >.<
return buffer;
}
/**
* Updates the controller mappings.
*/
public static void update_mappings()
{
try {
File mappings_file = new File("config/gamecontrollerdb.txt");
if (!mappings_file.exists())
return;
LambdaControlsClient.get().log("Updating controller mappings...");
ByteBuffer buffer = io_resource_to_buffer(mappings_file.getPath(), 1024);
GLFW.glfwUpdateGamepadMappings(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.controller;
import me.lambdaurora.lambdacontrols.client.ButtonState;
import me.lambdaurora.lambdacontrols.client.util.CreativeInventoryScreenAccessor;
import me.lambdaurora.lambdacontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.util.ScreenshotUtils;
import net.minecraft.item.ItemGroup;
import org.jetbrains.annotations.NotNull;
/**
* Represents some input handlers.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class InputHandlers
{
private static int hotbar_cooldown = 0;
private InputHandlers()
{
}
public static PressAction handle_hotbar(boolean right)
{
return (client, button, action) -> {
if (action == ButtonState.RELEASE)
return false;
// When ingame
if (client.currentScreen == null && client.player != null) {
if (right)
client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 8 ? 0 : client.player.inventory.selectedSlot + 1;
else
client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 0 ? 8 : client.player.inventory.selectedSlot - 1;
return true;
} else if (client.currentScreen instanceof CreativeInventoryScreen) {
CreativeInventoryScreenAccessor creative_inventory = (CreativeInventoryScreenAccessor) client.currentScreen;
int current_selected_tab = creative_inventory.get_selected_tab();
int next_tab = current_selected_tab + (right ? 1 : -1);
if (next_tab < 0)
next_tab = ItemGroup.GROUPS.length - 1;
else if (next_tab >= ItemGroup.GROUPS.length)
next_tab = 0;
creative_inventory.set_selected_tab(ItemGroup.GROUPS[next_tab]);
return true;
}
return false;
};
}
public static boolean handle_pause_game(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, @NotNull ButtonState action)
{
if (action == ButtonState.PRESS) {
// If in game, then pause the game.
if (client.currentScreen == null)
client.openPauseMenu(false);
else if (client.currentScreen instanceof AbstractContainerScreen && client.player != null) // If the current screen is a container then close it.
client.player.closeContainer();
else // Else just close the current screen.
client.currentScreen.onClose();
}
return true;
}
/**
* Handles the screenshot action.
*
* @param client The client instance.
* @param binding The binding which fired the action.
* @param action The action done on the binding.
* @return True if handled, else false.
*/
public static boolean handle_screenshot(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, @NotNull ButtonState action)
{
if (action == ButtonState.PRESS)
ScreenshotUtils.saveScreenshot(client.runDirectory, client.getWindow().getFramebufferWidth(), client.getWindow().getFramebufferHeight(), client.getFramebuffer(),
text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text)));
return true;
}
public static boolean handle_toggle_sneak(@NotNull MinecraftClient client, @NotNull ButtonBinding button, @NotNull ButtonState action)
{
if (client.player != null && !client.player.abilities.flying) {
button.as_key_binding().filter(binding -> action == ButtonState.PRESS).ifPresent(binding -> ((KeyBindingAccessor) binding).handle_press_state(!binding.isPressed()));
return true;
}
return false;
}
}

View File

@@ -0,0 +1,273 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.controller;
import me.lambdaurora.lambdacontrols.client.ButtonState;
import me.lambdaurora.lambdacontrols.client.LambdaControlsConfig;
import net.minecraft.client.MinecraftClient;
import org.aperlambda.lambdacommon.Identifier;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Represents an input manager for controllers.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class InputManager
{
private static final List<ButtonBinding> BINDINGS = new ArrayList<>();
private static final List<ButtonCategory> CATEGORIES = new ArrayList<>();
public static final Map<Integer, ButtonState> STATES = new HashMap<>();
/**
* Returns whether the specified binding is registered or not.
*
* @param binding The binding to check.
* @return True if the binding is registered, else false.
*/
public static boolean has_binding(@NotNull ButtonBinding binding)
{
return BINDINGS.contains(binding);
}
/**
* Returns whether the specified binding is registered or not.
*
* @param name The name of the binding to check.
* @return True if the binding is registered, else false.
*/
public static boolean has_binding(@NotNull String name)
{
return BINDINGS.parallelStream().map(ButtonBinding::get_name).anyMatch(binding -> binding.equalsIgnoreCase(name));
}
/**
* Returns whether the specified binding is registered or not.
*
* @param identifier The identifier of the binding to check.
* @return True if the binding is registered, else false.
*/
public static boolean has_binding(@NotNull Identifier identifier)
{
return has_binding(identifier.get_namespace() + "." + identifier.get_name());
}
/**
* Registers a button binding.
*
* @param binding The binding to register.
* @return The registered binding.
*/
public static @NotNull ButtonBinding register_binding(@NotNull ButtonBinding binding)
{
if (has_binding(binding))
throw new IllegalStateException("Cannot register twice a button binding in the registry.");
BINDINGS.add(binding);
return binding;
}
public static @NotNull ButtonBinding register_binding(@NotNull Identifier binding_id, int[] default_button, @NotNull List<PressAction> actions, boolean has_cooldown)
{
return register_binding(new ButtonBinding(binding_id.get_namespace() + "." + binding_id.get_name(), default_button, actions, has_cooldown));
}
public static @NotNull ButtonBinding register_binding(@NotNull Identifier binding_id, int[] default_button, boolean has_cooldown)
{
return register_binding(binding_id, default_button, Collections.emptyList(), has_cooldown);
}
public static @NotNull ButtonBinding register_binding(@NotNull net.minecraft.util.Identifier binding_id, int[] default_button, @NotNull List<PressAction> actions, boolean has_cooldown)
{
return register_binding(new Identifier(binding_id.getNamespace(), binding_id.getPath()), default_button, actions, has_cooldown);
}
public static @NotNull ButtonBinding register_binding(@NotNull net.minecraft.util.Identifier binding_id, int[] default_button, boolean has_cooldown)
{
return register_binding(binding_id, default_button, Collections.emptyList(), has_cooldown);
}
/**
* Sorts bindings to get bindings with the higher button counts first.
*/
public static void sort_bindings()
{
synchronized (BINDINGS) {
List<ButtonBinding> sorted_bindings = BINDINGS.stream().sorted(Collections.reverseOrder(Comparator.comparingInt(binding -> binding.get_button().length)))
.collect(Collectors.toList());
BINDINGS.clear();
BINDINGS.addAll(sorted_bindings);
}
}
/**
* Registers a category of button bindings.
*
* @param category The category to register.
* @return The registered category.
*/
public static ButtonCategory register_category(@NotNull ButtonCategory category)
{
CATEGORIES.add(category);
return category;
}
public static ButtonCategory register_category(@NotNull Identifier identifier, int priority)
{
return register_category(new ButtonCategory(identifier, priority));
}
public static ButtonCategory register_category(@NotNull Identifier identifier)
{
return register_category(new ButtonCategory(identifier));
}
protected static ButtonCategory register_default_category(@NotNull String key, @NotNull Consumer<ButtonCategory> key_adder)
{
ButtonCategory category = register_category(new Identifier("minecraft", key), CATEGORIES.size());
key_adder.accept(category);
return category;
}
/**
* Loads the button bindings from configuration.
*
* @param config The configuration instance.
*/
public static void load_button_bindings(@NotNull LambdaControlsConfig config)
{
List<ButtonBinding> load_queue = new ArrayList<>(BINDINGS);
load_queue.forEach(config::load_button_binding);
}
/**
* Returns the binding state.
*
* @param binding The binding.
* @return The current state of the binding.
*/
public static @NotNull ButtonState get_binding_state(@NotNull ButtonBinding binding)
{
ButtonState state = ButtonState.REPEAT;
for (int btn : binding.get_button()) {
ButtonState btn_state = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
if (btn_state == ButtonState.PRESS)
state = ButtonState.PRESS;
else if (btn_state == ButtonState.RELEASE) {
state = ButtonState.RELEASE;
break;
} else if (btn_state == ButtonState.NONE) {
state = ButtonState.NONE;
break;
}
}
return state;
}
/**
* Returns whether the button has duplicated bindings.
*
* @param button The button to check.
* @return True if the button has duplicated bindings, else false.
*/
public static boolean has_duplicated_bindings(int[] button)
{
return BINDINGS.parallelStream().filter(binding -> are_buttons_equivalent(binding.get_button(), button)).count() > 1;
}
/**
* Returns whether the specified buttons are equivalent or not.
*
* @param buttons1 First set of buttons.
* @param buttons2 Second set of buttons.
* @return True if the two sets of buttons are equivalent, else false.
*/
public static boolean are_buttons_equivalent(int[] buttons1, int[] buttons2)
{
if (buttons1.length != buttons2.length)
return false;
int count = 0;
for (int btn : buttons1) {
for (int btn2 : buttons2) {
if (btn == btn2) {
count++;
break;
}
}
}
return count == buttons1.length;
}
/**
* Returns whether the button set contains the specified button or not.
*
* @param buttons The button set.
* @param button The button to check.
* @return True if the button set contains the specified button, else false.
*/
public static boolean contains_button(int[] buttons, int button)
{
return Arrays.stream(buttons).anyMatch(btn -> btn == button);
}
/**
* Updates the button states.
*/
public static void update_states()
{
STATES.forEach((btn, state) -> {
if (state == ButtonState.PRESS)
STATES.put(btn, ButtonState.REPEAT);
else if (state == ButtonState.RELEASE)
STATES.put(btn, ButtonState.NONE);
});
}
public static void update_bindings(@NotNull MinecraftClient client)
{
List<Integer> skip_buttons = new ArrayList<>();
Map<ButtonBinding, ButtonState> states = new HashMap<>();
for (ButtonBinding binding : BINDINGS) {
ButtonState binding_state = get_binding_state(binding);
if (skip_buttons.stream().anyMatch(btn -> contains_button(binding.get_button(), btn))) {
if (binding.pressed)
binding_state = ButtonState.RELEASE;
else
binding_state = ButtonState.NONE;
}
binding.pressed = binding_state.is_pressed();
binding.update();
if (binding.pressed)
Arrays.stream(binding.get_button()).forEach(skip_buttons::add);
states.put(binding, binding_state);
}
states.forEach((binding, state) -> {
if (state != ButtonState.NONE)
binding.handle(client, state);
});
}
public static @NotNull Stream<ButtonBinding> stream_bindings()
{
return BINDINGS.stream();
}
public static @NotNull Stream<ButtonCategory> stream_categories()
{
return CATEGORIES.stream();
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.controller;
import me.lambdaurora.lambdacontrols.client.ButtonState;
import me.lambdaurora.lambdacontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.MinecraftClient;
import org.jetbrains.annotations.NotNull;
/**
* Represents a press action callback.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.0.0
*/
@FunctionalInterface
public interface PressAction
{
PressAction DEFAULT_ACTION = (client, button, action) -> {
if (action == ButtonState.REPEAT || client.currentScreen != null)
return false;
button.as_key_binding().ifPresent(key_binding -> ((KeyBindingAccessor) key_binding).handle_press_state(button.is_button_down()));
return true;
};
/**
* Handles when there is a press action.
*
* @param client The client instance.
* @param action The action done.
*/
boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, @NotNull ButtonState action);
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.gui;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.spruceui.AbstractIconButtonWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.utils.Pair;
import org.jetbrains.annotations.NotNull;
/**
* Represents a controller button widget.
*/
public class ControllerButtonWidget extends AbstractIconButtonWidget
{
private ButtonBinding binding;
private int icon_width;
public ControllerButtonWidget(int x, int y, int width, @NotNull ButtonBinding button_binding, @NotNull PressAction on_press)
{
super(x, y, width, 20, ButtonBinding.get_localized_button_name(button_binding.get_button()[0]), on_press);
this.binding = button_binding;
}
public void update()
{
int length = binding.get_button().length;
this.setMessage(this.binding.is_not_bound() ? I18n.translate("lambdacontrols.not_bound") :
(length > 0 ? ButtonBinding.get_localized_button_name(binding.get_button()[0]) : "<>"));
}
@Override
public String getMessage()
{
if (this.binding.get_button().length > 1)
return "";
return super.getMessage();
}
@Override
protected int render_icon(int mouse_x, int mouse_y, float delta, int x, int y)
{
if (this.binding.get_button().length > 1) {
x += (this.width / 2 - this.icon_width / 2) - 4;
}
Pair<Integer, Integer> size = LambdaControlsClient.draw_button(x, y, this.binding, MinecraftClient.getInstance());
this.icon_width = size.get_key();
return size.get_value();
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.gui;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.client.controller.ButtonCategory;
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ElementListWidget;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.util.Formatting;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Represents a control list widget.
*/
public class ControlsListWidget extends ElementListWidget<ControlsListWidget.Entry>
{
private static final int[] UNBOUND = new int[0];
private final LambdaControlsControlsScreen gui;
private int field_2733;
public ControlsListWidget(@NotNull LambdaControlsControlsScreen gui, @NotNull MinecraftClient client)
{
super(client, gui.width + 45, gui.height, 43, gui.height - 32, 24);
this.gui = gui;
InputManager.stream_categories()
.sorted(Comparator.comparingInt(ButtonCategory::get_priority))
.forEach(category -> {
this.addEntry(new CategoryEntry(category));
category.get_bindings().forEach(binding -> {
int i = client.textRenderer.getStringWidth(I18n.translate(binding.get_translation_key()));
if (i > this.field_2733) {
this.field_2733 = i;
}
this.addEntry(new ControlsListWidget.ButtonBindingEntry(binding));
});
});
}
@Override
protected int getScrollbarPosition()
{
return super.getScrollbarPosition() + 15;
}
@Override
public int getRowWidth()
{
return super.getRowWidth() + 32;
}
public class ButtonBindingEntry extends Entry
{
private final ButtonBinding binding;
private final String binding_name;
private final ControllerButtonWidget edit_button;
private final ButtonWidget reset_button;
private final ButtonWidget unbound_button;
ButtonBindingEntry(@NotNull ButtonBinding binding)
{
this.binding = binding;
this.binding_name = I18n.translate(this.binding.get_translation_key());
this.edit_button = new ControllerButtonWidget(0, 0, 110, this.binding, btn -> {
gui.focused_binding = binding;
gui.current_buttons.clear();
gui.waiting = true;
})
{
protected String getNarrationMessage()
{
return binding.is_not_bound() ? I18n.translate("narrator.controls.unbound", binding_name) : I18n.translate("narrator.controls.bound", binding_name, super.getNarrationMessage());
}
};
this.reset_button = new ButtonWidget(0, 0, 50, 20, I18n.translate("controls.reset"),
btn -> gui.mod.config.set_button_binding(binding, binding.get_default_button()))
{
protected String getNarrationMessage()
{
return I18n.translate("narrator.controls.reset", binding_name);
}
};
this.unbound_button = new ButtonWidget(0, 0, 50, 20, I18n.translate("lambdacontrols.menu.unbound"),
btn -> {
gui.mod.config.set_button_binding(binding, UNBOUND);
gui.focused_binding = null;
})
{
protected String getNarrationMessage()
{
return I18n.translate("lambdacontrols.narrator.unbound", binding_name);
}
};
}
@Override
public List<? extends Element> children()
{
return Collections.unmodifiableList(Arrays.asList(this.edit_button, this.reset_button));
}
@Override
public void render(int index, int y, int x, int width, int height, int mouse_x, int mouse_y, boolean hovering, float delta)
{
boolean focused = gui.focused_binding == this.binding;
TextRenderer text_renderer = ControlsListWidget.this.minecraft.textRenderer;
String binding_name = this.binding_name;
float var10002 = (float) (x + 70 - ControlsListWidget.this.field_2733);
int var10003 = y + height / 2;
text_renderer.draw(binding_name, var10002, (float) (var10003 - 9 / 2), 16777215);
this.reset_button.x = this.unbound_button.x = x + 190;
this.reset_button.y = this.unbound_button.y = y;
this.reset_button.active = !this.binding.is_default();
if (focused)
this.unbound_button.render(mouse_x, mouse_y, delta);
else
this.reset_button.render(mouse_x, mouse_y, delta);
this.edit_button.x = x + 75;
this.edit_button.y = y;
this.edit_button.update();
if (focused) {
this.edit_button.setMessage(Formatting.WHITE + "> " + Formatting.YELLOW + this.edit_button.getMessage() + Formatting.WHITE + " <");
} else if (!this.binding.is_not_bound() && InputManager.has_duplicated_bindings(this.binding.get_button())) {
this.edit_button.setMessage(Formatting.RED + this.edit_button.getMessage());
} else if (this.binding.is_not_bound()) {
this.edit_button.setMessage(Formatting.GOLD + this.edit_button.getMessage());
}
this.edit_button.render(mouse_x, mouse_y, delta);
}
public boolean mouseClicked(double mouse_x, double mouse_y, int button)
{
boolean focused = gui.focused_binding == this.binding;
if (this.edit_button.mouseClicked(mouse_x, mouse_y, button))
return true;
else
return focused ? this.unbound_button.mouseClicked(mouse_x, mouse_y, button) : this.reset_button.mouseClicked(mouse_x, mouse_y, button);
}
public boolean mouseReleased(double mouse_x, double mouse_y, int button)
{
return this.edit_button.mouseReleased(mouse_x, mouse_y, button) || this.reset_button.mouseReleased(mouse_x, mouse_y, button)
|| this.unbound_button.mouseReleased(mouse_x, mouse_y, button);
}
}
public class CategoryEntry extends Entry
{
private final String name;
private final int name_width;
public CategoryEntry(@NotNull ButtonCategory category)
{
this.name = category.get_translated_name();
this.name_width = ControlsListWidget.this.minecraft.textRenderer.getStringWidth(this.name);
}
@Override
public void render(int index, int y, int x, int width, int height, int mouse_x, int mouse_y, boolean hovering, float delta)
{
ControlsListWidget.this.minecraft.textRenderer.draw(this.name, (float) (ControlsListWidget.this.minecraft.currentScreen.width / 2 - this.name_width / 2),
(float) ((y + height) - 9 - 1), 16777215);
}
@Override
public boolean changeFocus(boolean bl)
{
return false;
}
@Override
public List<? extends Element> children()
{
return Collections.emptyList();
}
}
@Environment(EnvType.CLIENT)
public abstract static class Entry extends ElementListWidget.Entry<Entry>
{
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.gui;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import me.lambdaurora.spruceui.SpruceButtonWidget;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.options.ControlsOptionsScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.TranslatableText;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
/**
* Represents the controls screen.
*/
public class LambdaControlsControlsScreen extends Screen
{
private final Screen parent;
final LambdaControlsClient mod;
private final boolean hide_settings;
private ControlsListWidget bindings_list_widget;
private ButtonWidget reset_button;
public ButtonBinding focused_binding;
public boolean waiting = false;
public List<Integer> current_buttons = new ArrayList<>();
public LambdaControlsControlsScreen(@NotNull Screen parent, boolean hide_settings)
{
super(new TranslatableText("lambdacontrols.menu.title.controller_controls"));
this.parent = parent;
this.mod = LambdaControlsClient.get();
this.hide_settings = hide_settings;
}
@Override
public void removed()
{
this.mod.config.save();
super.removed();
}
@Override
protected void init()
{
this.addButton(new SpruceButtonWidget(this.width / 2 - 155, 18, this.hide_settings ? 310 : 150, 20, I18n.translate("lambdacontrols.menu.keyboard_controls"),
btn -> this.minecraft.openScreen(new ControlsOptionsScreen(this, this.minecraft.options))));
if (!this.hide_settings)
this.addButton(new SpruceButtonWidget(this.width / 2 - 155 + 160, 18, 150, 20, I18n.translate("menu.options"),
btn -> this.minecraft.openScreen(new LambdaControlsSettingsScreen(this, this.minecraft.options, true))));
this.bindings_list_widget = new ControlsListWidget(this, this.minecraft);
this.children.add(this.bindings_list_widget);
this.reset_button = this.addButton(new ButtonWidget(this.width / 2 - 155, this.height - 29, 150, 20, I18n.translate("controls.resetAll"),
btn -> InputManager.stream_bindings().forEach(binding -> this.mod.config.set_button_binding(binding, binding.get_default_button()))));
this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, 20, I18n.translate("gui.done"),
btn -> this.minecraft.openScreen(this.parent)));
}
// Replacement for Predicate#not as it is Java 11.
private <T> Predicate<T> not(Predicate<T> target)
{
Objects.requireNonNull(target);
return target.negate();
}
@Override
public void render(int mouse_x, int mouse_y, float delta)
{
this.renderBackground();
this.bindings_list_widget.render(mouse_x, mouse_y, delta);
this.drawCenteredString(this.font, this.title.asFormattedString(), this.width / 2, 8, 16777215);
this.reset_button.active = InputManager.stream_bindings().anyMatch(this.not(ButtonBinding::is_default));
super.render(mouse_x, mouse_y, delta);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.gui;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.client.HudSide;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.util.hit.HitResult;
import org.jetbrains.annotations.NotNull;
/**
* Represents the LambdaControls HUD.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.0.0
*/
public class LambdaControlsHud extends DrawableHelper
{
private final MinecraftClient client;
private final LambdaControlsClient mod;
private int width_bottom = 0;
private int width_top = 0;
public LambdaControlsHud(@NotNull MinecraftClient client, @NotNull LambdaControlsClient mod)
{
this.client = client;
this.mod = mod;
}
/**
* Renders the LambdaControls' HUD.
*/
public void render()
{
if (this.mod.config.get_controls_mode() == ControlsMode.CONTROLLER && this.mod.config.is_hud_enabled() && this.client.currentScreen == null && !this.client.options.hudHidden) {
int x = this.mod.config.get_hud_side() == HudSide.LEFT ? 10 : client.getWindow().getScaledWidth() - 10 - this.width_bottom, y = bottom(10);
x += (this.width_bottom = this.draw_button_tip(x, y, ButtonBinding.INVENTORY, true) + 10);
this.width_bottom += this.draw_button_tip(x, y, ButtonBinding.SWAP_HANDS, true);
x = this.mod.config.get_hud_side() == HudSide.LEFT ? 10 : client.getWindow().getScaledWidth() - 10 - this.width_top;
x += (this.width_top = this.draw_button_tip(x, (y -= 20), ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty()) + 10);
this.width_top += this.draw_button_tip(x, y, ButtonBinding.ATTACK.get_button(),
this.client.crosshairTarget.getType() == HitResult.Type.BLOCK ? "lambdacontrols.action.hit" : ButtonBinding.ATTACK.get_translation_key(),
this.client.crosshairTarget.getType() != HitResult.Type.MISS);
}
}
private int bottom(int y)
{
return this.client.getWindow().getScaledHeight() - y - 15;
}
private int draw_button_tip(int x, int y, @NotNull ButtonBinding button, boolean display)
{
return LambdaControlsClient.draw_button_tip(x, y, button, display, this.client);
}
private int draw_button_tip(int x, int y, int[] button, @NotNull String action, boolean display)
{
return LambdaControlsClient.draw_button_tip(x, y, button, action, display, this.client);
}
}

View File

@@ -0,0 +1,240 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.gui;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.Controller;
import me.lambdaurora.spruceui.SpruceButtonWidget;
import me.lambdaurora.spruceui.SpruceLabelWidget;
import me.lambdaurora.spruceui.Tooltip;
import me.lambdaurora.spruceui.option.*;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.options.ControlsOptionsScreen;
import net.minecraft.client.gui.widget.ButtonListWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.options.CyclingOption;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.Option;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
import net.minecraft.util.Util;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
/**
* Represents the LambdaControls settings screen.
*/
public class LambdaControlsSettingsScreen extends Screen
{
public static final String GAMEPAD_TOOL_URL = "http://generalarcade.com/gamepadtool/";
final LambdaControlsClient mod;
private final Screen parent;
private final boolean hide_controls;
// General options
private final Option auto_switch_mode_option;
private final Option rotation_speed_option;
private final Option mouse_speed_option;
private final Option reset_option;
// Controller options
private final Option controller_option;
private final Option second_controller_option;
private final Option controller_type_option;
private final Option dead_zone_option;
private final Option inverts_right_x_axis;
private final Option inverts_right_y_axis;
// Hud options
private final Option hud_enable_option;
private final Option hud_side_option;
private final String controller_mappings_url_text = I18n.translate("lambdacontrols.controller.mappings.2", Formatting.GOLD.toString(), GAMEPAD_TOOL_URL, Formatting.RESET.toString());
private ButtonListWidget list;
private SpruceLabelWidget gamepad_tool_url_label;
public LambdaControlsSettingsScreen(Screen parent, @NotNull GameOptions options, boolean hide_controls)
{
super(new TranslatableText("lambdacontrols.title.settings"));
this.mod = LambdaControlsClient.get();
this.parent = parent;
this.hide_controls = hide_controls;
// General options
this.auto_switch_mode_option = new SpruceBooleanOption("lambdacontrols.menu.auto_switch_mode", game_options -> this.mod.config.has_auto_switch_mode(),
(game_options, new_value) -> this.mod.config.set_auto_switch_mode(new_value), new TranslatableText("lambdacontrols.tooltip.auto_switch_mode"));
this.rotation_speed_option = new SpruceDoubleOption("lambdacontrols.menu.rotation_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_rotation_speed(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_rotation_speed(new_value);
}
}, (game_options, option) -> option.getDisplayPrefix() + option.get(options),
new TranslatableText("lambdacontrols.tooltip.rotation_speed"));
this.mouse_speed_option = new SpruceDoubleOption("lambdacontrols.menu.mouse_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_mouse_speed(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_mouse_speed(new_value);
}
}, (game_options, option) -> option.getDisplayPrefix() + option.get(options),
new TranslatableText("lambdacontrols.tooltip.mouse_speed"));
this.reset_option = new SpruceResetOption(btn -> {
this.mod.config.reset();
MinecraftClient client = MinecraftClient.getInstance();
this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
});
// Controller options
this.controller_option = new CyclingOption("lambdacontrols.menu.controller", (game_options, amount) -> {
int current_id = this.mod.config.get_controller().get_id();
current_id += amount;
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) -> {
String controller_name = this.mod.config.get_controller().get_name();
if (!this.mod.config.get_controller().is_connected())
return option.getDisplayPrefix() + Formatting.RED + controller_name;
else if (!this.mod.config.get_controller().is_gamepad())
return option.getDisplayPrefix() + Formatting.GOLD + controller_name;
else
return option.getDisplayPrefix() + controller_name;
});
this.second_controller_option = new SpruceCyclingOption("lambdacontrols.menu.controller2",
(game_options, amount) -> {
int current_id = this.mod.config.get_second_controller().map(Controller::get_id).orElse(-1);
current_id += amount;
if (current_id > GLFW.GLFW_JOYSTICK_LAST)
current_id = -1;
this.mod.config.set_second_controller(current_id == -1 ? null : Controller.by_id(current_id));
}, (game_options, option) -> this.mod.config.get_second_controller().map(controller -> {
String controller_name = controller.get_name();
if (!controller.is_connected())
return option.getDisplayPrefix() + Formatting.RED + controller_name;
else if (!controller.is_gamepad())
return option.getDisplayPrefix() + Formatting.GOLD + controller_name;
else
return option.getDisplayPrefix() + controller_name;
}).orElse(option.getDisplayPrefix() + Formatting.RED + I18n.translate("options.off")),
new TranslatableText("lambdacontrols.tooltip.controller2"));
this.controller_type_option = new SpruceCyclingOption("lambdacontrols.menu.controller_type",
(game_options, amount) -> this.mod.config.set_controller_type(this.mod.config.get_controller_type().next()),
(game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_controller_type().get_translated_name(),
new TranslatableText("lambdacontrols.tooltip.controller_type"));
this.dead_zone_option = new SpruceDoubleOption("lambdacontrols.menu.dead_zone", 0.05, 1.0, 0.05F, game_options -> this.mod.config.get_dead_zone(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_dead_zone(new_value);
}
}, (game_options, option) -> {
String value = String.valueOf(option.get(options));
return option.getDisplayPrefix() + value.substring(0, Math.min(value.length(), 5));
}, new TranslatableText("lambdacontrols.tooltip.dead_zone"));
this.inverts_right_x_axis = new SpruceBooleanOption("lambdacontrols.menu.invert_right_x_axis", game_options -> this.mod.config.does_invert_right_x_axis(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_invert_right_x_axis(new_value);
}
}, null);
this.inverts_right_y_axis = new SpruceBooleanOption("lambdacontrols.menu.invert_right_y_axis", game_options -> this.mod.config.does_invert_right_y_axis(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_invert_right_y_axis(new_value);
}
}, null);
// HUD options
this.hud_enable_option = new SpruceBooleanOption("lambdacontrols.menu.hud_enable", (game_options) -> this.mod.config.is_hud_enabled(),
(game_options, new_value) -> this.mod.config.set_hud_enabled(new_value), new TranslatableText("lambdacontrols.tooltip.hud_enable"));
this.hud_side_option = new SpruceCyclingOption("lambdacontrols.menu.hud_side",
(game_options, amount) -> this.mod.config.set_hud_side(this.mod.config.get_hud_side().next()),
(game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_hud_side().get_translated_name(),
new TranslatableText("lambdacontrols.tooltip.hud_side"));
}
@Override
public void removed()
{
this.mod.config.save();
super.removed();
}
@Override
public void onClose()
{
this.mod.config.save();
super.onClose();
}
private int get_text_height()
{
return (5 + this.font.fontHeight) * 3 + 5;
}
@Override
protected void init()
{
super.init();
int button_height = 20;
SpruceButtonWidget controls_mode_btn = new SpruceButtonWidget(this.width / 2 - 155, 18, this.hide_controls ? 310 : 150, button_height,
I18n.translate("lambdacontrols.menu.controls_mode") + ": " + I18n.translate(this.mod.config.get_controls_mode().get_translation_key()),
btn -> {
ControlsMode next = this.mod.config.get_controls_mode().next();
btn.setMessage(I18n.translate("lambdacontrols.menu.controls_mode") + ": " + I18n.translate(next.get_translation_key()));
this.mod.config.set_controls_mode(next);
this.mod.config.save();
});
controls_mode_btn.set_tooltip(new TranslatableText("lambdacontrols.tooltip.controls_mode"));
this.addButton(controls_mode_btn);
if (!this.hide_controls)
this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, 18, 150, button_height, I18n.translate("options.controls"),
btn -> {
if (this.mod.config.get_controls_mode() == ControlsMode.CONTROLLER)
this.minecraft.openScreen(new LambdaControlsControlsScreen(this, true));
else
this.minecraft.openScreen(new ControlsOptionsScreen(this, this.minecraft.options));
}));
this.list = new ButtonListWidget(this.minecraft, this.width, this.height, 43, this.height - 29 - this.get_text_height(), 25);
// General options
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.general", true, null));
this.list.addOptionEntry(this.rotation_speed_option, this.mouse_speed_option);
this.list.addSingleOptionEntry(this.auto_switch_mode_option);
// Controller options
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.controller", true, null));
this.list.addSingleOptionEntry(this.controller_option);
this.list.addSingleOptionEntry(this.second_controller_option);
this.list.addOptionEntry(this.controller_type_option, this.dead_zone_option);
this.list.addOptionEntry(this.inverts_right_x_axis, this.inverts_right_y_axis);
this.list.addSingleOptionEntry(new ReloadControllerMappingsOption());
// HUD options
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.hud", true, null));
this.list.addOptionEntry(this.hud_enable_option, this.hud_side_option);
this.children.add(this.list);
this.gamepad_tool_url_label = new SpruceLabelWidget(this.width / 2, this.height - 29 - (5 + this.font.fontHeight) * 2, this.controller_mappings_url_text, this.width,
label -> Util.getOperatingSystem().open(GAMEPAD_TOOL_URL), true);
this.gamepad_tool_url_label.set_tooltip(new TranslatableText("chat.link.open"));
this.children.add(this.gamepad_tool_url_label);
this.addButton(this.reset_option.createButton(this.minecraft.options, this.width / 2 - 155, this.height - 29, 150));
this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, button_height, I18n.translate("gui.done"),
(buttonWidget) -> this.minecraft.openScreen(this.parent)));
}
@Override
public void render(int mouse_x, int mouse_y, float delta)
{
this.renderBackground();
this.list.render(mouse_x, mouse_y, delta);
super.render(mouse_x, mouse_y, delta);
this.drawCenteredString(this.font, I18n.translate("lambdacontrols.menu.title"), this.width / 2, 8, 16777215);
this.drawCenteredString(this.font, I18n.translate("lambdacontrols.controller.mappings.1", Formatting.GREEN.toString(), Formatting.RESET.toString()), this.width / 2, this.height - 29 - (5 + this.font.fontHeight) * 3, 10526880);
this.gamepad_tool_url_label.render(mouse_x, mouse_y, delta);
this.drawCenteredString(this.font, I18n.translate("lambdacontrols.controller.mappings.3", Formatting.GREEN.toString(), Formatting.RESET.toString()), this.width / 2, this.height - 29 - (5 + this.font.fontHeight), 10526880);
Tooltip.render_all();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.gui;
import me.lambdaurora.lambdacontrols.client.controller.Controller;
import me.lambdaurora.spruceui.SpruceButtonWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.Option;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.TranslatableText;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
/**
* Represents the option to reload the controller mappings.
*/
public class ReloadControllerMappingsOption extends Option implements Nameable
{
private static final String KEY = "lambdacontrols.menu.reload_controller_mappings";
public ReloadControllerMappingsOption()
{
super(KEY);
}
@Override
public AbstractButtonWidget createButton(GameOptions options, int x, int y, int width)
{
SpruceButtonWidget button = new SpruceButtonWidget(x, y, width, 20, this.get_name(), btn -> {
MinecraftClient client = MinecraftClient.getInstance();
Controller.update_mappings();
if (client.currentScreen != null)
client.currentScreen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, new TranslatableText("lambdacontrols.controller.mappings.updated"), null));
});
button.set_tooltip(new TranslatableText("lambdacontrols.tooltip.reload_controller_mappings"));
return button;
}
@Override
public @NotNull String get_name()
{
return I18n.translate(KEY);
}
}

View File

@@ -0,0 +1,297 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.gui;
import me.lambdaurora.lambdacontrols.client.HudSide;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.util.KeyBindingAccessor;
import me.lambdaurora.spruceui.SpruceTexturedButtonWidget;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.GameMenuScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TexturedButtonWidget;
import net.minecraft.server.network.packet.PlayerActionC2SPacket;
import net.minecraft.text.LiteralText;
import net.minecraft.util.Arm;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X;
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y;
/**
* Represents the touchscreen overlay
*/
public class TouchscreenOverlay extends Screen
{
public static final Identifier WIDGETS_LOCATION = new Identifier("lambdacontrols", "textures/gui/widgets.png");
private LambdaControlsClient mod;
private SpruceTexturedButtonWidget jump_button;
private SpruceTexturedButtonWidget fly_button;
private SpruceTexturedButtonWidget fly_up_button;
private SpruceTexturedButtonWidget fly_down_button;
private int fly_button_enable_ticks = 0;
private int forward_button_tick = 0;
private SpruceTexturedButtonWidget forward_left_button;
private SpruceTexturedButtonWidget forward_right_button;
private SpruceTexturedButtonWidget start_sneak_button;
private SpruceTexturedButtonWidget end_sneak_button;
public TouchscreenOverlay(@NotNull LambdaControlsClient mod)
{
super(new LiteralText("Touchscreen overlay"));
this.mod = mod;
this.passEvents = true;
}
@Override
public boolean isPauseScreen()
{
return false;
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers)
{
return false;
}
private void pause_game(boolean bl)
{
if (this.minecraft == null)
return;
boolean bl2 = this.minecraft.isIntegratedServerRunning() && !this.minecraft.getServer().isRemote();
if (bl2) {
this.minecraft.openScreen(new GameMenuScreen(!bl));
this.minecraft.getSoundManager().pauseAll();
} else {
this.minecraft.openScreen(new GameMenuScreen(true));
}
}
/**
* Updates the forward button ticks cooldown.
*
* @param state The button state.
*/
private void update_forward_buttons_state(boolean state)
{
if (state)
this.forward_button_tick = -1;
else
this.forward_button_tick = 20;
}
/**
* Updates the jump buttons.
*/
private void update_jump_buttons()
{
if (this.minecraft == null)
return;
if (this.minecraft.player.abilities.allowFlying && this.minecraft.player.abilities.flying) {
boolean old_state_fly = this.fly_button.visible;
this.jump_button.visible = false;
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) {
this.fly_button_enable_ticks = 5;
this.handle_jump(null, false);
} else if (this.fly_button_enable_ticks > 0)
this.fly_button_enable_ticks--;
} else {
this.jump_button.visible = true;
this.fly_button.visible = false;
this.fly_up_button.visible = false;
this.fly_down_button.visible = false;
}
}
/**
* Handles the jump button.
*
* @param btn The pressed button.
* @param state The state of the jump button.
*/
private void handle_jump(ButtonWidget btn, boolean state)
{
((KeyBindingAccessor) this.minecraft.options.keyJump).handle_press_state(state);
}
@Override
public void tick()
{
if (this.forward_button_tick > 0) {
this.forward_button_tick--;
} else if (this.forward_button_tick == 0) {
if (this.forward_left_button.visible)
this.forward_left_button.visible = false;
if (this.forward_right_button.visible)
this.forward_right_button.visible = false;
}
this.update_jump_buttons();
}
@Override
protected void init()
{
super.init();
int scaled_width = this.minecraft.getWindow().getScaledWidth();
int scaled_height = this.minecraft.getWindow().getScaledHeight();
this.addButton(new TexturedButtonWidget(scaled_width / 2 - 20, 0, 20, 20, 0, 106, 20, ButtonWidget.WIDGETS_LOCATION, 256, 256,
btn -> this.minecraft.openScreen(new ChatScreen("")), ""));
this.addButton(new TexturedButtonWidget(scaled_width / 2, 0, 20, 20, 0, 0, 20, WIDGETS_LOCATION, 256, 256,
btn -> this.pause_game(false)));
// Inventory buttons.
int inventory_button_x = scaled_width / 2;
int inventory_button_y = scaled_height - 16 - 5;
if (this.minecraft.options.mainArm == Arm.LEFT) {
inventory_button_x = inventory_button_x - 91 - 24;
} else {
inventory_button_x = inventory_button_x + 91 + 4;
}
this.addButton(new TexturedButtonWidget(inventory_button_x, inventory_button_y, 20, 20, 20, 0, 20, WIDGETS_LOCATION, 256, 256,
btn -> {
if (this.minecraft.interactionManager.hasRidingInventory()) {
this.minecraft.player.openRidingInventory();
} else {
this.minecraft.getTutorialManager().onInventoryOpened();
this.minecraft.openScreen(new InventoryScreen(this.minecraft.player));
}
}));
int jump_button_x, swap_hands_x, sneak_button_x;
int sneak_button_y = scaled_height - 10 - 40 - 5;
if (this.mod.config.get_hud_side() == HudSide.LEFT) {
jump_button_x = scaled_width - 20 - 20;
swap_hands_x = jump_button_x - 5 - 40;
sneak_button_x = 10 + 20 + 5;
} else {
jump_button_x = 20;
swap_hands_x = jump_button_x + 5 + 40;
sneak_button_x = scaled_width - 10 - 40 - 5;
}
// Swap items hand.
this.addButton(new SpruceTexturedButtonWidget(swap_hands_x, sneak_button_y, 20, 20, 0, 160, 20, WIDGETS_LOCATION,
(btn, state) -> {
if (state) {
if (!this.minecraft.player.isSpectator()) {
this.minecraft.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_HELD_ITEMS, BlockPos.ORIGIN, Direction.DOWN));
}
}
}));
// Drop
this.addButton(new SpruceTexturedButtonWidget(swap_hands_x, sneak_button_y + 5 + 20, 20, 20, 20, 160, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keyDrop).handle_press_state(state)));
// Jump keys
this.addButton(this.jump_button = new SpruceTexturedButtonWidget(jump_button_x, sneak_button_y, 20, 20, 0, 40, 20, WIDGETS_LOCATION,
this::handle_jump));
this.addButton(this.fly_button = new SpruceTexturedButtonWidget(jump_button_x, sneak_button_y, 20, 20, 20, 40, 20, WIDGETS_LOCATION,
(btn, state) -> {
if (this.fly_button_enable_ticks == 0) this.minecraft.player.abilities.flying = false;
}));
this.addButton(this.fly_up_button = new SpruceTexturedButtonWidget(jump_button_x, sneak_button_y - 5 - 20, 20, 20, 40, 40, 20, WIDGETS_LOCATION,
this::handle_jump));
this.addButton(this.fly_down_button = new SpruceTexturedButtonWidget(jump_button_x, sneak_button_y + 20 + 5, 20, 20, 60, 40, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(state)));
this.update_jump_buttons();
// Movements keys
this.addButton((this.start_sneak_button = new SpruceTexturedButtonWidget(sneak_button_x, sneak_button_y, 20, 20, 0, 120, 20, WIDGETS_LOCATION,
(btn, state) -> {
if (state) {
((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(true);
this.start_sneak_button.visible = false;
this.end_sneak_button.visible = true;
}
})));
this.addButton((this.end_sneak_button = new SpruceTexturedButtonWidget(sneak_button_x, sneak_button_y, 20, 20, 20, 120, 20, WIDGETS_LOCATION,
(btn, state) -> {
if (state) {
((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(false);
this.end_sneak_button.visible = false;
this.start_sneak_button.visible = true;
}
})));
this.end_sneak_button.visible = false;
this.addButton(this.forward_left_button = new SpruceTexturedButtonWidget(sneak_button_x - 20 - 5, sneak_button_y - 5 - 20, 20, 20, 80, 80, 20, WIDGETS_LOCATION,
(btn, state) -> {
((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state);
((KeyBindingAccessor) this.minecraft.options.keyLeft).handle_press_state(state);
this.update_forward_buttons_state(state);
}));
this.forward_left_button.visible = false;
this.addButton(new SpruceTexturedButtonWidget(sneak_button_x, sneak_button_y - 5 - 20, 20, 20, 0, 80, 20, WIDGETS_LOCATION,
(btn, state) -> {
((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state);
this.update_forward_buttons_state(state);
this.forward_left_button.visible = true;
this.forward_right_button.visible = true;
}));
this.addButton(this.forward_right_button = new SpruceTexturedButtonWidget(sneak_button_x + 20 + 5, sneak_button_y - 5 - 20, 20, 20, 100, 80, 20, WIDGETS_LOCATION,
(btn, state) -> {
((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state);
((KeyBindingAccessor) this.minecraft.options.keyRight).handle_press_state(state);
this.update_forward_buttons_state(state);
}));
this.forward_right_button.visible = true;
this.addButton(new SpruceTexturedButtonWidget(sneak_button_x + 20 + 5, sneak_button_y, 20, 20, 20, 80, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keyRight).handle_press_state(state)));
this.addButton(new SpruceTexturedButtonWidget(sneak_button_x, sneak_button_y + 20 + 5, 20, 20, 40, 80, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keyBack).handle_press_state(state)));
this.addButton(new SpruceTexturedButtonWidget(sneak_button_x - 20 - 5, sneak_button_y, 20, 20, 60, 80, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.minecraft.options.keyLeft).handle_press_state(state)));
}
@Override
public void render(int mouseX, int mouseY, float delta)
{
super.render(mouseX, mouseY, delta);
}
@Override
public boolean mouseClicked(double mouse_x, double mouse_y, int button)
{
if (mouse_y >= (double) (this.height - 22) && this.minecraft != null && this.minecraft.player != null) {
int center_x = this.width / 2;
if (mouse_x >= (double) (center_x - 90) && mouse_x <= (double) (center_x + 90)) {
for (int slot = 0; slot < 9; ++slot) {
int slot_x = center_x - 90 + slot * 20 + 2;
if (mouse_x >= (double) slot_x && mouse_x <= (double) (slot_x + 20)) {
this.minecraft.player.inventory.selectedSlot = slot;
return true;
}
}
}
}
return super.mouseClicked(mouse_x, mouse_y, button);
}
@Override
public boolean mouseDragged(double mouse_x, double mouse_y, int button, double delta_x, double delta_y)
{
if (button == GLFW.GLFW_MOUSE_BUTTON_1 && this.minecraft != null) {
if (delta_y > 0.01)
this.mod.input.handle_look(this.minecraft, GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) Math.abs(delta_y / 5.0), 2);
else if (delta_y < 0.01)
this.mod.input.handle_look(this.minecraft, GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) Math.abs(delta_y / 5.0), 1);
if (delta_x > 0.01)
this.mod.input.handle_look(this.minecraft, GLFW_GAMEPAD_AXIS_RIGHT_X, (float) Math.abs(delta_x / 5.0), 2);
else if (delta_x < 0.01)
this.mod.input.handle_look(this.minecraft, GLFW_GAMEPAD_AXIS_RIGHT_X, (float) Math.abs(delta_x / 5.0), 1);
}
return super.mouseDragged(mouse_x, mouse_y, button, delta_x, delta_y);
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.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();
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.util.AbstractContainerScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
import net.minecraft.container.Slot;
import org.lwjgl.glfw.GLFW;
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;
/**
* Represents the mixin for the class AbstractContainerScreen.
*/
@Mixin(AbstractContainerScreen.class)
public abstract class AbstractContainerScreenMixin implements AbstractContainerScreenAccessor
{
protected int x;
protected int y;
@Shadow
protected abstract Slot getSlotAt(double xPosition, double yPosition);
@Override
public int get_x()
{
return this.x;
}
@Override
public int get_y()
{
return this.y;
}
@Override
public Slot get_slot_at(double pos_x, double pos_y)
{
return this.getSlotAt(pos_x, pos_y);
}
@Inject(method = "render", at = @At("RETURN"))
public void render(int mouseX, int mouseY, float delta, CallbackInfo ci)
{
if (LambdaControlsClient.get().config.get_controls_mode() == ControlsMode.CONTROLLER) {
MinecraftClient client = MinecraftClient.getInstance();
int x = 10, y = client.getWindow().getScaledHeight() - 10 - 15;
x += LambdaControlsClient.draw_button_tip(x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_A}, "lambdacontrols.action.pickup_all", true, client) + 10;
x += LambdaControlsClient.draw_button_tip(x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_B}, "lambdacontrols.action.exit", true, client) + 10;
x += LambdaControlsClient.draw_button_tip(x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_X}, "lambdacontrols.action.pickup", true, client) + 10;
LambdaControlsClient.draw_button_tip(x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_Y}, "lambdacontrols.action.quick_move", true, client);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsControlsScreen;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsSettingsScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.options.ControlsOptionsScreen;
import net.minecraft.client.gui.screen.options.GameOptionsScreen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Injects the new controls settings button.
*/
@Mixin(ControlsOptionsScreen.class)
public class ControlsOptionsScreenMixin extends GameOptionsScreen
{
public ControlsOptionsScreenMixin(Screen parent, GameOptions game_options, Text text)
{
super(parent, game_options, text);
}
@Redirect(method = "init", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/options/ControlsOptionsScreen;addButton(Lnet/minecraft/client/gui/widget/AbstractButtonWidget;)Lnet/minecraft/client/gui/widget/AbstractButtonWidget;", ordinal = 1))
private AbstractButtonWidget on_init(ControlsOptionsScreen screen, AbstractButtonWidget btn)
{
if (this.parent instanceof LambdaControlsControlsScreen)
return this.addButton(btn);
else
return this.addButton(new ButtonWidget(btn.x, btn.y, btn.getWidth(), ((AbstractButtonWidgetAccessor) btn).get_height(), I18n.translate("menu.options"),
b -> this.minecraft.openScreen(new LambdaControlsSettingsScreen(this, this.gameOptions, true))));
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.client.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);
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import net.minecraft.client.gui.widget.EntryListWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(EntryListWidget.class)
public interface EntryListWidgetAccessor
{
@Invoker("moveSelection")
void move_selection(int amount);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
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/Mouse;getX()D"))
private void on_render(float tick_delta, long start_time, boolean full_render, CallbackInfo ci)
{
if (this.client.currentScreen != null && LambdaControlsClient.get().config.get_controls_mode() == ControlsMode.CONTROLLER)
LambdaControlsClient.get().input.on_pre_render_screen(this.client, this.client.currentScreen);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.util.InputUtil;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(KeyBinding.class)
public class KeyBindingMixin implements KeyBindingAccessor
{
@Shadow
private InputUtil.KeyCode keyCode;
@Shadow
private int timesPressed;
@Shadow
private boolean pressed;
@Override
public @NotNull InputUtil.KeyCode get_key_code()
{
return this.keyCode;
}
@Override
public boolean lambdacontrols_press()
{
boolean old_pressed = this.pressed;
if (!this.pressed)
this.pressed = true;
++this.timesPressed;
return old_pressed != this.pressed;
}
@Override
public boolean lambdacontrols_unpress()
{
if (this.pressed) {
this.pressed = false;
return true;
}
return false;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.gui.TouchscreenOverlay;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.Window;
import org.jetbrains.annotations.Nullable;
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(MinecraftClient.class)
public abstract class MinecraftClientMixin
{
@Final
@Shadow
private Window window;
@Shadow
public boolean skipGameRender;
@Shadow
public Screen currentScreen;
@Inject(method = "<init>", at = @At("RETURN"))
private void on_init(CallbackInfo ci)
{
LambdaControlsClient.get().on_mc_init((MinecraftClient) (Object) this);
}
@Inject(method = "render", at = @At("HEAD"))
private void on_render(boolean full_render, CallbackInfo ci)
{
LambdaControlsClient.get().on_render((MinecraftClient) (Object) (this));
}
@Inject(method = "tick", at = @At("HEAD"))
private void on_handle_input_events(CallbackInfo ci)
{
LambdaControlsClient.get().on_tick((MinecraftClient) (Object) this);
}
@Inject(method = "openScreen", at = @At("RETURN"))
private void on_open_screen(@Nullable Screen screen, CallbackInfo ci)
{
LambdaControlsClient mod = LambdaControlsClient.get();
if (screen == null && mod.config.get_controls_mode() == ControlsMode.TOUCHSCREEN) {
screen = new TouchscreenOverlay(mod);
screen.init(((MinecraftClient) (Object) this), this.window.getScaledWidth(), this.window.getScaledHeight());
this.skipGameRender = false;
this.currentScreen = screen;
} else if (screen != null) {
mod.input.on_screen_open(((MinecraftClient) (Object) this), this.window.getWidth(), this.window.getHeight());
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.util.MouseAccessor;
import net.minecraft.client.Mouse;
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;
/**
* Adds extra access to the mouse.
*/
@Mixin(Mouse.class)
public abstract class MouseMixin implements MouseAccessor
{
@Shadow
protected abstract void onCursorPos(long window, double x, double y);
@Shadow
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)
{
if (LambdaControlsClient.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);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsControlsScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.SettingsScreen;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Injects the new controls settings button.
*/
@Mixin(SettingsScreen.class)
public class SettingsScreenMixin extends Screen
{
protected SettingsScreenMixin(Text title)
{
super(title);
}
@Redirect(method = "init", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/SettingsScreen;addButton(Lnet/minecraft/client/gui/widget/AbstractButtonWidget;)Lnet/minecraft/client/gui/widget/AbstractButtonWidget;", ordinal = 7))
private AbstractButtonWidget on_init(SettingsScreen screen, AbstractButtonWidget btn)
{
if (LambdaControlsClient.get().config.get_controls_mode() == ControlsMode.CONTROLLER) {
return this.addButton(new ButtonWidget(btn.x, btn.y, btn.getWidth(), ((AbstractButtonWidgetAccessor) btn).get_height(), btn.getMessage(),
b -> this.minecraft.openScreen(new LambdaControlsControlsScreen(this, false))));
} else {
return this.addButton(btn);
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.util;
import net.minecraft.container.Slot;
/**
* Represents an accessor to AbstractContainerScreen.
*/
public interface AbstractContainerScreenAccessor
{
/**
* Gets the left coordinate of the GUI.
*
* @return The left coordinate of the GUI.
*/
int get_x();
/**
* Gets the top coordinate of the GUI.
*
* @return The top coordinate of the GUI.
*/
int get_y();
/**
* Gets the slot at position.
*
* @param pos_x The X position to check.
* @param pos_y The Y position to check.
* @return The slot at the specified position.
*/
Slot get_slot_at(double pos_x, double pos_y);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.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);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.util;
import net.minecraft.client.util.InputUtil;
import org.jetbrains.annotations.NotNull;
/**
* Represents a Minecraft keybinding with extra access.
*/
public interface KeyBindingAccessor
{
@NotNull InputUtil.KeyCode get_key_code();
boolean lambdacontrols_press();
boolean lambdacontrols_unpress();
default boolean handle_press_state(boolean pressed)
{
if (pressed)
return this.lambdacontrols_press();
else
return this.lambdacontrols_unpress();
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.client.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);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.event;
import me.lambdaurora.lambdacontrols.ControlsMode;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.player.PlayerEntity;
import org.jetbrains.annotations.NotNull;
/**
* Represents an event callback which is fired when a player changes the controls mode.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
@FunctionalInterface
public interface PlayerChangeControlsModeCallback
{
Event<PlayerChangeControlsModeCallback> EVENT = EventFactory.createArrayBacked(PlayerChangeControlsModeCallback.class, listeners -> (player, controls_mode) -> {
for (PlayerChangeControlsModeCallback event : listeners) {
event.apply(player, controls_mode);
}
});
void apply(@NotNull PlayerEntity player, @NotNull ControlsMode controls_mode);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -0,0 +1,111 @@
{
"key.lambdacontrols.look_down": "Look down",
"key.lambdacontrols.look_left": "Look left",
"key.lambdacontrols.look_right": "Look right",
"key.lambdacontrols.look_up": "Look up",
"lambdacontrols.action.attack": "Attack",
"lambdacontrols.action.back": "Back",
"lambdacontrols.action.chat": "Open Chat",
"lambdacontrols.action.drop_item": "Drop Item",
"lambdacontrols.action.exit": "Exit",
"lambdacontrols.action.forward": "Forward",
"lambdacontrols.action.hit": "Hit",
"lambdacontrols.action.hotbar_left": "Hotbar left",
"lambdacontrols.action.hotbar_right": "Hotbar right",
"lambdacontrols.action.inventory": "Inventory",
"lambdacontrols.action.jump": "Jump",
"lambdacontrols.action.left": "Left",
"lambdacontrols.action.pause_game": "Pause Game",
"lambdacontrols.action.pick_block": "Pick Block",
"lambdacontrols.action.pickup": "Pickup",
"lambdacontrols.action.pickup_all": "Pickup all",
"lambdacontrols.action.player_list": "Player List",
"lambdacontrols.action.quick_move": "Quick move",
"lambdacontrols.action.right": "Right",
"lambdacontrols.action.screenshot": "Take Screenshot",
"lambdacontrols.action.sneak": "Sneak",
"lambdacontrols.action.sprint": "Sprint",
"lambdacontrols.action.swap_hands": "Swap Hands",
"lambdacontrols.action.toggle_perspective": "Toggle Perspective",
"lambdacontrols.action.toggle_smooth_camera": "Toggle Cinematic Camera",
"lambdacontrols.action.use": "Use",
"lambdacontrols.action.zoom": "Zoom",
"lambdacontrols.button.a": "A",
"lambdacontrols.button.b": "B",
"lambdacontrols.button.x": "X",
"lambdacontrols.button.y": "Y",
"lambdacontrols.button.left_bumper": "Left bumper",
"lambdacontrols.button.right_bumper": "Right bumper",
"lambdacontrols.button.back": "Back",
"lambdacontrols.button.start": "Start",
"lambdacontrols.button.guide": "Guide",
"lambdacontrols.button.left_thumb": "Left thumb",
"lambdacontrols.button.right_thumb": "Right thumb",
"lambdacontrols.button.dpad_up": "DPAD up",
"lambdacontrols.button.dpad_right": "DPAD right",
"lambdacontrols.button.dpad_down": "DPAD down",
"lambdacontrols.button.dpad_left": "DPAD left",
"lambdacontrols.axis.left_x+": "Left X+",
"lambdacontrols.axis.left_y+": "Left Y+",
"lambdacontrols.axis.right_x+": "Right X+",
"lambdacontrols.axis.right_y+": "Right Y+",
"lambdacontrols.axis.left_trigger": "Left trigger",
"lambdacontrols.axis.right_trigger": "Right trigger",
"lambdacontrols.axis.left_x-": "Left X-",
"lambdacontrols.axis.left_y-": "Left Y-",
"lambdacontrols.axis.right_x-": "Right X-",
"lambdacontrols.axis.right_y-": "Right Y-",
"lambdacontrols.button.unknown": "Unknown (%d)",
"lambdacontrols.controller.connected": "Controller %d connected.",
"lambdacontrols.controller.disconnected": "Controller %d disconnected.",
"lambdacontrols.controller.mappings.1": "To configure the controller mappings, please use %sSDL2 Gamepad Tool%s",
"lambdacontrols.controller.mappings.2": "(%s%s%s),",
"lambdacontrols.controller.mappings.3": "and put the mapping in `%s.minecraft/config/gamecontrollerdb.txt%s`.",
"lambdacontrols.controller.mappings.updated": "Updated mappings!",
"lambdacontrols.controller_type.default": "default",
"lambdacontrols.controller_type.dualshock": "DualShock",
"lambdacontrols.controller_type.switch": "Switch",
"lambdacontrols.controller_type.xbox": "Xbox",
"lambdacontrols.controller_type.steam": "Steam",
"lambdacontrols.controller_type.ouya": "OUYA",
"lambdacontrols.controls_mode.default": "Keyboard/Mouse",
"lambdacontrols.controls_mode.controller": "Controller",
"lambdacontrols.controls_mode.touchscreen": "Touchscreen",
"lambdacontrols.hud_side.left": "left",
"lambdacontrols.hud_side.right": "right",
"lambdacontrols.menu.auto_switch_mode": "Auto Switch Mode",
"lambdacontrols.menu.controller": "Controller",
"lambdacontrols.menu.controller2": "Second Controller",
"lambdacontrols.menu.controller_type": "Controller Type",
"lambdacontrols.menu.controls_mode": "Mode",
"lambdacontrols.menu.dead_zone": "Dead Zone",
"lambdacontrols.menu.front_block_placing": "Front Block Placing",
"lambdacontrols.menu.hud_enable": "Enable HUD",
"lambdacontrols.menu.hud_side": "HUD Side",
"lambdacontrols.menu.invert_right_x_axis": "Invert Right X",
"lambdacontrols.menu.invert_right_y_axis": "Invert Right Y",
"lambdacontrols.menu.keyboard_controls": "Keyboard Controls...",
"lambdacontrols.menu.mouse_speed": "Mouse Speed",
"lambdacontrols.menu.reload_controller_mappings": "Reload Controller Mappings",
"lambdacontrols.menu.rotation_speed": "Rotation Speed",
"lambdacontrols.menu.title": "LambdaControls - Settings",
"lambdacontrols.menu.title.controller": "Controller Options",
"lambdacontrols.menu.title.controller_controls": "Controller Controls",
"lambdacontrols.menu.title.gameplay": "Gameplay Options",
"lambdacontrols.menu.title.general": "General Options",
"lambdacontrols.menu.title.hud": "HUD Options",
"lambdacontrols.menu.unbound": "Unbound",
"lambdacontrols.narrator.unbound": "Unbound %s",
"lambdacontrols.not_bound": "Not bound",
"lambdacontrols.tooltip.auto_switch_mode": "If the controls mode should be switched to Controller automatically if one is connected.",
"lambdacontrols.tooltip.controller2": "Second controller to use, which allows Joy-Cons support for example.",
"lambdacontrols.tooltip.controller_type": "The controller type to display the correct buttons.",
"lambdacontrols.tooltip.controls_mode": "The controls mode.",
"lambdacontrols.tooltip.dead_zone": "The dead zone for the controller's analogue sticks.",
"lambdacontrols.tooltip.front_block_placing": "Enable front block placing, §cmight be considered cheating on some servers§r.",
"lambdacontrols.tooltip.hud_enable": "Toggles the on-screen controller button indicator.",
"lambdacontrols.tooltip.hud_side": "The position of the HUD.",
"lambdacontrols.tooltip.mouse_speed": "The controller's emulated mouse speed.",
"lambdacontrols.tooltip.rotation_speed": "The camera rotation speed in controller mode.",
"lambdacontrols.tooltip.reload_controller_mappings": "Reloads the controller mappings file."
}

View File

@@ -0,0 +1,108 @@
{
"key.lambdacontrols.look_down": "Regarder en bas",
"key.lambdacontrols.look_left": "Regarder à gauche",
"key.lambdacontrols.look_right": "Regarder à droite",
"key.lambdacontrols.look_up": "Regarder en haut",
"lambdacontrols.action.attack": "Attaquer",
"lambdacontrols.action.back": "Reculer",
"lambdacontrols.action.chat": "Ouvrir le tchat",
"lambdacontrols.action.drop_item": "Jeter l'objet",
"lambdacontrols.action.exit": "Sortir",
"lambdacontrols.action.forward": "Avancer",
"lambdacontrols.action.hit": "Taper",
"lambdacontrols.action.hotbar_left": "Case à gauche de la barre d'action",
"lambdacontrols.action.hotbar_right": "Case à droite de la barre d'action",
"lambdacontrols.action.inventory": "Inventaire",
"lambdacontrols.action.jump": "Sauter",
"lambdacontrols.action.left": "Aller à gauche",
"lambdacontrols.action.pause_game": "Mettre en pause le jeu",
"lambdacontrols.action.pick_block": "Choisir le bloc",
"lambdacontrols.action.pickup": "Prendre",
"lambdacontrols.action.pickup_all": "Prendre tout",
"lambdacontrols.action.player_list": "Afficher la liste des joueurs",
"lambdacontrols.action.quick_move": "Mouvement rapide",
"lambdacontrols.action.right": "Aller à droite",
"lambdacontrols.action.screenshot": "Prendre une capture d'écran",
"lambdacontrols.action.sneak": "S'accroupir",
"lambdacontrols.action.sprint": "Courir",
"lambdacontrols.action.swap_hands": "Échanger de mains",
"lambdacontrols.action.toggle_perspective": "Changer de point de vue",
"lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique",
"lambdacontrols.action.use": "Utiliser",
"lambdacontrols.action.zoom": "Zoom",
"lambdacontrols.button.a": "A",
"lambdacontrols.button.b": "B",
"lambdacontrols.button.x": "X",
"lambdacontrols.button.y": "Y",
"lambdacontrols.button.left_bumper": "Gâchette haute gauche",
"lambdacontrols.button.right_bumper": "Gâchette haute droite",
"lambdacontrols.button.back": "Retour",
"lambdacontrols.button.start": "Touche Menu",
"lambdacontrols.button.guide": "Guide",
"lambdacontrols.button.left_thumb": "Stick gauche",
"lambdacontrols.button.right_thumb": "Stick droit",
"lambdacontrols.button.dpad_up": "D-Pad haut",
"lambdacontrols.button.dpad_right": "D-Pad droit",
"lambdacontrols.button.dpad_down": "D-Pad bas",
"lambdacontrols.button.dpad_left": "D-Pad gauche",
"lambdacontrols.axis.left_x+": "X+ Gauche",
"lambdacontrols.axis.left_y+": "Y+ Gauche",
"lambdacontrols.axis.right_x+": "X+ Droit",
"lambdacontrols.axis.right_y+": "Y+ Droit",
"lambdacontrols.axis.left_trigger": "Gâchette gauche",
"lambdacontrols.axis.right_trigger": "Gâchette droite",
"lambdacontrols.axis.left_x-": "X- Gauche",
"lambdacontrols.axis.left_y-": "Y- Gauche",
"lambdacontrols.axis.right_x-": "X- Droit",
"lambdacontrols.axis.right_y-": "Y- Droit",
"lambdacontrols.button.unknown": "Inconnu (%d)",
"lambdacontrols.controller.connected": "Manette %d connecté.",
"lambdacontrols.controller.disconnected": "Manette %d déconnecté.",
"lambdacontrols.controller.mappings.1": "Pour configurer les correspondances de la manette, veuillez utiliser %sSDL2 Gamepad Tool%s",
"lambdacontrols.controller.mappings.2": "(%s%s%s),",
"lambdacontrols.controller.mappings.3": "et mettez les correspondances dans le fichier `%s.minecraft/config/gamecontrollerdb.txt%s`.",
"lambdacontrols.controller.mappings.updated": "Configuration des manettes mise à jour!",
"lambdacontrols.controller_type.default": "default",
"lambdacontrols.controller_type.dualshock": "DualShock",
"lambdacontrols.controller_type.switch": "Switch",
"lambdacontrols.controller_type.xbox": "Xbox",
"lambdacontrols.controller_type.steam": "Steam",
"lambdacontrols.controller_type.ouya": "OUYA",
"lambdacontrols.controls_mode.default": "Clavier/Souris",
"lambdacontrols.controls_mode.controller": "Manette",
"lambdacontrols.controls_mode.touchscreen": "Tactile",
"lambdacontrols.hud_side.left": "gauche",
"lambdacontrols.hud_side.right": "droit",
"lambdacontrols.menu.auto_switch_mode": "Changement auto de mode",
"lambdacontrols.menu.controller": "Manette",
"lambdacontrols.menu.controller2": "Deuxième manette",
"lambdacontrols.menu.controller_type": "Type de manette",
"lambdacontrols.menu.controls_mode": "Mode",
"lambdacontrols.menu.dead_zone": "Zone morte",
"lambdacontrols.menu.hud_enable": "Activer le HUD",
"lambdacontrols.menu.hud_side": "Côté du HUD",
"lambdacontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)",
"lambdacontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)",
"lambdacontrols.menu.keyboard_controls": "Contrôles clavier...",
"lambdacontrols.menu.mouse_speed": "Vitesse de la souris",
"lambdacontrols.menu.reload_controller_mappings": "Recharge la configuration des manettes",
"lambdacontrols.menu.rotation_speed": "Vitesse de rotation",
"lambdacontrols.menu.title": "LambdaControls - Paramètres",
"lambdacontrols.menu.title.controller": "Options de manettes",
"lambdacontrols.menu.title.controller_controls": "Contrôles de la manette",
"lambdacontrols.menu.title.general": "Options générales",
"lambdacontrols.menu.title.hud": "Options du HUD",
"lambdacontrols.menu.unbound": "Délier",
"lambdacontrols.narrator.unbound": "Délier %s",
"lambdacontrols.not_bound": "Non défini",
"lambdacontrols.tooltip.auto_switch_mode": "Détermine si le mode de contrôle doit automatiquement changer sur Manette si une manette est connectée et inversement.",
"lambdacontrols.tooltip.controller2": "Défini une deuxième manette, utile dans le cas d'utilisation de Joy-Cons.",
"lambdacontrols.tooltip.controller_type": "Le type de contrôle n'influe que sur les boutons affichés.",
"lambdacontrols.tooltip.controls_mode": "Change le mode de contrôle.",
"lambdacontrols.tooltip.dead_zone": "Détermine la zone morte des axes de la manette.",
"lambdacontrols.tooltip.hud_enable": "Détermine si l'indicateur des buttons de la manette doit être affiché ou non.",
"lambdacontrols.tooltip.hud_side": "Change la position du HUD.",
"lambdacontrols.tooltip.mouse_speed": "Change la vitesse de la souris émulée par la manette.",
"lambdacontrols.tooltip.rotation_speed": "Change la vitesse de rotation de la caméra.",
"lambdacontrols.tooltip.reload_controller_mappings": "Recharge le fichier de configuration des manettes."
}

View File

@@ -0,0 +1,108 @@
{
"key.lambdacontrols.look_down": "Regarder en bas",
"key.lambdacontrols.look_left": "Regarder à gauche",
"key.lambdacontrols.look_right": "Regarder à droite",
"key.lambdacontrols.look_up": "Regarder en haut",
"lambdacontrols.action.attack": "Attaquer",
"lambdacontrols.action.back": "Reculer",
"lambdacontrols.action.chat": "Ouvrir le tchat",
"lambdacontrols.action.drop_item": "Jeter l'objet",
"lambdacontrols.action.exit": "Sortir",
"lambdacontrols.action.forward": "Avancer",
"lambdacontrols.action.hit": "Taper",
"lambdacontrols.action.hotbar_left": "Case à gauche de la barre d'action",
"lambdacontrols.action.hotbar_right": "Case à droite de la barre d'action",
"lambdacontrols.action.inventory": "Inventaire",
"lambdacontrols.action.jump": "Sauter",
"lambdacontrols.action.left": "Aller à gauche",
"lambdacontrols.action.pause_game": "Mettre en pause le jeu",
"lambdacontrols.action.pick_block": "Choisir le bloc",
"lambdacontrols.action.pickup": "Prendre",
"lambdacontrols.action.pickup_all": "Prendre tout",
"lambdacontrols.action.player_list": "Afficher la liste des joueurs",
"lambdacontrols.action.quick_move": "Mouvement rapide",
"lambdacontrols.action.right": "Aller à droite",
"lambdacontrols.action.screenshot": "Prendre une capture d'écran",
"lambdacontrols.action.sneak": "S'accroupir",
"lambdacontrols.action.sprint": "Courir",
"lambdacontrols.action.swap_hands": "Échanger de mains",
"lambdacontrols.action.toggle_perspective": "Changer de point de vue",
"lambdacontrols.action.toggle_smooth_camera": "Basculer en mode cinématique",
"lambdacontrols.action.use": "Utiliser",
"lambdacontrols.action.zoom": "Zoom",
"lambdacontrols.button.a": "A",
"lambdacontrols.button.b": "B",
"lambdacontrols.button.x": "X",
"lambdacontrols.button.y": "Y",
"lambdacontrols.button.left_bumper": "Gâchette haute gauche",
"lambdacontrols.button.right_bumper": "Gâchette haute droite",
"lambdacontrols.button.back": "Retour",
"lambdacontrols.button.start": "Touche Menu",
"lambdacontrols.button.guide": "Guide",
"lambdacontrols.button.left_thumb": "Stick gauche",
"lambdacontrols.button.right_thumb": "Stick droit",
"lambdacontrols.button.dpad_up": "D-Pad haut",
"lambdacontrols.button.dpad_right": "D-Pad droit",
"lambdacontrols.button.dpad_down": "D-Pad bas",
"lambdacontrols.button.dpad_left": "D-Pad gauche",
"lambdacontrols.axis.left_x+": "X+ Gauche",
"lambdacontrols.axis.left_y+": "Y+ Gauche",
"lambdacontrols.axis.right_x+": "X+ Droit",
"lambdacontrols.axis.right_y+": "Y+ Droit",
"lambdacontrols.axis.left_trigger": "Gâchette gauche",
"lambdacontrols.axis.right_trigger": "Gâchette droite",
"lambdacontrols.axis.left_x-": "X- Gauche",
"lambdacontrols.axis.left_y-": "Y- Gauche",
"lambdacontrols.axis.right_x-": "X- Droit",
"lambdacontrols.axis.right_y-": "Y- Droit",
"lambdacontrols.button.unknown": "Inconnu (%d)",
"lambdacontrols.controller.connected": "Manette %d connecté.",
"lambdacontrols.controller.disconnected": "Manette %d déconnecté.",
"lambdacontrols.controller.mappings.1": "Pour configurer les correspondances de la manette, veuillez utiliser %sSDL2 Gamepad Tool%s",
"lambdacontrols.controller.mappings.2": "(%s%s%s),",
"lambdacontrols.controller.mappings.3": "et mettez les correspondances dans le fichier `%s.minecraft/config/gamecontrollerdb.txt%s`.",
"lambdacontrols.controller.mappings.updated": "Configuration des manettes mise à jour!",
"lambdacontrols.controller_type.default": "default",
"lambdacontrols.controller_type.dualshock": "DualShock",
"lambdacontrols.controller_type.switch": "Switch",
"lambdacontrols.controller_type.xbox": "Xbox",
"lambdacontrols.controller_type.steam": "Steam",
"lambdacontrols.controller_type.ouya": "OUYA",
"lambdacontrols.controls_mode.default": "Clavier/Souris",
"lambdacontrols.controls_mode.controller": "Manette",
"lambdacontrols.controls_mode.touchscreen": "Tactile",
"lambdacontrols.hud_side.left": "gauche",
"lambdacontrols.hud_side.right": "droit",
"lambdacontrols.menu.auto_switch_mode": "Changement auto de mode",
"lambdacontrols.menu.controller": "Manette",
"lambdacontrols.menu.controller2": "Deuxième manette",
"lambdacontrols.menu.controller_type": "Type de manette",
"lambdacontrols.menu.controls_mode": "Mode",
"lambdacontrols.menu.dead_zone": "Zone morte",
"lambdacontrols.menu.hud_enable": "Activer le HUD",
"lambdacontrols.menu.hud_side": "Côté du HUD",
"lambdacontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)",
"lambdacontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)",
"lambdacontrols.menu.keyboard_controls": "Contrôles clavier...",
"lambdacontrols.menu.mouse_speed": "Vitesse de la souris",
"lambdacontrols.menu.reload_controller_mappings": "Recharge la configuration des manettes",
"lambdacontrols.menu.rotation_speed": "Vitesse de rotation",
"lambdacontrols.menu.title": "LambdaControls - Paramètres",
"lambdacontrols.menu.title.controller": "Options de manettes",
"lambdacontrols.menu.title.controller_controls": "Contrôles de la manette",
"lambdacontrols.menu.title.general": "Options générales",
"lambdacontrols.menu.title.hud": "Options du HUD",
"lambdacontrols.menu.unbound": "Délier",
"lambdacontrols.narrator.unbound": "Délier %s",
"lambdacontrols.not_bound": "Non défini",
"lambdacontrols.tooltip.auto_switch_mode": "Détermine si le mode de contrôle doit automatiquement changer sur Manette si une manette est connectée et inversement.",
"lambdacontrols.tooltip.controller2": "Défini une deuxième manette, utile dans le cas d'utilisation de Joy-Cons.",
"lambdacontrols.tooltip.controller_type": "Le type de contrôle n'influe que sur les boutons affichés.",
"lambdacontrols.tooltip.controls_mode": "Change le mode de contrôle.",
"lambdacontrols.tooltip.dead_zone": "Zone morte des axes de la manette.",
"lambdacontrols.tooltip.hud_enable": "Détermine si l'indicateur des buttons de la manette doit être affiché ou non.",
"lambdacontrols.tooltip.hud_side": "Change la position du HUD.",
"lambdacontrols.tooltip.mouse_speed": "Change la vitesse de la souris émulée par la manette.",
"lambdacontrols.tooltip.rotation_speed": "Change la vitesse de rotation de la caméra.",
"lambdacontrols.tooltip.reload_controller_mappings": "Recharge le fichier de configuration des manettes."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,77 @@
# LambdaControls configuration.
# The controls mode. Available modes: default, controller, touchscreen
controls = "default"
# Auto switch mode.
auto_switch_mode = false
[hud]
# Enables the HUD.
enable = true
# Dertermines where the movements buttons are.
side = "left"
# Gameplay settings
[gameplay]
front_block_placing = false
# Controller settings
[controller]
# Controller to use.
id = 0
# Second controller to use.
id2 = -1
# Controller's type.
type = "default"
# Controller's dead zone.
dead_zone = 0.20
# Rotation speed for look directions.
rotation_speed = 40.0
# Mouse speed in GUI.
mouse_speed = 30.0
# Inverts the right X axis.
invert_right_x_axis = false
# Inverts the right Y axis.
invert_right_y_axis = false
# Controller controls.
[controller.controls]
# Attack control.
attack = "105"
# Back control.
back = "201"
# Open chat control.
chat = "12"
# Drop item control.
drop_item = "1"
# Forward control.
forward = "101"
# Hot-bar left control.
hotbar_left = "4"
# Hot-bar right control.
hotbar_right = "5"
# Inventory control.
inventory = "3"
# Jump control.
jump = "0"
# Pause game control.
pause_game = "7"
# Pick block control.
pick_block = "14"
# Show player list control.
player_list = "6"
# Take screenshot control.
screenshot = "11+0"
# Sneak control.
sneak = "10"
# Sprint control.
sprint = "9"
# Swap hands control.
swap_hands = "2"
# Toggle perspective control.
toggle_perspective = "11+3"
# Toggle smooth camera control.
toggle_smooth_camera = "-1"
# Use control.
use = "104"
# Zoom control.
zoom = "11+2"

View File

@@ -0,0 +1,48 @@
{
"schemaVersion": 1,
"id": "lambdacontrols",
"name": "LambdaControls",
"version": "${version}",
"description": "Adds better controls: controller and touchscreen support.",
"authors": [
"LambdAurora"
],
"contact": {
"homepage": "https://www.curseforge.com/minecraft/mc-mods/lambdacontrols",
"sources": "https://github.com/LambdAurora/LambdaControls.git",
"issues": "https://github.com/LambdAurora/LambdaControls/issues"
},
"license": "MIT",
"icon": "assets/lambdacontrols/icon.png",
"environment": "client",
"entrypoints": {
"main": [
"me.lambdaurora.lambdacontrols.LambdaControls"
],
"client": [
"me.lambdaurora.lambdacontrols.client.LambdaControlsClient"
],
"modmenu": [
"me.lambdaurora.lambdacontrols.client.LambdaControlsModMenu"
]
},
"mixins": [
"lambdacontrols.mixins.json"
],
"depends": {
"fabricloader": ">=0.4.0",
"fabric": "*",
"minecraft": "1.15.x",
"spruceui": ">=1.0.1"
},
"recommends": {
"modmenu": ">=1.8.0+build.16",
"okzoomer": ">=1.0.3"
},
"suggests": {
"flamingo": "*"
},
"custom": {
"modmenu:clientsideOnly": true
}
}

View File

@@ -0,0 +1,20 @@
{
"required": true,
"package": "me.lambdaurora.lambdacontrols.client.mixin",
"compatibilityLevel": "JAVA_8",
"client": [
"AbstractButtonWidgetAccessor",
"AbstractContainerScreenMixin",
"ControlsOptionsScreenMixin",
"CreativeInventoryScreenMixin",
"EntryListWidgetAccessor",
"GameRendererMixin",
"KeyBindingMixin",
"MinecraftClientMixin",
"MouseMixin",
"SettingsScreenMixin"
],
"injectors": {
"defaultRequire": 1
}
}