Compare commits

...

43 Commits

Author SHA1 Message Date
LambdAurora
4669e446dc 🔖 LambdaControls v1.3.0: Update to Minecraft 1.16.1. 2020-06-27 19:05:20 +02:00
LambdAurora
ff672f05a1 🔖 LambdaControls v1.2.0: Fast block placement, virtual mouse, etc. 2020-06-27 18:40:35 +02:00
LambdAurora
756e7d102d Improve rotation algorithm. 2020-06-27 18:21:46 +02:00
LambdAurora
e676a37c7f Update to 1.16. 2020-06-27 16:08:10 +02:00
LambdAurora
45dd94fd34 🐛 Fix some little bugs. 2020-02-18 19:20:32 +01:00
LambdAurora
f8cc1fcc20 🚧 More WIP on REI as hooking into it is quite difficult. 2020-02-18 18:09:50 +01:00
LambdAurora
d56126c680 🚧 WIP on REI compatiblity. 2020-02-18 10:43:27 +01:00
LambdAurora
5a2fad4445 🚧 WIP on REI compatiblity. 2020-02-18 10:01:39 +01:00
LambdAurora
0eadc054b6 Add proper fast block placing. 2020-02-17 22:52:59 +01:00
LambdAurora
07a296603a 🚧 WIP on fast block placing. 2020-02-17 12:05:06 +01:00
LambdAurora
8e082404f9 Add always more features. 2020-02-16 23:20:21 +01:00
LambdAurora
8efddb24b0 🎨 White outline for front block placing. 2020-02-16 10:55:21 +01:00
LambdAurora
581757aaee Add new recipe book controls and front block outline. 2020-02-16 00:46:03 +01:00
LambdAurora
40dc8d424c 🎨 Update README. 2020-02-15 00:20:18 +01:00
LambdAurora
230a9f6424 🎨 Update README. 2020-02-15 00:19:11 +01:00
LambdAurora
ba01df036b 🎨 Update README. 2020-02-15 00:02:02 +01:00
LambdAurora
ff90d55351 Improve HUD, add autojump option. 2020-02-14 19:30:54 +01:00
LambdAurora
efc2d6284d 🎨 Quick code cleaning. 2020-02-13 11:54:17 +01:00
LambdAurora
9e2c4720e9 🚧 WIP on better button tip rendering. 2020-02-13 11:51:29 +01:00
LambdAurora
8063116820 Some refactor and add missing behaviors in some GUIs. 2020-02-13 00:28:58 +01:00
LambdAurora
0050b0216c 🔖 LambdaControls v1.1.1: Add more precision in the fly drifting tooltips. 2020-02-05 15:39:19 +01:00
LambdAurora
e13569b71d Add precision to some tooltips and prepare for the fast/accurate block placement feature. 2020-02-05 13:28:23 +01:00
LambdAurora
47b0cfdad4 🔖 LambdaControls v1.1.0: Chording update. 2020-02-01 00:44:13 +01:00
LambdAurora
f56eb9e0ad 🔧 Fix build task dependency. 2020-01-16 11:58:38 +01:00
LambdAurora
bc712f138b Add new unfocused input option, improve front block placing. 2020-01-15 23:57:02 +01:00
LambdAurora
66721b00b9 💬 Increase max speed ranges. 2020-01-15 18:20:10 +01:00
LambdAurora
e0762b16ee ⬆️ Update SpruceUI to fix a crash with Not Enough Crashes. 2020-01-15 17:28:05 +01:00
LambdAurora
7bfff00841 🔧 Fix some dependency management. 2020-01-14 11:33:19 +01:00
LambdAurora
a8c969b176 🔧 Fix SpruceUI dependency for quick builds. 2020-01-13 00:52:47 +01:00
LambdAurora
f1607fe1bf 🔧 Fix ivy pattern layouts for SpruceUI. 2020-01-13 00:32:48 +01:00
LambdAurora
d52a630cb4 🔧 Fix ivy pattern layouts for SpruceUI and OkZoomer. 2020-01-13 00:28:05 +01:00
LambdAurora
502e9ff15e 🎨 Improve fly drifting injection. 2020-01-12 21:45:12 +01:00
LambdAurora
32e4ad1b7c Add fly drifting option. 2020-01-12 21:33:28 +01:00
LambdAurora
4ae6404ad2 🔧 Fix gradle project and update README. 2020-01-12 10:44:18 +01:00
LambdAurora
185d5e4303 🌐 Update localizations. 2020-01-11 21:04:16 +01:00
LambdAurora
0d02e56134 Add front block placing and clean up the code. 2020-01-11 20:02:24 +01:00
LambdAurora
738d094fb1 💥 Lot of breaking changes. 2020-01-04 15:19:02 +01:00
LambdAurora
7c06afb4a4 🐛 Fix config fixer. 2020-01-02 00:43:10 +01:00
LambdAurora
647e03134f Add chording and new button assignment system. 2020-01-02 00:31:00 +01:00
LambdAurora
1b5a0e470e Add working in-game chording, WIP controls screen. 2020-01-01 14:54:51 +01:00
LambdAurora
e6da7adf89 🔥 Remove now unused mixin. 2020-01-01 02:02:48 +01:00
LambdAurora
46d43c6f46 🚧 WIP of chording and next release. 2020-01-01 01:54:14 +01:00
LambdAurora
9ae1bc2a3b 🐛 Fix sneak toggle, arrow keys in chat not working, etc... 2019-12-23 00:01:57 +01:00
109 changed files with 8766 additions and 3719 deletions

47
CHANGELOG.md Normal file
View File

@@ -0,0 +1,47 @@
# Changelog
## v1.0.0
:tada: First release! :tada:
- Added controller support.
- Added new controls settings GUI.
- Added experimental touchscreen support.
- Added controller controls GUI.
- Added a lot of options.
- Added key bindings for look around.
- And more!
### v1.0.1
- Fixed tutorial toast to look around not affected by camera movement done with a controller. ([#2](https://github.com/LambdAurora/LambdaControls/issues/2))
### v1.0.2 (Unofficial)
This update was never pushed but was aiming to fix [#4](https://github.com/LambdAurora/LambdaControls/issues/4).
- Fixed the toggle sneak button binding.
- Fixed broken chat arrow keys.
- Optimized a little bit the button indicator. (need more work)
## v1.1.0 - Chording update
This update also has a backport 1.14.4 version ([#9](https://github.com/LambdAurora/LambdaControls/issues/9)).
- Rewrote everything (almost).
- Added [networking](https://github.com/LambdAurora/LambdaControls/wiki/LambdaControls-Networking) for some features.
- Added second controller support (Joycons supported now hopefully).
- Added chording.
- Added better developer API
- Added hover messages ([#5](https://github.com/LambdAurora/LambdaControls/issues/5)).
- Added hotbar button bindings ([#7](https://github.com/LambdAurora/LambdaControls/issues/7)).
- Added front block placing feature ([#8](https://github.com/LambdAurora/LambdaControls/issues/8)).
- Added no creative fly drifting ([#8](https://github.com/LambdAurora/LambdaControls/issues/8)).
- Added option to enable controller focus.
- Added [OkZoomer](https://github.com/joaoh1/OkZoomer) compatibility.
- Added D-pad movements in inventories.
- Increased max speed ranges.
- Added [SpruceUI](https://github.com/LambdAurora/SpruceUI) for cleaner custom UI widgets.
- Added reset settings button.
- HUD side affects button indicators now.
- Added support for Advancements tabs.

View File

@@ -1,6 +1,6 @@
MIT License
Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -3,6 +3,39 @@
![Java 8](https://img.shields.io/badge/language-Java%208-9B599A.svg?style=flat-square)
[![GitHub license](https://img.shields.io/github/license/LambdAurora/LambdaControls?style=flat-square)](https://raw.githubusercontent.com/LambdAurora/LambdaControls/master/LICENSE)
![Environment: Client](https://img.shields.io/badge/environment-client-1976d2?style=flat-square)
![Mod loader: Fabric](https://img.shields.io/badge/modloader-Fabric-1976d2?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTEyLTE2VDE2OjU0OjE3LTA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0wNy0yOFQyMToxNzo0OC0wNzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wNy0yOFQyMToxNzo0OC0wNzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowZWRiMWMyYy1mZjhjLWU0NDEtOTMxZi00OTVkNGYxNGM3NjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MGVkYjFjMmMtZmY4Yy1lNDQxLTkzMWYtNDk1ZDRmMTRjNzYwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MGVkYjFjMmMtZmY4Yy1lNDQxLTkzMWYtNDk1ZDRmMTRjNzYwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDowZWRiMWMyYy1mZjhjLWU0NDEtOTMxZi00OTVkNGYxNGM3NjAiIHN0RXZ0OndoZW49IjIwMTgtMTItMTZUMTY6NTQ6MTctMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE4IChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4/HiGMAAAAtUlEQVRYw+XXrQqAMBQF4D2P2eBL+QIG8RnEJFaNBjEum+0+zMQLtwwv+wV3ZzhhMDgfJ0wUSinxZUQWgKos1JP/AbD4OneIDyQPwCFniA+EJ4CaXm4TxAXCC0BNHgLhAdAnx9hC8PwGSRtAFVMQjF7cNTWED8B1cgwW20yfJgAvrssAsZ1cB3g/xckAxr6FmCDU5N6f488BrpCQ4rQBJkiMYh4ACmLzwOQF0CExinkCsvw7vgGikl+OotaKRwAAAABJRU5ErkJggg==)
![Version](https://img.shields.io/github/v/tag/LambdAurora/LambdaControls?label=version&style=flat-square)
[![CurseForge](http://cf.way2muchnoise.eu/title/354231.svg)](https://www.curseforge.com/minecraft/mc-mods/lambdacontrols)
A Fabric Minecraft mod which add better controls.
It allows the use of a controller or the touchscreen.
A Fabric Minecraft mod which adds better controls like controller support.
## What's this mod?
Haven't you dreamed to travel in your modded Minecraft world with your controller? Yes? Then this mod is made for you!
This mod adds a controller support (and an experimental touchscreen support).
## ✅ Features:
- Controller support
- Touchscreen support (very experimental and buggy).
- Keyboard controls to look around.
- Toggleable on screen button indicator (like in Bedrock Edition).
- Many Bedrock Edition features:
- Toggleable fly drifting
- Front block placing (be careful with this one)
- New controls settings!
- Many options in config to change to your liking.
- Many controllers supported and in a simply way your own controller mappings.
## 🎮 Supported Controllers:
- Dualshock controllers
- Xbox controllers
- Switch Pro controllers
- Joycons
- And many more!
## Build
Just do `./gradlew :fabric:build` and everything should build just fine!

View File

@@ -1,88 +1,35 @@
plugins {
id 'fabric-loom' version '0.2.6-SNAPSHOT'
id 'java-library'
id 'maven-publish'
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
allprojects {
group = project.maven_group
version = project.mod_version
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
minecraft {
}
repositories {
mavenLocal()
mavenCentral()
maven { url = 'https://aperlambda.github.io/maven' }
maven {
name = 'CottonMC'
url = 'http://server.bbkr.space:8081/artifactory/libs-snapshot'
}
repositories {
maven { url = "https://jitpack.io" }
mavenLocal()
mavenCentral()
maven { url = 'https://aperlambda.github.io/maven' }
}
dependencies {
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
}
}
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 "io.github.cottonmc:cotton-client-commands:0.4.2+1.14.3-SNAPSHOT"
implementation "org.jetbrains:annotations:17.0.0"
implementation "org.aperlambda:lambdajcommon:1.7.2"
implementation "com.electronwill.night-config:core:3.5.3"
implementation "com.electronwill.night-config:toml:3.5.3"
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"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
}
processResources {
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"
}
}
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
}
jar {
from "LICENSE"
}
/*
// configure the maven publication
publishing {
publications {
@@ -101,4 +48,4 @@ publishing {
repositories {
mavenLocal()
}
}
}*/

17
core/build.gradle Normal file
View File

@@ -0,0 +1,17 @@
plugins {
id 'java-library'
}
archivesBaseName = project.archives_base_name + "-core"
dependencies {
api "org.jetbrains:annotations:17.0.0"
api "org.aperlambda:lambdajcommon:1.8.0"
api "com.electronwill.night-config:core:3.5.3"
api "com.electronwill.night-config:toml:3.5.3"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -9,7 +9,6 @@
package me.lambdaurora.lambdacontrols;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
@@ -18,6 +17,10 @@ import java.util.Optional;
/**
* Represents the controls mode.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.0.0
*/
public enum ControlsMode implements Nameable
{
@@ -39,17 +42,18 @@ public enum ControlsMode implements Nameable
}
/**
* Gets the translated name of this controls mode.
* Gets the translation key of this controls mode.
*
* @return The translated name of this controls mode.
* @return The translated key of this controls mode.
* @since 1.1.0
*/
public String get_translated_name()
public String getTranslationKey()
{
return I18n.translate("lambdacontrols.controls_mode." + this.get_name());
return "lambdacontrols.controls_mode." + this.getName();
}
@Override
public @NotNull String get_name()
public @NotNull String getName()
{
return this.name().toLowerCase();
}
@@ -60,8 +64,8 @@ public enum ControlsMode implements Nameable
* @param id The identifier of the controls mode.
* @return The controls mode if found, else empty.
*/
public static Optional<ControlsMode> by_id(@NotNull String id)
public static Optional<ControlsMode> byId(@NotNull String id)
{
return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst();
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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 org.aperlambda.lambdacommon.Identifier;
/**
* Represents the constants used by LambdaControls.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class LambdaControlsConstants
{
public static final String NAMESPACE = "lambdacontrols";
public static final Identifier CONTROLS_MODE_CHANNEL = new Identifier(NAMESPACE, "controls_mode");
public static final Identifier FEATURE_CHANNEL = new Identifier(NAMESPACE, "feature");
public static final Identifier HELLO_CHANNEL = new Identifier(NAMESPACE, "hello");
}

View File

@@ -0,0 +1,161 @@
/*
* 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 org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* Represents a feature.
*
* @author LambdAurora
* @version 1.2.0
* @since 1.1.0
*/
public class LambdaControlsFeature implements Nameable
{
private static final List<LambdaControlsFeature> FEATURES = new ArrayList<>();
public static final LambdaControlsFeature FRONT_BLOCK_PLACING = new LambdaControlsFeature("front_block_placing", true, false);
public static final LambdaControlsFeature FAST_BLOCK_PLACING = new LambdaControlsFeature("fast_block_placing", true, true);
private final String key;
private final boolean defaultAllowed;
private boolean allowed;
private final boolean defaultEnabled;
private boolean enabled;
public LambdaControlsFeature(@NotNull String key, boolean allowed, boolean enabled)
{
Objects.requireNonNull(key, "Feature key cannot be null.");
this.key = key;
this.setAllowed(this.defaultAllowed = allowed);
this.setEnabled(this.defaultEnabled = enabled);
}
public LambdaControlsFeature(@NotNull String key)
{
this(key, false, false);
}
/**
* Allows the feature.
*/
public void allow()
{
this.setAllowed(true);
}
/**
* Returns whether this feature is allowed.
*
* @return True if this feature is allowed, else false.
*/
public boolean isAllowed()
{
return this.allowed;
}
/**
* Sets whether this feature is allowed.
*
* @param allowed True if this feature is allowed, else false.
*/
public void setAllowed(boolean allowed)
{
this.allowed = allowed;
}
/**
* Resets allowed state to default.
*/
public void resetAllowed()
{
this.setAllowed(this.defaultAllowed);
}
/**
* Returns whether this feature is enabled.
*
* @return True if this feature is enabled, else false.
*/
public boolean isEnabled()
{
return this.enabled;
}
/**
* Returns whether this feature is enabled.
*
* @param enabled True if this feature is enabled, else false.
*/
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
/**
* Returns whether this feature is available or not.
*
* @return True if this feature is available, else false.
* @see #isAllowed()
* @see #isEnabled()
*/
public boolean isAvailable()
{
return this.isAllowed() && this.isEnabled();
}
/**
* Resets the feature to its default values.
*/
public void reset()
{
this.resetAllowed();
this.setEnabled(this.defaultEnabled);
}
@Override
public @NotNull String getName()
{
return this.key;
}
public static @NotNull Optional<LambdaControlsFeature> fromName(@NotNull String key)
{
Objects.requireNonNull(key, "Cannot find features with a null name.");
return FEATURES.parallelStream().filter(feature -> feature.getName().equals(key)).findFirst();
}
/**
* Resets all features to their default values.
*/
public static void resetAll()
{
FEATURES.parallelStream().forEach(LambdaControlsFeature::reset);
}
/**
* Resets all features to allow state.
*/
public static void resetAllAllowed()
{
FEATURES.parallelStream().forEach(LambdaControlsFeature::resetAllowed);
}
static {
FEATURES.add(FRONT_BLOCK_PLACING);
FEATURES.add(FAST_BLOCK_PLACING);
}
}

21
elytra/build.gradle Normal file
View File

@@ -0,0 +1,21 @@
plugins {
id 'java-library'
}
archivesBaseName = project.archives_base_name + "-elytra"
repositories {
maven { url = 'https://hub.spigotmc.org/nexus/content/groups/public/' }
maven { url = 'https://mcelytra.github.io/maven/' }
maven { url = 'https://libraries.minecraft.net/' }
}
dependencies {
api project(":core")
implementation "org.mcelytra:elytra-core:1.0.0-SNAPSHOT"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -0,0 +1,53 @@
/*
* 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 me.lambdaurora.lambdacontrols.LambdaControlsConstants;
import org.aperlambda.lambdacommon.Identifier;
import org.jetbrains.annotations.NotNull;
import org.mcelytra.core.entity.EntityPlayer;
import org.mcelytra.core.event.HandlerList;
import org.mcelytra.core.event.player.PlayerEvent;
/**
* Represents an event which is fired when a player change their controls mode.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class PlayerControlsModeEvent extends PlayerEvent
{
private static final HandlerList HANDLERS = new HandlerList();
private final ControlsMode controlsMode;
public PlayerControlsModeEvent(@NotNull EntityPlayer player, @NotNull ControlsMode controlsMode)
{
super(new Identifier(LambdaControlsConstants.NAMESPACE, "player_controls_mode"), player, true);
this.controlsMode = controlsMode;
}
/**
* Returns the controls mode of the player.
*
* @return The player's controls mode.
*/
public ControlsMode getControlsMode()
{
return this.controlsMode;
}
@Override
public @NotNull HandlerList getHandlers()
{
return HANDLERS;
}
}

94
fabric/build.gradle Normal file
View File

@@ -0,0 +1,94 @@
plugins {
id 'fabric-loom' version '0.4-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" }
}
// OkZoomer
ivy {
url 'https://github.com/joaoh1/OkZoomer/releases/download/'
patternLayout {
artifact '[revision]/[module]-[revision].[ext]'
}
metadataSources() {
artifact()
}
}
}
configurations {
shadow
}
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:${project.modmenu_version}"
modCompile "com.github.lambdaurora:spruceui:${project.spruceui_version}"
include "com.github.lambdaurora:spruceui:${project.spruceui_version}"
// Compatibility mods
modCompile "io.github.joaoh1:okzoomer:4.0.0-alpha.3.1.16.pre5"
modCompile "me.shedaniel:RoughlyEnoughItems:4.5.5"
api project(":core")
shadow project(":core")
include "org.jetbrains:annotations:17.0.0"
include("org.aperlambda:lambdajcommon:1.8.0") {
exclude group: 'com.google.code.gson'
exclude group: 'com.google.guava'
}
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"
from {
configurations.shadow.filter {
it.getName().contains("lambdacontrols")
}.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
build.dependsOn(":core:build")

View File

@@ -0,0 +1,132 @@
/*
* 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 io.netty.buffer.Unpooled;
import me.lambdaurora.lambdacontrols.event.PlayerChangeControlsModeCallback;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.Optional;
/**
* Represents the LambdaControls mod.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.0.0
*/
public class LambdaControls implements ModInitializer
{
private static LambdaControls INSTANCE;
public static final Identifier CONTROLS_MODE_CHANNEL = new Identifier(LambdaControlsConstants.CONTROLS_MODE_CHANNEL.toString());
public static final Identifier FEATURE_CHANNEL = new Identifier(LambdaControlsConstants.FEATURE_CHANNEL.toString());
public static final Identifier HELLO_CHANNEL = new Identifier(LambdaControlsConstants.HELLO_CHANNEL.toString());
public static final TranslatableText NOT_BOUND_TEXT = new TranslatableText("lambdacontrols.not_bound");
public final Logger logger = LogManager.getLogger("LambdaControls");
@Override
public void onInitialize()
{
INSTANCE = this;
this.log("Initializing LambdaControls...");
ServerSidePacketRegistry.INSTANCE.register(HELLO_CHANNEL,
(context, attachedData) -> {
String version = attachedData.readString(16);
ControlsMode.byId(attachedData.readString(32))
.ifPresent(controlsMode -> context.getTaskQueue()
.execute(() -> PlayerChangeControlsModeCallback.EVENT.invoker().apply(context.getPlayer(), controlsMode)));
context.getTaskQueue().execute(() ->
ServerSidePacketRegistry.INSTANCE.sendToPlayer(context.getPlayer(), FEATURE_CHANNEL, this.makeFeatureBuffer(LambdaControlsFeature.FRONT_BLOCK_PLACING)));
});
ServerSidePacketRegistry.INSTANCE.register(CONTROLS_MODE_CHANNEL,
(context, attachedData) -> ControlsMode.byId(attachedData.readString(32))
.ifPresent(controlsMode -> context.getTaskQueue()
.execute(() -> PlayerChangeControlsModeCallback.EVENT.invoker().apply(context.getPlayer(), controlsMode))));
}
/**
* 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);
}
/**
* Returns a packet byte buffer made for the lambdacontrols:controls_mode plugin message.
*
* @param controlsMode The controls mode to send.
* @return The packet byte buffer.
*/
public PacketByteBuf makeControlsModeBuffer(@NotNull ControlsMode controlsMode)
{
Objects.requireNonNull(controlsMode, "Controls mode cannot be null.");
return new PacketByteBuf(Unpooled.buffer()).writeString(controlsMode.getName(), 32);
}
/**
* Returns a packet byte buffer made for the lambdacontrols:feature plugin message.
*
* @param feature The feature data to send.
* @return The packet byte buffer.
*/
public PacketByteBuf makeFeatureBuffer(@NotNull LambdaControlsFeature feature)
{
Objects.requireNonNull(feature, "Feature cannot be null.");
PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer()).writeString(feature.getName(), 64);
buffer.writeBoolean(feature.isAllowed());
return buffer;
}
public PacketByteBuf makeHello(@NotNull ControlsMode controlsMode)
{
String version = "";
Optional<ModContainer> container;
if ((container = FabricLoader.getInstance().getModContainer(LambdaControlsConstants.NAMESPACE)).isPresent()) {
version = container.get().getMetadata().getVersion().getFriendlyString();
}
return new PacketByteBuf(Unpooled.buffer()).writeString(version, 16).writeString(controlsMode.getName(), 32);
}
/**
* 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 isPressed()
{
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 isUnpressed()
{
return this == RELEASE || this == NONE;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,7 +7,7 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols;
package me.lambdaurora.lambdacontrols.client;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.utils.Nameable;
@@ -18,6 +18,10 @@ import java.util.Optional;
/**
* Represents a controller type.
*
* @author LambdAurora
* @version 1.0.0
* @since 1.0.0
*/
public enum ControllerType implements Nameable
{
@@ -40,7 +44,7 @@ public enum ControllerType implements Nameable
*
* @return The controller type's identifier.
*/
public int get_id()
public int getId()
{
return this.id;
}
@@ -63,13 +67,13 @@ public enum ControllerType implements Nameable
*
* @return The translated name of this controller type.
*/
public String get_translated_name()
public String getTranslatedName()
{
return I18n.translate("lambdacontrols.controller_type." + this.get_name());
return I18n.translate("lambdacontrols.controller_type." + this.getName());
}
@Override
public @NotNull String get_name()
public @NotNull String getName()
{
return this.name().toLowerCase();
}
@@ -80,8 +84,8 @@ public enum ControllerType implements Nameable
* @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)
public static Optional<ControllerType> byId(@NotNull String id)
{
return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst();
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,7 +7,7 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols;
package me.lambdaurora.lambdacontrols.client;
import net.minecraft.client.resource.language.I18n;
import org.aperlambda.lambdacommon.utils.Nameable;
@@ -18,6 +18,10 @@ import java.util.Optional;
/**
* Represents the hud side which is the side where the movements buttons are.
*
* @author LambdAurora
* @version 1.0.0
* @since 1.0.0
*/
public enum HudSide implements Nameable
{
@@ -42,13 +46,13 @@ public enum HudSide implements Nameable
*
* @return The translated name of this hud side.
*/
public String get_translated_name()
public String getTranslatedName()
{
return I18n.translate("lambdacontrols.hud_side." + this.get_name());
return I18n.translate("lambdacontrols.hud_side." + this.getName());
}
@Override
public @NotNull String get_name()
public @NotNull String getName()
{
return this.name().toLowerCase();
}
@@ -59,8 +63,8 @@ public enum HudSide implements Nameable
* @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)
public static Optional<HudSide> byId(@NotNull String id)
{
return Arrays.stream(values()).filter(mode -> mode.get_name().equalsIgnoreCase(id)).findFirst();
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -0,0 +1,187 @@
/*
* 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.ControlsMode;
import me.lambdaurora.lambdacontrols.LambdaControls;
import me.lambdaurora.lambdacontrols.LambdaControlsConstants;
import me.lambdaurora.lambdacontrols.LambdaControlsFeature;
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.controller.InputManager;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsHud;
import me.lambdaurora.lambdacontrols.client.gui.TouchscreenOverlay;
import me.lambdaurora.spruceui.event.OpenScreenCallback;
import me.lambdaurora.spruceui.hud.HudManager;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.options.KeyBinding;
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.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
/**
* Represents the LambdaControls client mod.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.1.0
*/
public class LambdaControlsClient extends LambdaControls implements ClientModInitializer
{
private static LambdaControlsClient INSTANCE;
public static final KeyBinding BINDING_LOOK_UP = InputManager.makeKeyBinding(new Identifier(LambdaControlsConstants.NAMESPACE, "look_up"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_RIGHT = InputManager.makeKeyBinding(new Identifier(LambdaControlsConstants.NAMESPACE, "look_right"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_DOWN = InputManager.makeKeyBinding(new Identifier(LambdaControlsConstants.NAMESPACE, "look_down"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_LEFT = InputManager.makeKeyBinding(new Identifier(LambdaControlsConstants.NAMESPACE, "look_left"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement");
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 static final Identifier CURSOR_TEXTURE = new Identifier(LambdaControlsConstants.NAMESPACE, "textures/gui/cursor.png");
public final LambdaControlsConfig config = new LambdaControlsConfig(this);
public final LambdaInput input = new LambdaInput(this);
private LambdaControlsHud hud;
private ControlsMode previousControlsMode;
@Override
public void onInitializeClient()
{
INSTANCE = this;
KeyBindingHelper.registerKeyBinding(BINDING_LOOK_UP);
KeyBindingHelper.registerKeyBinding(BINDING_LOOK_RIGHT);
KeyBindingHelper.registerKeyBinding(BINDING_LOOK_DOWN);
KeyBindingHelper.registerKeyBinding(BINDING_LOOK_LEFT);
ClientSidePacketRegistry.INSTANCE.register(CONTROLS_MODE_CHANNEL, (context, attachedData) -> context.getTaskQueue()
.execute(() -> ClientSidePacketRegistry.INSTANCE.sendToServer(CONTROLS_MODE_CHANNEL, this.makeControlsModeBuffer(this.config.getControlsMode()))));
ClientSidePacketRegistry.INSTANCE.register(FEATURE_CHANNEL, (context, attachedData) -> {
String name = attachedData.readString(64);
boolean allowed = attachedData.readBoolean();
LambdaControlsFeature.fromName(name).ifPresent(feature -> context.getTaskQueue().execute(() -> feature.setAllowed(allowed)));
});
ClientTickEvents.END_CLIENT_TICK.register(this::onTick);
OpenScreenCallback.EVENT.register((client, screen) -> {
if (screen == null && this.config.getControlsMode() == ControlsMode.TOUCHSCREEN) {
screen = new TouchscreenOverlay(this);
screen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
client.skipGameRender = false;
client.currentScreen = screen;
} else if (screen != null) {
this.input.onScreenOpen(client, client.getWindow().getWidth(), client.getWindow().getHeight());
}
});
HudManager.register(this.hud = new LambdaControlsHud(this));
}
/**
* This method is called when Minecraft is initializing.
*/
public void onMcInit(@NotNull MinecraftClient client)
{
ButtonBinding.init(client.options);
this.config.load();
this.hud.setVisible(this.config.isHudEnabled());
Controller.updateMappings();
GLFW.glfwSetJoystickCallback((jid, event) -> {
if (event == GLFW.GLFW_CONNECTED) {
Controller controller = Controller.byId(jid);
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, new TranslatableText("lambdacontrols.controller.connected", jid),
new LiteralText(controller.getName())));
} else if (event == GLFW.GLFW_DISCONNECTED) {
client.getToastManager().add(new SystemToast(SystemToast.Type.TUTORIAL_HINT, new TranslatableText("lambdacontrols.controller.disconnected", jid),
null));
}
this.switchControlsMode();
});
LambdaControlsCompat.init(this);
}
/**
* This method is called every Minecraft tick.
*
* @param client The client instance.
*/
public void onTick(@NotNull MinecraftClient client)
{
this.input.onTick(client);
if (this.config.getControlsMode() == ControlsMode.CONTROLLER && (client.isWindowFocused() || this.config.hasUnfocusedInput()))
this.input.onControllerTick(client);
}
public void onRender(MinecraftClient client)
{
this.input.onRender(client.getTickDelta(), client);
}
/**
* Called when leaving a server.
*/
public void onLeave()
{
LambdaControlsFeature.resetAllAllowed();
}
/**
* Switches the controls mode if the auto switch is enabled.
*/
public void switchControlsMode()
{
if (this.config.hasAutoSwitchMode()) {
if (this.config.getController().isGamepad()) {
this.previousControlsMode = this.config.getControlsMode();
this.config.setControlsMode(ControlsMode.CONTROLLER);
} else {
if (this.previousControlsMode == null) {
this.previousControlsMode = ControlsMode.DEFAULT;
}
this.config.setControlsMode(this.previousControlsMode);
}
}
}
/**
* Sets whether the HUD is enabled or not.
*
* @param enabled True if the HUD is enabled, else false.
*/
public void setHudEnabled(boolean enabled)
{
this.config.setHudEnabled(enabled);
this.hud.setVisible(enabled);
}
/**
* Gets the LambdaControls client instance.
*
* @return The LambdaControls client instance.
*/
public static LambdaControlsClient get()
{
return INSTANCE;
}
}

View File

@@ -0,0 +1,774 @@
/*
* 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.LambdaControlsFeature;
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
{
// General
private static final ControlsMode DEFAULT_CONTROLS_MODE = ControlsMode.DEFAULT;
private static final boolean DEFAULT_AUTO_SWITCH_MODE = false;
// HUD
private static final boolean DEFAULT_HUD_ENABLE = true;
private static final HudSide DEFAULT_HUD_SIDE = HudSide.LEFT;
// Gameplay
private static final boolean DEFAULT_FAST_BLOCK_INTERACTION = true;
private static final boolean DEFAULT_FLY_DRIFTING = false;
private static final boolean DEFAULT_FLY_VERTICAL_DRIFTING = true;
private static final boolean DEFAULT_FRONT_BLOCK_PLACING = false;
private static final boolean DEFAULT_FRONT_BLOCK_OUTLINE = true;
// Controller
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 boolean DEFAULT_UNFOCUSED_INPUT = false;
private static final boolean DEFAULT_VIRTUAL_MOUSE = false;
private static final VirtualMouseSkin DEFAULT_VIRTUAL_MOUSE_SKIN = VirtualMouseSkin.DEFAULT_LIGHT;
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 controlsMode;
private ControllerType controllerType;
// Gameplay.
private boolean shouldRenderFrontBlockOutline;
private int[] frontBlockOutlineColor;
// Controller settings
private double deadZone;
private double rotationSpeed;
private double mouseSpeed;
private boolean unfocusedInput;
private boolean virtualMouse;
private VirtualMouseSkin virtualMouseSkin;
// HUD settings.
private boolean hudEnable;
private HudSide hudSide;
public LambdaControlsConfig(@NotNull LambdaControlsClient mod)
{
this.mod = mod;
}
/**
* Loads the configuration
*/
public void load()
{
this.config.load();
this.checkAndFix();
this.mod.log("Configuration loaded.");
this.controlsMode = ControlsMode.byId(this.config.getOrElse("controls", DEFAULT_CONTROLS_MODE.getName())).orElse(DEFAULT_CONTROLS_MODE);
// HUD settings.
this.hudEnable = this.config.getOrElse("hud.enable", DEFAULT_HUD_ENABLE);
this.hudSide = HudSide.byId(this.config.getOrElse("hud.side", DEFAULT_HUD_SIDE.getName())).orElse(DEFAULT_HUD_SIDE);
// Gameplay
LambdaControlsFeature.FAST_BLOCK_PLACING.setEnabled(this.config.getOrElse("gameplay.fast_block_placing", DEFAULT_FAST_BLOCK_INTERACTION));
LambdaControlsFeature.FRONT_BLOCK_PLACING.setEnabled(this.config.getOrElse("gameplay.front_block_placing.enabled", DEFAULT_FRONT_BLOCK_PLACING));
this.shouldRenderFrontBlockOutline = this.config.getOrElse("gameplay.front_block_placing.outline", DEFAULT_FRONT_BLOCK_OUTLINE);
this.frontBlockOutlineColor = this.config.getOptional("gameplay.front_block_placing.outline_color").map(hex -> parseColor((String) hex)).orElse(new int[]{255, 255, 255, 102});
// Controller settings.
this.controllerType = ControllerType.byId(this.config.getOrElse("controller.type", DEFAULT_CONTROLLER_TYPE.getName())).orElse(DEFAULT_CONTROLLER_TYPE);
this.deadZone = this.config.getOrElse("controller.dead_zone", DEFAULT_DEAD_ZONE);
this.rotationSpeed = this.config.getOrElse("controller.rotation_speed", DEFAULT_ROTATION_SPEED);
this.mouseSpeed = this.config.getOrElse("controller.mouse_speed", DEFAULT_MOUSE_SPEED);
this.unfocusedInput = this.config.getOrElse("controller.unfocused_input", DEFAULT_UNFOCUSED_INPUT);
this.virtualMouse = this.config.getOrElse("controller.virtual_mouse", DEFAULT_VIRTUAL_MOUSE);
this.virtualMouseSkin = VirtualMouseSkin.byId(this.config.getOrElse("controller.virtual_mouse_skin", DEFAULT_VIRTUAL_MOUSE_SKIN.getName())).orElse(DEFAULT_VIRTUAL_MOUSE_SKIN);
// Controller controls.
InputManager.loadButtonBindings(this);
}
/**
* Saves the configuration.
*/
public void save()
{
this.config.set("controller.dead_zone", this.deadZone);
this.config.set("controller.rotation_speed", this.rotationSpeed);
this.config.set("controller.mouse_speed", this.mouseSpeed);
this.config.set("controller.unfocused_input", this.unfocusedInput);
this.config.set("controller.virtual_mouse", this.virtualMouse);
this.config.save();
this.mod.log("Configuration saved.");
}
public void checkAndFix()
{
InputManager.streamBindings().forEach(binding -> {
String path = "controller.controls." + binding.getName();
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));
}
});
// This shouldn't happen if the configuration is new.
if (!this.config.contains("gameplay.front_block_placing.enabled") && this.config.contains("gameplay.front_block_placing")) {
this.config.remove("gameplay.front_block_placing");
this.config.set("gameplay.front_block_placing.enabled", DEFAULT_FRONT_BLOCK_PLACING);
}
this.renamed("controller.controls.tab_left", "controller.controls.tab_back");
this.renamed("controller.controls.tab_right", "controller.controls.tab_next");
}
private void renamed(String oldPath, String newPath)
{
if (!this.config.contains(oldPath))
return;
Object raw = this.config.getRaw(oldPath);
this.config.remove(oldPath);
this.config.set(newPath, raw);
}
/**
* Resets the configuration to default values.
*/
public void reset()
{
// General
this.setControlsMode(DEFAULT_CONTROLS_MODE);
this.setAutoSwitchMode(DEFAULT_AUTO_SWITCH_MODE);
// Gameplay
this.setFastBlockPlacing(DEFAULT_FAST_BLOCK_INTERACTION);
this.setFlyDrifting(DEFAULT_FLY_DRIFTING);
this.setFlyVerticalDrifting(DEFAULT_FLY_VERTICAL_DRIFTING);
this.setFrontBlockPlacing(DEFAULT_FRONT_BLOCK_PLACING);
this.setRenderFrontBlockOutline(DEFAULT_FRONT_BLOCK_OUTLINE);
// Controller
this.setControllerType(DEFAULT_CONTROLLER_TYPE);
this.setDeadZone(DEFAULT_DEAD_ZONE);
this.setRotationSpeed(DEFAULT_ROTATION_SPEED);
this.setMouseSpeed(DEFAULT_MOUSE_SPEED);
this.setUnfocusedInput(DEFAULT_UNFOCUSED_INPUT);
this.setVirtualMouse(DEFAULT_VIRTUAL_MOUSE);
this.setVirtualMouseSkin(DEFAULT_VIRTUAL_MOUSE_SKIN);
// HUD
this.setHudEnabled(DEFAULT_HUD_ENABLE);
this.setHudSide(DEFAULT_HUD_SIDE);
// Collect prevents concurrent modification.
InputManager.streamBindings().collect(Collectors.toList()).forEach(binding -> this.setButtonBinding(binding, binding.getDefaultButton()));
}
/**
* Gets the controls mode from the configuration.
*
* @return The controls mode.
*/
public @NotNull ControlsMode getControlsMode()
{
return this.controlsMode;
}
/**
* Sets the controls mode in the configuration.
*
* @param controlsMode The controls mode.
*/
public void setControlsMode(@NotNull ControlsMode controlsMode)
{
this.controlsMode = controlsMode;
this.config.set("controls", controlsMode.getName());
}
/**
* Returns whether the auto switch mode is enabled or not.
*
* @return True if the auto switch mode is enabled, else false.
*/
public boolean hasAutoSwitchMode()
{
return this.config.getOrElse("auto_switch_mode", DEFAULT_AUTO_SWITCH_MODE);
}
/**
* Sets whether the auto switch mode is enabled or not.
*
* @param autoSwitchMode True if the auto switch mode is enabled, else false.
*/
public void setAutoSwitchMode(boolean autoSwitchMode)
{
this.config.set("auto_switch_mode", autoSwitchMode);
}
/*
HUD settings
*/
/**
* Returns whether the HUD is enabled.
*
* @return True if the HUD is enabled, else false.
*/
public boolean isHudEnabled()
{
return this.hudEnable;
}
/**
* Sets whether the HUD is enabled.
*
* @param enable True if the HUD is enabled, else false.
*/
public void setHudEnabled(boolean enable)
{
this.hudEnable = enable;
this.config.set("hud.enable", this.hudEnable);
}
/**
* Gets the HUD side from the configuration.
*
* @return The HUD side.
*/
public @NotNull HudSide getHudSide()
{
return this.hudSide;
}
/**
* Sets the HUD side in the configuration.
*
* @param hudSide The HUD side.
*/
public void setHudSide(@NotNull HudSide hudSide)
{
this.hudSide = hudSide;
this.config.set("hud.side", hudSide.getName());
}
/*
Gameplay settings
*/
/**
* Gets whether fast block placing is enabled or not.
*
* @return True if fast block placing is enabled, else false.
*/
public boolean hasFastBlockPlacing()
{
return LambdaControlsFeature.FAST_BLOCK_PLACING.isEnabled();
}
/**
* Sets whether fast block placing is enabled or not.
*
* @param enable True if fast block placing is enabled, else false.
*/
public void setFastBlockPlacing(boolean enable)
{
LambdaControlsFeature.FAST_BLOCK_PLACING.setEnabled(enable);
this.config.set("gameplay.fast_block_placing", enable);
}
/**
* Returns whether fly drifting is enabled or not.
*
* @return True if fly drifting is enabled, else false.
*/
public boolean hasFlyDrifting()
{
return this.config.getOrElse("gameplay.fly.drifting", DEFAULT_FLY_DRIFTING);
}
/**
* Sets whether fly drifting is enabled or not.
*
* @param flyDrifting True if fly drifting is enabled, else false.
*/
public void setFlyDrifting(boolean flyDrifting)
{
this.config.set("gameplay.fly.drifting", flyDrifting);
}
/**
* Returns whether vertical fly drifting is enabled or not.
*
* @return True if vertical fly drifting is enabled, else false.
*/
public boolean hasFlyVerticalDrifting()
{
return this.config.getOrElse("gameplay.fly.vertical_drifting", DEFAULT_FLY_VERTICAL_DRIFTING);
}
/**
* Sets whether vertical fly drifting is enabled or not.
*
* @param flyDrifting True if vertical fly drifting is enabled, else false.
*/
public void setFlyVerticalDrifting(boolean flyDrifting)
{
this.config.set("gameplay.fly.vertical_drifting", flyDrifting);
}
/**
* Returns whether front block placing is enabled or not.
*
* @return True if front block placing is enabled, else false.
*/
public boolean hasFrontBlockPlacing()
{
return LambdaControlsFeature.FRONT_BLOCK_PLACING.isEnabled();
}
/**
* Sets whether front block placing is enabled or not.
*
* @param enable True if front block placing is enabled, else false.
*/
public void setFrontBlockPlacing(boolean enable)
{
LambdaControlsFeature.FRONT_BLOCK_PLACING.setEnabled(enable);
this.config.set("gameplay.front_block_placing.enabled", enable);
}
/**
* Returns whether front block placing outline is enabled or not.
*
* @return True if front block placing outline is enabled, else false.
*/
public boolean shouldRenderFrontBlockOutline()
{
return this.shouldRenderFrontBlockOutline;
}
/**
* Sets whether front block placing outline is enabled or not.
*
* @param render True if front block placing outline is enabled, else false.
*/
public void setRenderFrontBlockOutline(boolean render)
{
this.config.set("gameplay.front_block_placing.outline", this.shouldRenderFrontBlockOutline = render);
}
/**
* Returns the front block placing outline color as an integer array.
* <p>
* The integer array has 4 elements: red, green, blue and alpha.
*
* @return The color as a RGBA integer array.
*/
public int[] getFrontBlockOutlineColor()
{
return this.frontBlockOutlineColor;
}
/*
Controller settings
*/
/**
* Gets the used controller.
*
* @return The used controller.
*/
public @NotNull Controller getController()
{
Object raw = this.config.getRaw("controller.id");
if (raw instanceof Number) {
return Controller.byId((Integer) raw);
} else if (raw instanceof String) {
return Controller.byGuid((String) raw).orElse(Controller.byId(GLFW.GLFW_JOYSTICK_1));
}
return Controller.byId(GLFW.GLFW_JOYSTICK_1);
}
/**
* Sets the used controller.
*
* @param controller The used controller.
*/
public void setController(@NotNull Controller controller)
{
this.config.set("controller.id", controller.getId());
}
/**
* Gets the second controller (for Joy-Con supports).
*
* @return The second controller.
*/
public @NotNull Optional<Controller> getSecondController()
{
Object raw = this.config.getRaw("controller.id2");
if (raw instanceof Number) {
if ((int) raw == -1)
return Optional.empty();
return Optional.of(Controller.byId((Integer) raw));
} else if (raw instanceof String) {
return Optional.of(Controller.byGuid((String) raw).orElse(Controller.byId(GLFW.GLFW_JOYSTICK_1)));
}
return Optional.empty();
}
/**
* Sets the second controller.
*
* @param controller The second controller.
*/
public void setSecondController(@Nullable Controller controller)
{
this.config.set("controller.id2", controller == null ? -1 : controller.getId());
}
/**
* Gets the controller's type.
*
* @return The controller's type.
*/
public @NotNull ControllerType getControllerType()
{
return this.controllerType;
}
/**
* Sets the controller's type.
*
* @param controllerType The controller's type.
*/
public void setControllerType(@NotNull ControllerType controllerType)
{
this.controllerType = controllerType;
this.config.set("controller.type", controllerType.getName());
}
/**
* Gets the controller's dead zone from the configuration.
*
* @return The controller's dead zone value.
*/
public double getDeadZone()
{
return this.deadZone;
}
/**
* Sets the controller's dead zone in the configuration.
*
* @param deadZone The new controller's dead zone value.
*/
public void setDeadZone(double deadZone)
{
this.deadZone = deadZone;
}
/**
* Gets the controller's rotation speed.
*
* @return The rotation speed.
*/
public double getRotationSpeed()
{
return this.rotationSpeed;
}
/**
* Sets the controller's rotation speed.
*
* @param rotationSpeed The rotation speed.
*/
public void setRotationSpeed(double rotationSpeed)
{
this.rotationSpeed = rotationSpeed;
}
/**
* Gets the controller's mouse speed.
*
* @return The mouse speed.
*/
public double getMouseSpeed()
{
return this.mouseSpeed;
}
/**
* Sets the controller's mouse speed.
*
* @param mouseSpeed The mouse speed.
*/
public void setMouseSpeed(double mouseSpeed)
{
this.mouseSpeed = mouseSpeed;
}
/**
* Returns whether the right X axis is inverted or not.
*
* @return True if the right X axis is inverted, else false.
*/
public boolean doesInvertRightXAxis()
{
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 setInvertRightXAxis(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 doesInvertRightYAxis()
{
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 setInvertRightYAxis(boolean invert)
{
this.config.set("controller.invert_right_y_axis", invert);
}
/**
* Returns whether unfocused controller input is allowed or not.
*
* @return True if unfocused controller input is allowed, else false.
*/
public boolean hasUnfocusedInput()
{
return this.unfocusedInput;
}
/**
* Sets whether unfocused controller input is allowed or not.
*
* @param unfocusedInput True if unfocused controller input is allowed, else false.
*/
public void setUnfocusedInput(boolean unfocusedInput)
{
this.unfocusedInput = unfocusedInput;
}
/**
* Returns whether the mouse is virtual or not.
*
* @return True if the mouse is virtual, else false.
*/
public boolean hasVirtualMouse()
{
return this.virtualMouse;
}
/**
* Sets whether the mouse is virtual or not.
*
* @param virtualMouse True if the mouse is virtual, else false.
*/
public void setVirtualMouse(boolean virtualMouse)
{
this.virtualMouse = virtualMouse;
}
/**
* Gets the virtual mouse skin.
*
* @return The virtual mouse skin.
*/
public VirtualMouseSkin getVirtualMouseSkin()
{
return this.virtualMouseSkin;
}
/**
* Sets the virtual mouse skin.
*
* @param skin The virtual mouse skin.
*/
public void setVirtualMouseSkin(VirtualMouseSkin skin)
{
this.virtualMouseSkin = skin;
this.config.set("controller.virtual_mouse_skin", skin.getName());
}
/**
* Gets the right X axis sign.
*
* @return The right X axis sign.
*/
public double getRightXAxisSign()
{
return this.doesInvertRightXAxis() ? -1.0 : 1.0;
}
/**
* Gets the right Y axis sign.
*
* @return The right Y axis sign.
*/
public double getRightYAxisSign()
{
return this.doesInvertRightYAxis() ? -1.0 : 1.0;
}
/**
* Loads the button binding from configuration.
*
* @param button The button binding.
*/
public void loadButtonBinding(@NotNull ButtonBinding button)
{
button.setButton(button.getDefaultButton());
String code = this.config.getOrElse("controller.controls." + button.getName(), button.getButtonCode());
Matcher matcher = BUTTON_BINDING_PATTERN.matcher(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.checkValidity(button, code, current = matcher.group(1)))
return;
buttons[count - 1] = Integer.parseInt(current);
}
if (count == 0) {
this.mod.warn("Malformed config value \"" + code + "\" for binding \"" + button.getName() + "\".");
this.setButtonBinding(button, new int[]{-1});
}
button.setButton(buttons);
} catch (Exception e) {
this.mod.warn("Malformed config value \"" + code + "\" for binding \"" + button.getName() + "\".");
this.config.set("controller.controls." + button.getName(), button.getButtonCode());
}
}
private boolean checkValidity(@NotNull ButtonBinding binding, @NotNull String input, String group)
{
if (group == null) {
this.mod.warn("Malformed config value \"" + input + "\" for binding \"" + binding.getName() + "\".");
this.config.set("controller.controls." + binding.getName(), binding.getButtonCode());
return false;
}
return true;
}
/**
* Sets the button binding in configuration.
*
* @param binding The button binding.
* @param button The button.
*/
public void setButtonBinding(@NotNull ButtonBinding binding, int[] button)
{
binding.setButton(button);
this.config.set("controller.controls." + binding.getName(), binding.getButtonCode());
}
public boolean isBackButton(int btn, boolean isBtn, int state)
{
if (!isBtn && state == 0)
return false;
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false) == ButtonBinding.axisAsButton(btn, state == 1);
}
public boolean isForwardButton(int btn, boolean isBtn, int state)
{
if (!isBtn && state == 0)
return false;
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true) == ButtonBinding.axisAsButton(btn, state == 1);
}
public boolean isLeftButton(int btn, boolean isBtn, int state)
{
if (!isBtn && state == 0)
return false;
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false) == ButtonBinding.axisAsButton(btn, state == 1);
}
public boolean isRightButton(int btn, boolean isBtn, int state)
{
if (!isBtn && state == 0)
return false;
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true) == ButtonBinding.axisAsButton(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 isMovementAxis(int axis)
{
return axis == GLFW_GAMEPAD_AXIS_LEFT_Y || axis == GLFW_GAMEPAD_AXIS_LEFT_X;
}
/**
* Parses a color from a hexadecimal color string.
*
* @param hex The hexadecimal color.
* @return The color instance, null if invalid.
*/
private static int[] parseColor(String hex)
{
hex = hex.replace("#", "");
switch (hex.length()) {
case 6:
return new int[]{
Integer.valueOf(hex.substring(0, 2), 16),
Integer.valueOf(hex.substring(2, 4), 16),
Integer.valueOf(hex.substring(4, 6), 16),
255
};
case 8:
return new int[]{
Integer.valueOf(hex.substring(0, 2), 16),
Integer.valueOf(hex.substring(2, 4), 16),
Integer.valueOf(hex.substring(4, 6), 16),
Integer.valueOf(hex.substring(6, 8), 16)
};
}
return null;
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi;
import me.lambdaurora.lambdacontrols.LambdaControlsConstants;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsSettingsScreen;
/**
* Represents the API implementation of ModMenu for LambdaControls.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.1.0
*/
public class LambdaControlsModMenu implements ModMenuApi
{
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory()
{
return parent -> new LambdaControlsSettingsScreen(parent, false);
}
}

View File

@@ -0,0 +1,750 @@
/*
* 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.LambdaControlsFeature;
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.controller.InputManager;
import me.lambdaurora.lambdacontrols.client.gui.ControllerControlsScreen;
import me.lambdaurora.lambdacontrols.client.gui.TouchscreenOverlay;
import me.lambdaurora.lambdacontrols.client.mixin.AdvancementsScreenAccessor;
import me.lambdaurora.lambdacontrols.client.mixin.CreativeInventoryScreenAccessor;
import me.lambdaurora.lambdacontrols.client.mixin.EntryListWidgetAccessor;
import me.lambdaurora.lambdacontrols.client.util.HandledScreenAccessor;
import me.lambdaurora.lambdacontrols.client.util.MouseAccessor;
import me.lambdaurora.spruceui.SpruceLabelWidget;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.FluidBlock;
import net.minecraft.block.SlabBlock;
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.AdvancementTab;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
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.EntryListWidget;
import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
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.axisAsButton;
import static me.lambdaurora.lambdacontrols.client.controller.InputManager.INPUT_MANAGER;
import static org.lwjgl.glfw.GLFW.*;
/**
* Represents the LambdaControls' input handler.
*
* @author LambdAurora
* @version 1.3.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 actionGuiCooldown = 0;
private int ignoreNextA = 0;
private double targetYaw = 0.0;
private double targetPitch = 0.0;
private float prevXAxis = 0.F;
private float prevYAxis = 0.F;
private int targetMouseX = 0;
private int targetMouseY = 0;
private float mouseSpeedX = 0.F;
private float mouseSpeedY = 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 onTick(@NotNull MinecraftClient client)
{
this.targetYaw = 0.F;
this.targetPitch = 0.F;
// Handles the key bindings.
if (LambdaControlsClient.BINDING_LOOK_UP.isPressed()) {
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 2);
} else if (LambdaControlsClient.BINDING_LOOK_DOWN.isPressed()) {
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 1);
}
if (LambdaControlsClient.BINDING_LOOK_LEFT.isPressed()) {
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 2);
} else if (LambdaControlsClient.BINDING_LOOK_RIGHT.isPressed()) {
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 1);
}
INPUT_MANAGER.tick(client);
}
/**
* This method is called every Minecraft tick for controller input update.
*
* @param client The client instance.
*/
public void onControllerTick(@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.actionGuiCooldown > 0)
--this.actionGuiCooldown;
InputManager.updateStates();
Controller controller = this.config.getController();
if (controller.isConnected()) {
GLFWGamepadState state = controller.getState();
this.fetchButtonInput(client, state, false);
this.fetchAxeInput(client, state, false);
}
this.config.getSecondController().filter(Controller::isConnected)
.ifPresent(joycon -> {
GLFWGamepadState state = joycon.getState();
this.fetchButtonInput(client, state, true);
this.fetchAxeInput(client, state, true);
});
boolean allowInput = true;
if (client.currentScreen instanceof ControllerControlsScreen && ((ControllerControlsScreen) client.currentScreen).focusedBinding != null)
allowInput = false;
if (allowInput)
InputManager.updateBindings(client);
if (this.ignoreNextA > 0)
this.ignoreNextA--;
if (client.currentScreen instanceof ControllerControlsScreen && InputManager.STATES.entrySet().parallelStream().map(Map.Entry::getValue).allMatch(ButtonState::isUnpressed)) {
ControllerControlsScreen screen = (ControllerControlsScreen) client.currentScreen;
if (screen.focusedBinding != null && !screen.waiting) {
int[] buttons = new int[screen.currentButtons.size()];
for (int i = 0; i < screen.currentButtons.size(); i++)
buttons[i] = screen.currentButtons.get(i);
screen.focusedBinding.setButton(buttons);
screen.focusedBinding = null;
}
}
}
/**
* This method is called before the screen is rendered.
*
* @param client The client instance.
* @param screen The screen to render.
*/
public void onPreRenderScreen(@NotNull MinecraftClient client, @NotNull Screen screen)
{
if (!isScreenInteractive(screen)) {
INPUT_MANAGER.updateMousePosition(client);
}
}
/**
* This method is called when Minecraft renders.
*
* @param client The client instance.
*/
public void onRender(float tickDelta, @NotNull MinecraftClient client)
{
if (!(client.currentScreen == null || client.currentScreen instanceof TouchscreenOverlay))
return;
PlayerEntity player = client.player;
if (player == null)
return;
if (this.targetYaw != 0F || this.targetPitch != 0F) {
float rotationYaw = (float) (player.prevYaw + (this.targetYaw / 0.10) * tickDelta);
float rotationPitch = (float) (player.prevPitch + (this.targetPitch / 0.10) * tickDelta);
client.player.yaw = rotationYaw;
client.player.pitch = MathHelper.clamp(rotationPitch, -90.F, 90.F);
if (client.player.isRiding()) {
client.player.getVehicle().copyPositionAndRotation(client.player);
}
client.getTutorialManager().onUpdateMouse(this.targetPitch, this.targetYaw);
}
}
/**
* This method is called when a Screen is opened.
*
* @param client The client instance.
* @param windowWidth The window width.
* @param windowHeight The window height.
*/
public void onScreenOpen(@NotNull MinecraftClient client, int windowWidth, int windowHeight)
{
if (client.currentScreen == null) {
this.mouseSpeedX = this.mouseSpeedY = 0.0F;
INPUT_MANAGER.resetMousePosition(windowWidth, windowHeight);
} else if (isScreenInteractive(client.currentScreen) && this.config.hasVirtualMouse()) {
((MouseAccessor) client.mouse).lambdacontrols_onCursorPos(client.getWindow().getHandle(), 0, 0);
INPUT_MANAGER.resetMouseTarget(client);
}
}
private void fetchButtonInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon)
{
ByteBuffer buffer = gamepadState.buttons();
for (int i = 0; i < buffer.limit(); i++) {
int btn = leftJoycon ? ButtonBinding.controller2Button(i) : i;
boolean btnState = buffer.get() == (byte) 1;
ButtonState state = ButtonState.NONE;
ButtonState previousState = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
if (btnState != previousState.isPressed()) {
state = btnState ? ButtonState.PRESS : ButtonState.RELEASE;
this.handleButton(client, btn, btnState ? 0 : 1, btnState);
if (btnState)
BUTTON_COOLDOWNS.put(btn, 5);
} else if (btnState) {
state = ButtonState.REPEAT;
if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) {
BUTTON_COOLDOWNS.put(btn, 5);
this.handleButton(client, btn, 2, true);
}
}
InputManager.STATES.put(btn, state);
}
}
private void fetchAxeInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon)
{
FloatBuffer buffer = gamepadState.axes();
for (int i = 0; i < buffer.limit(); i++) {
int axis = leftJoycon ? ButtonBinding.controller2Button(i) : i;
float value = buffer.get();
float absValue = Math.abs(value);
if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y)
value *= -1.0F;
int state = value > this.config.getDeadZone() ? 1 : (value < -this.config.getDeadZone() ? 2 : 0);
this.handleAxe(client, axis, value, absValue, state);
}
}
private void handleButton(@NotNull MinecraftClient client, int button, int action, boolean state)
{
if (client.currentScreen instanceof ControllerControlsScreen) {
ControllerControlsScreen screen = (ControllerControlsScreen) client.currentScreen;
if (screen.focusedBinding != null) {
if (action == 0 && !screen.currentButtons.contains(button)) {
screen.currentButtons.add(button);
int[] buttons = new int[screen.currentButtons.size()];
for (int i = 0; i < screen.currentButtons.size(); i++)
buttons[i] = screen.currentButtons.get(i);
screen.focusedBinding.setButton(buttons);
screen.waiting = false;
}
return;
}
}
if (action == 0 || action == 2) {
if (client.currentScreen != null && isScreenInteractive(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.actionGuiCooldown == 0) {
if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP) {
this.changeFocus(client.currentScreen, false);
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN) {
this.changeFocus(client.currentScreen, true);
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT) {
this.handleLeftRight(client.currentScreen, false);
} else {
this.handleLeftRight(client.currentScreen, true);
}
}
return;
}
}
if (action == 1) {
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null) {
if (this.actionGuiCooldown == 0) {
Element focused = client.currentScreen.getFocused();
if (focused != null && isScreenInteractive(client.currentScreen)) {
if (this.handleAButton(client.currentScreen, focused)) {
this.actionGuiCooldown = 5; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
return;
}
}
}
}
if (client.currentScreen instanceof HandledScreen && client.interactionManager != null && client.player != null) {
double x = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double y = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
Slot slot = ((HandledScreenAccessor) client.currentScreen).lambdacontrols_getSlotAt(x, y);
SlotActionType slotAction = SlotActionType.PICKUP;
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && slot != null) {
if (client.currentScreen instanceof CreativeInventoryScreen) {
if (((CreativeInventoryScreenAccessor) client.currentScreen).lambdacontrols_isCreativeInventorySlot(slot))
slotAction = SlotActionType.CLONE;
}
client.interactionManager.clickSlot(((HandledScreen) client.currentScreen).getScreenHandler().syncId, slot.id, GLFW.GLFW_MOUSE_BUTTON_1, slotAction, client.player);
client.player.playerScreenHandler.sendContentUpdates();
this.actionGuiCooldown = 5;
return;
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) {
client.player.closeHandledScreen();
return;
} else if (button == GLFW.GLFW_GAMEPAD_BUTTON_X && slot != null) {
client.interactionManager.clickSlot(((HandledScreen) client.currentScreen).getScreenHandler().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(((HandledScreen) client.currentScreen).getScreenHandler().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 && !isScreenInteractive(client.currentScreen) && this.actionGuiCooldown == 0 && this.ignoreNextA == 0) {
double mouseX = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
if (action == 0) {
client.currentScreen.mouseClicked(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1);
} else if (action == 1) {
client.currentScreen.mouseReleased(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1);
}
this.actionGuiCooldown = 5;
}
}
private void handleAxe(@NotNull MinecraftClient client, int axis, float value, float absValue, int state)
{
int asButtonState = value > 0.5F ? 1 : (value < -0.5F ? 2 : 0);
if (axis == GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER || axis == ButtonBinding.controller2Button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER) ||
axis == ButtonBinding.controller2Button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER))
if (asButtonState == 2)
asButtonState = 0;
{
boolean currentPlusState = asButtonState == 1;
boolean currentMinusState = asButtonState == 2;
ButtonState previousPlusState = InputManager.STATES.getOrDefault(axisAsButton(axis, true), ButtonState.NONE);
ButtonState previousMinusState = InputManager.STATES.getOrDefault(axisAsButton(axis, false), ButtonState.NONE);
if (currentPlusState != previousPlusState.isPressed()) {
InputManager.STATES.put(axisAsButton(axis, true), currentPlusState ? ButtonState.PRESS : ButtonState.RELEASE);
if (currentPlusState)
BUTTON_COOLDOWNS.put(axisAsButton(axis, true), 5);
} else if (currentPlusState) {
InputManager.STATES.put(axisAsButton(axis, true), ButtonState.REPEAT);
if (BUTTON_COOLDOWNS.getOrDefault(axisAsButton(axis, true), 0) == 0) {
BUTTON_COOLDOWNS.put(axisAsButton(axis, true), 5);
}
}
if (currentMinusState != previousMinusState.isPressed()) {
InputManager.STATES.put(axisAsButton(axis, false), currentMinusState ? ButtonState.PRESS : ButtonState.RELEASE);
if (currentMinusState)
BUTTON_COOLDOWNS.put(axisAsButton(axis, false), 5);
} else if (currentMinusState) {
InputManager.STATES.put(axisAsButton(axis, false), ButtonState.REPEAT);
if (BUTTON_COOLDOWNS.getOrDefault(axisAsButton(axis, false), 0) == 0) {
BUTTON_COOLDOWNS.put(axisAsButton(axis, false), 5);
}
}
}
double deadZone = this.config.getDeadZone();
if (client.currentScreen instanceof ControllerControlsScreen) {
ControllerControlsScreen screen = (ControllerControlsScreen) client.currentScreen;
if (screen.focusedBinding != null) {
if (asButtonState != 0 && !screen.currentButtons.contains(axisAsButton(axis, asButtonState == 1))) {
screen.currentButtons.add(axisAsButton(axis, asButtonState == 1));
int[] buttons = new int[screen.currentButtons.size()];
for (int i = 0; i < screen.currentButtons.size(); i++)
buttons[i] = screen.currentButtons.get(i);
screen.focusedBinding.setButton(buttons);
screen.waiting = false;
}
return;
}
} else if (client.currentScreen instanceof CreativeInventoryScreen) {
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
CreativeInventoryScreen screen = (CreativeInventoryScreen) client.currentScreen;
CreativeInventoryScreenAccessor accessor = (CreativeInventoryScreenAccessor) screen;
if (accessor.lambdacontrols_hasScrollbar() && absValue >= deadZone) {
screen.mouseScrolled(0.0, 0.0, -value);
}
return;
}
} else if (client.currentScreen instanceof AdvancementsScreen) {
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X || axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
AdvancementsScreen screen = (AdvancementsScreen) client.currentScreen;
AdvancementsScreenAccessor accessor = (AdvancementsScreenAccessor) screen;
if (absValue >= deadZone) {
AdvancementTab tab = accessor.getSelectedTab();
tab.move(axis == GLFW_GAMEPAD_AXIS_RIGHT_X ? -value * 5.0 : 0.0, axis == GLFW_GAMEPAD_AXIS_RIGHT_Y ? -value * 5.0 : 0.0);
}
return;
}
}
if (client.currentScreen == null) {
// Handles the look direction.
absValue -= this.config.getDeadZone();
this.handleLook(client, axis, (float) (absValue / (1.0 - this.config.getDeadZone())), state);
} else {
boolean allowMouseControl = true;
if (this.actionGuiCooldown == 0 && this.config.isMovementAxis(axis) && isScreenInteractive(client.currentScreen)) {
if (this.config.isForwardButton(axis, false, asButtonState)) {
allowMouseControl = this.changeFocus(client.currentScreen, false);
} else if (this.config.isBackButton(axis, false, asButtonState)) {
allowMouseControl = this.changeFocus(client.currentScreen, true);
} else if (this.config.isLeftButton(axis, false, asButtonState)) {
allowMouseControl = this.handleLeftRight(client.currentScreen, false);
} else if (this.config.isRightButton(axis, false, asButtonState)) {
allowMouseControl = this.handleLeftRight(client.currentScreen, true);
}
}
float movementX = 0.0F;
float movementY = 0.0F;
if (this.config.isBackButton(axis, false, (value > 0 ? 1 : 2))) {
movementY = absValue;
} else if (this.config.isForwardButton(axis, false, (value > 0 ? 1 : 2))) {
movementY = -absValue;
} else if (this.config.isLeftButton(axis, false, (value > 0 ? 1 : 2))) {
movementX = -absValue;
} else if (this.config.isRightButton(axis, false, (value > 0 ? 1 : 2))) {
movementX = absValue;
}
if (client.currentScreen != null && allowMouseControl) {
boolean moving = Math.abs(movementY) >= deadZone || Math.abs(movementX) >= deadZone;
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(prevXAxis) < deadZone && Math.abs(prevYAxis) < deadZone) {
INPUT_MANAGER.resetMouseTarget(client);
}
if (Math.abs(movementX) >= deadZone)
this.mouseSpeedX = movementX;
else
this.mouseSpeedX = 0.F;
if (Math.abs(movementY) >= deadZone)
this.mouseSpeedY = movementY;
else
this.mouseSpeedY = 0.F;
} else {
this.mouseSpeedX = 0.F;
this.mouseSpeedY = 0.F;
}
if (Math.abs(this.mouseSpeedX) >= .05F || Math.abs(this.mouseSpeedY) >= .05F) {
InputManager.queueMoveMousePosition(this.mouseSpeedX * this.config.getMouseSpeed(), this.mouseSpeedY * this.config.getMouseSpeed());
}
this.moveMouseToClosestSlot(client, client.currentScreen);
}
this.prevXAxis = movementX;
this.prevYAxis = movementY;
}
}
private boolean handleAButton(@NotNull Screen screen, @NotNull Element focused)
{
if (focused instanceof AbstractPressableButtonWidget) {
AbstractPressableButtonWidget widget = (AbstractPressableButtonWidget) focused;
widget.playDownSound(MinecraftClient.getInstance().getSoundManager());
widget.onPress();
return true;
} else if (focused instanceof SpruceLabelWidget) {
((SpruceLabelWidget) focused).onPress();
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 childFocused = ((ParentElement) focused).getFocused();
if (childFocused != null)
return this.handleAButton(screen, childFocused);
}
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 handleLeftRight(@NotNull Screen screen, boolean right)
{
Element focused = screen.getFocused();
if (focused != null)
if (this.handleRightLeftElement(focused, right))
return this.changeFocus(screen, right);
return true;
}
private boolean handleRightLeftElement(@NotNull Element element, boolean right)
{
if (element instanceof SliderWidget) {
SliderWidget slider = (SliderWidget) element;
slider.keyPressed(right ? 262 : 263, 0, 0);
this.actionGuiCooldown = 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).lambdacontrols_moveSelection(right ? EntryListWidget.class_5403.field_25661 : EntryListWidget.class_5403.field_25662);
return false;
} else if (element instanceof ParentElement) {
ParentElement entryList = (ParentElement) element;
Element focused = entryList.getFocused();
if (focused == null)
return true;
return this.handleRightLeftElement(focused, right);
}
return true;
}
/**
* Handles the look direction input.
*
* @param client The client instance.
* @param axis The axis to change.
* @param value The value of the look.
* @param state The state.
*/
public void handleLook(@NotNull MinecraftClient client, int axis, float value, int state)
{
// Handles the look direction.
if (client.player != null) {
double powValue = Math.pow(value, 2.0);
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
if (state == 2) {
this.targetPitch = -this.config.getRightYAxisSign() * (this.config.getRotationSpeed() * powValue) * 0.33D;
} else if (state == 1) {
this.targetPitch = this.config.getRightYAxisSign() * (this.config.getRotationSpeed() * powValue) * 0.33D;
}
}
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X) {
if (state == 2) {
this.targetYaw = -this.config.getRightXAxisSign() * (this.config.getRotationSpeed() * powValue) * 0.33D;
} else if (state == 1) {
this.targetYaw = this.config.getRightXAxisSign() * (this.config.getRotationSpeed() * powValue) * 0.33D;
}
}
}
}
private boolean changeFocus(@NotNull Screen screen, boolean down)
{
if (!screen.changeFocus(down)) {
if (screen.changeFocus(down)) {
this.actionGuiCooldown = 5;
return false;
}
return true;
} else {
this.actionGuiCooldown = 5;
return false;
}
}
public static boolean isScreenInteractive(@NotNull Screen screen)
{
return !(screen instanceof AdvancementsScreen || screen instanceof HandledScreen || LambdaControlsCompat.requireMouseOnScreen(screen));
}
// Inspired from https://github.com/MrCrayfish/Controllable/blob/1.14.X/src/main/java/com/mrcrayfish/controllable/client/ControllerInput.java#L686.
private void moveMouseToClosestSlot(@NotNull MinecraftClient client, @Nullable Screen screen)
{
// Makes the mouse attracted to slots. This helps with selecting items when using a controller.
if (screen instanceof HandledScreen) {
HandledScreen inventoryScreen = (HandledScreen) screen;
HandledScreenAccessor accessor = (HandledScreenAccessor) inventoryScreen;
int guiLeft = accessor.getX();
int guiTop = accessor.getY();
int mouseX = (int) (targetMouseX * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
int mouseY = (int) (targetMouseY * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight());
// Finds the closest slot in the GUI within 14 pixels.
Optional<Pair<Slot, Double>> closestSlot = inventoryScreen.getScreenHandler().slots.parallelStream()
.map(slot -> {
int x = guiLeft + slot.x + 8;
int y = guiTop + slot.y + 8;
// Distance between the slot and the cursor.
double distance = Math.sqrt(Math.pow(x - mouseX, 2) + Math.pow(y - mouseY, 2));
return Pair.of(slot, distance);
}).filter(entry -> entry.value <= 14.0)
.min(Comparator.comparingDouble(p -> p.value));
if (closestSlot.isPresent()) {
Slot slot = closestSlot.get().key;
if (slot.hasStack() || !client.player.inventory.getMainHandStack().isEmpty()) {
int slotCenterXScaled = guiLeft + slot.x + 8;
int slotCenterYScaled = guiTop + slot.y + 8;
int slotCenterX = (int) (slotCenterXScaled / ((double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth()));
int slotCenterY = (int) (slotCenterYScaled / ((double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight()));
double deltaX = slotCenterX - targetMouseX;
double deltaY = slotCenterY - targetMouseY;
if (mouseX != slotCenterXScaled || mouseY != slotCenterYScaled) {
this.targetMouseX += deltaX * 0.75;
this.targetMouseY += deltaY * 0.75;
} else {
this.mouseSpeedX *= 0.3F;
this.mouseSpeedY *= 0.3F;
}
this.mouseSpeedX *= .75F;
this.mouseSpeedY *= .75F;
} else {
this.mouseSpeedX *= .1F;
this.mouseSpeedY *= .1F;
}
} else {
this.mouseSpeedX *= .3F;
this.mouseSpeedY *= .3F;
}
} else {
this.mouseSpeedX = 0.F;
this.mouseSpeedY = 0.F;
}
}
public static Direction getMoveDirection(@Nullable BlockPos lastPos, @NotNull BlockPos newPos)
{
if (lastPos == null)
return null;
BlockPos vector = newPos.subtract(lastPos);
if (vector.getX() > 0)
return Direction.EAST;
else if (vector.getX() < 0)
return Direction.WEST;
else if (vector.getZ() > 0)
return Direction.SOUTH;
else if (vector.getZ() < 0)
return Direction.NORTH;
else if (vector.getY() > 0)
return Direction.UP;
else if (vector.getY() < 0)
return Direction.DOWN;
return null;
}
/**
* Returns a nullable block hit result if front placing is possible.
*
* @param client The client instance.
* @return A block hit result if front placing is possible.
*/
public static @Nullable BlockHitResult tryFrontPlace(@NotNull MinecraftClient client)
{
if (!LambdaControlsFeature.FRONT_BLOCK_PLACING.isAvailable())
return null;
if (client.player != null && client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.MISS && client.player.isOnGround() && client.player.pitch > 35.0F) {
if (client.player.isRiding())
return null;
BlockPos playerPos = client.player.getBlockPos().down();
BlockPos targetPos = new BlockPos(client.crosshairTarget.getPos()).subtract(playerPos);
BlockPos vector = new BlockPos(MathHelper.clamp(targetPos.getX(), -1, 1), 0, MathHelper.clamp(targetPos.getZ(), -1, 1));
BlockPos blockPos = playerPos.add(vector);
Direction direction = client.player.getHorizontalFacing();
BlockState state = client.world.getBlockState(blockPos);
if (!state.isAir())
return null;
BlockState adjacentBlockState = client.world.getBlockState(blockPos.offset(direction.getOpposite()));
if (adjacentBlockState.isAir() || adjacentBlockState.getBlock() instanceof FluidBlock || (vector.getX() == 0 && vector.getZ() == 0)) {
return null;
}
return new BlockHitResult(client.crosshairTarget.getPos(), direction, blockPos, false);
}
return null;
}
public static @NotNull BlockHitResult withSideForFrontPlace(@NotNull BlockHitResult result, @Nullable ItemStack stack)
{
if (stack == null || stack.isEmpty() || !(stack.getItem() instanceof BlockItem))
return result;
return withSideForFrontPlace(result, Block.getBlockFromItem(stack.getItem()));
}
public static @NotNull BlockHitResult withSideForFrontPlace(@NotNull BlockHitResult result, @NotNull Block block)
{
if (block instanceof SlabBlock)
result = result.withSide(Direction.DOWN);
return result;
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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 virtual mouse skins.
*
* @version 1.2.0
* @since 1.2.0
*/
public enum VirtualMouseSkin implements Nameable
{
DEFAULT_LIGHT("default_light"),
DEFAULT_DARK("default_dark"),
SECOND_LIGHT("second_light"),
SECOND_DARK("second_dark");
private String name;
VirtualMouseSkin(String name) {
this.name = name;
}
/**
* Returns the next virtual mouse skin available.
*
* @return The next available virtual mouse skin.
*/
public VirtualMouseSkin next()
{
VirtualMouseSkin[] 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 getTranslatedName()
{
return I18n.translate("lambdacontrols.virtual_mouse.skin." + this.getName());
}
@Override
public @NotNull String getName()
{
return this.name;
}
/**
* 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<VirtualMouseSkin> byId(@NotNull String id)
{
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
}
}

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 net.minecraft.client.gui.screen.Screen;
import org.jetbrains.annotations.NotNull;
/**
* Represents a compatibility handler for a mod.
*
* @author LambdAurora
* @version 1.2.0
* @since 1.1.0
*/
public interface CompatHandler
{
/**
* Handles compatibility of a mod.
*
* @param mod This mod instance.
*/
void handle(@NotNull LambdaControlsClient mod);
/**
* Returns whether the mouse is required on the specified screen.
*
* @param screen The screen.
* @return True if the mouse is requried on the specified screen, else false.
*/
default boolean requireMouseOnScreen(Screen screen)
{
return false;
}
}

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.compat;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.gui.screen.Screen;
import org.aperlambda.lambdacommon.utils.LambdaReflection;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a compatibility handler.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class LambdaControlsCompat
{
private static final List<CompatHandler> HANDLERS = new ArrayList<>();
/**
* 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.doesClassExist(OkZoomerCompat.OKZOOMER_CLASS_PATH)) {
mod.log("Adding okzoomer compatibility...");
HANDLERS.add(new OkZoomerCompat());
}
if (isReiPresent()) {
mod.log("Adding REI compatiblity...");
HANDLERS.add(new ReiCompat());
}
HANDLERS.forEach(handler -> handler.handle(mod));
InputManager.loadButtonBindings(mod.config);
}
/**
* Returns whether the mouse is required on the specified screen.
*
* @param screen The screen.
* @return True if the mouse is requried on the specified screen, else false.
*/
public static boolean requireMouseOnScreen(Screen screen)
{
return HANDLERS.stream().anyMatch(handler -> handler.requireMouseOnScreen(screen));
}
/**
* Returns whether Roughly Enough Items is present.
*
* @return True if Roughly Enough Items is present, else false.
*/
public static boolean isReiPresent()
{
return FabricLoader.getInstance().isModLoaded("roughlyenoughitems");
}
}

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.client.compat;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
* This plugin is only present for the conditional mixins.
*
* @author LambdAurora
* @version 1.2.0
* @since 1.2.0
*/
public class LambdaControlsMixinPlugin implements IMixinConfigPlugin
{
private final HashMap<String, Boolean> conditionalMixins = new HashMap<>();
public LambdaControlsMixinPlugin()
{
this.conditionalMixins.put("me.lambdaurora.lambdacontrols.client.compat.mixin.RecipeViewingScreenAccessor", LambdaControlsCompat.isReiPresent());
this.conditionalMixins.put("me.lambdaurora.lambdacontrols.client.compat.mixin.VillagerRecipeViewingScreenAccessor", LambdaControlsCompat.isReiPresent());
}
@Override
public void onLoad(String mixinPackage)
{
}
@Override
public String getRefMapperConfig()
{
return null;
}
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName)
{
return this.conditionalMixins.getOrDefault(mixinClassName, Boolean.TRUE);
}
@Override
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets)
{
}
@Override
public List<String> getMixins()
{
return null;
}
@Override
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo)
{
}
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo)
{
}
}

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 io.github.joaoh1.okzoomer.client.OkZoomerClientMod;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
/**
* Represents a compatibility handler for OkZoomer.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.1.0
*/
public class OkZoomerCompat implements CompatHandler
{
public static final String OKZOOMER_CLASS_PATH = "io.github.joaoh1.okzoomer.client.OkZoomerClientMod";
@Override
public void handle(@NotNull LambdaControlsClient mod)
{
new ButtonBinding.Builder("zoom")
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_X)
.onlyInGame()
.cooldown(true)
.category(ButtonBinding.MISC_CATEGORY)
.linkKeybind(OkZoomerClientMod.zoomKeyBinding)
.register();
}
}

View File

@@ -0,0 +1,178 @@
/*
* 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.ButtonState;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.compat.mixin.RecipeViewingScreenAccessor;
import me.lambdaurora.lambdacontrols.client.compat.mixin.VillagerRecipeViewingScreenAccessor;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.client.controller.InputHandlers;
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
import me.lambdaurora.lambdacontrols.client.controller.PressAction;
import me.shedaniel.rei.api.RecipeCategory;
import me.shedaniel.rei.gui.ContainerScreenOverlay;
import me.shedaniel.rei.gui.RecipeViewingScreen;
import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
import me.shedaniel.rei.gui.widget.EntryListWidget;
import me.shedaniel.rei.impl.ScreenHelper;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.Identifier;
import org.aperlambda.lambdacommon.utils.LambdaReflection;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Optional;
import static org.lwjgl.glfw.GLFW.*;
/**
* Represents a compatibility handler for REI.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.2.0
*/
public class ReiCompat implements CompatHandler
{
private static EntryListWidget ENTRY_LIST_WIDGET;
public static ButtonBinding TAB_BACK;
@Override
public void handle(@NotNull LambdaControlsClient mod)
{
InputManager.registerBinding(new ButtonBinding.Builder(new Identifier("rei", "category_back"))
.buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
.filter((client, binding) -> isViewingScreen(client.currentScreen))
.action(handleTab(false))
.cooldown(true)
.build());
InputManager.registerBinding(new ButtonBinding.Builder(new Identifier("rei", "category_next"))
.buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
.filter((client, binding) -> isViewingScreen(client.currentScreen))
.action(handleTab(true))
.cooldown(true)
.build());
InputManager.registerBinding(new ButtonBinding.Builder(new Identifier("rei", "page_back"))
.buttons(ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_RIGHT_X, false))
.filter((client, binding) -> InputHandlers.inInventory(client, binding) || isViewingScreen(client.currentScreen))
.action(handlePage(false))
.cooldown(true)
.build());
InputManager.registerBinding(new ButtonBinding.Builder(new Identifier("rei", "page_next"))
.buttons(ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_RIGHT_X, true))
.filter((client, binding) -> InputHandlers.inInventory(client, binding) || isViewingScreen(client.currentScreen))
.action(handlePage(true))
.cooldown(true)
.build());
// For some reasons this is broken.
InputManager.registerBinding(new ButtonBinding.Builder(new Identifier("rei", "show_usage"))
.buttons(GLFW_GAMEPAD_BUTTON_RIGHT_THUMB)
.filter((client, binding) -> InputHandlers.inInventory(client, binding) || isViewingScreen(client.currentScreen))
.action((client, button, action) -> {
if (action != ButtonState.RELEASE)
return false;
Optional<ContainerScreenOverlay> overlay = ScreenHelper.getOptionalOverlay();
if (!overlay.isPresent())
return false;
double mouseX = client.mouse.getX();
double mouseY = client.mouse.getY();
EntryListWidget widget = getEntryListWidget();
if (widget == null)
return false;
return widget.mouseClicked(mouseX, mouseY, GLFW_MOUSE_BUTTON_2);
})
.cooldown(true)
.build());
}
@Override
public boolean requireMouseOnScreen(Screen screen)
{
return isViewingScreen(screen);
}
private static boolean isViewingScreen(Screen screen)
{
return screen instanceof RecipeViewingScreen || screen instanceof VillagerRecipeViewingScreen;
}
private static EntryListWidget getEntryListWidget()
{
if (ENTRY_LIST_WIDGET == null) {
ENTRY_LIST_WIDGET = LambdaReflection.getFirstFieldOfType(ContainerScreenOverlay.class, EntryListWidget.class)
.map(field -> (EntryListWidget) LambdaReflection.getFieldValue(null, field))
.orElse(null);
}
return ENTRY_LIST_WIDGET;
}
private static PressAction handlePage(boolean next)
{
return (client, button, action) -> {
if (action == ButtonState.RELEASE)
return false;
Optional<ContainerScreenOverlay> overlay = ScreenHelper.getOptionalOverlay();
if (!overlay.isPresent())
return false;
EntryListWidget widget = getEntryListWidget();
if (widget == null)
return false;
if (next)
widget.nextPage();
else
widget.previousPage();
widget.updateEntriesPosition();
return true;
};
}
/**
* Returns the handler for category tabs buttons.
*
* @param next True if the action is to switch to the next tab.
* @return The handler.
*/
private static PressAction handleTab(boolean next)
{
return (client, button, action) -> {
if (action != ButtonState.RELEASE)
return false;
if (client.currentScreen instanceof RecipeViewingScreen) {
RecipeViewingScreenAccessor screen = (RecipeViewingScreenAccessor) client.currentScreen;
if (next)
screen.getCategoryNext().onClick();
else
screen.getCategoryBack().onClick();
return true;
} else if (client.currentScreen instanceof VillagerRecipeViewingScreen) {
VillagerRecipeViewingScreenAccessor screen = (VillagerRecipeViewingScreenAccessor) client.currentScreen;
List<RecipeCategory<?>> categories = screen.getCategories();
int currentTab = screen.getSelectedCategoryIndex();
int nextTab = currentTab + (next ? 1 : -1);
if (nextTab < 0)
nextTab = categories.size() - 1;
else if (nextTab >= categories.size())
nextTab = 0;
screen.setSelectedCategoryIndex(nextTab);
screen.lambdacontrols_init();
return true;
}
return false;
};
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.mixin;
import me.shedaniel.rei.api.widgets.Button;
import me.shedaniel.rei.gui.RecipeViewingScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
/**
* Represents an accessor to REI's RecipeViewingScreen.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.2.0
*/
@Mixin(RecipeViewingScreen.class)
public interface RecipeViewingScreenAccessor
{
@Accessor("categoryBack")
Button getCategoryBack();
@Accessor("categoryNext")
Button getCategoryNext();
}

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.compat.mixin;
import me.shedaniel.rei.api.RecipeCategory;
import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.List;
/**
* Represents an accessor to REI's VillagerRecipeViewingScreen.
*
* @author LambdAurora
* @version 1.2.0
* @since 1.2.0
*/
@Mixin(VillagerRecipeViewingScreen.class)
public interface VillagerRecipeViewingScreenAccessor
{
@Accessor("categories")
List<RecipeCategory<?>> getCategories();
@Accessor("selectedCategoryIndex")
int getSelectedCategoryIndex();
@Accessor("selectedCategoryIndex")
void setSelectedCategoryIndex(int selectedCategoryIndex);
@Invoker("init")
void lambdacontrols_init();
}

View File

@@ -0,0 +1,597 @@
/*
* 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 net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import org.aperlambda.lambdacommon.Identifier;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.aperlambda.lambdacommon.utils.function.PairPredicate;
import org.aperlambda.lambdacommon.utils.function.Predicates;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import static me.lambdaurora.lambdacontrols.client.controller.InputManager.registerDefaultCategory;
import static org.lwjgl.glfw.GLFW.*;
/**
* Represents a button binding.
*
* @author LambdAurora
* @version 1.3.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 = new Builder("attack").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true)).onlyInGame().register();
public static final ButtonBinding BACK = new Builder("back").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false)).onlyInGame().register();
public static final ButtonBinding CHAT = new Builder("chat").buttons(GLFW_GAMEPAD_BUTTON_DPAD_RIGHT).onlyInGame().cooldown(true).register();
public static final ButtonBinding DROP_ITEM = new Builder("drop_item").buttons(GLFW_GAMEPAD_BUTTON_B).onlyInGame().cooldown(true).register();
public static final ButtonBinding FORWARD = new Builder("forward").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true)).onlyInGame().register();
public static final ButtonBinding HOTBAR_LEFT = new Builder("hotbar_left").buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
.action(InputHandlers.handleHotbar(false)).onlyInGame().cooldown(true).register();
public static final ButtonBinding HOTBAR_RIGHT = new Builder("hotbar_right").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
.action(InputHandlers.handleHotbar(true)).onlyInGame().cooldown(true).register();
public static final ButtonBinding INVENTORY = new Builder("inventory").buttons(GLFW_GAMEPAD_BUTTON_Y).onlyInGame().cooldown(true).register();
public static final ButtonBinding JUMP = new Builder("jump").buttons(GLFW_GAMEPAD_BUTTON_A).onlyInGame().register();
public static final ButtonBinding LEFT = new Builder("left").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false)).onlyInGame().register();
public static final ButtonBinding PAUSE_GAME = new Builder("pause_game").buttons(GLFW_GAMEPAD_BUTTON_START).action(InputHandlers::handlePauseGame).cooldown(true).register();
public static final ButtonBinding PICK_BLOCK = new Builder("pick_block").buttons(GLFW_GAMEPAD_BUTTON_DPAD_LEFT).onlyInGame().cooldown(true).register();
public static final ButtonBinding PLAYER_LIST = new Builder("player_list").buttons(GLFW_GAMEPAD_BUTTON_BACK).onlyInGame().register();
public static final ButtonBinding RIGHT = new Builder("right").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true)).register();
public static final ButtonBinding SCREENSHOT = new Builder("screenshot").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_A)
.action(InputHandlers::handleScreenshot).cooldown(true).register();
public static final ButtonBinding SLOT_DOWN = new Builder("slot_down").buttons(GLFW_GAMEPAD_BUTTON_DPAD_DOWN)
.action(InputHandlers.handleInventorySlotPad(1)).onlyInInventory().cooldown(true).register();
public static final ButtonBinding SLOT_LEFT = new Builder("slot_left").buttons(GLFW_GAMEPAD_BUTTON_DPAD_LEFT)
.action(InputHandlers.handleInventorySlotPad(3)).onlyInInventory().cooldown(true).register();
public static final ButtonBinding SLOT_RIGHT = new Builder("slot_right").buttons(GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)
.action(InputHandlers.handleInventorySlotPad(2)).onlyInInventory().cooldown(true).register();
public static final ButtonBinding SLOT_UP = new Builder("slot_up").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP)
.action(InputHandlers.handleInventorySlotPad(0)).onlyInInventory().cooldown(true).register();
public static final ButtonBinding SMOOTH_CAMERA = new Builder("toggle_smooth_camera").cooldown(true).register();
public static final ButtonBinding SNEAK = new Builder("sneak").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_THUMB)
.actions(PressAction.DEFAULT_ACTION, InputHandlers::handleToggleSneak).onlyInGame().cooldown(true).register();
public static final ButtonBinding SPRINT = new Builder("sprint").buttons(GLFW_GAMEPAD_BUTTON_LEFT_THUMB).register();
public static final ButtonBinding SWAP_HANDS = new Builder("swap_hands").buttons(GLFW_GAMEPAD_BUTTON_X).cooldown(true).register();
public static final ButtonBinding TAB_LEFT = new Builder("tab_back").buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
.action(InputHandlers.handleHotbar(false)).filter(Predicates.or(InputHandlers::inInventory, InputHandlers::inAdvancements)).cooldown(true).register();
public static final ButtonBinding TAB_RIGHT = new Builder("tab_next").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
.action(InputHandlers.handleHotbar(true)).filter(Predicates.or(InputHandlers::inInventory, InputHandlers::inAdvancements)).cooldown(true).register();
public static final ButtonBinding TOGGLE_PERSPECTIVE = new Builder("toggle_perspective").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_Y).cooldown(true).register();
public static final ButtonBinding USE = new Builder("use").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true)).register();
private int[] button;
private int[] defaultButton;
private String key;
private KeyBinding mcKeyBinding = null;
protected PairPredicate<MinecraftClient, ButtonBinding> filter;
private List<PressAction> actions = new ArrayList<>(Collections.singletonList(PressAction.DEFAULT_ACTION));
private boolean hasCooldown;
private int cooldown = 0;
boolean pressed = false;
public ButtonBinding(@NotNull String key, int[] defaultButton, @NotNull List<PressAction> actions, PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown)
{
this.setButton(this.defaultButton = defaultButton);
this.key = key;
this.filter = filter;
this.actions.addAll(actions);
this.hasCooldown = hasCooldown;
}
public ButtonBinding(@NotNull String key, int[] defaultButton, boolean hasCooldown)
{
this(key, defaultButton, Collections.emptyList(), Predicates.pairAlwaysTrue(), hasCooldown);
}
/**
* Returns the button bound.
*
* @return The bound button.
*/
public int[] getButton()
{
return this.button;
}
/**
* Sets the bound button.
*
* @param button The bound button.
*/
public void setButton(int[] button)
{
this.button = button;
if (InputManager.hasBinding(this))
InputManager.sortBindings();
}
/**
* 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 isButton(int[] button)
{
return InputManager.areButtonsEquivalent(button, this.button);
}
/**
* Returns whether this button is down or not.
*
* @return True if the button is down, else false.
*/
public boolean isButtonDown()
{
return this.pressed;
}
/**
* Returns whether this button binding is bound or not.
*
* @return True if this button binding is bound, else false.
*/
public boolean isNotBound()
{
return this.button.length == 0 || this.button[0] == -1;
}
/**
* Gets the default button assigned to this binding.
*
* @return The default button.
*/
public int[] getDefaultButton()
{
return this.defaultButton;
}
/**
* Returns whether the assigned button is the default button.
*
* @return True if the assigned button is the default button, else false.
*/
public boolean isDefault()
{
return this.button.length == this.defaultButton.length && InputManager.areButtonsEquivalent(this.button, this.defaultButton);
}
/**
* Returns the button code.
*
* @return The button code.
*/
public @NotNull
String getButtonCode()
{
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 keyBinding The optional key binding.
*/
public void setKeyBinding(@Nullable KeyBinding keyBinding)
{
this.mcKeyBinding = keyBinding;
}
/**
* Returns whether the button binding is available in the current context.
*
* @param client The client instance.
* @return True if the button binding is available, else false.
*/
public boolean isAvailable(@NotNull MinecraftClient client)
{
return this.filter.test(client, this);
}
/**
* Updates the button binding cooldown.
*/
public void update()
{
if (this.hasCooldown && 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.hasCooldown && this.cooldown != 0)
return;
if (this.hasCooldown && state.isPressed()) {
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 getName()
{
return this.key;
}
/**
* Returns the translation key of this button binding.
*
* @return The translation key.
*/
public @NotNull
String getTranslationKey()
{
return "lambdacontrols.action." + this.getName();
}
/**
* Returns the key binding equivalent of this button binding.
*
* @return The key binding equivalent.
*/
public @NotNull
Optional<KeyBinding> asKeyBinding()
{
return Optional.ofNullable(this.mcKeyBinding);
}
/**
* 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 axisAsButton(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 controller2Button(int button)
{
return 500 + button;
}
public static void init(@NotNull GameOptions options)
{
ATTACK.mcKeyBinding = options.keyAttack;
BACK.mcKeyBinding = options.keyBack;
CHAT.mcKeyBinding = options.keyChat;
DROP_ITEM.mcKeyBinding = options.keyDrop;
FORWARD.mcKeyBinding = options.keyForward;
INVENTORY.mcKeyBinding = options.keyInventory;
JUMP.mcKeyBinding = options.keyJump;
LEFT.mcKeyBinding = options.keyLeft;
PICK_BLOCK.mcKeyBinding = options.keyPickItem;
PLAYER_LIST.mcKeyBinding = options.keyPlayerList;
RIGHT.mcKeyBinding = options.keyRight;
SCREENSHOT.mcKeyBinding = options.keyScreenshot;
SMOOTH_CAMERA.mcKeyBinding = options.keySmoothCamera;
SNEAK.mcKeyBinding = options.keySneak;
SPRINT.mcKeyBinding = options.keySprint;
SWAP_HANDS.mcKeyBinding = options.keySwapHands;
TOGGLE_PERSPECTIVE.mcKeyBinding = options.keyTogglePerspective;
USE.mcKeyBinding = options.keyUse;
}
/**
* Returns the localized name of the specified button.
*
* @param button The button.
* @return The localized name of the button.
*/
public static @NotNull Text getLocalizedButtonName(int button)
{
switch (button % 500) {
case -1:
return new TranslatableText("key.keyboard.unknown");
case GLFW_GAMEPAD_BUTTON_A:
return new TranslatableText("lambdacontrols.button.a");
case GLFW_GAMEPAD_BUTTON_B:
return new TranslatableText("lambdacontrols.button.b");
case GLFW_GAMEPAD_BUTTON_X:
return new TranslatableText("lambdacontrols.button.x");
case GLFW_GAMEPAD_BUTTON_Y:
return new TranslatableText("lambdacontrols.button.y");
case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER:
return new TranslatableText("lambdacontrols.button.left_bumper");
case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER:
return new TranslatableText("lambdacontrols.button.right_bumper");
case GLFW_GAMEPAD_BUTTON_BACK:
return new TranslatableText("lambdacontrols.button.back");
case GLFW_GAMEPAD_BUTTON_START:
return new TranslatableText("lambdacontrols.button.start");
case GLFW_GAMEPAD_BUTTON_GUIDE:
return new TranslatableText("lambdacontrols.button.guide");
case GLFW_GAMEPAD_BUTTON_LEFT_THUMB:
return new TranslatableText("lambdacontrols.button.left_thumb");
case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB:
return new TranslatableText("lambdacontrols.button.right_thumb");
case GLFW_GAMEPAD_BUTTON_DPAD_UP:
return new TranslatableText("lambdacontrols.button.dpad_up");
case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT:
return new TranslatableText("lambdacontrols.button.dpad_right");
case GLFW_GAMEPAD_BUTTON_DPAD_DOWN:
return new TranslatableText("lambdacontrols.button.dpad_down");
case GLFW_GAMEPAD_BUTTON_DPAD_LEFT:
return new TranslatableText("lambdacontrols.button.dpad_left");
case 100:
return new TranslatableText("lambdacontrols.axis.left_x+");
case 101:
return new TranslatableText("lambdacontrols.axis.left_y+");
case 102:
return new TranslatableText("lambdacontrols.axis.right_x+");
case 103:
return new TranslatableText("lambdacontrols.axis.right_y+");
case 104:
return new TranslatableText("lambdacontrols.axis.left_trigger");
case 105:
return new TranslatableText("lambdacontrols.axis.right_trigger");
case 200:
return new TranslatableText("lambdacontrols.axis.left_x-");
case 201:
return new TranslatableText("lambdacontrols.axis.left_y-");
case 202:
return new TranslatableText("lambdacontrols.axis.right_x-");
case 203:
return new TranslatableText("lambdacontrols.axis.right_y-");
default:
return new TranslatableText("lambdacontrols.button.unknown", button);
}
}
static {
MOVEMENT_CATEGORY = registerDefaultCategory("key.categories.movement", category -> category.registerAllBindings(
ButtonBinding.FORWARD,
ButtonBinding.BACK,
ButtonBinding.LEFT,
ButtonBinding.RIGHT,
ButtonBinding.JUMP,
ButtonBinding.SNEAK,
ButtonBinding.SPRINT));
GAMEPLAY_CATEGORY = registerDefaultCategory("key.categories.gameplay", category -> category.registerAllBindings(
ButtonBinding.ATTACK,
ButtonBinding.PICK_BLOCK,
ButtonBinding.USE
));
INVENTORY_CATEGORY = registerDefaultCategory("key.categories.inventory", category -> category.registerAllBindings(
ButtonBinding.DROP_ITEM,
ButtonBinding.HOTBAR_LEFT,
ButtonBinding.HOTBAR_RIGHT,
ButtonBinding.INVENTORY,
ButtonBinding.SWAP_HANDS
));
MULTIPLAYER_CATEGORY = registerDefaultCategory("key.categories.multiplayer",
category -> category.registerAllBindings(ButtonBinding.CHAT, ButtonBinding.PLAYER_LIST));
MISC_CATEGORY = registerDefaultCategory("key.categories.misc", category -> category.registerAllBindings(
ButtonBinding.SCREENSHOT,
//SMOOTH_CAMERA,
ButtonBinding.TOGGLE_PERSPECTIVE
));
}
/**
* Represents a quick {@link ButtonBinding} builder.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public static class Builder
{
private final String key;
private int[] buttons = new int[0];
private List<PressAction> actions = new ArrayList<>();
private PairPredicate<MinecraftClient, ButtonBinding> filter = Predicates.pairAlwaysTrue();
private boolean cooldown = false;
private ButtonCategory category = null;
private KeyBinding mcBinding = null;
/**
* This constructor shouldn't be used for other mods.
*
* @param key The key with format {@code "<namespace>.<name>"}.
*/
public Builder(@NotNull String key)
{
this.key = key;
this.unbound();
}
public Builder(@NotNull Identifier identifier)
{
this(identifier.getNamespace() + "." + identifier.getName());
}
public Builder(@NotNull net.minecraft.util.Identifier identifier)
{
this(new Identifier(identifier.toString()));
}
/**
* Defines the default buttons of the {@link ButtonBinding}.
*
* @param buttons The default buttons.
* @return The builder instance.
*/
public Builder buttons(int... buttons)
{
this.buttons = buttons;
return this;
}
/**
* Sets the {@link ButtonBinding} to unbound.
*
* @return The builder instance.
*/
public Builder unbound()
{
return this.buttons(-1);
}
/**
* Adds the actions to the {@link ButtonBinding}.
*
* @param actions The actions to add.
* @return The builder instance.
*/
public Builder actions(@NotNull PressAction... actions)
{
this.actions.addAll(Arrays.asList(actions));
return this;
}
/**
* Adds an action to the {@link ButtonBinding}.
*
* @param action The action to add.
* @return The builder instance.
*/
public Builder action(@NotNull PressAction action)
{
this.actions.add(action);
return this;
}
/**
* Sets a filter for the {@link ButtonBinding}.
*
* @param filter The filter.
* @return The builder instance.
*/
public Builder filter(@NotNull PairPredicate<MinecraftClient, ButtonBinding> filter)
{
this.filter = filter;
return this;
}
/**
* Sets the filter of {@link ButtonBinding} to only in game.
*
* @return The builder instance.
* @see #filter(PairPredicate)
* @see InputHandlers#inGame(MinecraftClient, ButtonBinding)
*/
public Builder onlyInGame()
{
return this.filter(InputHandlers::inGame);
}
/**
* Sets the filter of {@link ButtonBinding} to only in inventory.
*
* @return The builder instance.
* @see #filter(PairPredicate)
* @see InputHandlers#inInventory(MinecraftClient, ButtonBinding)
*/
public Builder onlyInInventory()
{
return this.filter(InputHandlers::inInventory);
}
/**
* Sets whether the {@link ButtonBinding} has a cooldown or not.
*
* @param cooldown True if the {@link ButtonBinding} has a cooldown, else false.
* @return The builder instance.
*/
public Builder cooldown(boolean cooldown)
{
this.cooldown = cooldown;
return this;
}
/**
* Sets the category of the {@link ButtonBinding}.
*
* @param category The category.
* @return The builder instance.
*/
public Builder category(@Nullable ButtonCategory category)
{
this.category = category;
return this;
}
/**
* Sets the keybinding linked to the {@link ButtonBinding}.
*
* @param binding The keybinding to link.
* @return The builder instance.
*/
public Builder linkKeybind(@Nullable KeyBinding binding)
{
this.mcBinding = binding;
return this;
}
/**
* Builds the {@link ButtonBinding}.
*
* @return The built {@link ButtonBinding}.
*/
public ButtonBinding build()
{
ButtonBinding binding = new ButtonBinding(this.key, this.buttons, this.actions, this.filter, this.cooldown);
if (this.category != null)
this.category.registerBinding(binding);
if (this.mcBinding != null)
binding.setKeyBinding(this.mcBinding);
return binding;
}
/**
* Builds and registers the {@link ButtonBinding}.
*
* @return The built {@link ButtonBinding}.
* @see #build()
*/
public ButtonBinding register()
{
return InputManager.registerBinding(this.build());
}
}
}

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 registerBinding(@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 registerAllBindings(@NotNull ButtonBinding... bindings)
{
this.registerAllBindings(Arrays.asList(bindings));
}
public void registerAllBindings(@NotNull List<ButtonBinding> bindings)
{
bindings.forEach(this::registerBinding);
}
/**
* Gets the bindings assigned to this category.
*
* @return The bindings assigned to this category.
*/
public @NotNull List<ButtonBinding> getBindings()
{
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 getTranslatedName()
{
if (this.id.getNamespace().equals("minecraft"))
return I18n.translate(this.id.getName());
else
return I18n.translate(this.id.getNamespace() + "." + this.id.getName());
}
/**
* 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 getPriority()
{
return this.priority;
}
@Override
public @NotNull Identifier getIdentifier()
{
return this.id;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,8 +7,9 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols;
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;
@@ -31,6 +32,10 @@ import static org.lwjgl.BufferUtils.createByteBuffer;
/**
* Represents a controller.
*
* @author LambdAurora
* @version 1.0.0
* @since 1.0.0
*/
public class Controller implements Nameable
{
@@ -47,7 +52,7 @@ public class Controller implements Nameable
*
* @return The identifier of this controller.
*/
public int get_id()
public int getId()
{
return this.id;
}
@@ -57,7 +62,7 @@ public class Controller implements Nameable
*
* @return The controller's GUID.
*/
public String get_guid()
public String getGuid()
{
String guid = GLFW.glfwGetJoystickGUID(this.id);
return guid == null ? "" : guid;
@@ -68,7 +73,7 @@ public class Controller implements Nameable
*
* @return True if this controller is connected, else false.
*/
public boolean is_connected()
public boolean isConnected()
{
return GLFW.glfwJoystickPresent(this.id);
}
@@ -78,9 +83,9 @@ public class Controller implements Nameable
*
* @return True if this controller is a gamepad, else false.
*/
public boolean is_gamepad()
public boolean isGamepad()
{
return this.is_connected() && GLFW.glfwJoystickIsGamepad(this.id);
return this.isConnected() && GLFW.glfwJoystickIsGamepad(this.id);
}
/**
@@ -89,10 +94,10 @@ public class Controller implements Nameable
* @return The controller's name.
*/
@Override
public @NotNull String get_name()
public @NotNull String getName()
{
String name = this.is_gamepad() ? GLFW.glfwGetGamepadName(this.id) : GLFW.glfwGetJoystickName(this.id);
return name == null ? String.valueOf(this.get_id()) : name;
String name = this.isGamepad() ? GLFW.glfwGetGamepadName(this.id) : GLFW.glfwGetJoystickName(this.id);
return name == null ? String.valueOf(this.getId()) : name;
}
/**
@@ -100,18 +105,18 @@ public class Controller implements Nameable
*
* @return The state of the controller input.
*/
public GLFWGamepadState get_state()
public GLFWGamepadState getState()
{
GLFWGamepadState state = GLFWGamepadState.create();
if (this.is_gamepad())
if (this.isGamepad())
GLFW.glfwGetGamepadState(this.id, state);
return state;
}
public static @NotNull Controller by_id(int id)
public static @NotNull Controller byId(int id)
{
if (id > GLFW.GLFW_JOYSTICK_LAST) {
LambdaControls.get().log("Controller '" + id + "' doesn't exist.");
LambdaControlsClient.get().log("Controller '" + id + "' doesn't exist.");
id = GLFW.GLFW_JOYSTICK_LAST;
}
Controller controller;
@@ -124,22 +129,22 @@ public class Controller implements Nameable
}
}
public static @NotNull Optional<Controller> by_guid(@NotNull String guid)
public static @NotNull Optional<Controller> byGuid(@NotNull String guid)
{
return CONTROLLERS.values().stream().filter(Controller::is_connected)
.filter(controller -> controller.get_guid().equals(guid))
.max(Comparator.comparingInt(Controller::get_id));
return CONTROLLERS.values().stream().filter(Controller::isConnected)
.filter(controller -> controller.getGuid().equals(guid))
.max(Comparator.comparingInt(Controller::getId));
}
/**
* 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.
* @param resource The resource to read.
* @param bufferSize 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
private static ByteBuffer ioResourceToBuffer(String resource, int bufferSize) throws IOException
{
ByteBuffer buffer = null;
@@ -159,14 +164,14 @@ public class Controller implements Nameable
/**
* Updates the controller mappings.
*/
public static void update_mappings()
public static void updateMappings()
{
try {
File mappings_file = new File("config/gamecontrollerdb.txt");
if (!mappings_file.exists())
File mappingsFile = new File("config/gamecontrollerdb.txt");
if (!mappingsFile.exists())
return;
LambdaControls.get().log("Updating controller mappings...");
ByteBuffer buffer = io_resource_to_buffer(mappings_file.getPath(), 1024);
LambdaControlsClient.get().log("Updating controller mappings...");
ByteBuffer buffer = ioResourceToBuffer(mappingsFile.getPath(), 1024);
GLFW.glfwUpdateGamepadMappings(buffer);
} catch (IOException e) {
e.printStackTrace();

View File

@@ -0,0 +1,261 @@
/*
* 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.mixin.AdvancementsScreenAccessor;
import me.lambdaurora.lambdacontrols.client.mixin.CreativeInventoryScreenAccessor;
import me.lambdaurora.lambdacontrols.client.mixin.RecipeBookWidgetAccessor;
import me.lambdaurora.lambdacontrols.client.util.HandledScreenAccessor;
import me.lambdaurora.lambdacontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.advancement.AdvancementTab;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget;
import net.minecraft.client.util.ScreenshotUtils;
import net.minecraft.item.ItemGroup;
import net.minecraft.screen.slot.Slot;
import org.aperlambda.lambdacommon.utils.Pair;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Represents some input handlers.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.1.0
*/
public class InputHandlers
{
private InputHandlers()
{
}
public static PressAction handleHotbar(boolean next)
{
return (client, button, action) -> {
if (action == ButtonState.RELEASE)
return false;
// When ingame
if (client.currentScreen == null && client.player != null) {
if (next)
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 inventory = (CreativeInventoryScreenAccessor) client.currentScreen;
int currentTab = inventory.getSelectedTab();
int nextTab = currentTab + (next ? 1 : -1);
if (nextTab < 0)
nextTab = ItemGroup.GROUPS.length - 1;
else if (nextTab >= ItemGroup.GROUPS.length)
nextTab = 0;
inventory.lambdacontrols_setSelectedTab(ItemGroup.GROUPS[nextTab]);
return true;
} else if (client.currentScreen instanceof InventoryScreen) {
RecipeBookWidgetAccessor recipeBook = (RecipeBookWidgetAccessor) ((InventoryScreen) client.currentScreen).getRecipeBookWidget();
List<RecipeGroupButtonWidget> tabs = recipeBook.getTabButtons();
RecipeGroupButtonWidget currentTab = recipeBook.getCurrentTab();
int nextTab = tabs.indexOf(currentTab) + (next ? 1 : -1);
if (nextTab < 0)
nextTab = tabs.size() - 1;
else if (nextTab >= tabs.size())
nextTab = 0;
currentTab.setToggled(false);
recipeBook.setCurrentTab(currentTab = tabs.get(nextTab));
currentTab.setToggled(true);
recipeBook.lambdacontrols_refreshResults(true);
} else if (client.currentScreen instanceof AdvancementsScreen) {
AdvancementsScreenAccessor screen = (AdvancementsScreenAccessor) client.currentScreen;
List<AdvancementTab> tabs = screen.getTabs().values().stream().distinct().collect(Collectors.toList());
AdvancementTab tab = screen.getSelectedTab();
for (int i = 0; i < tabs.size(); i++) {
if (tabs.get(i).equals(tab)) {
int nextTab = i + (next ? 1 : -1);
if (nextTab < 0)
nextTab = tabs.size() - 1;
else if (nextTab >= tabs.size())
nextTab = 0;
screen.getAdvancementManager().selectTab(tabs.get(nextTab).getRoot(), true);
break;
}
}
}
return false;
};
}
public static boolean handlePauseGame(@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 HandledScreen && client.player != null) // If the current screen is a container then close it.
client.player.closeHandledScreen();
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 handleScreenshot(@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 handleToggleSneak(@NotNull MinecraftClient client, @NotNull ButtonBinding button, @NotNull ButtonState action)
{
if (client.player != null && !client.player.abilities.flying) {
button.asKeyBinding().filter(binding -> action == ButtonState.PRESS).ifPresent(binding -> ((KeyBindingAccessor) binding).lambdacontrols_handlePressState(!binding.isPressed()));
return true;
}
return false;
}
public static PressAction handleInventorySlotPad(int direction)
{
return (client, binding, action) -> {
if (!(client.currentScreen instanceof HandledScreen && action != ButtonState.RELEASE))
return false;
HandledScreen inventory = (HandledScreen) client.currentScreen;
HandledScreenAccessor accessor = (HandledScreenAccessor) inventory;
int guiLeft = accessor.getX();
int guiTop = accessor.getY();
double mouseX = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
// Finds the hovered slot.
Slot mouseSlot = accessor.lambdacontrols_getSlotAt(mouseX, mouseY);
// Finds the closest slot in the GUI within 14 pixels.
Optional<Slot> closestSlot = inventory.getScreenHandler().slots.parallelStream()
.filter(Predicate.isEqual(mouseSlot).negate())
.map(slot -> {
int posX = guiLeft + slot.x + 8;
int posY = guiTop + slot.y + 8;
int otherPosX = (int) mouseX;
int otherPosY = (int) mouseY;
if (mouseSlot != null) {
otherPosX = guiLeft + mouseSlot.x + 8;
otherPosY = guiTop + mouseSlot.y + 8;
}
// Distance between the slot and the cursor.
double distance = Math.sqrt(Math.pow(posX - otherPosX, 2) + Math.pow(posY - otherPosY, 2));
return Pair.of(slot, distance);
}).filter(entry -> {
Slot slot = entry.key;
int posX = guiLeft + slot.x + 8;
int posY = guiTop + slot.y + 8;
int otherPosX = (int) mouseX;
int otherPosY = (int) mouseY;
if (mouseSlot != null) {
otherPosX = guiLeft + mouseSlot.x + 8;
otherPosY = guiTop + mouseSlot.y + 8;
}
if (direction == 0)
return posY < otherPosY;
else if (direction == 1)
return posY > otherPosY;
else if (direction == 2)
return posX > otherPosX;
else if (direction == 3)
return posX < otherPosX;
else
return false;
})
.min(Comparator.comparingDouble(p -> p.value))
.map(p -> p.key);
if (closestSlot.isPresent()) {
Slot slot = closestSlot.get();
int x = guiLeft + slot.x + 8;
int y = guiTop + slot.y + 8;
InputManager.queueMousePosition(x * (double) client.getWindow().getWidth() / (double) client.getWindow().getScaledWidth(),
y * (double) client.getWindow().getHeight() / (double) client.getWindow().getScaledHeight());
return true;
}
return false;
};
}
/**
* Returns always true to the filter.
*
* @param client The client instance.
* @param binding The affected binding.
* @return True.
*/
public static boolean always(@NotNull MinecraftClient client, @NotNull ButtonBinding binding)
{
return true;
}
/**
* Returns whether the client is in game or not.
*
* @param client The client instance.
* @param binding The affected binding.
* @return True if the client is in game, else false.
*/
public static boolean inGame(@NotNull MinecraftClient client, @NotNull ButtonBinding binding)
{
return client.currentScreen == null;
}
/**
* Returns whether the client is in an inventory or not.
*
* @param client The client instance.
* @param binding The affected binding.
* @return True if the client is in an inventory, else false.
*/
public static boolean inInventory(@NotNull MinecraftClient client, @NotNull ButtonBinding binding)
{
return client.currentScreen instanceof HandledScreen;
}
/**
* Returns whether the client is in the advancements screen or not.
*
* @param client The client instance.
* @param binding The affected binding.
* @return True if the client is in the advancements screen, else false.
*/
public static boolean inAdvancements(@NotNull MinecraftClient client, @NotNull ButtonBinding binding)
{
return client.currentScreen instanceof AdvancementsScreen;
}
}

View File

@@ -0,0 +1,393 @@
/*
* 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.ControlsMode;
import me.lambdaurora.lambdacontrols.client.ButtonState;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.LambdaControlsConfig;
import me.lambdaurora.lambdacontrols.client.util.MouseAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.util.InputUtil;
import net.minecraft.util.math.MathHelper;
import org.aperlambda.lambdacommon.Identifier;
import org.aperlambda.lambdacommon.utils.function.PairPredicate;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
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.3.0
* @since 1.1.0
*/
public class InputManager
{
public static final InputManager INPUT_MANAGER = new 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<>();
private int prevTargetMouseX = 0;
private int prevTargetMouseY = 0;
private int targetMouseX = 0;
private int targetMouseY = 0;
protected InputManager()
{
}
public void tick(@NotNull MinecraftClient client)
{
if (LambdaControlsClient.get().config.getControlsMode() == ControlsMode.CONTROLLER) {
this.controllerTick(client);
}
}
public void controllerTick(@NotNull MinecraftClient client)
{
this.prevTargetMouseX = this.targetMouseX;
this.prevTargetMouseY = this.targetMouseY;
}
/**
* Updates the mouse position. Should only be called on pre render of a screen.
*
* @param client The client instance.
*/
public void updateMousePosition(@NotNull MinecraftClient client)
{
Objects.requireNonNull(client, "Client instance cannot be null.");
if (this.prevTargetMouseX != this.targetMouseX || this.prevTargetMouseY != this.targetMouseY) {
double mouseX = this.prevTargetMouseX + (this.targetMouseX - this.prevTargetMouseX) * client.getTickDelta() + 0.5;
double mouseY = this.prevTargetMouseY + (this.targetMouseY - this.prevTargetMouseY) * client.getTickDelta() + 0.5;
if (!LambdaControlsClient.get().config.hasVirtualMouse())
GLFW.glfwSetCursorPos(client.getWindow().getHandle(), mouseX, mouseY);
((MouseAccessor) client.mouse).lambdacontrols_onCursorPos(client.getWindow().getHandle(), mouseX, mouseY);
}
}
/**
* Resets the mouse position.
*
* @param windowWidth The window width.
* @param windowHeight The window height.
*/
public void resetMousePosition(int windowWidth, int windowHeight)
{
this.targetMouseX = this.prevTargetMouseX = (int) (windowWidth / 2.F);
this.targetMouseY = this.prevTargetMouseY = (int) (windowHeight / 2.F);
}
public void resetMouseTarget(@NotNull MinecraftClient client)
{
double mouseX = client.mouse.getX();
double mouseY = client.mouse.getY();
this.prevTargetMouseX = this.targetMouseX = (int) mouseX;
this.prevTargetMouseY = this.targetMouseY = (int) mouseY;
}
/**
* 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 hasBinding(@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 hasBinding(@NotNull String name)
{
return BINDINGS.parallelStream().map(ButtonBinding::getName).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 hasBinding(@NotNull Identifier identifier)
{
return hasBinding(identifier.getNamespace() + "." + identifier.getName());
}
/**
* Registers a button binding.
*
* @param binding The binding to register.
* @return The registered binding.
*/
public static @NotNull ButtonBinding registerBinding(@NotNull ButtonBinding binding)
{
if (hasBinding(binding))
throw new IllegalStateException("Cannot register twice a button binding in the registry.");
BINDINGS.add(binding);
return binding;
}
public static @NotNull ButtonBinding registerBinding(@NotNull Identifier id, int[] defaultButton, @NotNull List<PressAction> actions, @NotNull PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown)
{
return registerBinding(new ButtonBinding(id.getNamespace() + "." + id.getName(), defaultButton, actions, filter, hasCooldown));
}
public static @NotNull ButtonBinding registerBinding(@NotNull Identifier id, int[] defaultButton, boolean hasCooldown)
{
return registerBinding(id, defaultButton, Collections.emptyList(), InputHandlers::always, hasCooldown);
}
public static @NotNull ButtonBinding registerBinding(@NotNull net.minecraft.util.Identifier id, int[] defaultButton, @NotNull List<PressAction> actions, @NotNull PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown)
{
return registerBinding(new Identifier(id.getNamespace(), id.getPath()), defaultButton, actions, filter, hasCooldown);
}
public static @NotNull ButtonBinding registerBinding(@NotNull net.minecraft.util.Identifier id, int[] defaultButton, boolean hasCooldown)
{
return registerBinding(id, defaultButton, Collections.emptyList(), InputHandlers::always, hasCooldown);
}
/**
* Sorts bindings to get bindings with the higher button counts first.
*/
public static void sortBindings()
{
synchronized (BINDINGS) {
List<ButtonBinding> sorted = BINDINGS.stream().sorted(Collections.reverseOrder(Comparator.comparingInt(binding -> binding.getButton().length)))
.collect(Collectors.toList());
BINDINGS.clear();
BINDINGS.addAll(sorted);
}
}
/**
* Registers a category of button bindings.
*
* @param category The category to register.
* @return The registered category.
*/
public static ButtonCategory registerCategory(@NotNull ButtonCategory category)
{
CATEGORIES.add(category);
return category;
}
public static ButtonCategory registerCategory(@NotNull Identifier identifier, int priority)
{
return registerCategory(new ButtonCategory(identifier, priority));
}
public static ButtonCategory registerCategory(@NotNull Identifier identifier)
{
return registerCategory(new ButtonCategory(identifier));
}
protected static ButtonCategory registerDefaultCategory(@NotNull String key, @NotNull Consumer<ButtonCategory> keyAdder)
{
ButtonCategory category = registerCategory(new Identifier("minecraft", key), CATEGORIES.size());
keyAdder.accept(category);
return category;
}
/**
* Loads the button bindings from configuration.
*
* @param config The configuration instance.
*/
public static void loadButtonBindings(@NotNull LambdaControlsConfig config)
{
List<ButtonBinding> queue = new ArrayList<>(BINDINGS);
queue.forEach(config::loadButtonBinding);
}
/**
* Returns the binding state.
*
* @param binding The binding.
* @return The current state of the binding.
*/
public static @NotNull ButtonState getBindingState(@NotNull ButtonBinding binding)
{
ButtonState state = ButtonState.REPEAT;
for (int btn : binding.getButton()) {
ButtonState btnState = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
if (btnState == ButtonState.PRESS)
state = ButtonState.PRESS;
else if (btnState == ButtonState.RELEASE) {
state = ButtonState.RELEASE;
break;
} else if (btnState == 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 hasDuplicatedBindings(int[] button)
{
return BINDINGS.parallelStream().filter(binding -> areButtonsEquivalent(binding.getButton(), button)).count() > 1;
}
/**
* Returns whether the button has duplicated bindings.
*
* @param binding The binding to check.
* @return True if the button has duplicated bindings, else false.
*/
public static boolean hasDuplicatedBindings(ButtonBinding binding)
{
return BINDINGS.parallelStream().filter(other -> areButtonsEquivalent(other.getButton(), binding.getButton()) && other.filter.equals(binding.filter)).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 areButtonsEquivalent(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 containsButton(int[] buttons, int button)
{
return Arrays.stream(buttons).anyMatch(btn -> btn == button);
}
/**
* Updates the button states.
*/
public static void updateStates()
{
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 updateBindings(@NotNull MinecraftClient client)
{
List<Integer> skipButtons = new ArrayList<>();
Map<ButtonBinding, ButtonState> states = new HashMap<>();
for (ButtonBinding binding : BINDINGS) {
ButtonState state = binding.isAvailable(client) ? getBindingState(binding) : ButtonState.NONE;
if (skipButtons.stream().anyMatch(btn -> containsButton(binding.getButton(), btn))) {
if (binding.pressed)
state = ButtonState.RELEASE;
else
state = ButtonState.NONE;
}
binding.pressed = state.isPressed();
binding.update();
if (binding.pressed)
Arrays.stream(binding.getButton()).forEach(skipButtons::add);
states.put(binding, state);
}
states.forEach((binding, state) -> {
if (state != ButtonState.NONE) {
binding.handle(client, state);
}
});
}
public static void queueMousePosition(double x, double y)
{
INPUT_MANAGER.targetMouseX = (int) MathHelper.clamp(x, 0, MinecraftClient.getInstance().getWindow().getWidth());
INPUT_MANAGER.targetMouseY = (int) MathHelper.clamp(y, 0, MinecraftClient.getInstance().getWindow().getHeight());
}
public static void queueMoveMousePosition(double x, double y)
{
queueMousePosition(INPUT_MANAGER.targetMouseX + x, INPUT_MANAGER.targetMouseY + y);
}
public static @NotNull Stream<ButtonBinding> streamBindings()
{
return BINDINGS.stream();
}
public static @NotNull Stream<ButtonCategory> streamCategories()
{
return CATEGORIES.stream();
}
/**
* Returns a new key binding instance.
* @param id The identifier of the key binding.
* @param type The type.
* @param code The code.
* @param category The category of the key binding.
* @return The key binding.
*
* @see #makeKeyBinding(Identifier, InputUtil.Type, int, String)
*/
public static @NotNull KeyBinding makeKeyBinding(@NotNull net.minecraft.util.Identifier id, InputUtil.Type type, int code, @NotNull String category)
{
return makeKeyBinding(new Identifier(id.getNamespace(), id.getPath()), type, code, category);
}
/**
* Returns a new key binding instance.
* @param id The identifier of the key binding.
* @param type The type.
* @param code The code.
* @param category The category of the key binding.
* @return The key binding.
*
* @see #makeKeyBinding(net.minecraft.util.Identifier, InputUtil.Type, int, String)
*/
public static @NotNull KeyBinding makeKeyBinding(@NotNull Identifier id, InputUtil.Type type, int code, @NotNull String category)
{
return new KeyBinding(String.format("key.%s.%s", id.getNamespace(), id.getName()), type, code, category);
}
}

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.asKeyBinding().ifPresent(binding -> ((KeyBindingAccessor) binding).lambdacontrols_handlePressState(button.isButtonDown()));
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,61 @@
/*
* 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.LambdaControls;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.spruceui.AbstractIconButtonWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
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 iconWidth;
public ControllerButtonWidget(int x, int y, int width, @NotNull ButtonBinding binding, @NotNull PressAction action)
{
super(x, y, width, 20, ButtonBinding.getLocalizedButtonName(binding.getButton()[0]), action);
this.binding = binding;
}
public void update()
{
int length = binding.getButton().length;
this.setMessage(this.binding.isNotBound() ? LambdaControls.NOT_BOUND_TEXT :
(length > 0 ? ButtonBinding.getLocalizedButtonName(binding.getButton()[0]) : new LiteralText("<>")));
}
@Override
public Text getMessage()
{
if (this.binding.getButton().length > 1)
return LiteralText.EMPTY;
return super.getMessage();
}
@Override
protected int renderIcon(MatrixStack matrices, int mouseX, int mouseY, float delta, int x, int y)
{
if (this.binding.getButton().length > 1) {
x += (this.width / 2 - this.iconWidth / 2) - 4;
}
Pair<Integer, Integer> size = LambdaControlsRenderer.drawButton(matrices, x, y, this.binding, MinecraftClient.getInstance());
this.iconWidth = size.key;
return size.value;
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.util.math.MatrixStack;
import net.minecraft.text.TranslatableText;
import org.aperlambda.lambdacommon.utils.function.Predicates;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Represents the controls screen.
*/
public class ControllerControlsScreen extends Screen
{
private final Screen parent;
final LambdaControlsClient mod;
private final boolean hideSettings;
private ControlsListWidget bindingsListWidget;
private ButtonWidget resetButton;
public ButtonBinding focusedBinding;
public boolean waiting = false;
public List<Integer> currentButtons = new ArrayList<>();
public ControllerControlsScreen(@NotNull Screen parent, boolean hideSettings)
{
super(new TranslatableText("lambdacontrols.menu.title.controller_controls"));
this.parent = parent;
this.mod = LambdaControlsClient.get();
this.hideSettings = hideSettings;
}
@Override
public void removed()
{
this.mod.config.save();
super.removed();
}
@Override
protected void init()
{
this.addButton(new SpruceButtonWidget(this.width / 2 - 155, 18, this.hideSettings ? 310 : 150, 20,
new TranslatableText("lambdacontrols.menu.keyboard_controls"),
btn -> this.client.openScreen(new ControlsOptionsScreen(this, this.client.options))));
if (!this.hideSettings)
this.addButton(new SpruceButtonWidget(this.width / 2 - 155 + 160, 18, 150, 20,
new TranslatableText("menu.options"),
btn -> this.client.openScreen(new LambdaControlsSettingsScreen(this, true))));
this.bindingsListWidget = new ControlsListWidget(this, this.client);
this.children.add(this.bindingsListWidget);
this.resetButton = this.addButton(new ButtonWidget(this.width / 2 - 155, this.height - 29, 150, 20,
new TranslatableText("controls.resetAll"),
btn -> InputManager.streamBindings().forEach(binding -> this.mod.config.setButtonBinding(binding, binding.getDefaultButton()))));
this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, 20,
new TranslatableText("gui.done"),
btn -> this.client.openScreen(this.parent)));
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta)
{
this.renderBackground(matrices);
this.bindingsListWidget.render(matrices, mouseX, mouseY, delta);
this.drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 8, 16777215);
this.resetButton.active = InputManager.streamBindings().anyMatch(Predicates.not(ButtonBinding::isDefault));
super.render(matrices, mouseX, mouseY, delta);
}
}

View File

@@ -0,0 +1,213 @@
/*
* 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.client.util.math.MatrixStack;
import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText;
import net.minecraft.text.TranslatableText;
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 ControllerControlsScreen gui;
private int field_2733;
public ControlsListWidget(@NotNull ControllerControlsScreen gui, @NotNull MinecraftClient client)
{
super(client, gui.width + 45, gui.height, 43, gui.height - 32, 24);
this.gui = gui;
InputManager.streamCategories()
.sorted(Comparator.comparingInt(ButtonCategory::getPriority))
.forEach(category -> {
this.addEntry(new CategoryEntry(category));
category.getBindings().forEach(binding -> {
int i = client.textRenderer.getWidth(I18n.translate(binding.getTranslationKey()));
if (i > this.field_2733) {
this.field_2733 = i;
}
this.addEntry(new ControlsListWidget.ButtonBindingEntry(binding));
});
});
}
@Override
protected int getScrollbarPositionX()
{
return super.getScrollbarPositionX() + 15;
}
@Override
public int getRowWidth()
{
return super.getRowWidth() + 32;
}
public class ButtonBindingEntry extends Entry
{
private final ButtonBinding binding;
private final String bindingName;
private final ControllerButtonWidget editButton;
private final ButtonWidget resetButton;
private final ButtonWidget unboundButton;
ButtonBindingEntry(@NotNull ButtonBinding binding)
{
this.binding = binding;
this.bindingName = I18n.translate(this.binding.getTranslationKey());
this.editButton = new ControllerButtonWidget(0, 0, 110, this.binding, btn -> {
gui.focusedBinding = binding;
gui.currentButtons.clear();
gui.waiting = true;
})
{
protected MutableText getNarrationMessage()
{
return binding.isNotBound() ? new TranslatableText("narrator.controls.unbound", bindingName) : new TranslatableText("narrator.controls.bound", bindingName, super.getNarrationMessage());
}
};
this.resetButton = new ButtonWidget(0, 0, 50, 20, new TranslatableText("controls.reset"),
btn -> gui.mod.config.setButtonBinding(binding, binding.getDefaultButton()))
{
protected MutableText getNarrationMessage()
{
return new TranslatableText("narrator.controls.reset", bindingName);
}
};
this.unboundButton = new ButtonWidget(0, 0, 50, 20, new TranslatableText("lambdacontrols.menu.unbound"),
btn -> {
gui.mod.config.setButtonBinding(binding, UNBOUND);
gui.focusedBinding = null;
})
{
protected MutableText getNarrationMessage()
{
return new TranslatableText("lambdacontrols.narrator.unbound", bindingName);
}
};
}
@Override
public List<? extends Element> children()
{
return Collections.unmodifiableList(Arrays.asList(this.editButton, this.resetButton));
}
@Override
public void render(MatrixStack matrices, int index, int y, int x, int width, int height, int mouseX, int mouseY, boolean hovering, float delta)
{
boolean focused = gui.focusedBinding == this.binding;
TextRenderer textRenderer = ControlsListWidget.this.client.textRenderer;
String bindingName = this.bindingName;
float var10002 = (float) (x + 70 - ControlsListWidget.this.field_2733);
int var10003 = y + height / 2;
textRenderer.draw(matrices, bindingName, var10002, (float) (var10003 - 9 / 2), 16777215);
this.resetButton.x = this.unboundButton.x = x + 190;
this.resetButton.y = this.unboundButton.y = y;
this.resetButton.active = !this.binding.isDefault();
if (focused)
this.unboundButton.render(matrices, mouseX, mouseY, delta);
else
this.resetButton.render(matrices, mouseX, mouseY, delta);
this.editButton.x = x + 75;
this.editButton.y = y;
this.editButton.update();
if (focused) {
MutableText text = new LiteralText("> ").formatted(Formatting.WHITE);
text.append(this.editButton.getMessage().copy().formatted(Formatting.YELLOW));
this.editButton.setMessage(text.append(new LiteralText(" <").formatted(Formatting.WHITE)));
} else if (!this.binding.isNotBound() && InputManager.hasDuplicatedBindings(this.binding)) {
MutableText text = this.editButton.getMessage().copy();
this.editButton.setMessage(text.formatted(Formatting.RED));
} else if (this.binding.isNotBound()) {
MutableText text = this.editButton.getMessage().copy();
this.editButton.setMessage(text.formatted(Formatting.GOLD));
}
this.editButton.render(matrices, mouseX, mouseY, delta);
}
public boolean mouseClicked(double mouseX, double mouseY, int button)
{
boolean focused = gui.focusedBinding == this.binding;
if (this.editButton.mouseClicked(mouseX, mouseY, button))
return true;
else
return focused ? this.unboundButton.mouseClicked(mouseX, mouseY, button) : this.resetButton.mouseClicked(mouseX, mouseY, button);
}
public boolean mouseReleased(double mouseX, double mouseY, int button)
{
return this.editButton.mouseReleased(mouseX, mouseY, button) || this.resetButton.mouseReleased(mouseX, mouseY, button)
|| this.unboundButton.mouseReleased(mouseX, mouseY, button);
}
}
public class CategoryEntry extends Entry
{
private final String name;
private final int nameWidth;
public CategoryEntry(@NotNull ButtonCategory category)
{
this.name = category.getTranslatedName();
this.nameWidth = ControlsListWidget.this.client.textRenderer.getWidth(this.name);
}
@Override
public void render(MatrixStack matrices, int index, int y, int x, int width, int height, int mouseX, int mouseY, boolean hovering, float delta)
{
ControlsListWidget.this.client.textRenderer.draw(matrices, this.name, (float) (ControlsListWidget.this.client.currentScreen.width / 2 - this.nameWidth / 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,256 @@
/*
* 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.LambdaControlsConstants;
import me.lambdaurora.lambdacontrols.client.HudSide;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.LambdaInput;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.spruceui.hud.Hud;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents the LambdaControls HUD.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.0.0
*/
public class LambdaControlsHud extends Hud
{
private final LambdaControlsClient mod;
private MinecraftClient client;
private int attackWidth = 0;
private int attackButtonWidth = 0;
private int dropItemWidth = 0;
private int dropItemButtonWidth = 0;
private int inventoryWidth = 0;
private int inventoryButtonWidth = 0;
private int swapHandsWidth = 0;
private int swapHandsButtonWidth = 0;
private int useWidth = 0;
private int useButtonWidth = 0;
private BlockHitResult placeHitResult;
private String attackAction = "";
private String placeAction = "";
public LambdaControlsHud(@NotNull LambdaControlsClient mod)
{
super(new Identifier(LambdaControlsConstants.NAMESPACE, "hud/button_indicator"));
this.mod = mod;
}
@Override
public void init(@NotNull MinecraftClient client, int screenWidth, int screenHeight)
{
super.init(client, screenWidth, screenHeight);
this.client = client;
this.inventoryWidth = this.width(ButtonBinding.INVENTORY);
this.inventoryButtonWidth = LambdaControlsRenderer.getBindingIconWidth(ButtonBinding.INVENTORY);
this.swapHandsWidth = this.width(ButtonBinding.SWAP_HANDS);
this.swapHandsButtonWidth = LambdaControlsRenderer.getBindingIconWidth(ButtonBinding.SWAP_HANDS);
this.dropItemWidth = this.width(ButtonBinding.DROP_ITEM);
this.dropItemButtonWidth = LambdaControlsRenderer.getBindingIconWidth(ButtonBinding.DROP_ITEM);
this.attackButtonWidth = LambdaControlsRenderer.getBindingIconWidth(ButtonBinding.ATTACK);
this.useButtonWidth = LambdaControlsRenderer.getBindingIconWidth(ButtonBinding.USE);
}
/**
* Renders the LambdaControls' HUD.
*/
@Override
public void render(MatrixStack matrices, float tickDelta)
{
if (this.mod.config.getControlsMode() == ControlsMode.CONTROLLER && this.client.currentScreen == null) {
int y = bottom(2);
this.renderFirstIcons(matrices, this.mod.config.getHudSide() == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderSecondIcons(matrices, this.mod.config.getHudSide() == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderFirstSection(matrices, this.mod.config.getHudSide() == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderSecondSection(matrices, this.mod.config.getHudSide() == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
}
}
public void renderFirstIcons(MatrixStack matrices, int x, int y)
{
int offset = 2 + this.inventoryWidth + this.inventoryButtonWidth + 4;
int currentX = this.mod.config.getHudSide() == HudSide.LEFT ? x : x - this.inventoryButtonWidth;
this.drawButton(matrices, currentX, y, ButtonBinding.INVENTORY, true);
this.drawButton(matrices, currentX += (this.mod.config.getHudSide() == HudSide.LEFT ? offset : -offset), y, ButtonBinding.SWAP_HANDS, true);
offset = 2 + this.swapHandsWidth + this.dropItemButtonWidth + 4;
if (this.client.options.showSubtitles && this.mod.config.getHudSide() == HudSide.RIGHT) {
currentX += -offset;
} else {
currentX = this.mod.config.getHudSide() == HudSide.LEFT ? x : x - this.dropItemButtonWidth;
y -= 24;
}
this.drawButton(matrices, currentX, y, ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty());
}
public void renderSecondIcons(MatrixStack matrices, int x, int y)
{
int offset;
int currentX = x;
if (!this.placeAction.isEmpty()) {
if (this.mod.config.getHudSide() == HudSide.LEFT)
currentX -= this.useButtonWidth;
this.drawButton(matrices, currentX, y, ButtonBinding.USE, true);
offset = 2 + this.useWidth + 4;
if (this.client.options.showSubtitles && this.mod.config.getHudSide() == HudSide.LEFT) {
currentX -= offset;
} else {
currentX = x;
y -= 24;
}
}
if (this.mod.config.getHudSide() == HudSide.LEFT)
currentX -= this.attackButtonWidth;
this.drawButton(matrices, currentX, y, ButtonBinding.ATTACK, this.attackWidth != 0);
}
public void renderFirstSection(MatrixStack matrices, int x, int y)
{
int currentX = this.mod.config.getHudSide() == HudSide.LEFT ? x + this.inventoryButtonWidth + 2 : x - this.inventoryButtonWidth - 2 - this.inventoryWidth;
this.drawTip(matrices, currentX, y, ButtonBinding.INVENTORY, true);
currentX += this.mod.config.getHudSide() == HudSide.LEFT ? this.inventoryWidth + 4 + this.swapHandsButtonWidth + 2
: -this.swapHandsWidth - 2 - this.swapHandsButtonWidth - 4;
this.drawTip(matrices, currentX, y, ButtonBinding.SWAP_HANDS, true);
if (this.client.options.showSubtitles && this.mod.config.getHudSide() == HudSide.RIGHT) {
currentX += -this.dropItemWidth - 2 - this.dropItemButtonWidth - 4;
} else {
y -= 24;
currentX = this.mod.config.getHudSide() == HudSide.LEFT ? x + this.dropItemButtonWidth + 2 : x - this.dropItemButtonWidth - 2 - this.dropItemWidth;
}
this.drawTip(matrices, currentX, y, ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty());
}
public void renderSecondSection(MatrixStack matrices, int x, int y)
{
int currentX = x;
if (!this.placeAction.isEmpty()) {
currentX += this.mod.config.getHudSide() == HudSide.RIGHT ? this.useButtonWidth + 2 : -this.useButtonWidth - 2 - this.useWidth;
this.drawTip(matrices, currentX, y, this.placeAction, true);
if (this.client.options.showSubtitles && this.mod.config.getHudSide() == HudSide.LEFT) {
currentX -= 4;
} else {
currentX = x;
y -= 24;
}
}
currentX += this.mod.config.getHudSide() == HudSide.RIGHT ? this.attackButtonWidth + 2 : -this.attackButtonWidth - 2 - this.attackWidth;
this.drawTip(matrices, currentX, y, this.attackAction, this.attackWidth != 0);
}
@Override
public void tick()
{
super.tick();
if (this.mod.config.getControlsMode() == ControlsMode.CONTROLLER) {
if (this.client.crosshairTarget == null)
return;
String placeAction;
// Update "Use" tip status.
if (this.client.crosshairTarget.getType() == HitResult.Type.MISS) {
this.placeHitResult = LambdaInput.tryFrontPlace(this.client);
this.attackAction = "";
this.attackWidth = 0;
} else {
if (this.client.crosshairTarget.getType() == HitResult.Type.BLOCK)
this.placeHitResult = (BlockHitResult) this.client.crosshairTarget;
else
this.placeHitResult = null;
this.attackAction = this.client.crosshairTarget.getType() == HitResult.Type.BLOCK ? "lambdacontrols.action.hit" : ButtonBinding.ATTACK.getTranslationKey();
this.attackWidth = this.width(attackAction);
}
ItemStack stack = this.client.player.getMainHandStack();
if ((stack == null || stack.isEmpty()) && ((stack = this.client.player.getOffHandStack()) == null || stack.isEmpty())) {
placeAction = "";
} else {
if (this.placeHitResult != null && stack.getItem() instanceof BlockItem) {
placeAction = "lambdacontrols.action.place";
} else {
placeAction = ButtonBinding.USE.getTranslationKey();
}
}
this.placeAction = placeAction;
// Cache the "Use" tip width.
if (this.placeAction.isEmpty())
this.useWidth = 0;
else
this.useWidth = this.width(this.placeAction);
}
}
@Override
public boolean hasTicks()
{
return true;
}
private int bottom(int y)
{
return this.client.getWindow().getScaledHeight() - y - LambdaControlsRenderer.ICON_SIZE;
}
private int width(@NotNull ButtonBinding binding)
{
return this.width(binding.getTranslationKey());
}
private int width(@Nullable String text)
{
if (text == null || text.isEmpty())
return 0;
return this.client.textRenderer.getWidth(I18n.translate(text));
}
private void drawButton(MatrixStack matrices, int x, int y, @NotNull ButtonBinding button, boolean display)
{
if (display)
LambdaControlsRenderer.drawButton(matrices, x, y, button, this.client);
}
private void drawTip(MatrixStack matrices, int x, int y, @NotNull ButtonBinding button, boolean display)
{
this.drawTip(matrices, x, y, button.getTranslationKey(), display);
}
private void drawTip(MatrixStack matrices, int x, int y, @NotNull String action, boolean display)
{
if (!display)
return;
String translatedAction = I18n.translate(action);
int textY = (LambdaControlsRenderer.ICON_SIZE / 2 - this.client.textRenderer.fontHeight / 2) + 1;
this.client.textRenderer.draw(matrices, translatedAction, (float) x, (float) (y + textY), 14737632);
}
}

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 com.mojang.blaze3d.systems.RenderSystem;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
import me.lambdaurora.lambdacontrols.client.util.HandledScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.slot.Slot;
import org.aperlambda.lambdacommon.utils.Pair;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import java.util.Comparator;
import java.util.Optional;
/**
* Represents the LambdaControls renderer.
*
* @author LambdAurora
* @version 1.3.0
* @since 1.2.0
*/
public class LambdaControlsRenderer
{
public static final int ICON_SIZE = 20;
private static final int BUTTON_SIZE = 15;
private static final int AXIS_SIZE = 18;
public static int getButtonSize(int button)
{
switch (button) {
case -1:
return 0;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 100:
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 200:
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 100:
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 200:
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 100:
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 200:
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 100:
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 200:
return AXIS_SIZE;
default:
return BUTTON_SIZE;
}
}
/**
* Gets the binding icon width.
*
* @param binding The binding.
* @return The width.
*/
public static int getBindingIconWidth(@NotNull ButtonBinding binding)
{
return getBindingIconWidth(binding.getButton());
}
/**
* Gets the binding icon width.
*
* @param buttons The buttons.
* @return The width.
*/
public static int getBindingIconWidth(int[] buttons)
{
int width = 0;
for (int i = 0; i < buttons.length; i++) {
width += ICON_SIZE;
if (i + 1 < buttons.length) {
width += 2;
}
}
return width;
}
public static Pair<Integer, Integer> drawButton(MatrixStack matrices, int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client)
{
return drawButton(matrices, x, y, button.getButton(), client);
}
public static Pair<Integer, Integer> drawButton(MatrixStack matrices, int x, int y, int[] buttons, @NotNull MinecraftClient client)
{
int height = 0;
int length = 0;
int currentX = x;
for (int i = 0; i < buttons.length; i++) {
int btn = buttons[i];
int size = drawButton(matrices, currentX, y, btn, client);
if (size > height)
height = size;
length += size;
if (i + 1 < buttons.length) {
length += 2;
currentX = x + length;
}
}
return Pair.of(length, height);
}
@SuppressWarnings("deprecated")
public static int drawButton(MatrixStack matrices, int x, int y, int button, @NotNull MinecraftClient client)
{
boolean second = false;
if (button == -1)
return 0;
else if (button >= 500) {
button -= 1000;
second = true;
}
int controllerType = LambdaControlsClient.get().config.getControllerType().getId();
boolean axis = false;
int buttonOffset = button * 15;
switch (button) {
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER:
buttonOffset = 7 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER:
buttonOffset = 8 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_BACK:
buttonOffset = 4 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_START:
buttonOffset = 6 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE:
buttonOffset = 5 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB:
buttonOffset = 15 * 15;
break;
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB:
buttonOffset = 16 * 15;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 100:
buttonOffset = 0;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 100:
buttonOffset = 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 100:
buttonOffset = 2 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 100:
buttonOffset = 3 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 200:
buttonOffset = 4 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 200:
buttonOffset = 5 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 200:
buttonOffset = 6 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 200:
buttonOffset = 7 * 18;
axis = true;
break;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 100:
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 200:
buttonOffset = 9 * 15;
break;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 100:
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 200:
buttonOffset = 10 * 15;
break;
}
client.getTextureManager().bindTexture(axis ? LambdaControlsClient.CONTROLLER_AXIS : LambdaControlsClient.CONTROLLER_BUTTONS);
RenderSystem.disableDepthTest();
int assetSize = axis ? AXIS_SIZE : BUTTON_SIZE;
RenderSystem.color4f(1.0F, second ? 0.0F : 1.0F, 1.0F, 1.0F);
DrawableHelper.drawTexture(matrices, x + (ICON_SIZE / 2 - assetSize / 2), y + (ICON_SIZE / 2 - assetSize / 2),
(float) buttonOffset, (float) (controllerType * (axis ? AXIS_SIZE : BUTTON_SIZE)),
assetSize, assetSize,
256, 256);
RenderSystem.enableDepthTest();
return ICON_SIZE;
}
public static int drawButtonTip(MatrixStack matrices, int x, int y, @NotNull ButtonBinding button, boolean display, @NotNull MinecraftClient client)
{
return drawButtonTip(matrices, x, y, button.getButton(), button.getTranslationKey(), display, client);
}
public static int drawButtonTip(MatrixStack matrices, int x, int y, int[] button, @NotNull String action, boolean display, @NotNull MinecraftClient client)
{
if (display) {
int buttonWidth = drawButton(matrices, x, y, button, client).key;
String translatedAction = I18n.translate(action);
int textY = (LambdaControlsRenderer.ICON_SIZE / 2 - client.textRenderer.fontHeight / 2) + 1;
return client.textRenderer.drawWithShadow(matrices, translatedAction, (float) (x + buttonWidth + 2), (float) (y + textY), 14737632);
}
return -10;
}
private static int getButtonTipWidth(@NotNull String action, @NotNull TextRenderer textRenderer)
{
return 15 + 5 + textRenderer.getWidth(action);
}
public static void renderVirtualCursor(@NotNull MatrixStack matrices, @NotNull MinecraftClient client)
{
if (!LambdaControlsClient.get().config.hasVirtualMouse() || client.currentScreen == null)
return;
int mouseX = (int) (client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
int mouseY = (int) (client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight());
boolean hoverSlot = false;
if (client.currentScreen instanceof HandledScreen) {
HandledScreen inventoryScreen = (HandledScreen) client.currentScreen;
HandledScreenAccessor accessor = (HandledScreenAccessor) inventoryScreen;
int guiLeft = accessor.getX();
int guiTop = accessor.getY();
// Finds the closest slot in the GUI within 14 pixels.
int finalMouseX = mouseX;
int finalMouseY = mouseY;
Optional<Pair<Slot, Double>> closestSlot = inventoryScreen.getScreenHandler().slots.parallelStream()
.map(slot -> {
int x = guiLeft + slot.x + 8;
int y = guiTop + slot.y + 8;
// Distance between the slot and the cursor.
double distance = Math.sqrt(Math.pow(x - finalMouseX, 2) + Math.pow(y - finalMouseY, 2));
return Pair.of(slot, distance);
}).filter(entry -> entry.value <= 9.0)
.min(Comparator.comparingDouble(p -> p.value));
if (closestSlot.isPresent()) {
Slot slot = closestSlot.get().key;
mouseX = guiLeft + slot.x;
mouseY = guiTop + slot.y;
hoverSlot = true;
}
}
if (!hoverSlot) {
mouseX -= 8;
mouseY -= 8;
}
drawCursor(matrices, mouseX, mouseY, hoverSlot, client);
}
/**
* Draws the virtual cursor.
*
* @param matrices The matrix stack.
* @param x X coordinate.
* @param y Y coordinate.
* @param hoverSlot True if hovering a slot, else false.
* @param client The client instance.
*/
public static void drawCursor(@NotNull MatrixStack matrices, int x, int y, boolean hoverSlot, @NotNull MinecraftClient client)
{
client.getTextureManager().bindTexture(LambdaControlsClient.CURSOR_TEXTURE);
RenderSystem.disableDepthTest();
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
DrawableHelper.drawTexture(matrices, x, y, hoverSlot ? 16.F : 0.F, LambdaControlsClient.get().config.getVirtualMouseSkin().ordinal() * 16.F, 16, 16, 32, 64);
RenderSystem.enableDepthTest();
}
}

View File

@@ -0,0 +1,283 @@
/*
* 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.LambdaControls;
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.fabricmc.fabric.api.network.ClientSidePacketRegistry;
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.Option;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
import net.minecraft.util.Util;
import org.lwjgl.glfw.GLFW;
/**
* Represents the LambdaControls settings screen.
*/
public class LambdaControlsSettingsScreen extends Screen
{
public static final String GAMEPAD_TOOL_URL = "https://generalarcade.com/gamepadtool/";
final LambdaControlsClient mod;
private final Screen parent;
private final boolean hideControls;
// General options
private final Option autoSwitchModeOption;
private final Option rotationSpeedOption;
private final Option mouseSpeedOption;
private final Option resetOption;
// Gameplay options
private final Option autoJumpOption;
private final Option fastBlockPlacingOption;
private final Option frontBlockPlacingOption;
private final Option flyDriftingOption;
private final Option flyVerticalDriftingOption;
// Controller options
private final Option controllerOption;
private final Option secondControllerOption;
private final Option controllerTypeOption;
private final Option deadZoneOption;
private final Option invertsRightXAxis;
private final Option invertsRightYAxis;
private final Option unfocusedInputOption;
private final Option virtualMouseOption;
private final Option virtualMouseSkinOption;
// Hud options
private final Option hudEnableOption;
private final Option hudSideOption;
private final MutableText controllerMappingsUrlText = new LiteralText("(")
.append(new LiteralText(GAMEPAD_TOOL_URL).formatted(Formatting.GOLD))
.append("),");
private ButtonListWidget list;
private SpruceLabelWidget gamepadToolUrlLabel;
public LambdaControlsSettingsScreen(Screen parent, boolean hideControls)
{
super(new TranslatableText("lambdacontrols.title.settings"));
this.mod = LambdaControlsClient.get();
this.parent = parent;
this.hideControls = hideControls;
// General options
this.autoSwitchModeOption = new SpruceBooleanOption("lambdacontrols.menu.auto_switch_mode", this.mod.config::hasAutoSwitchMode,
this.mod.config::setAutoSwitchMode, new TranslatableText("lambdacontrols.tooltip.auto_switch_mode"), true);
this.rotationSpeedOption = new SpruceDoubleOption("lambdacontrols.menu.rotation_speed", 0.0, 100.0, 0.5F, this.mod.config::getRotationSpeed,
newValue -> {
synchronized (this.mod.config) {
this.mod.config.setRotationSpeed(newValue);
}
}, option -> option.getDisplayPrefix().append(String.valueOf(option.get())),
new TranslatableText("lambdacontrols.tooltip.rotation_speed"));
this.mouseSpeedOption = new SpruceDoubleOption("lambdacontrols.menu.mouse_speed", 0.0, 150.0, 0.5F, this.mod.config::getMouseSpeed,
newValue -> {
synchronized (this.mod.config) {
this.mod.config.setMouseSpeed(newValue);
}
}, option -> option.getDisplayPrefix().append(String.valueOf(option.get())),
new TranslatableText("lambdacontrols.tooltip.mouse_speed"));
this.resetOption = new SpruceResetOption(btn -> {
this.mod.config.reset();
MinecraftClient client = MinecraftClient.getInstance();
this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
});
// Gameplay options
this.autoJumpOption = SpruceBooleanOption.fromVanilla("options.autoJump", Option.AUTO_JUMP, null, true);
this.fastBlockPlacingOption = new SpruceBooleanOption("lambdacontrols.menu.fast_block_placing", this.mod.config::hasFastBlockPlacing,
this.mod.config::setFastBlockPlacing, new TranslatableText("lambdacontrols.tooltip.fast_block_placing"), true);
this.frontBlockPlacingOption = new SpruceBooleanOption("lambdacontrols.menu.front_block_placing", this.mod.config::hasFrontBlockPlacing,
this.mod.config::setFrontBlockPlacing, new TranslatableText("lambdacontrols.tooltip.front_block_placing"), true);
this.flyDriftingOption = new SpruceBooleanOption("lambdacontrols.menu.fly_drifting", this.mod.config::hasFlyDrifting,
this.mod.config::setFlyDrifting, new TranslatableText("lambdacontrols.tooltip.fly_drifting"), true);
this.flyVerticalDriftingOption = new SpruceBooleanOption("lambdacontrols.menu.fly_drifting_vertical", this.mod.config::hasFlyVerticalDrifting,
this.mod.config::setFlyVerticalDrifting, new TranslatableText("lambdacontrols.tooltip.fly_drifting_vertical"), true);
// Controller options
this.controllerOption = new SpruceCyclingOption("lambdacontrols.menu.controller", amount -> {
int id = this.mod.config.getController().getId();
id += amount;
if (id > GLFW.GLFW_JOYSTICK_LAST)
id = GLFW.GLFW_JOYSTICK_1;
this.mod.config.setController(Controller.byId(id));
}, option -> {
String controllerName = this.mod.config.getController().getName();
if (!this.mod.config.getController().isConnected())
return option.getDisplayPrefix().append(new LiteralText(controllerName).formatted(Formatting.RED));
else if (!this.mod.config.getController().isGamepad())
return option.getDisplayPrefix().append(new LiteralText(controllerName).formatted(Formatting.GOLD));
else
return option.getDisplayPrefix().append(controllerName);
}, null);
this.secondControllerOption = new SpruceCyclingOption("lambdacontrols.menu.controller2",
amount -> {
int id = this.mod.config.getSecondController().map(Controller::getId).orElse(-1);
id += amount;
if (id > GLFW.GLFW_JOYSTICK_LAST)
id = -1;
this.mod.config.setSecondController(id == -1 ? null : Controller.byId(id));
}, option -> this.mod.config.getSecondController().map(controller -> {
String controllerName = controller.getName();
if (!controller.isConnected())
return option.getDisplayPrefix().append(new LiteralText(controllerName).formatted(Formatting.RED));
else if (!controller.isGamepad())
return option.getDisplayPrefix().append(new LiteralText(controllerName).formatted(Formatting.GOLD));
else
return option.getDisplayPrefix().append(controllerName);
}).orElse(option.getDisplayPrefix().append(new TranslatableText("options.off").formatted(Formatting.RED))),
new TranslatableText("lambdacontrols.tooltip.controller2"));
this.controllerTypeOption = new SpruceCyclingOption("lambdacontrols.menu.controller_type",
amount -> this.mod.config.setControllerType(this.mod.config.getControllerType().next()),
option -> option.getDisplayPrefix().append(this.mod.config.getControllerType().getTranslatedName()),
new TranslatableText("lambdacontrols.tooltip.controller_type"));
this.deadZoneOption = new SpruceDoubleOption("lambdacontrols.menu.dead_zone", 0.05, 1.0, 0.05F, this.mod.config::getDeadZone,
newValue -> {
synchronized (this.mod.config) {
this.mod.config.setDeadZone(newValue);
}
}, option -> {
String value = String.valueOf(option.get());
return option.getDisplayPrefix().append(value.substring(0, Math.min(value.length(), 5)));
}, new TranslatableText("lambdacontrols.tooltip.dead_zone"));
this.invertsRightXAxis = new SpruceBooleanOption("lambdacontrols.menu.invert_right_x_axis", this.mod.config::doesInvertRightXAxis,
newValue -> {
synchronized (this.mod.config) {
this.mod.config.setInvertRightXAxis(newValue);
}
}, null, true);
this.invertsRightYAxis = new SpruceBooleanOption("lambdacontrols.menu.invert_right_y_axis", this.mod.config::doesInvertRightYAxis,
newValue -> {
synchronized (this.mod.config) {
this.mod.config.setInvertRightYAxis(newValue);
}
}, null, true);
this.unfocusedInputOption = new SpruceBooleanOption("lambdacontrols.menu.unfocused_input", this.mod.config::hasUnfocusedInput,
this.mod.config::setUnfocusedInput, new TranslatableText("lambdacontrols.tooltip.unfocused_input"), true);
this.virtualMouseOption = new SpruceBooleanOption("lambdacontrols.menu.virtual_mouse", this.mod.config::hasVirtualMouse,
this.mod.config::setVirtualMouse, new TranslatableText("lambdacontrols.tooltip.virtual_mouse"), true);
this.virtualMouseSkinOption = new SpruceCyclingOption("lambdacontrols.menu.virtual_mouse.skin",
amount -> this.mod.config.setVirtualMouseSkin(this.mod.config.getVirtualMouseSkin().next()),
option -> option.getDisplayPrefix().append(this.mod.config.getVirtualMouseSkin().getTranslatedName()),
null);
// HUD options
this.hudEnableOption = new SpruceBooleanOption("lambdacontrols.menu.hud_enable", this.mod.config::isHudEnabled,
this.mod::setHudEnabled, new TranslatableText("lambdacontrols.tooltip.hud_enable"), true);
this.hudSideOption = new SpruceCyclingOption("lambdacontrols.menu.hud_side",
amount -> this.mod.config.setHudSide(this.mod.config.getHudSide().next()),
option -> option.getDisplayPrefix().append(this.mod.config.getHudSide().getTranslatedName()),
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 getTextHeight()
{
return (5 + this.textRenderer.fontHeight) * 3 + 5;
}
@Override
protected void init()
{
super.init();
int buttonHeight = 20;
SpruceButtonWidget controlsModeBtn = new SpruceButtonWidget(this.width / 2 - 155, 18, this.hideControls ? 310 : 150, buttonHeight,
new TranslatableText("lambdacontrols.menu.controls_mode").append(": ").append(new TranslatableText(this.mod.config.getControlsMode().getTranslationKey())),
btn -> {
ControlsMode next = this.mod.config.getControlsMode().next();
btn.setMessage(new TranslatableText("lambdacontrols.menu.controls_mode").append(": ").append(new TranslatableText(next.getTranslationKey())));
this.mod.config.setControlsMode(next);
this.mod.config.save();
if (this.client.player != null) {
ClientSidePacketRegistry.INSTANCE.sendToServer(LambdaControls.CONTROLS_MODE_CHANNEL, this.mod.makeControlsModeBuffer(next));
}
});
controlsModeBtn.setTooltip(new TranslatableText("lambdacontrols.tooltip.controls_mode"));
this.addButton(controlsModeBtn);
if (!this.hideControls)
this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, 18, 150, buttonHeight, new TranslatableText("options.controls"),
btn -> {
if (this.mod.config.getControlsMode() == ControlsMode.CONTROLLER)
this.client.openScreen(new ControllerControlsScreen(this, true));
else
this.client.openScreen(new ControlsOptionsScreen(this, this.client.options));
}));
this.list = new ButtonListWidget(this.client, this.width, this.height, 43, this.height - 29 - this.getTextHeight(), 25);
// General options
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.general", true, null));
this.list.addOptionEntry(this.rotationSpeedOption, this.mouseSpeedOption);
this.list.addSingleOptionEntry(this.autoSwitchModeOption);
// Gameplay options
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.gameplay", true, null));
this.list.addSingleOptionEntry(this.autoJumpOption);
this.list.addOptionEntry(this.fastBlockPlacingOption, this.frontBlockPlacingOption);
this.list.addSingleOptionEntry(this.flyDriftingOption);
this.list.addSingleOptionEntry(this.flyVerticalDriftingOption);
// Controller options
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.controller", true, null));
this.list.addSingleOptionEntry(this.controllerOption);
this.list.addSingleOptionEntry(this.secondControllerOption);
this.list.addOptionEntry(this.controllerTypeOption, this.deadZoneOption);
this.list.addOptionEntry(this.invertsRightXAxis, this.invertsRightYAxis);
this.list.addOptionEntry(this.unfocusedInputOption, this.virtualMouseOption);
this.list.addSingleOptionEntry(this.virtualMouseSkinOption);
this.list.addSingleOptionEntry(new ReloadControllerMappingsOption());
// HUD options
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.hud", true, null));
this.list.addOptionEntry(this.hudEnableOption, this.hudSideOption);
this.children.add(this.list);
this.gamepadToolUrlLabel = new SpruceLabelWidget(this.width / 2, this.height - 29 - (5 + this.textRenderer.fontHeight) * 2, this.controllerMappingsUrlText, this.width,
label -> Util.getOperatingSystem().open(GAMEPAD_TOOL_URL), true);
this.gamepadToolUrlLabel.setTooltip(new TranslatableText("chat.link.open"));
this.children.add(this.gamepadToolUrlLabel);
this.addButton(this.resetOption.createButton(this.client.options, this.width / 2 - 155, this.height - 29, 150));
this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, this.height - 29, 150, buttonHeight, new TranslatableText("gui.done"),
(buttonWidget) -> this.client.openScreen(this.parent)));
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta)
{
this.renderBackground(matrices);
this.list.render(matrices, mouseX, mouseY, delta);
super.render(matrices, mouseX, mouseY, delta);
this.drawCenteredString(matrices, this.textRenderer, I18n.translate("lambdacontrols.menu.title"), this.width / 2, 8, 16777215);
this.drawCenteredString(matrices, this.textRenderer, I18n.translate("lambdacontrols.controller.mappings.1", Formatting.GREEN.toString(), Formatting.RESET.toString()), this.width / 2, this.height - 29 - (5 + this.textRenderer.fontHeight) * 3, 10526880);
this.gamepadToolUrlLabel.render(matrices, mouseX, mouseY, delta);
this.drawCenteredString(matrices, this.textRenderer, I18n.translate("lambdacontrols.controller.mappings.3", Formatting.GREEN.toString(), Formatting.RESET.toString()), this.width / 2, this.height - 29 - (5 + this.textRenderer.fontHeight), 10526880);
Tooltip.renderAll(matrices);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,12 +7,12 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
package me.lambdaurora.lambdacontrols.client.gui;
import me.lambdaurora.lambdacontrols.Controller;
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.gui.widget.ButtonWidget;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.Option;
import net.minecraft.client.resource.language.I18n;
@@ -36,17 +36,19 @@ public class ReloadControllerMappingsOption extends Option implements Nameable
@Override
public AbstractButtonWidget createButton(GameOptions options, int x, int y, int width)
{
return new ButtonWidget(x, y, width, 20, this.get_name(), btn -> {
SpruceButtonWidget button = new SpruceButtonWidget(x, y, width, 20, new TranslatableText(KEY), btn -> {
MinecraftClient client = MinecraftClient.getInstance();
Controller.update_mappings();
Controller.updateMappings();
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.setTooltip(new TranslatableText("lambdacontrols.tooltip.reload_controller_mappings"));
return button;
}
@Override
public @NotNull String get_name()
public @NotNull String getName()
{
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.network.packet.c2s.play.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 jumpButton;
private SpruceTexturedButtonWidget flyButton;
private SpruceTexturedButtonWidget flyUpButton;
private SpruceTexturedButtonWidget flyDownButton;
private int flyButtonEnableTicks = 0;
private int forwardButtonTick = 0;
private SpruceTexturedButtonWidget forwardLeftButton;
private SpruceTexturedButtonWidget forwardRightButton;
private SpruceTexturedButtonWidget startSneakButton;
private SpruceTexturedButtonWidget endSneakButton;
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 pauseGame(boolean bl)
{
if (this.client == null)
return;
boolean bl2 = this.client.isIntegratedServerRunning() && !this.client.getServer().isRemote();
if (bl2) {
this.client.openScreen(new GameMenuScreen(!bl));
this.client.getSoundManager().pauseAll();
} else {
this.client.openScreen(new GameMenuScreen(true));
}
}
/**
* Updates the forward button ticks cooldown.
*
* @param state The button state.
*/
private void updateForwardButtonsState(boolean state)
{
if (state)
this.forwardButtonTick = -1;
else
this.forwardButtonTick = 20;
}
/**
* Updates the jump buttons.
*/
private void updateJumpButtons()
{
if (this.client == null)
return;
if (this.client.player.abilities.allowFlying && this.client.player.abilities.flying) {
boolean oldStateFly = this.flyButton.visible;
this.jumpButton.visible = false;
this.flyButton.visible = true;
this.flyUpButton.visible = true;
this.flyDownButton.visible = true;
if (oldStateFly != this.flyButton.visible) {
this.flyButtonEnableTicks = 5;
this.handleJump(null, false);
} else if (this.flyButtonEnableTicks > 0)
this.flyButtonEnableTicks--;
} else {
this.jumpButton.visible = true;
this.flyButton.visible = false;
this.flyUpButton.visible = false;
this.flyDownButton.visible = false;
}
}
/**
* Handles the jump button.
*
* @param btn The pressed button.
* @param state The state of the jump button.
*/
private void handleJump(ButtonWidget btn, boolean state)
{
((KeyBindingAccessor) this.client.options.keyJump).lambdacontrols_handlePressState(state);
}
@Override
public void tick()
{
if (this.forwardButtonTick > 0) {
this.forwardButtonTick--;
} else if (this.forwardButtonTick == 0) {
if (this.forwardLeftButton.visible)
this.forwardLeftButton.visible = false;
if (this.forwardRightButton.visible)
this.forwardRightButton.visible = false;
}
this.updateJumpButtons();
}
@Override
protected void init()
{
super.init();
int scaledWidth = this.client.getWindow().getScaledWidth();
int scaledHeight = this.client.getWindow().getScaledHeight();
this.addButton(new TexturedButtonWidget(scaledWidth / 2 - 20, 0, 20, 20, 0, 106, 20, ButtonWidget.WIDGETS_LOCATION, 256, 256,
btn -> this.client.openScreen(new ChatScreen("")), LiteralText.EMPTY));
this.addButton(new TexturedButtonWidget(scaledWidth / 2, 0, 20, 20, 0, 0, 20, WIDGETS_LOCATION, 256, 256,
btn -> this.pauseGame(false)));
// Inventory buttons.
int inventoryButtonX = scaledWidth / 2;
int inventoryButtonY = scaledHeight - 16 - 5;
if (this.client.options.mainArm == Arm.LEFT) {
inventoryButtonX = inventoryButtonX - 91 - 24;
} else {
inventoryButtonX = inventoryButtonX + 91 + 4;
}
this.addButton(new TexturedButtonWidget(inventoryButtonX, inventoryButtonY, 20, 20, 20, 0, 20, WIDGETS_LOCATION, 256, 256,
btn -> {
if (this.client.interactionManager.hasRidingInventory()) {
this.client.player.openRidingInventory();
} else {
this.client.getTutorialManager().onInventoryOpened();
this.client.openScreen(new InventoryScreen(this.client.player));
}
}));
int jumpButtonX, swapHandsX, sneakButtonX;
int sneakButtonY = scaledHeight - 10 - 40 - 5;
if (this.mod.config.getHudSide() == HudSide.LEFT) {
jumpButtonX = scaledWidth - 20 - 20;
swapHandsX = jumpButtonX - 5 - 40;
sneakButtonX = 10 + 20 + 5;
} else {
jumpButtonX = 20;
swapHandsX = jumpButtonX + 5 + 40;
sneakButtonX = scaledWidth - 10 - 40 - 5;
}
// Swap items hand.
this.addButton(new SpruceTexturedButtonWidget(swapHandsX, sneakButtonY, 20, 20, 0, 160, 20, WIDGETS_LOCATION,
(btn, state) -> {
if (state) {
if (!this.client.player.isSpectator()) {
this.client.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.DOWN));
}
}
}));
// Drop
this.addButton(new SpruceTexturedButtonWidget(swapHandsX, sneakButtonY + 5 + 20, 20, 20, 20, 160, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.client.options.keyDrop).lambdacontrols_handlePressState(state)));
// Jump keys
this.addButton(this.jumpButton = new SpruceTexturedButtonWidget(jumpButtonX, sneakButtonY, 20, 20, 0, 40, 20, WIDGETS_LOCATION,
this::handleJump));
this.addButton(this.flyButton = new SpruceTexturedButtonWidget(jumpButtonX, sneakButtonY, 20, 20, 20, 40, 20, WIDGETS_LOCATION,
(btn, state) -> {
if (this.flyButtonEnableTicks == 0) this.client.player.abilities.flying = false;
}));
this.addButton(this.flyUpButton = new SpruceTexturedButtonWidget(jumpButtonX, sneakButtonY - 5 - 20, 20, 20, 40, 40, 20, WIDGETS_LOCATION,
this::handleJump));
this.addButton(this.flyDownButton = new SpruceTexturedButtonWidget(jumpButtonX, sneakButtonY + 20 + 5, 20, 20, 60, 40, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.client.options.keySneak).lambdacontrols_handlePressState(state)));
this.updateJumpButtons();
// Movements keys
this.addButton((this.startSneakButton = new SpruceTexturedButtonWidget(sneakButtonX, sneakButtonY, 20, 20, 0, 120, 20, WIDGETS_LOCATION,
(btn, state) -> {
if (state) {
((KeyBindingAccessor) this.client.options.keySneak).lambdacontrols_handlePressState(true);
this.startSneakButton.visible = false;
this.endSneakButton.visible = true;
}
})));
this.addButton((this.endSneakButton = new SpruceTexturedButtonWidget(sneakButtonX, sneakButtonY, 20, 20, 20, 120, 20, WIDGETS_LOCATION,
(btn, state) -> {
if (state) {
((KeyBindingAccessor) this.client.options.keySneak).lambdacontrols_handlePressState(false);
this.endSneakButton.visible = false;
this.startSneakButton.visible = true;
}
})));
this.endSneakButton.visible = false;
this.addButton(this.forwardLeftButton = new SpruceTexturedButtonWidget(sneakButtonX - 20 - 5, sneakButtonY - 5 - 20, 20, 20, 80, 80, 20, WIDGETS_LOCATION,
(btn, state) -> {
((KeyBindingAccessor) this.client.options.keyForward).lambdacontrols_handlePressState(state);
((KeyBindingAccessor) this.client.options.keyLeft).lambdacontrols_handlePressState(state);
this.updateForwardButtonsState(state);
}));
this.forwardLeftButton.visible = false;
this.addButton(new SpruceTexturedButtonWidget(sneakButtonX, sneakButtonY - 5 - 20, 20, 20, 0, 80, 20, WIDGETS_LOCATION,
(btn, state) -> {
((KeyBindingAccessor) this.client.options.keyForward).lambdacontrols_handlePressState(state);
this.updateForwardButtonsState(state);
this.forwardLeftButton.visible = true;
this.forwardRightButton.visible = true;
}));
this.addButton(this.forwardRightButton = new SpruceTexturedButtonWidget(sneakButtonX + 20 + 5, sneakButtonY - 5 - 20, 20, 20, 100, 80, 20, WIDGETS_LOCATION,
(btn, state) -> {
((KeyBindingAccessor) this.client.options.keyForward).lambdacontrols_handlePressState(state);
((KeyBindingAccessor) this.client.options.keyRight).lambdacontrols_handlePressState(state);
this.updateForwardButtonsState(state);
}));
this.forwardRightButton.visible = true;
this.addButton(new SpruceTexturedButtonWidget(sneakButtonX + 20 + 5, sneakButtonY, 20, 20, 20, 80, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.client.options.keyRight).lambdacontrols_handlePressState(state)));
this.addButton(new SpruceTexturedButtonWidget(sneakButtonX, sneakButtonY + 20 + 5, 20, 20, 40, 80, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.client.options.keyBack).lambdacontrols_handlePressState(state)));
this.addButton(new SpruceTexturedButtonWidget(sneakButtonX - 20 - 5, sneakButtonY, 20, 20, 60, 80, 20, WIDGETS_LOCATION,
(btn, state) -> ((KeyBindingAccessor) this.client.options.keyLeft).lambdacontrols_handlePressState(state)));
this.buttons.forEach(button -> {
if (button instanceof SpruceTexturedButtonWidget) {
((SpruceTexturedButtonWidget) button).setSilent(true);
}
});
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button)
{
if (mouseY >= (double) (this.height - 22) && this.client != null && this.client.player != null) {
int centerX = this.width / 2;
if (mouseX >= (double) (centerX - 90) && mouseX <= (double) (centerX + 90)) {
for (int slot = 0; slot < 9; ++slot) {
int slotX = centerX - 90 + slot * 20 + 2;
if (mouseX >= (double) slotX && mouseX <= (double) (slotX + 20)) {
this.client.player.inventory.selectedSlot = slot;
return true;
}
}
}
}
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY)
{
if (button == GLFW.GLFW_MOUSE_BUTTON_1 && this.client != null) {
if (deltaY > 0.01)
this.mod.input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) Math.abs(deltaY / 5.0), 2);
else if (deltaY < 0.01)
this.mod.input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) Math.abs(deltaY / 5.0), 1);
if (deltaX > 0.01)
this.mod.input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_X, (float) Math.abs(deltaX / 5.0), 2);
else if (deltaX < 0.01)
this.mod.input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_X, (float) Math.abs(deltaX / 5.0), 1);
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,7 +7,7 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.mixin;
package me.lambdaurora.lambdacontrols.client.mixin;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import org.spongepowered.asm.mixin.Mixin;
@@ -17,5 +17,5 @@ import org.spongepowered.asm.mixin.gen.Accessor;
public interface AbstractButtonWidgetAccessor
{
@Accessor("height")
int get_height();
int getHeight();
}

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.client.mixin;
import net.minecraft.advancement.Advancement;
import net.minecraft.client.gui.screen.advancement.AdvancementTab;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.network.ClientAdvancementManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
/**
* Represents an accessor of {@link AdvancementsScreen}.
*/
@Mixin(AdvancementsScreen.class)
public interface AdvancementsScreenAccessor
{
@Accessor("advancementHandler")
ClientAdvancementManager getAdvancementManager();
@Accessor("tabs")
Map<Advancement, AdvancementTab> getTabs();
@Accessor("selectedTab")
AdvancementTab getSelectedTab();
}

View File

@@ -0,0 +1,32 @@
/*
* 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.LambdaControls;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayNetworkHandler.class)
public class ClientPlayNetworkHandlerMixin
{
@Inject(method = "onGameJoin", at = @At(value = "TAIL"))
private void onGameJoin(GameJoinS2CPacket packet, CallbackInfo ci)
{
ClientSidePacketRegistry.INSTANCE.sendToServer(LambdaControls.HELLO_CHANNEL, LambdaControls.get().makeHello(LambdaControlsClient.get().config.getControlsMode()));
ClientSidePacketRegistry.INSTANCE.sendToServer(LambdaControls.CONTROLS_MODE_CHANNEL,
LambdaControls.get().makeControlsModeBuffer(LambdaControlsClient.get().config.getControlsMode()));
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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 com.mojang.authlib.GameProfile;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.input.Input;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.MovementType;
import net.minecraft.util.math.Vec3d;
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;
/**
* Injects the anti fly drifting feature.
*/
@Mixin(ClientPlayerEntity.class)
public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity
{
private boolean lambdacontrols_driftingPrevented = false;
@Shadow
protected abstract boolean hasMovementInput();
@Shadow
@Final
protected MinecraftClient client;
@Shadow
public Input input;
@Shadow
protected abstract boolean isCamera();
public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile)
{
super(world, profile);
}
@Inject(method = "move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V"))
public void onMove(MovementType type, Vec3d movement, CallbackInfo ci)
{
LambdaControlsClient mod = LambdaControlsClient.get();
if (type == MovementType.SELF) {
if (this.abilities.flying && (!mod.config.hasFlyDrifting() || !mod.config.hasFlyVerticalDrifting())) {
if (!this.hasMovementInput()) {
if (!this.lambdacontrols_driftingPrevented) {
if (!mod.config.hasFlyDrifting())
this.setVelocity(this.getVelocity().multiply(0, 1.0, 0));
}
this.lambdacontrols_driftingPrevented = true;
} else
this.lambdacontrols_driftingPrevented = false;
}
}
}
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isCamera()Z"))
public void onTickMovement(CallbackInfo ci)
{
if (this.abilities.flying && this.isCamera()) {
if (LambdaControlsClient.get().config.hasFlyVerticalDrifting())
return;
int moving = 0;
if (this.input.sneaking) {
--moving;
}
if (this.input.jumping) {
++moving;
}
if (moving == 0) {
this.setVelocity(this.getVelocity().multiply(1.0, 0.0, 1.0));
}
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.ControllerControlsScreen;
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 net.minecraft.text.TranslatableText;
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 gameOptions, Text text)
{
super(parent, gameOptions, 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 onInit(ControlsOptionsScreen screen, AbstractButtonWidget btn)
{
if (this.parent instanceof ControllerControlsScreen)
return this.addButton(btn);
else
return this.addButton(new ButtonWidget(btn.x, btn.y, btn.getWidth(), ((AbstractButtonWidgetAccessor) btn).getHeight(), new TranslatableText("menu.options"),
b -> this.client.openScreen(new LambdaControlsSettingsScreen(this, true))));
}
}

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.mixin;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.item.ItemGroup;
import net.minecraft.screen.slot.Slot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
/**
* Represents an accessor to CreativeInventoryScreen.
*/
@Mixin(CreativeInventoryScreen.class)
public interface CreativeInventoryScreenAccessor
{
/**
* Gets the selected tab.
*
* @return The selected tab index.
*/
@Accessor("selectedTab")
int getSelectedTab();
/**
* Sets the selected tab.
*
* @param group The tab's item group.
*/
@Invoker("setSelectedTab")
void lambdacontrols_setSelectedTab(@NotNull ItemGroup group);
/**
* Returns whether the slot belongs to the creative inventory or not.
*
* @param slot The slot to check.
* @return True if the slot is from the creative inventory, else false.
*/
@Invoker("isCreativeInventorySlot")
boolean lambdacontrols_isCreativeInventorySlot(@Nullable Slot slot);
/**
* Returns whether the current tab has a scrollbar or not.
*
* @return True if the current tab has a scrollbar, else false.
*/
@Invoker("hasScrollbar")
boolean lambdacontrols_hasScrollbar();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,7 +7,7 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.mixin;
package me.lambdaurora.lambdacontrols.client.mixin;
import net.minecraft.client.gui.widget.EntryListWidget;
import org.spongepowered.asm.mixin.Mixin;
@@ -17,5 +17,5 @@ import org.spongepowered.asm.mixin.gen.Invoker;
public interface EntryListWidgetAccessor
{
@Invoker("moveSelection")
void move_selection(int amount);
void lambdacontrols_moveSelection(EntryListWidget.class_5403 direction);
}

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 net.minecraft.client.options.GameOptions;
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 a mixin to GameOptions.
* <p>
* Sets the default of the Auto-Jump option to false.
*/
@Mixin(GameOptions.class)
public class GameOptionsMixin
{
@Shadow
public boolean autoJump;
@Inject(method = "load", at = @At("HEAD"))
public void onInit(CallbackInfo ci)
{
// Set default value of the Auto-Jump option to false.
this.autoJump = false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,10 +7,10 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.mixin;
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.LambdaControls;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.GameRenderer;
import org.spongepowered.asm.mixin.Final;
@@ -28,9 +28,9 @@ public class GameRendererMixin
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)
private void onRender(float tickDelta, long startTime, boolean fullRender, CallbackInfo ci)
{
if (this.client.currentScreen != null && LambdaControls.get().config.get_controls_mode() == ControlsMode.CONTROLLER)
LambdaControls.get().input.on_pre_render_screen(this.client, this.client.currentScreen);
if (this.client.currentScreen != null && LambdaControlsClient.get().config.getControlsMode() == ControlsMode.CONTROLLER)
LambdaControlsClient.get().input.onPreRenderScreen(this.client, this.client.currentScreen);
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.compat.LambdaControlsCompat;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsRenderer;
import me.lambdaurora.lambdacontrols.client.util.HandledScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.slot.Slot;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
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 ContainerScreen.
*/
@Mixin(HandledScreen.class)
public abstract class HandledScreenMixin implements HandledScreenAccessor
{
@Accessor("x")
public abstract int getX();
@Accessor("y")
public abstract int getY();
@Invoker("getSlotAt")
public abstract Slot lambdacontrols_getSlotAt(double posX, double posY);
@Inject(method = "render", at = @At("RETURN"))
public void onRender(MatrixStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci)
{
if (LambdaControlsClient.get().config.getControlsMode() == ControlsMode.CONTROLLER) {
MinecraftClient client = MinecraftClient.getInstance();
int x = 2, y = client.getWindow().getScaledHeight() - 2 - LambdaControlsRenderer.ICON_SIZE;
x = LambdaControlsRenderer.drawButtonTip(matrices, x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_A}, "lambdacontrols.action.pickup_all", true, client) + 2;
x = LambdaControlsRenderer.drawButtonTip(matrices, x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_B}, "lambdacontrols.action.exit", true, client) + 2;
if (LambdaControlsCompat.isReiPresent()) {
x = 2;
y -= 24;
}
x = LambdaControlsRenderer.drawButtonTip(matrices, x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_X}, "lambdacontrols.action.pickup", true, client) + 2;
LambdaControlsRenderer.drawButtonTip(matrices, x, y, new int[]{GLFW.GLFW_GAMEPAD_BUTTON_Y}, "lambdacontrols.action.quick_move", true, client);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,41 +7,30 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.mixin;
package me.lambdaurora.lambdacontrols.client.mixin;
import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor;
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;
boolean oldPressed = this.pressed;
if (!this.pressed)
this.pressed = true;
++this.timesPressed;
return old_pressed != this.pressed;
return oldPressed != this.pressed;
}
@Override

View File

@@ -0,0 +1,177 @@
/*
* 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.LambdaControlsFeature;
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.LambdaInput;
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsRenderer;
import me.lambdaurora.lambdacontrols.client.util.FrontBlockPlaceResultAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
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;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(MinecraftClient.class)
public abstract class MinecraftClientMixin implements FrontBlockPlaceResultAccessor
{
@Shadow
@Nullable
public HitResult crosshairTarget;
@Shadow
@Nullable
public ClientPlayerEntity player;
@Shadow
@Nullable
public ClientPlayerInteractionManager interactionManager;
@Shadow
@Nullable
public ClientWorld world;
@Shadow
@Final
public GameRenderer gameRenderer;
@Shadow
private int itemUseCooldown;
private BlockHitResult lambdacontrols_frontBlockPlaceResult = null;
private BlockPos lambdacontrols_lastTargetPos;
private Vec3d lambdacontrols_lastPos;
private Direction lambdacontrols_lastTargetSide;
@Override
public @Nullable BlockHitResult lambdacontrols_getFrontBlockPlaceResult()
{
return this.lambdacontrols_frontBlockPlaceResult;
}
@Inject(method = "<init>", at = @At("RETURN"))
private void onInit(CallbackInfo ci)
{
LambdaControlsClient.get().onMcInit((MinecraftClient) (Object) this);
}
@Inject(method = "tick", at = @At("HEAD"))
private void onStartTick(CallbackInfo ci)
{
if (this.player == null)
return;
this.lambdacontrols_frontBlockPlaceResult = LambdaInput.tryFrontPlace(((MinecraftClient) (Object) this));
if (!LambdaControlsFeature.FAST_BLOCK_PLACING.isAvailable())
return;
if (this.lambdacontrols_lastPos == null)
this.lambdacontrols_lastPos = this.player.getPos();
int cooldown = this.itemUseCooldown;
BlockHitResult hitResult;
if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.BLOCK && this.player.abilities.flying) {
hitResult = (BlockHitResult) this.crosshairTarget;
BlockPos targetPos = hitResult.getBlockPos();
Direction side = hitResult.getSide();
boolean sidewaysBlockPlacing = this.lambdacontrols_lastTargetPos == null || !targetPos.equals(this.lambdacontrols_lastTargetPos.offset(this.lambdacontrols_lastTargetSide));
boolean backwardsBlockPlacing = this.player.input.movementForward < 0.0f && (this.lambdacontrols_lastTargetPos == null || targetPos.equals(this.lambdacontrols_lastTargetPos.offset(this.lambdacontrols_lastTargetSide)));
if (cooldown > 1
&& !targetPos.equals(this.lambdacontrols_lastTargetPos)
&& (sidewaysBlockPlacing || backwardsBlockPlacing)) {
this.itemUseCooldown = 1;
}
this.lambdacontrols_lastTargetPos = targetPos.toImmutable();
this.lambdacontrols_lastTargetSide = side;
}
// Removed front placing sprinting as way too cheaty.
/* else if (this.player.isSprinting()) {
hitResult = this.lambdacontrols_frontBlockPlaceResult;
if (hitResult != null) {
if (cooldown > 0)
this.itemUseCooldown = 0;
}
} */
this.lambdacontrols_lastPos = this.player.getPos();
}
@Inject(method = "render", at = @At("HEAD"))
private void onRender(boolean fullRender, CallbackInfo ci)
{
LambdaControlsClient.get().onRender((MinecraftClient) (Object) (this));
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;render(FJZ)V", shift = At.Shift.AFTER))
private void renderVirtualCursor(boolean fullRender, CallbackInfo ci) {
LambdaControlsRenderer.renderVirtualCursor(new MatrixStack(), (MinecraftClient) (Object) this);
}
@Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V", at = @At("RETURN"))
private void onLeave(@Nullable Screen screen, CallbackInfo ci)
{
LambdaControlsClient.get().onLeave();
}
@Inject(method = "doItemUse()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/hit/HitResult;getType()Lnet/minecraft/util/hit/HitResult$Type;"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true)
private void onItemUse(CallbackInfo ci, Hand[] hands, int handCount, int handIndex, Hand hand, ItemStack stackInHand)
{
if (!stackInHand.isEmpty() && this.player.pitch > 35.0F && LambdaControlsFeature.FRONT_BLOCK_PLACING.isAvailable()) {
if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.MISS && this.player.isOnGround()) {
if (!stackInHand.isEmpty() && stackInHand.getItem() instanceof BlockItem) {
BlockHitResult hitResult = LambdaInput.tryFrontPlace(((MinecraftClient) (Object) this));
if (hitResult == null)
return;
hitResult = LambdaInput.withSideForFrontPlace(hitResult, stackInHand);
int previousStackCount = stackInHand.getCount();
ActionResult result = this.interactionManager.interactBlock(this.player, this.world, hand, hitResult);
if (result.isAccepted()) {
if (result.shouldSwingHand()) {
this.player.swingHand(hand);
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || this.interactionManager.hasCreativeInventory())) {
this.gameRenderer.firstPersonRenderer.resetEquipProgress(hand);
}
}
ci.cancel();
}
if (result == ActionResult.FAIL) {
ci.cancel();
}
}
}
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.LambdaControlsConfig;
import me.lambdaurora.lambdacontrols.client.util.MouseAccessor;
import net.minecraft.client.Mouse;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
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
{
@Invoker("onCursorPos")
public abstract void lambdacontrols_onCursorPos(long window, double x, double y);
@Inject(method = "lockCursor", at = @At("HEAD"), cancellable = true)
private void onMouseLocked(CallbackInfo ci)
{
LambdaControlsConfig config = LambdaControlsClient.get().config;
if (config.getControlsMode() == ControlsMode.TOUCHSCREEN || config.hasVirtualMouse())
ci.cancel();
}
}

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.ControllerControlsScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.options.OptionsScreen;
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(OptionsScreen.class)
public class OptionsScreenMixin extends Screen
{
protected OptionsScreenMixin(Text title)
{
super(title);
}
@Redirect(method = "init", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/options/OptionsScreen;addButton(Lnet/minecraft/client/gui/widget/AbstractButtonWidget;)Lnet/minecraft/client/gui/widget/AbstractButtonWidget;", ordinal = 7))
private AbstractButtonWidget lambdacontrols_onInit(OptionsScreen screen, AbstractButtonWidget btn)
{
if (LambdaControlsClient.get().config.getControlsMode() == ControlsMode.CONTROLLER) {
return this.addButton(new ButtonWidget(btn.x, btn.y, btn.getWidth(), ((AbstractButtonWidgetAccessor) btn).getHeight(), btn.getMessage(),
b -> this.client.openScreen(new ControllerControlsScreen(this, false))));
} else {
return this.addButton(btn);
}
}
}

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 net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.List;
@Mixin(RecipeBookWidget.class)
public interface RecipeBookWidgetAccessor
{
@Accessor("tabButtons")
List<RecipeGroupButtonWidget> getTabButtons();
@Accessor("currentTab")
RecipeGroupButtonWidget getCurrentTab();
@Accessor("currentTab")
void setCurrentTab(RecipeGroupButtonWidget currentTab);
@Invoker("refreshResults")
void lambdacontrols_refreshResults(boolean resetCurrentPage);
}

View File

@@ -0,0 +1,99 @@
/*
* 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.LambdaControlsClient;
import me.lambdaurora.lambdacontrols.client.LambdaInput;
import me.lambdaurora.lambdacontrols.client.util.FrontBlockPlaceResultAccessor;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.util.shape.VoxelShape;
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;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
/**
* Represents a mixin to WorldRenderer.
* <p>
* Handles the rendering of the block outline of the front block placing.
*/
@Mixin(WorldRenderer.class)
public abstract class WorldRendererMixin
{
@Shadow
@Final
private MinecraftClient client;
@Shadow
private ClientWorld world;
@Shadow
private static void drawShapeOutline(MatrixStack matrixStack, VertexConsumer vertexConsumer, VoxelShape voxelShape, double d, double e, double f, float g, float h, float i, float j)
{
}
@Inject(
method = "render",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/client/MinecraftClient;crosshairTarget:Lnet/minecraft/util/hit/HitResult;",
ordinal = 1,
shift = At.Shift.AFTER
),
locals = LocalCapture.CAPTURE_FAILEXCEPTION
)
private void onOutlineRender(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer,
LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci,
Profiler profiler, Vec3d cameraPos, double x, double y, double z, Matrix4f modelMatrix, boolean bl, Frustum frustum2, boolean bl3,
VertexConsumerProvider.Immediate immediate)
{
if (this.client.crosshairTarget == null || this.client.crosshairTarget.getType() != HitResult.Type.MISS || !LambdaControlsClient.get().config.shouldRenderFrontBlockOutline())
return;
BlockHitResult result = ((FrontBlockPlaceResultAccessor) client).lambdacontrols_getFrontBlockPlaceResult();
if (result == null)
return;
BlockPos blockPos = result.getBlockPos();
if (this.world.getWorldBorder().contains(blockPos)) {
ItemStack stack = this.client.player.getStackInHand(Hand.MAIN_HAND);
if (stack == null || !(stack.getItem() instanceof BlockItem))
return;
Block block = ((BlockItem) stack.getItem()).getBlock();
result = LambdaInput.withSideForFrontPlace(result, block);
ItemPlacementContext context = new ItemPlacementContext(new ItemUsageContext(this.client.player, Hand.MAIN_HAND, result));
VertexConsumer vertexConsumer = immediate.getBuffer(RenderLayer.getLines());
BlockState placementState = block.getPlacementState(context);
if (placementState == null)
return;
VoxelShape outlineShape = placementState.getOutlineShape(this.client.world, blockPos, ShapeContext.of(camera.getFocusedEntity()));
int[] color = LambdaControlsClient.get().config.getFrontBlockOutlineColor();
drawShapeOutline(matrices, vertexConsumer, outlineShape, (double) blockPos.getX() - x, (double) blockPos.getY() - y, (double) blockPos.getZ() - z, color[0] / 255.f, color[1] / 255.f, color[2] / 255.f, color[3] / 255.f);
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.util.hit.BlockHitResult;
import org.jetbrains.annotations.Nullable;
/**
* Represents an accessor of the BlockHitResult for the front block placing feature.
* <p>
* It is implemented by {@link net.minecraft.client.MinecraftClient}.
*
* @author LambdAurora
* @version 1.2.0
* @since 1.2.0
*/
public interface FrontBlockPlaceResultAccessor
{
/**
* Returns the {@link BlockHitResult} if a block can be placed with the front block placing feature.
*
* @return If possible a {@link BlockHitResult}, else a null value.
*/
@Nullable BlockHitResult lambdacontrols_getFrontBlockPlaceResult();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,28 +7,28 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.util;
package me.lambdaurora.lambdacontrols.client.util;
import net.minecraft.container.Slot;
import net.minecraft.screen.slot.Slot;
/**
* Represents an accessor to AbstractContainerScreen.
*/
public interface AbstractContainerScreenAccessor
public interface HandledScreenAccessor
{
/**
* Gets the left coordinate of the GUI.
*
* @return The left coordinate of the GUI.
*/
int get_x();
int getX();
/**
* Gets the top coordinate of the GUI.
*
* @return The top coordinate of the GUI.
*/
int get_y();
int getY();
/**
* Gets the slot at position.
@@ -37,5 +37,5 @@ public interface AbstractContainerScreenAccessor
* @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);
Slot lambdacontrols_getSlotAt(double pos_x, double pos_y);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
@@ -7,23 +7,18 @@
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.util;
import net.minecraft.client.util.InputUtil;
import org.jetbrains.annotations.NotNull;
package me.lambdaurora.lambdacontrols.client.util;
/**
* 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)
default boolean lambdacontrols_handlePressState(boolean pressed)
{
if (pressed)
return this.lambdacontrols_press();

View File

@@ -0,0 +1,18 @@
/*
* 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 lambdacontrols_onCursorPos(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, controlsMode) -> {
for (PlayerChangeControlsModeCallback event : listeners) {
event.apply(player, controlsMode);
}
});
void apply(@NotNull PlayerEntity player, @NotNull ControlsMode controlsMode);
}

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -10,6 +10,8 @@
"lambdacontrols.action.exit": "Exit",
"lambdacontrols.action.forward": "Forward",
"lambdacontrols.action.hit": "Hit",
"lambdacontrols.action.hotbar_left": "Hotbar left",
"lambdacontrols.action.hotbar_right": "Hotbar right",
"lambdacontrols.action.inventory": "Inventory",
"lambdacontrols.action.jump": "Jump",
"lambdacontrols.action.left": "Left",
@@ -17,6 +19,7 @@
"lambdacontrols.action.pick_block": "Pick Block",
"lambdacontrols.action.pickup": "Pickup",
"lambdacontrols.action.pickup_all": "Pickup all",
"lambdacontrols.action.place": "Place",
"lambdacontrols.action.player_list": "Player List",
"lambdacontrols.action.quick_move": "Quick move",
"lambdacontrols.action.right": "Right",
@@ -27,6 +30,7 @@
"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",
@@ -56,7 +60,6 @@
"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",
@@ -70,18 +73,54 @@
"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.auto_switch_mode": "Auto Switch Mode",
"lambdacontrols.menu.controller": "Controller",
"lambdacontrols.menu.controller_type": "Controller type",
"lambdacontrols.menu.controller2": "Second Controller",
"lambdacontrols.menu.controller_type": "Controller Type",
"lambdacontrols.menu.controls_mode": "Mode",
"lambdacontrols.menu.dead_zone": "Dead zone",
"lambdacontrols.menu.dead_zone": "Dead Zone",
"lambdacontrols.menu.fast_block_placing": "Fast Block Placing",
"lambdacontrols.menu.fly_drifting": "Fly Drifting",
"lambdacontrols.menu.fly_drifting_vertical": "Vertical Fly Drifting",
"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.mouse_speed": "Mouse speed",
"lambdacontrols.menu.reload_controller_mappings": "Reload controller mappings",
"lambdacontrols.menu.rotation_speed": "Rotation speed",
"lambdacontrols.menu.hud_side": "HUD Side",
"lambdacontrols.menu.invert_right_x_axis": "Invert Right X",
"lambdacontrols.menu.invert_right_y_axis": "Invert Right Y",
"lambdacontrols.menu.keyboard_controls": "Keyboard Controls...",
"lambdacontrols.menu.mouse_speed": "Mouse Speed",
"lambdacontrols.menu.reload_controller_mappings": "Reload Controller Mappings",
"lambdacontrols.menu.rotation_speed": "Rotation Speed",
"lambdacontrols.menu.title": "LambdaControls - Settings",
"lambdacontrols.menu.title.controller_controls": "Controller controls"
"lambdacontrols.menu.title.controller": "Controller Options",
"lambdacontrols.menu.title.controller_controls": "Controller Controls",
"lambdacontrols.menu.title.gameplay": "Gameplay Options",
"lambdacontrols.menu.title.general": "General Options",
"lambdacontrols.menu.title.hud": "HUD Options",
"lambdacontrols.menu.unbound": "Unbound",
"lambdacontrols.menu.unfocused_input": "Unfocused Input",
"lambdacontrols.menu.virtual_mouse": "Virtual Mouse",
"lambdacontrols.menu.virtual_mouse.skin": "Virtual Mouse Skin",
"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.fast_block_placing": "While flying in creative mode, enables fast block placing depending on your speed. §cOn some servers this might be considered as cheating.",
"lambdacontrols.tooltip.fly_drifting": "While flying, enables Vanilla drifting/inertia.",
"lambdacontrols.tooltip.fly_drifting_vertical": "While flying, enables Vanilla vertical drifting/intertia.",
"lambdacontrols.tooltip.front_block_placing": "Enables 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.",
"lambdacontrols.tooltip.unfocused_input": "Allow controller input when the window is not focused.",
"lambdacontrols.tooltip.virtual_mouse": "Enable the virtual mouse which is handful in the case of a splitscreen.",
"lambdacontrols.virtual_mouse.skin.default_light": "Default Light",
"lambdacontrols.virtual_mouse.skin.default_dark": "Default Dark",
"lambdacontrols.virtual_mouse.skin.second_light": "Second Light",
"lambdacontrols.virtual_mouse.skin.second_dark": "Second Dark"
}

View File

@@ -0,0 +1,124 @@
{
"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.place": "Placer",
"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.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.fly_drifting": "Inertie de vol",
"lambdacontrols.menu.fly_drifting_vertical": "Inertie verticale de vol",
"lambdacontrols.menu.front_block_placing": "Placement avant de bloc",
"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.gameplay": "Options de Gameplay",
"lambdacontrols.menu.title.general": "Options générales",
"lambdacontrols.menu.title.hud": "Options du HUD",
"lambdacontrols.menu.unbound": "Délier",
"lambdacontrols.menu.unfocused_input": "Entrée en fond",
"lambdacontrols.menu.virtual_mouse": "Souris virtuelle",
"lambdacontrols.menu.virtual_mouse.skin": "Apparence souris virtuelle",
"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.fly_drifting": "Pendant que le joueur vole, active le glissement Vanilla.",
"lambdacontrols.tooltip.fly_drifting_vertical": "Pendant que le joueur vole, active le glissement vertical Vanilla.",
"lambdacontrols.tooltip.front_block_placing": "Active le placement avant de blocs, §cpeut être considérer comme de la trice sur certains serveurs§r.",
"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.",
"lambdacontrols.tooltip.unfocused_input": "Autorise les entrées manette quand la fenêtre n'est pas sélectionnée.",
"lambdacontrols.tooltip.virtual_mouse": "Active la souris virtuelle qui est pratique dans le cas d'un écran partagé.",
"lambdacontrols.virtual_mouse.skin.default_light": "défaut clair",
"lambdacontrols.virtual_mouse.skin.default_dark": "défaut foncé",
"lambdacontrols.virtual_mouse.skin.second_light": "second clair",
"lambdacontrols.virtual_mouse.skin.second_dark": "second foncé"
}

View File

@@ -10,6 +10,8 @@
"lambdacontrols.action.exit": "Sortir",
"lambdacontrols.action.forward": "Avancer",
"lambdacontrols.action.hit": "Taper",
"lambdacontrols.action.hotbar_left": "Case à gauche de la barre d'action",
"lambdacontrols.action.hotbar_right": "Case à droite de la barre d'action",
"lambdacontrols.action.inventory": "Inventaire",
"lambdacontrols.action.jump": "Sauter",
"lambdacontrols.action.left": "Aller à gauche",
@@ -17,6 +19,7 @@
"lambdacontrols.action.pick_block": "Choisir le bloc",
"lambdacontrols.action.pickup": "Prendre",
"lambdacontrols.action.pickup_all": "Prendre tout",
"lambdacontrols.action.place": "Placer",
"lambdacontrols.action.player_list": "Afficher la liste des joueurs",
"lambdacontrols.action.quick_move": "Mouvement rapide",
"lambdacontrols.action.right": "Aller à droite",
@@ -27,6 +30,7 @@
"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",
@@ -36,8 +40,8 @@
"lambdacontrols.button.back": "Retour",
"lambdacontrols.button.start": "Touche Menu",
"lambdacontrols.button.guide": "Guide",
"lambdacontrols.button.left_thumb": "Stick analogique gauche",
"lambdacontrols.button.right_thumb": "Stick analogique droit",
"lambdacontrols.button.left_thumb": "Stick gauche",
"lambdacontrols.button.right_thumb": "Stick droit",
"lambdacontrols.button.dpad_up": "D-Pad haut",
"lambdacontrols.button.dpad_right": "D-Pad droit",
"lambdacontrols.button.dpad_down": "D-Pad bas",
@@ -56,7 +60,6 @@
"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",
@@ -72,16 +75,50 @@
"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.fly_drifting": "Inertie de vol",
"lambdacontrols.menu.fly_drifting_vertical": "Inertie verticale de vol",
"lambdacontrols.menu.front_block_placing": "Placement avant de bloc",
"lambdacontrols.menu.hud_enable": "Activer le HUD",
"lambdacontrols.menu.hud_side": "Côté du HUD",
"lambdacontrols.menu.invert_right_x_axis": "Inverser le joystick droit (X)",
"lambdacontrols.menu.invert_right_y_axis": "Inverser le joystick droit (Y)",
"lambdacontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)",
"lambdacontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)",
"lambdacontrols.menu.keyboard_controls": "Contrôles clavier...",
"lambdacontrols.menu.mouse_speed": "Vitesse de la souris",
"lambdacontrols.menu.reload_controller_mappings": "Recharge la configuration des manettes",
"lambdacontrols.menu.rotation_speed": "Vitesse de rotation",
"lambdacontrols.menu.title": "LambdaControls - Paramètres",
"lambdacontrols.menu.title.controller_controls": "Contrôles de la manette"
"lambdacontrols.menu.title.controller": "Options de manettes",
"lambdacontrols.menu.title.controller_controls": "Contrôles de la manette",
"lambdacontrols.menu.title.gameplay": "Options de Gameplay",
"lambdacontrols.menu.title.general": "Options générales",
"lambdacontrols.menu.title.hud": "Options du HUD",
"lambdacontrols.menu.unbound": "Délier",
"lambdacontrols.menu.unfocused_input": "Entrée en fond",
"lambdacontrols.menu.virtual_mouse": "Souris virtuelle",
"lambdacontrols.menu.virtual_mouse.skin": "Apparence souris virtuelle",
"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.fly_drifting": "Pendant que le joueur vole, active le glissement Vanilla.",
"lambdacontrols.tooltip.fly_drifting_vertical": "Pendant que le joueur vole, active le glissement vertical Vanilla.",
"lambdacontrols.tooltip.front_block_placing": "Active le placement avant de blocs, §cpeut être considérer comme de la trice sur certains serveurs§r.",
"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.",
"lambdacontrols.tooltip.unfocused_input": "Autorise les entrées manette quand la fenêtre n'est pas sélectionnée.",
"lambdacontrols.tooltip.virtual_mouse": "Active la souris virtuelle qui est pratique dans le cas d'un écran partagé.",
"lambdacontrols.virtual_mouse.skin.default_light": "défaut clair",
"lambdacontrols.virtual_mouse.skin.default_dark": "défaut foncé",
"lambdacontrols.virtual_mouse.skin.second_light": "second clair",
"lambdacontrols.virtual_mouse.skin.second_dark": "second foncé"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,113 @@
# 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]
# Enables fast block placing like in Bedrock Edition.
fast_block_placing = true
# Fly behaviors
[gameplay.fly]
# Enables fly drifting.
drifting = false
# Enables vertical fly drifting.
vertical_drifting = true
[gameplay.front_block_placing]
# Enables front block placing like in Bedrock Edition.
enabled = false
# Enables front block placing outline.
outline = true
# The color in a hexadecimal format of the outline.
outline_color = "#ffffff66"
# 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 = 10.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
# Allow unfocused input.
unfocused_input = false
# Virtual mouse.
virtual_mouse = false
# Virtual mouse skin
virtual_mouse_skin = "default_light"
# 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"
# Left movement control.
left = "200"
# Pause game control.
pause_game = "7"
# Pick block control.
pick_block = "14"
# Show player list control.
player_list = "6"
# Right movement control.
right = "100"
# Take screenshot control.
screenshot = "11+0"
# Down slot control.
slot_down = "13"
# Left slot control.
slot_left = "14"
# Right slot control.
slot_right = "12"
# Up slot control.
slot_up = "11"
# Sneak control.
sneak = "10"
# Sprint control.
sprint = "9"
# Swap hands control.
swap_hands = "2"
# Switch to back tab control.
tab_back = "4"
# Switch to next tab control.
tab_next = "5"
# 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

@@ -16,20 +16,37 @@
"icon": "assets/lambdacontrols/icon.png",
"environment": "client",
"entrypoints": {
"client": [
"main": [
"me.lambdaurora.lambdacontrols.LambdaControls"
],
"client": [
"me.lambdaurora.lambdacontrols.client.LambdaControlsClient"
],
"modmenu": [
"me.lambdaurora.lambdacontrols.client.LambdaControlsModMenu"
]
},
"mixins": [
"lambdacontrols.mixins.json"
"lambdacontrols.mixins.json",
"lambdacontrols_compat.mixins.json"
],
"depends": {
"fabricloader": ">=0.4.0",
"fabricloader": ">=0.8.0",
"fabric": "*",
"minecraft": "1.15.x"
"minecraft": ">=1.16",
"spruceui": ">=1.5.1"
},
"recommends": {
"modmenu": ">=1.12.2",
"okzoomer": ">=4.0.0"
},
"suggests": {
"flamingo": "*"
"flamingo": "*",
"roughlyenoughitems": ">=4.5.5"
},
"breaks": {
"modmenu": "<1.12.2",
"optifabric": "*"
},
"custom": {
"modmenu:clientsideOnly": true

View File

@@ -0,0 +1,26 @@
{
"required": true,
"package": "me.lambdaurora.lambdacontrols.client.mixin",
"compatibilityLevel": "JAVA_8",
"client": [
"AbstractButtonWidgetAccessor",
"AdvancementsScreenAccessor",
"ClientPlayerEntityMixin",
"ClientPlayNetworkHandlerMixin",
"ControlsOptionsScreenMixin",
"CreativeInventoryScreenAccessor",
"EntryListWidgetAccessor",
"GameOptionsMixin",
"GameRendererMixin",
"HandledScreenMixin",
"KeyBindingMixin",
"MinecraftClientMixin",
"MouseMixin",
"OptionsScreenMixin",
"RecipeBookWidgetAccessor",
"WorldRendererMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -0,0 +1,13 @@
{
"required": true,
"package": "me.lambdaurora.lambdacontrols.client.compat.mixin",
"plugin": "me.lambdaurora.lambdacontrols.client.compat.LambdaControlsMixinPlugin",
"compatibilityLevel": "JAVA_8",
"client": [
"RecipeViewingScreenAccessor",
"VillagerRecipeViewingScreenAccessor"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -3,16 +3,17 @@ org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=1.15.1
yarn_mappings=1.15.1+build.3:v2
loader_version=0.7.2+build.174
minecraft_version=1.16.1
yarn_mappings=1.16.1+build.9:v2
loader_version=0.8.8+build.202
# Mod Properties
mod_version = 1.0.1
mod_version = 1.3.0
maven_group = me.lambdaurora
archives_base_name = lambdacontrols
# Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
fabric_version=0.4.25+build.282-1.15
fabric_version=0.14.0+build.371-1.16
spruceui_version=1.5.1
modmenu_version=1.12.2+build.17

View File

@@ -8,3 +8,6 @@ pluginManagement {
gradlePluginPortal()
}
}
rootProject.name = 'lambdacontrols'
include 'core', 'fabric', 'spigot'

50
spigot/build.gradle Normal file
View File

@@ -0,0 +1,50 @@
plugins {
id 'java-library'
}
archivesBaseName = project.archives_base_name + "-spigot"
repositories {
maven { url = 'https://hub.spigotmc.org/nexus/content/groups/public/' }
maven { url = 'https://libraries.minecraft.net/' }
}
configurations {
include
}
dependencies {
api project(":core")
include(project(":core")) {
exclude group: 'com.google.code.gson'
exclude group: 'com.google.guava'
}
api 'org.spigotmc:spigot-api:1.15.1-R0.1-SNAPSHOT'
api 'io.netty:netty-all:4.1.28.Final'
}
processResources {
inputs.property "version", project.version
from(sourceSets.main.resources.srcDirs) {
include "plugin.yml"
expand 'version': project.version.toString().replace("#", "")
}
}
jar {
from '../LICENSE'
dependsOn configurations.include
from {
(configurations.include).collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

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;
import com.electronwill.nightconfig.core.file.FileConfig;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* Represents the LambdaControls Spigot configuration.
*/
public class LambdaControlsConfig
{
private static final boolean DEFAULT_FRONT_BLOCK_PLACING = true;
protected final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/server_config.toml").build();
private final LambdaControlsSpigot plugin;
public LambdaControlsConfig(@NotNull LambdaControlsSpigot plugin)
{
this.plugin = plugin;
}
public void load()
{
File configDir = new File("config/");
if (!configDir.exists())
configDir.mkdirs();
this.config.load();
this.plugin.log("Configuration loaded.");
LambdaControlsFeature.FRONT_BLOCK_PLACING.setAllowed(this.config.getOrElse("gameplay.front_block_placing", DEFAULT_FRONT_BLOCK_PLACING));
}
}

View File

@@ -0,0 +1,148 @@
/*
* 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 io.netty.buffer.Unpooled;
import me.lambdaurora.lambdacontrols.event.PlayerChangeControlsModeEvent;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static me.lambdaurora.lambdacontrols.LambdaControlsConstants.*;
/**
* Represents the LambdaControls spigot plugin which provides extra features for servers.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class LambdaControlsSpigot extends JavaPlugin implements PluginMessageListener, Listener
{
private static final Map<Player, ControlsMode> PLAYERS_CONTROLS_MODE = new HashMap<>();
public final LambdaControlsConfig config = new LambdaControlsConfig(this);
@Override
public void onEnable()
{
super.onEnable();
this.config.load();
// Note that Spigot has a bullshit channel size restriction as Minecraft SUPPORTS UP TO 32767 AS CHANNEL SIZE.
// Please stop using that bad server software, move over Sponge or idk other things. REALLY.
this.getServer().getMessenger().registerIncomingPluginChannel(this, CONTROLS_MODE_CHANNEL.toString(), this);
this.getServer().getMessenger().registerOutgoingPluginChannel(this, CONTROLS_MODE_CHANNEL.toString());
this.getServer().getMessenger().registerOutgoingPluginChannel(this, FEATURE_CHANNEL.toString());
this.getServer().getMessenger().registerIncomingPluginChannel(this, HELLO_CHANNEL.toString(), this);
this.getServer().getPluginManager().registerEvents(this, this);
this.getServer().getOnlinePlayers().forEach(player -> {
PLAYERS_CONTROLS_MODE.put(player, ControlsMode.DEFAULT);
this.requestPlayerControlsMode(player);
this.updatePlayerFeature(player, LambdaControlsFeature.FRONT_BLOCK_PLACING);
});
}
@Override
public void onDisable()
{
super.onDisable();
this.getServer().getMessenger().unregisterIncomingPluginChannel(this, CONTROLS_MODE_CHANNEL.toString());
this.getServer().getMessenger().unregisterOutgoingPluginChannel(this, CONTROLS_MODE_CHANNEL.toString());
this.getServer().getMessenger().unregisterOutgoingPluginChannel(this, FEATURE_CHANNEL.toString());
this.getServer().getMessenger().unregisterIncomingPluginChannel(this, HELLO_CHANNEL.toString());
PLAYERS_CONTROLS_MODE.clear();
}
public void requestPlayerControlsMode(@NotNull Player player)
{
player.sendPluginMessage(this, CONTROLS_MODE_CHANNEL.toString(), new byte[0]);
}
public void updatePlayerFeature(@NotNull Player player, @NotNull LambdaControlsFeature feature)
{
Objects.requireNonNull(player);
Objects.requireNonNull(feature);
player.sendPluginMessage(this, FEATURE_CHANNEL.toString(), this.makeFeatureMessage(feature));
}
/**
* Prints a message to the terminal.
*
* @param info The message to print.
*/
public void log(String info)
{
this.getLogger().info(info);
}
@Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message)
{
if (channel.equals(HELLO_CHANNEL.toString())) {
NettyPacketBuffer buffer = new NettyPacketBuffer(Unpooled.copiedBuffer(message));
String version = buffer.readString(16);
ControlsMode.byId(buffer.readString(32)).ifPresent(controlsMode -> {
PLAYERS_CONTROLS_MODE.put(player, controlsMode);
PlayerChangeControlsModeEvent event = new PlayerChangeControlsModeEvent(player, controlsMode);
this.getServer().getPluginManager().callEvent(event);
});
this.updatePlayerFeature(player, LambdaControlsFeature.FRONT_BLOCK_PLACING);
} else if (channel.equals(CONTROLS_MODE_CHANNEL.toString())) {
NettyPacketBuffer buffer = new NettyPacketBuffer(Unpooled.copiedBuffer(message));
ControlsMode.byId(buffer.readString(32)).ifPresent(controlsMode -> {
PLAYERS_CONTROLS_MODE.put(player, controlsMode);
PlayerChangeControlsModeEvent event = new PlayerChangeControlsModeEvent(player, controlsMode);
this.getServer().getPluginManager().callEvent(event);
});
}
}
/**
* Returns a packet byte buffer made for the lambdacontrols:feature plugin message.
*
* @param feature The feature data to send.
* @return The packet byte buffer.
*/
public byte[] makeFeatureMessage(@NotNull LambdaControlsFeature feature)
{
Objects.requireNonNull(feature, "Feature cannot be null.");
NettyPacketBuffer buffer = new NettyPacketBuffer(Unpooled.buffer());
buffer.writeString(feature.getName());
buffer.writeBoolean(feature.isAllowed());
return buffer.array();
}
@EventHandler
public void onPlayerJoin(@NotNull PlayerJoinEvent event)
{
PLAYERS_CONTROLS_MODE.put(event.getPlayer(), ControlsMode.DEFAULT);
}
@EventHandler
public void onPlayerLeave(@NotNull PlayerQuitEvent event)
{
PLAYERS_CONTROLS_MODE.remove(event.getPlayer());
}
}

View File

@@ -0,0 +1,102 @@
package me.lambdaurora.lambdacontrols;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class NettyPacketBuffer extends PacketBuffer
{
public NettyPacketBuffer(ByteBuf byteBuf)
{
super(byteBuf);
}
@Override
public int readVarint()
{
int var1 = 0;
int var2 = 0;
byte var3;
do {
var3 = this.readByte();
var1 |= (var3 & 127) << var2++ * 7;
if (var2 > 5)
throw new RuntimeException("VarInt too big");
} while ((var3 & 128) == 128);
return var1;
}
@Override
public void writeVarint(int input)
{
while ((input & -128) != 0) {
this.writeByte(input & 127 | 128);
input >>>= 7;
}
this.writeByte(input);
}
@Override
public String readString(int maxLength)
{
int var2 = this.readVarint();
if (var2 > maxLength * 4)
throw new DecoderException("The received encoded string buffer length is longer than maximum allowed (" + var2 + " > " + maxLength * 4 + ")");
else if (var2 < 0)
throw new DecoderException("The received encoded string buffer length is less than zero! Weird string!");
else {
String var3 = this.readCharSequence(var2, StandardCharsets.UTF_8).toString();
if (var3.length() > maxLength)
throw new DecoderException("The received string length is longer than maximum allowed (" + var2 + " > " + maxLength + ")");
else
return var3;
}
}
@Override
public void writeString(String string)
{
byte[] var2 = string.getBytes(Charset.forName("UTF-8"));
if (var2.length > 32767) {
throw new EncoderException("String too big (was " + string.length() + " data encoded, max " + 32767 + ")");
} else {
this.writeVarint(var2.length);
this.writeCharSequence(string, StandardCharsets.UTF_8);
}
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
NettyPacketBuffer buffer = (NettyPacketBuffer) o;
return byteBuf.equals(buffer.byteBuf);
}
@Override
public int compareTo(ByteBuf buffer)
{
return this.byteBuf.compareTo(buffer);
}
@Override
public String toString()
{
return this.byteBuf.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
/*
* 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 org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* Represents an event which is fired when a player change their controls mode.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class PlayerChangeControlsModeEvent extends PlayerEvent
{
private static final HandlerList HANDLERS = new HandlerList();
private final ControlsMode controlsMode;
public PlayerChangeControlsModeEvent(@NotNull Player who, @NotNull ControlsMode controlsMode)
{
super(who);
this.controlsMode = controlsMode;
}
/**
* Returns the controls mode of the player.
*
* @return The player's controls mode.
*/
public ControlsMode getControlsMode()
{
return this.controlsMode;
}
@Override
public String toString()
{
return "PlayerChangeControlsModeEvent{" +
"player=" + this.player +
", controls_mode=" + this.controlsMode +
'}';
}
@Override
public @NotNull HandlerList getHandlers()
{
return HANDLERS;
}
public static @NotNull HandlerList getHandlerList()
{
return HANDLERS;
}
}

View File

@@ -0,0 +1,6 @@
name: LambdaControls
version: ${version}
description: A quick Spigot plugin for LambdaControls which allow server admins to disable some features.
main: me.lambdaurora.lambdacontrols.LambdaControlsSpigot
api-version: 1.13

View File

@@ -0,0 +1,6 @@
# LambdaControls server configuration.
# Gameplay settings
[gameplay]
# Allows front block placing like in Bedrock Edition.
front_block_placing = true

View File

@@ -1,507 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols;
import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.ScreenshotUtils;
import org.aperlambda.lambdacommon.Identifier;
import org.aperlambda.lambdacommon.utils.Identifiable;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Represents a button binding.
*
* @author LambdAurora
*/
public class ButtonBinding implements Nameable
{
private static final List<ButtonBinding> BINDINGS = new ArrayList<>();
private static final List<Category> CATEGORIES = new ArrayList<>();
public static final Category MOVEMENT_CATEGORY;
public static final Category GAMEPLAY_CATEGORY;
public static final Category INVENTORY_CATEGORY;
public static final Category MULTIPLAYER_CATEGORY;
public static final Category MISC_CATEGORY;
public static final ButtonBinding ATTACK = new ButtonBinding("attack", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true));
public static final ButtonBinding BACK = new ButtonBinding("back", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, false));
public static final ButtonBinding CHAT = new ButtonBinding("chat", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT);
public static final ButtonBinding DROP_ITEM = new ButtonBinding("drop_item", GLFW.GLFW_GAMEPAD_BUTTON_B);
public static final ButtonBinding FORWARD = new ButtonBinding("forward", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y, true));
public static final ButtonBinding INVENTORY = new ButtonBinding("inventory", GLFW.GLFW_GAMEPAD_BUTTON_Y);
public static final ButtonBinding JUMP = new ButtonBinding("jump", GLFW.GLFW_GAMEPAD_BUTTON_A);
public static final ButtonBinding LEFT = new ButtonBinding("left", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, false));
public static final ButtonBinding PAUSE_GAME = new ButtonBinding("pause_game", GLFW.GLFW_GAMEPAD_BUTTON_START);
public static final ButtonBinding PICK_BLOCK = new ButtonBinding("pick_block", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT);
public static final ButtonBinding PLAYER_LIST = new ButtonBinding("player_list", GLFW.GLFW_GAMEPAD_BUTTON_BACK);
public static final ButtonBinding RIGHT = new ButtonBinding("right", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X, true));
public static final ButtonBinding SCREENSHOT = new ButtonBinding("screenshot", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN,
Collections.singletonList((client, action) -> {
if (action == 0)
ScreenshotUtils.saveScreenshot(client.runDirectory, client.getWindow().getFramebufferWidth(), client.getWindow().getFramebufferHeight(), client.getFramebuffer(),
text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text)));
return true;
}));
public static final ButtonBinding SMOOTH_CAMERA = new ButtonBinding("toggle_smooth_camera", -1);
public static final ButtonBinding SNEAK = new ButtonBinding("sneak", GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB);
public static final ButtonBinding SPRINT = new ButtonBinding("sprint", GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB);
public static final ButtonBinding SWAP_HANDS = new ButtonBinding("swap_hands", GLFW.GLFW_GAMEPAD_BUTTON_X);
public static final ButtonBinding TOGGLE_PERSPECTIVE = new ButtonBinding("toggle_perspective", GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP);
public static final ButtonBinding USE = new ButtonBinding("use", axis_as_button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true));
private int button;
private int default_button;
private String key;
private KeyBinding minecraft_key_binding = null;
private List<PressAction> actions = new ArrayList<>(Collections.singletonList((client, action) -> {
this.as_key_binding().ifPresent(key_binding -> ((KeyBindingAccessor) key_binding).handle_press_state(this.is_button_down()));
return true;
}));
private boolean pressed = false;
protected ButtonBinding(@NotNull String key, int default_button, @NotNull List<PressAction> actions)
{
this.default_button = this.button = default_button;
this.key = key;
this.actions.addAll(actions);
BINDINGS.add(this);
}
protected ButtonBinding(@NotNull String key, int default_button)
{
this(key, default_button, Collections.emptyList());
}
/**
* Returns the button bound.
*
* @return The bound button.
*/
public int get_button()
{
return this.button;
}
/**
* Sets the bound button.
*
* @param button The bound button.
*/
public void set_button(int button)
{
this.button = button;
}
/**
* Returns whether the bound button is the specified button or not.
*
* @param button The button to check.
* @return True if the bound button is the specified button, else false.
*/
public boolean is_button(int button)
{
return this.button == button;
}
/**
* Returns whether this button is down or not.
*
* @return True if the button is down, else false.
*/
public boolean is_button_down()
{
return this.pressed;
}
/**
* Returns whether this button binding is bound or not.
*
* @return True if this button binding is bound, else false.
*/
public boolean is_not_bound()
{
return this.button == -1;
}
/**
* Gets the default button assigned to this binding.
*
* @return The default button.
*/
public int get_default_button()
{
return this.default_button;
}
/**
* Returns whether the assigned button is the default button.
*
* @return True if the assigned button is the default button, else false.
*/
public boolean is_default()
{
return this.button == this.default_button;
}
@Override
public @NotNull String get_name()
{
return this.key;
}
/**
* Returns the translation key of this button binding.
*
* @return The translation key.
*/
public @NotNull String get_translation_key()
{
return "lambdacontrols.action." + this.get_name();
}
/**
* Returns the key binding equivalent of this button binding.
*
* @return The key binding equivalent.
*/
public @NotNull Optional<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 joycon2_button(int button)
{
return 300 + button;
}
public static void init(@NotNull GameOptions options)
{
ATTACK.minecraft_key_binding = options.keyAttack;
BACK.minecraft_key_binding = options.keyBack;
CHAT.minecraft_key_binding = options.keyChat;
DROP_ITEM.minecraft_key_binding = options.keyDrop;
FORWARD.minecraft_key_binding = options.keyForward;
INVENTORY.minecraft_key_binding = options.keyInventory;
JUMP.minecraft_key_binding = options.keyJump;
LEFT.minecraft_key_binding = options.keyLeft;
PICK_BLOCK.minecraft_key_binding = options.keyPickItem;
PLAYER_LIST.minecraft_key_binding = options.keyPlayerList;
RIGHT.minecraft_key_binding = options.keyRight;
SCREENSHOT.minecraft_key_binding = options.keyScreenshot;
SMOOTH_CAMERA.minecraft_key_binding = options.keySmoothCamera;
SNEAK.minecraft_key_binding = options.keySneak;
SPRINT.minecraft_key_binding = options.keySprint;
SWAP_HANDS.minecraft_key_binding = options.keySwapHands;
TOGGLE_PERSPECTIVE.minecraft_key_binding = options.keyTogglePerspective;
USE.minecraft_key_binding = options.keyUse;
}
public static void load_from_config(@NotNull LambdaControlsConfig config)
{
BINDINGS.forEach(config::load_button_binding);
}
public static void set_button_state(int button, boolean state)
{
BINDINGS.parallelStream().filter(binding -> Objects.equals(binding.button, button))
.forEach(binding -> binding.pressed = state);
}
public static void handle_button(@NotNull MinecraftClient client, int button, int action)
{
BINDINGS.parallelStream().filter(binding -> binding.button == button)
.forEach(binding -> {
for (int i = binding.actions.size() - 1; i >= 0; i--) {
if (binding.actions.get(i).press(client, action))
break;
}
});
}
/**
* Returns whether the button has duplicated bindings.
*
* @param button The button to check.
* @return True if the button has duplicated bindings, else false.
*/
public static boolean has_duplicates(int button)
{
return BINDINGS.parallelStream().filter(binding -> binding.button == button).count() > 1;
}
/**
* Returns the localized name of the specified button.
*
* @param button The button.
* @return The localized name of the button.
*/
public static @NotNull String get_localized_button_name(int button)
{
switch (button) {
case -1:
return I18n.translate("key.keyboard.unknown");
case GLFW.GLFW_GAMEPAD_BUTTON_A:
return I18n.translate("lambdacontrols.button.a");
case GLFW.GLFW_GAMEPAD_BUTTON_B:
return I18n.translate("lambdacontrols.button.b");
case GLFW.GLFW_GAMEPAD_BUTTON_X:
return I18n.translate("lambdacontrols.button.x");
case GLFW.GLFW_GAMEPAD_BUTTON_Y:
return I18n.translate("lambdacontrols.button.y");
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER:
return I18n.translate("lambdacontrols.button.left_bumper");
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER:
return I18n.translate("lambdacontrols.button.right_bumper");
case GLFW.GLFW_GAMEPAD_BUTTON_BACK:
return I18n.translate("lambdacontrols.button.back");
case GLFW.GLFW_GAMEPAD_BUTTON_START:
return I18n.translate("lambdacontrols.button.start");
case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE:
return I18n.translate("lambdacontrols.button.guide");
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB:
return I18n.translate("lambdacontrols.button.left_thumb");
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB:
return I18n.translate("lambdacontrols.button.right_thumb");
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP:
return I18n.translate("lambdacontrols.button.dpad_up");
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT:
return I18n.translate("lambdacontrols.button.dpad_right");
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN:
return I18n.translate("lambdacontrols.button.dpad_down");
case GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT:
return I18n.translate("lambdacontrols.button.dpad_left");
case 100:
return I18n.translate("lambdacontrols.axis.left_x+");
case 101:
return I18n.translate("lambdacontrols.axis.left_y+");
case 102:
return I18n.translate("lambdacontrols.axis.right_x+");
case 103:
return I18n.translate("lambdacontrols.axis.right_y+");
case 104:
return I18n.translate("lambdacontrols.axis.left_trigger");
case 105:
return I18n.translate("lambdacontrols.axis.right_trigger");
case 200:
return I18n.translate("lambdacontrols.axis.left_x-");
case 201:
return I18n.translate("lambdacontrols.axis.left_y-");
case 202:
return I18n.translate("lambdacontrols.axis.right_x-");
case 203:
return I18n.translate("lambdacontrols.axis.right_y-");
default:
return I18n.translate("lambdacontrols.button.unknown", button);
}
}
public static @NotNull Stream<ButtonBinding> stream()
{
return BINDINGS.stream();
}
public static @NotNull Stream<Category> stream_categories()
{
return CATEGORIES.stream();
}
static {
MOVEMENT_CATEGORY = register_default_category("key.categories.movement", category -> category.register_all_bindings(
FORWARD,
BACK,
LEFT,
RIGHT,
JUMP,
SNEAK,
SPRINT));
GAMEPLAY_CATEGORY = register_default_category("key.categories.gameplay", category -> category.register_all_bindings(
ATTACK,
PICK_BLOCK,
USE
));
INVENTORY_CATEGORY = register_default_category("key.categories.inventory", category -> category.register_all_bindings(
DROP_ITEM,
INVENTORY,
SWAP_HANDS
));
MULTIPLAYER_CATEGORY = register_default_category("key.categories.multiplayer",
category -> category.register_all_bindings(CHAT, PLAYER_LIST));
MISC_CATEGORY = register_default_category("key.categories.misc", category -> category.register_all_bindings(
SCREENSHOT,
//SMOOTH_CAMERA,
TOGGLE_PERSPECTIVE
));
}
public static ButtonBinding register(@NotNull Identifier binding_id, int default_button, @NotNull List<PressAction> actions)
{
return new ButtonBinding(binding_id.get_namespace() + "." + binding_id.get_name(), default_button, actions);
}
public static ButtonBinding register(@NotNull Identifier binding_id, int default_button)
{
return register(binding_id, default_button, Collections.emptyList());
}
public static ButtonBinding register(@NotNull net.minecraft.util.Identifier binding_id, int default_button, @NotNull List<PressAction> actions)
{
return register(new Identifier(binding_id.getNamespace(), binding_id.getPath()), default_button, actions);
}
public static ButtonBinding register(@NotNull net.minecraft.util.Identifier binding_id, int default_button)
{
return register(binding_id, default_button, Collections.emptyList());
}
/**
* Registers a category of button bindings.
*
* @param category The category to register.
* @return The registered category.
*/
public static Category register_category(@NotNull Category category)
{
CATEGORIES.add(category);
return category;
}
public static Category register_category(@NotNull Identifier identifier, int priority)
{
return register_category(new Category(identifier, priority));
}
public static Category register_category(@NotNull Identifier identifier)
{
return register_category(new Category(identifier));
}
private static Category register_default_category(@NotNull String key, @NotNull Consumer<Category> key_adder)
{
Category category = register_category(new Identifier("minecraft", key), CATEGORIES.size());
key_adder.accept(category);
return category;
}
public static class Category implements Identifiable
{
private final List<ButtonBinding> bindings = new ArrayList<>();
private final Identifier id;
private int priority;
public Category(@NotNull Identifier id, int priority)
{
this.id = id;
this.priority = priority;
}
public Category(@NotNull Identifier id)
{
this(id, 100);
}
public void register_binding(@NotNull ButtonBinding binding)
{
if (this.bindings.contains(binding))
throw new IllegalStateException("Cannot register twice a button binding in the same category.");
this.bindings.add(binding);
}
public void register_all_bindings(@NotNull ButtonBinding... bindings)
{
this.register_all_bindings(Arrays.asList(bindings));
}
public void register_all_bindings(@NotNull List<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;
}
}
@FunctionalInterface
public static interface PressAction
{
/**
* Handles when there is a press action on the button.
*
* @param client The client instance.
* @param action The action done.
*/
boolean press(@NotNull MinecraftClient client, int action);
}
}

View File

@@ -1,251 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols;
import com.mojang.blaze3d.platform.GlStateManager;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
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.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
/**
* Represents the LambdaControls mod.
*/
public class LambdaControls implements ClientModInitializer
{
private static LambdaControls INSTANCE;
public static final FabricKeyBinding BINDING_LOOK_UP = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_up"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement").build();
public static final FabricKeyBinding BINDING_LOOK_RIGHT = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_right"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement").build();
public static final FabricKeyBinding BINDING_LOOK_DOWN = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_down"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement").build();
public static final FabricKeyBinding BINDING_LOOK_LEFT = FabricKeyBinding.Builder.create(new Identifier("lambdacontrols", "look_left"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement").build();
public static final Identifier CONTROLLER_BUTTONS = new Identifier("lambdacontrols", "textures/gui/controller_buttons.png");
public static final Identifier CONTROLLER_AXIS = new Identifier("lambdacontrols", "textures/gui/controller_axis.png");
public final Logger logger = LogManager.getLogger("LambdaControls");
public final LambdaControlsConfig config = new LambdaControlsConfig(this);
public final LambdaInput input = new LambdaInput(this);
private ControlsMode previous_controls_mode;
@Override
public void onInitializeClient()
{
INSTANCE = this;
this.log("Initializing LambdaControls...");
this.config.load();
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_UP);
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_RIGHT);
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_DOWN);
KeyBindingRegistry.INSTANCE.register(BINDING_LOOK_LEFT);
}
/**
* This method is called when Minecraft is initializing.
*/
public void on_mc_init(@NotNull MinecraftClient client)
{
Controller.update_mappings();
ButtonBinding.init(client.options);
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 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);
}
}
}
/**
* Prints a message to the terminal.
*
* @param info The message to print.
*/
public void log(String info)
{
this.logger.info("[LambdaControls] " + info);
}
/**
* Gets the LambdaControls instance.
*
* @return The LambdaControls instance.
*/
public static LambdaControls get()
{
return INSTANCE;
}
public static int draw_button(int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client)
{
return draw_button(x, y, button.get_button(), client);
}
public static int draw_button(int x, int y, int button, @NotNull MinecraftClient client)
{
if (button == -1)
return 0;
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 ? LambdaControls.CONTROLLER_AXIS : LambdaControls.CONTROLLER_BUTTONS);
GlStateManager.disableDepthTest();
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
DrawableHelper.blit(x, y, (float) button_offset, (float) (controller_type * (axis ? 18 : 15)), axis ? 18 : 15, axis ? 18 : 15, 256, 256);
GlStateManager.enableDepthTest();
return axis ? 18 : 15;
}
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)
{
String translated_action = I18n.translate(action);
if (display) {
int button_width = draw_button(x, y, button, client);
int text_y = (15 - client.textRenderer.fontHeight) / 2;
client.textRenderer.drawWithShadow(translated_action, (float) (x + button_width + 5), (float) (y + text_y), 14737632);
}
return display ? get_button_tip_width(translated_action, client.textRenderer) : -10;
}
private static int get_button_tip_width(@NotNull String action, @NotNull TextRenderer text_renderer)
{
return 15 + 5 + text_renderer.getStringWidth(action);
}
}

View File

@@ -1,399 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols;
import com.electronwill.nightconfig.core.file.FileConfig;
import net.minecraft.client.options.KeyBinding;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* Represents LambdaControls configuration.
*/
public class LambdaControlsConfig
{
private final FileConfig config = FileConfig.builder("config/lambdacontrols.toml").concurrent().defaultResource("/config.toml").build();
private final Map<String, KeyBinding> keybinding_mappings = new HashMap<>();
private final LambdaControls 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 LambdaControls mod)
{
this.mod = mod;
}
public void load()
{
this.keybinding_mappings.clear();
this.config.load();
this.mod.log("Configuration loaded.");
this.controls_mode = ControlsMode.by_id(this.config.getOrElse("controls", "default")).orElse(ControlsMode.DEFAULT);
// HUD settings.
this.hud_enable = this.config.getOrElse("hud.enable", true);
this.hud_side = HudSide.by_id(this.config.getOrElse("hud.side", "left")).orElse(HudSide.LEFT);
// Controller settings.
this.controller_type = ControllerType.by_id(this.config.getOrElse("controller.type", "default")).orElse(ControllerType.DEFAULT);
this.dead_zone = this.config.getOrElse("controller.dead_zone", 0.25);
this.rotation_speed = this.config.getOrElse("controller.rotation_speed", 40.0);
this.mouse_speed = this.config.getOrElse("controller.mouse_speed", 25.0);
// Controller controls.
ButtonBinding.load_from_config(this);
}
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.");
}
/**
* 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", false);
}
/**
* 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 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;
}
/**
* Returns the keybindings.
*
* @return The keybindings.
*/
public @NotNull Map<String, KeyBinding> get_keybindings()
{
return this.keybinding_mappings;
}
public Optional<KeyBinding> get_keybind(@NotNull String id)
{
return Optional.ofNullable(this.keybinding_mappings.get(id));
}
/**
* Loads the button binding from configuration.
*
* @param button The button binding.
*/
public void load_button_binding(@NotNull ButtonBinding button)
{
button.set_button(this.config.getOrElse("controller.controls." + button.get_name(), button.get_button()));
}
/**
* 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(), button);
}
public boolean is_back_button(int btn, boolean is_btn, int state)
{
if (!is_btn && state == 0)
return false;
return ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(btn, state == 1));
}
public boolean is_forward_button(int btn, boolean is_btn, int state)
{
if (!is_btn && state == 0)
return false;
return ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(btn, state == 1));
}
public boolean is_left_button(int btn, boolean is_btn, int state)
{
if (!is_btn && state == 0)
return false;
return ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(btn, state == 1));
}
public boolean is_right_button(int btn, boolean is_btn, int state)
{
if (!is_btn && state == 0)
return false;
return ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(btn, state == 1));
}
/**
* Returns whether the specified axis is an axis used for movements.
*
* @param i The axis index.
* @return True if the axis is used for movements, else false.
*/
public boolean is_movement_axis(int i)
{
return ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.FORWARD.is_button(ButtonBinding.axis_as_button(i, false))
|| ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.BACK.is_button(ButtonBinding.axis_as_button(i, false))
|| ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.LEFT.is_button(ButtonBinding.axis_as_button(i, false))
|| ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(i, true)) || ButtonBinding.RIGHT.is_button(ButtonBinding.axis_as_button(i, false));
}
}

View File

@@ -1,671 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols;
import me.lambdaurora.lambdacontrols.gui.LabelWidget;
import me.lambdaurora.lambdacontrols.gui.LambdaControlsControlsScreen;
import me.lambdaurora.lambdacontrols.gui.TouchscreenOverlay;
import me.lambdaurora.lambdacontrols.mixin.EntryListWidgetAccessor;
import me.lambdaurora.lambdacontrols.util.AbstractContainerScreenAccessor;
import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor;
import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor;
import me.lambdaurora.lambdacontrols.util.MouseAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.ParentElement;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
import net.minecraft.client.gui.screen.world.WorldListWidget;
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.item.ItemGroup;
import net.minecraft.util.math.MathHelper;
import org.aperlambda.lambdacommon.utils.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import 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.ButtonBinding.*;
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X;
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y;
/**
* Represents the LambdaControls' input handler.
*/
public class LambdaInput
{
private static final Map<Integer, Boolean> BUTTON_STATES = new HashMap<>();
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
private static final Map<Integer, Boolean> AXIS_STATES = new HashMap<>();
private static final Map<Integer, Integer> AXIS_COOLDOWNS = new HashMap<>();
private final LambdaControlsConfig config;
private int action_gui_cooldown = 0;
private int ignore_next_a = 0;
private int last_sneak = 0;
private double prev_target_yaw = 0.0;
private double prev_target_pitch = 0.0;
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 LambdaControls 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 (LambdaControls.BINDING_LOOK_UP.isPressed()) {
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 2);
} else if (LambdaControls.BINDING_LOOK_DOWN.isPressed()) {
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 1);
}
if (LambdaControls.BINDING_LOOK_LEFT.isPressed()) {
this.handle_look(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 2);
} else if (LambdaControls.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));
AXIS_COOLDOWNS.entrySet().stream().filter(entry -> entry.getValue() > 0).forEach(entry -> AXIS_COOLDOWNS.put(entry.getKey(), entry.getValue() - 1));
// Decreases the last_sneak counter which allows to double press to sneak continuously.
if (this.last_sneak > 0)
--this.last_sneak;
// 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;
Controller controller = this.config.get_controller();
if (controller.is_connected()) {
GLFWGamepadState state = controller.get_state();
this.fetch_button_input(client, state);
this.fetch_axe_input(client, state);
}
if (this.ignore_next_a > 0)
this.ignore_next_a--;
}
/**
* 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)
{
ByteBuffer buffer = gamepad_state.buttons();
for (int btn = 0; btn < buffer.limit(); btn++) {
boolean btn_state = buffer.get() == (byte) 1;
boolean previous_state = BUTTON_STATES.getOrDefault(btn, false);
ButtonBinding.set_button_state(btn, btn_state);
if (btn_state != previous_state) {
this.handle_button(client, btn, btn_state ? 0 : 1, btn_state);
if (btn_state)
BUTTON_COOLDOWNS.put(btn, 5);
} else if (btn_state) {
if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) {
BUTTON_COOLDOWNS.put(btn, 5);
this.handle_button(client, btn, 2, true);
}
}
BUTTON_STATES.put(btn, btn_state);
}
}
private void fetch_axe_input(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepad_state)
{
FloatBuffer buffer = gamepad_state.axes();
for (int i = 0; i < buffer.limit(); i++) {
float value = buffer.get();
float abs_value = Math.abs(value);
if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y)
value *= -1.0F;
ButtonBinding.set_button_state(axis_as_button(i, true), value > 0.5F);
ButtonBinding.set_button_state(axis_as_button(i, false), value < -0.5F);
int state = value > this.config.get_dead_zone() ? 1 : (value < -this.config.get_dead_zone() ? 2 : 0);
this.handle_axe(client, i, value, abs_value, state);
}
}
private void handle_button(@NotNull MinecraftClient client, int button, int action, boolean state)
{
if (client.currentScreen instanceof LambdaControlsControlsScreen && action == 0) {
LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
if (controls_screen.focused_binding != null) {
this.config.set_button_binding(controls_screen.focused_binding, button);
controls_screen.focused_binding = null;
return;
}
}
if (action == 0 || action == 2) {
// Handles RB and LB buttons.
if (button == GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER || button == GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) {
this.handle_rb_lb(client, button == GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER);
return;
}
if (client.currentScreen != null && is_screen_interactive(client.currentScreen)
&& (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN
|| button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)) {
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 == 0) {
// Handles when the player presses the Start button.
if (PAUSE_GAME.is_button(button)) {
// If in game, then pause the game.
if (client.currentScreen == null)
client.openPauseMenu(false);
else if (client.currentScreen instanceof AbstractContainerScreen) // If the current screen is a container then close it.
client.player.closeContainer();
else// Else just close the current screen.
client.currentScreen.onClose();
return;
}
if (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;
}
}
// Handles sneak button.
if (SNEAK.is_button(button) && client.player != null) {
this.toggle_sneaking(client);
}
}
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;
return;
}
if (client.currentScreen == null && action != 2) {
ButtonBinding.handle_button(client, button, action);
}
}
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 (client.currentScreen instanceof LambdaControlsControlsScreen && as_button_state != 0
&& !(as_button_state == 2 && (axis == GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER))) {
LambdaControlsControlsScreen controls_screen = (LambdaControlsControlsScreen) client.currentScreen;
if (controls_screen.focused_binding != null) {
this.config.set_button_binding(controls_screen.focused_binding, axis_as_button(axis, as_button_state == 1));
controls_screen.focused_binding = null;
return;
}
}
double dead_zone = this.config.get_dead_zone();
if (client.currentScreen == null) {
{
int axis_minus = axis + 100;
boolean current_plus_state = as_button_state == 1;
boolean current_minus_state = as_button_state == 2;
boolean previous_plus_state = AXIS_STATES.getOrDefault(axis, false);
boolean previous_minus_state = AXIS_STATES.getOrDefault(axis_minus, false);
if (current_plus_state != previous_plus_state) {
ButtonBinding.handle_button(client, ButtonBinding.axis_as_button(axis, true), 0);
if (current_plus_state)
AXIS_COOLDOWNS.put(axis, 5);
} else if (current_plus_state) {
if (AXIS_COOLDOWNS.getOrDefault(axis, 0) == 0) {
AXIS_COOLDOWNS.put(axis, 5);
}
}
if (current_minus_state != previous_minus_state) {
ButtonBinding.handle_button(client, ButtonBinding.axis_as_button(axis, false), 0);
if (current_minus_state)
AXIS_COOLDOWNS.put(axis_minus, 5);
} else if (current_minus_state) {
if (AXIS_COOLDOWNS.getOrDefault(axis_minus, 0) == 0) {
AXIS_COOLDOWNS.put(axis_minus, 5);
}
}
AXIS_STATES.put(axis, current_plus_state);
AXIS_STATES.put(axis_minus, current_minus_state);
}
// Handles the look direction.
this.handle_look(client, axis, abs_value, 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();
prev_target_mouse_x = target_mouse_x = (int) mouse_x;
prev_target_mouse_y = target_mouse_y = (int) mouse_y;
}
if (Math.abs(movement_x) >= dead_zone)
this.mouse_speed_x = movement_x;
else
this.mouse_speed_x = 0.F;
if (Math.abs(movement_y) >= dead_zone)
this.mouse_speed_y = movement_y;
else
this.mouse_speed_y = 0.F;
} else {
this.mouse_speed_x = 0.F;
this.mouse_speed_y = 0.F;
}
if (Math.abs(this.mouse_speed_x) > .05F || Math.abs(this.mouse_speed_y) > .05F) {
this.target_mouse_x += this.mouse_speed_x * this.config.get_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;
}
}
/**
* Handles the press on RB on LB.
*
* @param client The client's instance.
* @param right True if RB is pressed, else false.
*/
private void handle_rb_lb(@NotNull MinecraftClient client, boolean right)
{
// When ingame
if (client.currentScreen == null && client.player != null) {
if (right)
client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 8 ? 0 : client.player.inventory.selectedSlot + 1;
else
client.player.inventory.selectedSlot = client.player.inventory.selectedSlot == 0 ? 8 : client.player.inventory.selectedSlot - 1;
} else if (client.currentScreen instanceof CreativeInventoryScreen) {
CreativeInventoryScreenAccessor creative_inventory = (CreativeInventoryScreenAccessor) client.currentScreen;
int current_selected_tab = creative_inventory.get_selected_tab();
int next_tab = current_selected_tab + (right ? 1 : -1);
if (next_tab < 0)
next_tab = ItemGroup.GROUPS.length - 1;
else if (next_tab >= ItemGroup.GROUPS.length)
next_tab = 0;
creative_inventory.set_selected_tab(ItemGroup.GROUPS[next_tab]);
}
}
private boolean handle_a_button(@NotNull Screen screen, @NotNull Element focused)
{
if (focused instanceof AbstractPressableButtonWidget) {
AbstractPressableButtonWidget button_widget = (AbstractPressableButtonWidget) focused;
button_widget.playDownSound(MinecraftClient.getInstance().getSoundManager());
button_widget.onPress();
return true;
} else if (focused instanceof LabelWidget) {
((LabelWidget) 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;
}
/**
* Toggles whether the player is sneaking.
*
* @param client The client's instance.
*/
private void toggle_sneaking(@NotNull MinecraftClient client)
{
((KeyBindingAccessor) client.options.keySneak).handle_press_state(!client.options.keySneak.isPressed());
}
/**
* 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, 2.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

@@ -1,55 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.NotNull;
/**
* Represents a button with an icon instead of text.
*/
public abstract class AbstractIconButtonWidget extends ButtonWidget
{
private int icon_size = 0;
public AbstractIconButtonWidget(int x, int y, int width, int height, @NotNull String message, @NotNull PressAction on_press)
{
super(x, y, width, height, message, on_press);
}
protected abstract int render_icon(int mouse_x, int mouse_y, float delta, int x, int y);
@Override
public void renderButton(int mouse_x, int mouse_y, float delta)
{
MinecraftClient client = MinecraftClient.getInstance();
client.getTextureManager().bindTexture(WIDGETS_LOCATION);
RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
int i = this.getYImage(this.isHovered());
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
this.blit(this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
this.blit(this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
this.renderBg(client, mouse_x, mouse_y);
this.icon_size = this.render_icon(mouse_x, mouse_y, delta, this.x + 4, this.y + (this.height / 2 - this.icon_size / 2));
if (!this.getMessage().isEmpty()) {
int j = this.active ? 16777215 : 10526880;
this.drawCenteredString(client.textRenderer, this.getMessage(), this.x + 8 + this.icon_size + (this.width - 8 - this.icon_size - 6) / 2,
this.y + (this.height - 8) / 2, j | MathHelper.ceil(this.alpha * 255.0F) << 24);
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import me.lambdaurora.lambdacontrols.ButtonBinding;
import me.lambdaurora.lambdacontrols.LambdaControls;
import net.minecraft.client.MinecraftClient;
import org.jetbrains.annotations.NotNull;
/**
* Represents a controller button widget.
*/
public class ControllerButtonWidget extends AbstractIconButtonWidget
{
private ButtonBinding binding;
public ControllerButtonWidget(int x, int y, int width, @NotNull ButtonBinding button_binding, @NotNull PressAction on_press)
{
super(x, y, width, 20, ButtonBinding.get_localized_button_name(button_binding.get_button()), on_press);
this.binding = button_binding;
}
public void update()
{
this.setMessage(ButtonBinding.get_localized_button_name(binding.get_button()));
}
@Override
protected int render_icon(int mouse_x, int mouse_y, float delta, int x, int y)
{
return LambdaControls.draw_button(x, y, this.binding, MinecraftClient.getInstance());
}
}

View File

@@ -1,181 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import me.lambdaurora.lambdacontrols.ButtonBinding;
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 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;
ButtonBinding.stream_categories()
.sorted(Comparator.comparingInt(ButtonBinding.Category::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;
ButtonBindingEntry(@NotNull ButtonBinding binding)
{
this.binding = binding;
this.binding_name = I18n.translate(this.binding.get_translation_key());
this.edit_button = new ControllerButtonWidget(0, 0, 90, this.binding, btn -> gui.focused_binding = binding)
{
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);
}
};
}
@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 = x + 190;
this.reset_button.y = y;
this.reset_button.active = !this.binding.is_default();
this.reset_button.render(mouse_x, mouse_y, delta);
this.edit_button.x = x + 85;
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() && ButtonBinding.has_duplicates(this.binding.get_button())) {
this.edit_button.setMessage(Formatting.RED + this.edit_button.getMessage());
} else if (this.binding.is_not_bound()) {
this.edit_button.setMessage(Formatting.GOLD + edit_button.getMessage());
}
this.edit_button.render(mouse_x, mouse_y, delta);
}
public boolean mouseClicked(double mouseX, double mouseY, int button)
{
if (this.edit_button.mouseClicked(mouseX, mouseY, button))
return true;
else
return this.reset_button.mouseClicked(mouseX, mouseY, button);
}
public boolean mouseReleased(double mouseX, double mouseY, int button)
{
return this.edit_button.mouseReleased(mouseX, mouseY, button) || this.reset_button.mouseReleased(mouseX, mouseY, button);
}
}
public class CategoryEntry extends Entry
{
private final String name;
private final int name_width;
public CategoryEntry(@NotNull ButtonBinding.Category 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

@@ -1,239 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.Element;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.Matrix4f;
import net.minecraft.client.util.math.MatrixStack;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import java.util.List;
import java.util.function.Consumer;
/**
* Represents a label widget.
*/
// @TODO move this to a GUI library.
public class LabelWidget extends DrawableHelper implements Element, Drawable
{
public static final Consumer<LabelWidget> DEFAULT_ACTION = label -> {
};
private final MinecraftClient client = MinecraftClient.getInstance();
private final Consumer<LabelWidget> press_action;
private final int x;
private final int y;
private final int max_width;
//private final int max_height;
private String text;
private String tooltip_text;
public boolean visible;
private int width;
private int height;
private boolean centered;
protected boolean hovered;
protected boolean focused;
public LabelWidget(int x, int y, @NotNull String text, int max_width, @NotNull Consumer<LabelWidget> press_action, boolean centered)
{
this.visible = true;
this.x = x;
this.y = y;
this.max_width = max_width;
this.press_action = press_action;
this.centered = centered;
this.set_text(text);
}
public LabelWidget(int x, int y, @NotNull String text, int max_width, @NotNull Consumer<LabelWidget> press_action)
{
this(x, y, text, max_width, press_action, false);
}
public LabelWidget(int x, int y, @NotNull String text, int max_width, boolean centered)
{
this(x, y, text, max_width, DEFAULT_ACTION, centered);
}
public LabelWidget(int x, int y, @NotNull String text, int max_width)
{
this(x, y, text, max_width, DEFAULT_ACTION);
}
/**
* Sets the text of this label.
*
* @param text The text to set.
*/
public void set_text(@NotNull String text)
{
int width = this.client.textRenderer.getStringWidth(text);
while (width > this.max_width) {
text = text.substring(0, text.length() - 1);
width = this.client.textRenderer.getStringWidth(text);
}
this.text = text;
this.width = width;
this.height = this.client.textRenderer.fontHeight;
}
/**
* Sets the tooltip text of this label.
*
* @param text The tooltip text.
*/
public void set_tooltip_text(String text)
{
this.tooltip_text = text;
}
/**
* Gets the width of this label widget.
*
* @return The width of this label widget.
*/
public int get_width()
{
return this.width;
}
/**
* Gets the height of this label widget.
*
* @return The height of this label widget.
*/
public int get_height()
{
return this.height;
}
/**
* Fires the press event on this label widget.
*/
public void on_press()
{
this.press_action.accept(this);
}
@Override
public void render(int mouse_x, int mouse_y, float delta)
{
if (this.visible) {
int x = this.centered ? this.x - this.client.textRenderer.getStringWidth(this.text) / 2 : this.x;
this.hovered = mouse_x >= x && mouse_y >= this.y && mouse_x < x + this.width && mouse_y < this.y + this.height;
this.drawString(this.client.textRenderer, this.text, x, this.y, 10526880);
if (this.tooltip_text != null && !this.tooltip_text.isEmpty()) {
List<String> wrapped_tooltip_text = this.client.textRenderer.wrapStringToWidthAsList(this.tooltip_text, Math.max(this.width / 2, 200));
if (this.hovered)
this.render_tooltip(wrapped_tooltip_text, mouse_x, mouse_y);
else if (this.focused)
this.render_tooltip(wrapped_tooltip_text, this.x - 12, this.y);
}
}
}
@Override
public boolean mouseClicked(double mouse_x, double mouse_y, int button)
{
if (this.visible && button == GLFW.GLFW_MOUSE_BUTTON_1) {
if (this.hovered) {
this.on_press();
return true;
}
}
return false;
}
@Override
public boolean changeFocus(boolean down)
{
if (this.visible) {
this.focused = !this.focused;
return this.focused;
} else {
return false;
}
}
public void render_tooltip(List<String> text, int x, int y)
{
if (!text.isEmpty()) {
RenderSystem.disableRescaleNormal();
RenderSystem.disableDepthTest();
int i = 0;
for (String string : text) {
int j = this.client.textRenderer.getStringWidth(string);
if (j > i) {
i = j;
}
}
int k = x + 12;
int l = y - 12;
int n = 8;
if (text.size() > 1) {
n += 2 + (text.size() - 1) * 10;
}
if (k + i > this.client.getWindow().getScaledWidth()) {
k -= 28 + i;
}
if (l + n + 6 > this.client.getWindow().getScaledHeight()) {
l = this.client.getWindow().getScaledHeight() - n - 6;
}
this.setBlitOffset(300);
this.client.getItemRenderer().zOffset = 300.0F;
this.fillGradient(k - 3, l - 4, k + i + 3, l - 3, -267386864, -267386864);
this.fillGradient(k - 3, l + n + 3, k + i + 3, l + n + 4, -267386864, -267386864);
this.fillGradient(k - 3, l - 3, k + i + 3, l + n + 3, -267386864, -267386864);
this.fillGradient(k - 4, l - 3, k - 3, l + n + 3, -267386864, -267386864);
this.fillGradient(k + i + 3, l - 3, k + i + 4, l + n + 3, -267386864, -267386864);
this.fillGradient(k - 3, l - 3 + 1, k - 3 + 1, l + n + 3 - 1, 1347420415, 1344798847);
this.fillGradient(k + i + 2, l - 3 + 1, k + i + 3, l + n + 3 - 1, 1347420415, 1344798847);
this.fillGradient(k - 3, l - 3, k + i + 3, l - 3 + 1, 1347420415, 1347420415);
this.fillGradient(k - 3, l + n + 2, k + i + 3, l + n + 3, 1344798847, 1344798847);
MatrixStack matrix_stack = new MatrixStack();
VertexConsumerProvider.Immediate immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
matrix_stack.translate(0.0D, 0.0D, this.client.getItemRenderer().zOffset);
Matrix4f matrix4f = matrix_stack.peek().getModel();
for (int r = 0; r < text.size(); ++r) {
String string2 = text.get(r);
if (string2 != null) {
this.client.textRenderer.draw(string2, (float) k, (float) l, -1, true, matrix4f, immediate, false, 0, 15728880);
}
if (r == 0) {
l += 2;
}
l += 10;
}
immediate.draw();
this.setBlitOffset(0);
this.client.getItemRenderer().zOffset = 0.0F;
RenderSystem.enableDepthTest();
RenderSystem.enableRescaleNormal();
}
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import me.lambdaurora.lambdacontrols.ButtonBinding;
import me.lambdaurora.lambdacontrols.LambdaControls;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.options.BooleanOption;
import net.minecraft.client.options.Option;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.TranslatableText;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.function.Predicate;
/**
* Represents the controls screen.
*/
public class LambdaControlsControlsScreen extends Screen
{
private final LambdaControlsSettingsScreen parent;
final LambdaControls mod;
private final Option inverts_right_x_axis;
private final Option inverts_right_y_axis;
private ControlsListWidget bindings_list_widget;
private ButtonWidget reset_button;
public ButtonBinding focused_binding;
protected LambdaControlsControlsScreen(@NotNull LambdaControlsSettingsScreen parent)
{
super(new TranslatableText("lambdacontrols.menu.title.controller_controls"));
this.parent = parent;
this.mod = parent.mod;
this.inverts_right_x_axis = new BooleanOption("lambdacontrols.menu.invert_right_x_axis", game_options -> this.mod.config.does_invert_right_x_axis(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_invert_right_x_axis(new_value);
}
});
this.inverts_right_y_axis = new BooleanOption("lambdacontrols.menu.invert_right_y_axis", game_options -> this.mod.config.does_invert_right_y_axis(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_invert_right_y_axis(new_value);
}
});
}
@Override
public void removed()
{
this.mod.config.save();
super.removed();
}
@Override
protected void init()
{
this.addButton(this.inverts_right_x_axis.createButton(this.minecraft.options, this.width / 2 - 155, 18, 150));
this.addButton(this.inverts_right_y_axis.createButton(this.minecraft.options, this.width / 2 - 155 + 160, 18, 150));
this.bindings_list_widget = new ControlsListWidget(this, this.minecraft);
this.children.add(this.bindings_list_widget);
this.reset_button = this.addButton(new ButtonWidget(this.width / 2 - 155, this.height - 29, 150, 20, I18n.translate("controls.resetAll"),
btn -> ButtonBinding.stream().forEach(binding -> this.mod.config.set_button_binding(binding, binding.get_default_button()))));
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 = ButtonBinding.stream().anyMatch(this.not(ButtonBinding::is_default));
super.render(mouse_x, mouse_y, delta);
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import me.lambdaurora.lambdacontrols.ButtonBinding;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.LambdaControls;
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.
*/
public class LambdaControlsHud extends DrawableHelper
{
private final MinecraftClient client;
private final LambdaControls mod;
public LambdaControlsHud(@NotNull MinecraftClient client, @NotNull LambdaControls 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 = 10, y = bottom(10);
x += this.draw_button_tip(x, y, ButtonBinding.INVENTORY, true) + 10;
this.draw_button_tip(x, y, ButtonBinding.SWAP_HANDS, true);
x = 10;
x += this.draw_button_tip(x, (y -= 20), ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty()) + 10;
this.draw_button_tip(x, y, ButtonBinding.ATTACK.get_button(),
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 LambdaControls.draw_button_tip(x, y, button, display, this.client);
}
private int draw_button_tip(int x, int y, int button, @NotNull String action, boolean display)
{
return LambdaControls.draw_button_tip(x, y, button, action, display, this.client);
}
}

View File

@@ -1,170 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import me.lambdaurora.lambdacontrols.Controller;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.LambdaControls;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.options.ControlsOptionsScreen;
import net.minecraft.client.gui.widget.ButtonListWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.options.*;
import net.minecraft.client.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 LambdaControls mod;
private final Screen parent;
private final GameOptions options;
private final Option auto_switch_mode_option;
private final Option controller_option;
private final Option controller_type_option;
private final Option dead_zone_option;
private final Option hud_enable_option;
private final Option hud_side_option;
private final Option mouse_speed_option;
private final Option rotation_speed_option;
private final String controller_mappings_url_text = I18n.translate("lambdacontrols.controller.mappings.2", Formatting.GOLD.toString(), GAMEPAD_TOOL_URL, Formatting.RESET.toString());
private ButtonListWidget list;
private LabelWidget gamepad_tool_url_label;
public LambdaControlsSettingsScreen(Screen parent, @NotNull GameOptions options)
{
super(new TranslatableText("lambdacontrols.title.settings"));
this.mod = LambdaControls.get();
this.parent = parent;
this.options = options;
this.auto_switch_mode_option = new BooleanOption("lambdacontrols.menu.auto_switch_mode", game_options -> this.mod.config.has_auto_switch_mode(),
(game_options, new_value) -> this.mod.config.set_auto_switch_mode(new_value));
this.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.controller_type_option = new CyclingOption("lambdacontrols.menu.controller_type",
(game_options, amount) -> this.mod.config.set_controller_type(this.mod.config.get_controller_type().next()),
(game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_controller_type().get_translated_name());
this.hud_enable_option = new BooleanOption("lambdacontrols.menu.hud_enable", (game_options) -> this.mod.config.is_hud_enabled(),
(game_options, new_value) -> this.mod.config.set_hud_enabled(new_value));
this.hud_side_option = new CyclingOption("lambdacontrols.menu.hud_side",
(game_options, amount) -> this.mod.config.set_hud_side(this.mod.config.get_hud_side().next()),
(game_options, option) -> option.getDisplayPrefix() + this.mod.config.get_hud_side().get_translated_name());
this.dead_zone_option = new DoubleOption("lambdacontrols.menu.dead_zone", 0.05, 1.0, 0.05F, game_options -> this.mod.config.get_dead_zone(),
(game_options, 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));
});
this.rotation_speed_option = new DoubleOption("lambdacontrols.menu.rotation_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_rotation_speed(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_rotation_speed(new_value);
}
}, (game_options, option) -> option.getDisplayPrefix() + option.get(options));
this.mouse_speed_option = new DoubleOption("lambdacontrols.menu.mouse_speed", 0.0, 50.0, 0.5F, game_options -> this.mod.config.get_mouse_speed(),
(game_options, new_value) -> {
synchronized (this.mod.config) {
this.mod.config.set_mouse_speed(new_value);
}
}, (game_options, option) -> option.getDisplayPrefix() + option.get(options));
}
@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;
this.addButton(new ButtonWidget(this.width / 2 - 155, 18, 150, button_height, I18n.translate("lambdacontrols.menu.controls_mode") + ": " + this.mod.config.get_controls_mode().get_translated_name(),
btn -> {
ControlsMode next = this.mod.config.get_controls_mode().next();
btn.setMessage(I18n.translate("lambdacontrols.menu.controls_mode") + ": " + next.get_translated_name());
this.mod.config.set_controls_mode(next);
this.mod.config.save();
}));
this.addButton(new ButtonWidget(this.width / 2 - 155 + 160, 18, 150, button_height, I18n.translate("options.controls"),
btn -> {
if (this.mod.config.get_controls_mode() == ControlsMode.CONTROLLER)
this.minecraft.openScreen(new LambdaControlsControlsScreen(this));
else
this.minecraft.openScreen(new ControlsOptionsScreen(this, this.options));
}));
this.list = new ButtonListWidget(this.minecraft, this.width, this.height, 43, this.height - 29 - this.get_text_height(), 25);
this.list.addSingleOptionEntry(this.controller_option);
this.list.addOptionEntry(this.controller_type_option, this.dead_zone_option);
this.list.addOptionEntry(this.hud_enable_option, this.hud_side_option);
this.list.addOptionEntry(this.rotation_speed_option, this.mouse_speed_option);
this.list.addSingleOptionEntry(this.auto_switch_mode_option);
this.list.addSingleOptionEntry(new ReloadControllerMappingsOption());
this.children.add(this.list);
this.gamepad_tool_url_label = new LabelWidget(this.width / 2, this.height - 29 - (5 + this.font.fontHeight) * 2, this.controller_mappings_url_text, this.width,
label -> Util.getOperatingSystem().open(GAMEPAD_TOOL_URL), true);
this.gamepad_tool_url_label.set_tooltip_text(I18n.translate("chat.link.open"));
this.children.add(this.gamepad_tool_url_label);
this.addButton(new ButtonWidget(this.width / 2 - 155, this.height - 29, 300, button_height, I18n.translate("gui.done"),
(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);
}
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import net.minecraft.client.gui.widget.TexturedButtonWidget;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
/**
* Represents a touchscreen button widget.
*/
public class TouchscreenButtonWidget extends TexturedButtonWidget
{
private final Consumer<Boolean> on_change_state;
public TouchscreenButtonWidget(int x, int y, int width, int height, int u, int v, int hovered_v_offset, Identifier texture, @NotNull Consumer<Boolean> on_changed_state)
{
super(x, y, width, height, u, v, hovered_v_offset, texture, 256, 256, btn -> on_changed_state.accept(true));
this.on_change_state = on_changed_state;
}
@Override
public void onRelease(double mouseX, double mouseY)
{
super.onRelease(mouseX, mouseY);
this.on_change_state.accept(false);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button)
{
if (this.active && this.visible) {
if (this.isValidClickButton(button)) {
boolean clicked = this.clicked(mouseX, mouseY);
if (clicked) {
this.onClick(mouseX, mouseY);
return true;
}
}
return false;
} else
return false;
}
@Override
protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY)
{
super.onDrag(mouseX, mouseY, deltaX, deltaY);
if (this.active && !this.isHovered)
this.on_change_state.accept(false);
}
}

View File

@@ -1,295 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.gui;
import me.lambdaurora.lambdacontrols.HudSide;
import me.lambdaurora.lambdacontrols.LambdaControls;
import me.lambdaurora.lambdacontrols.util.KeyBindingAccessor;
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 LambdaControls mod;
private TouchscreenButtonWidget jump_button;
private TouchscreenButtonWidget fly_button;
private TouchscreenButtonWidget fly_up_button;
private TouchscreenButtonWidget fly_down_button;
private int fly_button_enable_ticks = 0;
private int forward_button_tick = 0;
private TouchscreenButtonWidget forward_left_button;
private TouchscreenButtonWidget forward_right_button;
private TouchscreenButtonWidget start_sneak_button;
private TouchscreenButtonWidget end_sneak_button;
public TouchscreenOverlay(@NotNull LambdaControls 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(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 state The state of the jump button.
*/
private void handle_jump(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 TouchscreenButtonWidget(swap_hands_x, sneak_button_y, 20, 20, 0, 160, 20, WIDGETS_LOCATION,
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 TouchscreenButtonWidget(swap_hands_x, sneak_button_y + 5 + 20, 20, 20, 20, 160, 20, WIDGETS_LOCATION,
state -> ((KeyBindingAccessor) this.minecraft.options.keyDrop).handle_press_state(state)));
// Jump keys
this.addButton(this.jump_button = new TouchscreenButtonWidget(jump_button_x, sneak_button_y, 20, 20, 0, 40, 20, WIDGETS_LOCATION,
this::handle_jump));
this.addButton(this.fly_button = new TouchscreenButtonWidget(jump_button_x, sneak_button_y, 20, 20, 20, 40, 20, WIDGETS_LOCATION,
state -> {
if (this.fly_button_enable_ticks == 0) this.minecraft.player.abilities.flying = false;
}));
this.addButton(this.fly_up_button = new TouchscreenButtonWidget(jump_button_x, sneak_button_y - 5 - 20, 20, 20, 40, 40, 20, WIDGETS_LOCATION,
this::handle_jump));
this.addButton(this.fly_down_button = new TouchscreenButtonWidget(jump_button_x, sneak_button_y + 20 + 5, 20, 20, 60, 40, 20, WIDGETS_LOCATION,
state -> ((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(state)));
this.update_jump_buttons();
// Movements keys
this.addButton((this.start_sneak_button = new TouchscreenButtonWidget(sneak_button_x, sneak_button_y, 20, 20, 0, 120, 20, WIDGETS_LOCATION,
state -> {
if (state) {
((KeyBindingAccessor) this.minecraft.options.keySneak).handle_press_state(true);
this.start_sneak_button.visible = false;
this.end_sneak_button.visible = true;
}
})));
this.addButton((this.end_sneak_button = new TouchscreenButtonWidget(sneak_button_x, sneak_button_y, 20, 20, 20, 120, 20, WIDGETS_LOCATION,
state -> {
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 TouchscreenButtonWidget(sneak_button_x - 20 - 5, sneak_button_y - 5 - 20, 20, 20, 80, 80, 20, WIDGETS_LOCATION,
state -> {
((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state);
((KeyBindingAccessor) this.minecraft.options.keyLeft).handle_press_state(state);
this.update_forward_buttons_state(state);
}));
this.forward_left_button.visible = false;
this.addButton(new TouchscreenButtonWidget(sneak_button_x, sneak_button_y - 5 - 20, 20, 20, 0, 80, 20, WIDGETS_LOCATION,
state -> {
((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state);
this.update_forward_buttons_state(state);
this.forward_left_button.visible = true;
this.forward_right_button.visible = true;
}));
this.addButton(this.forward_right_button = new TouchscreenButtonWidget(sneak_button_x + 20 + 5, sneak_button_y - 5 - 20, 20, 20, 100, 80, 20, WIDGETS_LOCATION,
state -> {
((KeyBindingAccessor) this.minecraft.options.keyForward).handle_press_state(state);
((KeyBindingAccessor) this.minecraft.options.keyRight).handle_press_state(state);
this.update_forward_buttons_state(state);
}));
this.forward_right_button.visible = true;
this.addButton(new TouchscreenButtonWidget(sneak_button_x + 20 + 5, sneak_button_y, 20, 20, 20, 80, 20, WIDGETS_LOCATION,
state -> ((KeyBindingAccessor) this.minecraft.options.keyRight).handle_press_state(state)));
this.addButton(new TouchscreenButtonWidget(sneak_button_x, sneak_button_y + 20 + 5, 20, 20, 40, 80, 20, WIDGETS_LOCATION,
state -> ((KeyBindingAccessor) this.minecraft.options.keyBack).handle_press_state(state)));
this.addButton(new TouchscreenButtonWidget(sneak_button_x - 20 - 5, sneak_button_y, 20, 20, 60, 80, 20, WIDGETS_LOCATION,
state -> ((KeyBindingAccessor) this.minecraft.options.keyLeft).handle_press_state(state)));
}
@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

@@ -1,69 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.mixin;
import me.lambdaurora.lambdacontrols.ControlsMode;
import me.lambdaurora.lambdacontrols.LambdaControls;
import me.lambdaurora.lambdacontrols.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 (LambdaControls.get().config.get_controls_mode() == ControlsMode.CONTROLLER) {
MinecraftClient client = MinecraftClient.getInstance();
int x = 10, y = client.getWindow().getScaledHeight() - 10 - 15;
x += LambdaControls.draw_button_tip(x, y, GLFW.GLFW_GAMEPAD_BUTTON_A, "lambdacontrols.action.pickup_all", true, client) + 10;
x += LambdaControls.draw_button_tip(x, y, GLFW.GLFW_GAMEPAD_BUTTON_B, "lambdacontrols.action.exit", true, client) + 10;
x += LambdaControls.draw_button_tip(x, y, GLFW.GLFW_GAMEPAD_BUTTON_X, "lambdacontrols.action.pickup", true, client) + 10;
LambdaControls.draw_button_tip(x, y, GLFW.GLFW_GAMEPAD_BUTTON_Y, "lambdacontrols.action.quick_move", true, client);
}
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright © 2019 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of LambdaControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package me.lambdaurora.lambdacontrols.mixin;
import me.lambdaurora.lambdacontrols.util.CreativeInventoryScreenAccessor;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.item.ItemGroup;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(CreativeInventoryScreen.class)
public abstract class CreativeInventoryScreenMixin implements CreativeInventoryScreenAccessor
{
@Shadow
protected abstract void setSelectedTab(ItemGroup itemGroup);
@Accessor("selectedTab")
public abstract int get_selected_tab();
@Override
public void set_selected_tab(@NotNull ItemGroup group)
{
this.setSelectedTab(group);
}
}

Some files were not shown because too many files have changed in this diff Show More