mirror of
https://github.com/chylex/Better-Controls.git
synced 2025-09-17 05:24:52 +02:00
Compare commits
20 Commits
9c0b3b0ac1
...
main
Author | SHA1 | Date | |
---|---|---|---|
2e19fffe44
|
|||
32f6a6e7fe
|
|||
dfefab3482
|
|||
ffd56a4407
|
|||
6b574f8568
|
|||
76a1353dbf
|
|||
0757d30f12
|
|||
64c4b51f0e
|
|||
5111ac5f7e
|
|||
ee3db91f0c
|
|||
9da758937e
|
|||
aa499bead5
|
|||
767ab9cb7d
|
|||
ba0d04ba51
|
|||
f55c82aa95
|
|||
bd572bdfba
|
|||
2cb16b1b74
|
|||
464bd3bcb6
|
|||
0d0b71e5aa
|
|||
3f7cea4750
|
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,3 +1 @@
|
|||||||
github: chylex
|
|
||||||
patreon: chylex
|
|
||||||
ko_fi: chylex
|
ko_fi: chylex
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
|
import net.fabricmc.loom.configuration.ide.RunConfigSettings
|
||||||
import org.gradle.jvm.tasks.Jar
|
import org.gradle.jvm.tasks.Jar
|
||||||
|
|
||||||
val modId: String by project
|
val modId: String by project
|
||||||
|
val modSides: String by project
|
||||||
val minecraftVersion: String by project
|
val minecraftVersion: String by project
|
||||||
val fabricVersion: String by project
|
val fabricVersion: String by project
|
||||||
|
|
||||||
@@ -24,12 +26,24 @@ loom {
|
|||||||
ideConfigGenerated(true)
|
ideConfigGenerated(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
named("client") {
|
fun side(name: String, configure: RunConfigSettings.() -> Unit) {
|
||||||
|
if (modSides == "both" || modSides == name) {
|
||||||
|
named(name, configure)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
findByName(name)?.let(::remove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
side("client") {
|
||||||
configName = "Fabric Client"
|
configName = "Fabric Client"
|
||||||
client()
|
client()
|
||||||
}
|
}
|
||||||
|
|
||||||
findByName("server")?.let(::remove)
|
side("server") {
|
||||||
|
configName = "Fabric Server"
|
||||||
|
server()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin {
|
mixin {
|
||||||
@@ -51,7 +65,7 @@ tasks.register<Jar>("uncompressedRemapJar") {
|
|||||||
group = "fabric"
|
group = "fabric"
|
||||||
|
|
||||||
from(tasks.remapJar.map { it.outputs.files.map(::zipTree) })
|
from(tasks.remapJar.map { it.outputs.files.map(::zipTree) })
|
||||||
|
|
||||||
archiveClassifier.set("uncompressed")
|
archiveClassifier.set("uncompressed")
|
||||||
entryCompression = ZipEntryCompression.STORED // Reduces size of multiloader jar.
|
entryCompression = ZipEntryCompression.STORED // Reduces size of multiloader jar.
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,10 @@ package chylex.bettercontrols.compatibility;
|
|||||||
import chylex.bettercontrols.gui.BetterControlsScreen;
|
import chylex.bettercontrols.gui.BetterControlsScreen;
|
||||||
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||||
import com.terraformersmc.modmenu.api.ModMenuApi;
|
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
|
|
||||||
public class ModMenuSupport implements ModMenuApi {
|
public class ModMenuSupport implements ModMenuApi {
|
||||||
@Override
|
@Override
|
||||||
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
||||||
return parentScreen -> new BetterControlsScreen(Minecraft.getInstance(), parentScreen);
|
return BetterControlsScreen::new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,24 +1,47 @@
|
|||||||
|
import net.neoforged.moddevgradle.dsl.RunModel
|
||||||
|
|
||||||
|
val modId: String by project
|
||||||
|
val modSides: String by project
|
||||||
val neoForgeVersion: String by project
|
val neoForgeVersion: String by project
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("net.neoforged.gradle.userdev")
|
id("net.neoforged.moddev")
|
||||||
id("net.neoforged.gradle.mixin")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
neoForge {
|
||||||
implementation("net.neoforged:neoforge:$neoForgeVersion")
|
version = neoForgeVersion
|
||||||
}
|
|
||||||
|
|
||||||
runs {
|
|
||||||
val runJvmArgs: Set<String> by project
|
|
||||||
|
|
||||||
configureEach {
|
mods {
|
||||||
workingDirectory = file("../run")
|
register(modId) {
|
||||||
modSource(project.sourceSets.main.get())
|
sourceSet(sourceSets.main.get())
|
||||||
jvmArguments(runJvmArgs)
|
sourceSet(rootProject.sourceSets.main.get())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeIf { it.name != "client" }
|
runs {
|
||||||
|
val runJvmArgs: Set<String> by project
|
||||||
|
|
||||||
|
configureEach {
|
||||||
|
gameDirectory = file("../run")
|
||||||
|
jvmArguments.addAll(runJvmArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun side(name: String, configure: RunModel.() -> Unit) {
|
||||||
|
if (modSides == "both" || modSides == name) {
|
||||||
|
register(name, configure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
side("client") {
|
||||||
|
ideName.set("NeoForge Client")
|
||||||
|
client()
|
||||||
|
}
|
||||||
|
|
||||||
|
side("server") {
|
||||||
|
ideName.set("NeoForge Server")
|
||||||
|
server()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.processResources {
|
tasks.processResources {
|
||||||
|
@@ -2,19 +2,26 @@ package chylex.bettercontrols;
|
|||||||
|
|
||||||
import chylex.bettercontrols.config.BetterControlsConfig;
|
import chylex.bettercontrols.config.BetterControlsConfig;
|
||||||
import chylex.bettercontrols.gui.BetterControlsScreen;
|
import chylex.bettercontrols.gui.BetterControlsScreen;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.neoforged.api.distmarker.Dist;
|
import net.neoforged.api.distmarker.Dist;
|
||||||
|
import net.neoforged.fml.ModContainer;
|
||||||
import net.neoforged.fml.ModLoadingContext;
|
import net.neoforged.fml.ModLoadingContext;
|
||||||
import net.neoforged.fml.common.Mod;
|
import net.neoforged.fml.common.Mod;
|
||||||
import net.neoforged.fml.loading.FMLEnvironment;
|
import net.neoforged.fml.loading.FMLEnvironment;
|
||||||
import net.neoforged.fml.loading.FMLPaths;
|
import net.neoforged.fml.loading.FMLPaths;
|
||||||
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
|
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@Mod("bettercontrols")
|
@Mod("bettercontrols")
|
||||||
public final class BetterControlsMod {
|
public final class BetterControlsMod {
|
||||||
public BetterControlsMod() {
|
public BetterControlsMod() {
|
||||||
if (FMLEnvironment.dist == Dist.CLIENT) {
|
if (FMLEnvironment.dist == Dist.CLIENT) {
|
||||||
BetterControlsCommon.setConfig(BetterControlsConfig.load(FMLPaths.CONFIGDIR.get().resolve("BetterControls.json")));
|
BetterControlsCommon.setConfig(BetterControlsConfig.load(FMLPaths.CONFIGDIR.get().resolve("BetterControls.json")));
|
||||||
ModLoadingContext.get().registerExtensionPoint(IConfigScreenFactory.class, () -> BetterControlsScreen::new);
|
ModLoadingContext.get().registerExtensionPoint(IConfigScreenFactory.class, () -> BetterControlsMod::createOptionsScreen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static BetterControlsScreen createOptionsScreen(ModContainer modContainer, @Nullable Screen parentScreen) {
|
||||||
|
return new BetterControlsScreen(parentScreen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
23
README.md
23
README.md
@@ -11,28 +11,33 @@ Another major difference is the amount and granularity of options. Better Contro
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
The mod adds **Toggle Keybinds** for sprinting, sneaking, flying (creative mode), walking, and jumping. You can use modifier keys (`Control` / `Shift` / `Alt`) for each, including for example setting `Control` to Sneak, and `Control + Y` to Toggle Sneak. If you press the original key, the toggle will be canceled (in the previous example, you can Toggle Sneak by pressing `Control + Y`, and stop sneaking by simply tapping Sneak). Note that the vanilla options for toggling sprinting/sneaking are disabled to avoid conflicts with the custom keybinds.
|
The mod adds **Toggle Keybinds** for sprinting, sneaking, flying (creative mode), walking, and jumping. You can use modifier keys (`Control` / `Shift` / `Alt`) for each, including for example setting `Control` to Sneak, and `Control + Y` to Toggle Sneak. If you press the original key, the toggle will be canceled (in the previous example, you can Toggle Sneak by pressing `Control + Y`, and stop sneaking by simply tapping `Control`). Note that the vanilla options for toggling sprinting/sneaking are disabled to avoid conflicts with the custom keybinds.
|
||||||
|
|
||||||
You can also bind a key that resets all **Toggle Keybinds** at once. That makes it easy to for ex. turn on walking, jumping, and sprinting, and then turn all of them off again by pressing one key instead of three.
|
You can also bind a key that resets all **Toggle Keybinds** at once. That makes it easy to for ex. turn on walking, jumping, and sprinting, and then turn all of them off again by pressing one key instead of three.
|
||||||
|
|
||||||
#### Sprinting
|
#### Sprinting
|
||||||
|
|
||||||
* **Sprint Key Mode** changes how the Sprint key behaves. You can choose between *Tap To Start Sprinting*, *Tap To Start / Stop Sprinting*, and *Hold To Sprint*.
|
* **Sprint Key Mode** changes how the Sprint key behaves. You can choose between *Tap To Start Sprinting*, *Tap To Start / Stop Sprinting*, and *Hold To Sprint*.
|
||||||
* **Double Tap 'Walk Forwards' To Sprint** can be turned off to prevent accidental sprinting.
|
* **Double Tap 'Walk Forwards' To Sprint** can be turned off.
|
||||||
* **Resume Sprinting After Hitting Obstacle** automatically presses the Sprint key once you are no longer touching any blocks (helpful when climbing hills, especially if the previous option is enabled).
|
* **Resume Sprinting After Hitting Obstacle** re-activates sprinting once you are no longer touching any blocks.
|
||||||
|
|
||||||
#### Sneaking
|
#### Sneaking
|
||||||
|
|
||||||
* **Move Camera Smoothly** lets you disable the smooth movement when sneaking or unsneaking.
|
* **Move Camera Smoothly** can be turned off to disable the smooth movement when sneaking or unsneaking.
|
||||||
|
|
||||||
|
#### Gliding
|
||||||
|
|
||||||
|
* **Start a Glide** with a dedicated key.
|
||||||
|
* **Double Tap 'Jump' To Glide** can be turned off.
|
||||||
|
|
||||||
#### Flying
|
#### Flying
|
||||||
|
|
||||||
* **Double Tap 'Jump' To Fly** can be turned off to prevent accidental flight toggling.
|
* **Double Tap 'Jump' To Fly** can be turned off.
|
||||||
* **Disable Flight Inertia** stops you instantly when you stop holding movement keys.
|
* **Flight Inertia Multiplier** changes how quickly you stop moving in the air when you stop holding movement keys.
|
||||||
* **Disable Field Of View Changing** prevents sprinting, potions, and other factors from changing the FOV while flying in creative and spectator mode.
|
* **Disable Field Of View Changing** prevents sprinting, potions, and other factors from changing the FOV while flying in creative and spectator mode.
|
||||||
* **Fly On Ground** lets you fly while touching the ground in creative mode (and also lets you stop flying by tapping Sneak while touching the ground).
|
* **Fly On Ground** lets you fly while touching the ground in creative mode. Stop flying by tapping Sneak while touching the ground.
|
||||||
* **Flight Speed Multiplier** (0.25x - 8x) changes how fast you fly in creative and spectator mode.
|
* **Flight Speed Multiplier** (0.25x - 8x) changes flight speed in creative and spectator mode.
|
||||||
* **Vertical Speed Boost** (up to +300%) adds additional vertical speed boost while flying in creative and spectator mode.
|
* **Vertical Speed Boost** (up to +300%) adds additional vertical flight speed boost in creative and spectator mode.
|
||||||
|
|
||||||
Both speed boosts can be configured separately for sprinting, which will be active when the Sprint key is held. Unlike in vanilla, the sprinting flight boost works in all directions.
|
Both speed boosts can be configured separately for sprinting, which will be active when the Sprint key is held. Unlike in vanilla, the sprinting flight boost works in all directions.
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ val modSides: String by project
|
|||||||
|
|
||||||
val minecraftVersion: String by project
|
val minecraftVersion: String by project
|
||||||
val mixinVersion: String by project
|
val mixinVersion: String by project
|
||||||
|
val mixinExtrasVersion: String by project
|
||||||
|
|
||||||
val minimumMinecraftVersion: String by project
|
val minimumMinecraftVersion: String by project
|
||||||
val minimumNeoForgeVersion: String by project
|
val minimumNeoForgeVersion: String by project
|
||||||
@@ -65,6 +66,8 @@ dependencies {
|
|||||||
mappings(loom.officialMojangMappings())
|
mappings(loom.officialMojangMappings())
|
||||||
|
|
||||||
compileOnly("net.fabricmc:sponge-mixin:$mixinVersion")
|
compileOnly("net.fabricmc:sponge-mixin:$mixinVersion")
|
||||||
|
compileOnly("io.github.llamalad7:mixinextras-common:$mixinExtrasVersion")
|
||||||
|
|
||||||
api("com.google.code.findbugs:jsr305:3.0.2")
|
api("com.google.code.findbugs:jsr305:3.0.2")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,10 +140,8 @@ subprojects {
|
|||||||
archivesName.set("$modNameStripped-${project.name}")
|
archivesName.set("$modNameStripped-${project.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
listOf("compileJava", "compileTestJava").forEach {
|
tasks.compileJava {
|
||||||
tasks.named<JavaCompile>(it) {
|
source({ rootProject.sourceSets.main.get().allSource })
|
||||||
source({ rootProject.sourceSets.main.get().allSource })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.processResources {
|
tasks.processResources {
|
||||||
|
@@ -6,28 +6,33 @@ Another major difference is the amount and granularity of options. Better Contro
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
The mod adds **Toggle Keybinds** for sprinting, sneaking, flying (creative mode), walking, and jumping. You can use modifier keys (`Control` / `Shift` / `Alt`) for each, including for example setting `Control` to Sneak, and `Control + Y` to Toggle Sneak. If you press the original key, the toggle will be canceled (in the previous example, you can Toggle Sneak by pressing `Control + Y`, and stop sneaking by simply tapping Sneak). Note that the vanilla options for toggling sprinting/sneaking are disabled to avoid conflicts with the custom keybinds.
|
The mod adds **Toggle Keybinds** for sprinting, sneaking, flying (creative mode), walking, and jumping. You can use modifier keys (`Control` / `Shift` / `Alt`) for each, including for example setting `Control` to Sneak, and `Control + Y` to Toggle Sneak. If you press the original key, the toggle will be canceled (in the previous example, you can Toggle Sneak by pressing `Control + Y`, and stop sneaking by simply tapping `Control`). Note that the vanilla options for toggling sprinting/sneaking are disabled to avoid conflicts with the custom keybinds.
|
||||||
|
|
||||||
You can also bind a key that resets all **Toggle Keybinds** at once. That makes it easy to for ex. turn on walking, jumping, and sprinting, and then turn all of them off again by pressing one key instead of three.
|
You can also bind a key that resets all **Toggle Keybinds** at once. That makes it easy to for ex. turn on walking, jumping, and sprinting, and then turn all of them off again by pressing one key instead of three.
|
||||||
|
|
||||||
#### Sprinting
|
#### Sprinting
|
||||||
|
|
||||||
* **Sprint Key Mode** changes how the Sprint key behaves. You can choose between *Tap To Start Sprinting*, *Tap To Start / Stop Sprinting*, and *Hold To Sprint*.
|
* **Sprint Key Mode** changes how the Sprint key behaves. You can choose between *Tap To Start Sprinting*, *Tap To Start / Stop Sprinting*, and *Hold To Sprint*.
|
||||||
* **Double Tap 'Walk Forwards' To Sprint** can be turned off to prevent accidental sprinting.
|
* **Double Tap 'Walk Forwards' To Sprint** can be turned off.
|
||||||
* **Resume Sprinting After Hitting Obstacle** automatically presses the Sprint key once you are no longer touching any blocks (helpful when climbing hills, especially if the previous option is enabled).
|
* **Resume Sprinting After Hitting Obstacle** re-activates sprinting once you are no longer touching any blocks.
|
||||||
|
|
||||||
#### Sneaking
|
#### Sneaking
|
||||||
|
|
||||||
* **Move Camera Smoothly** lets you disable the smooth movement when sneaking or unsneaking.
|
* **Move Camera Smoothly** can be turned off to disable the smooth movement when sneaking or unsneaking.
|
||||||
|
|
||||||
|
#### Gliding
|
||||||
|
|
||||||
|
* **Start a Glide** with a dedicated key.
|
||||||
|
* **Double Tap 'Jump' To Glide** can be turned off.
|
||||||
|
|
||||||
#### Flying
|
#### Flying
|
||||||
|
|
||||||
* **Double Tap 'Jump' To Fly** can be turned off to prevent accidental flight toggling.
|
* **Double Tap 'Jump' To Fly** can be turned off.
|
||||||
* **Disable Flight Inertia** stops you instantly when you stop holding movement keys.
|
* **Flight Inertia Multiplier** changes how quickly you stop moving in the air when you stop holding movement keys.
|
||||||
* **Disable Field Of View Changing** prevents sprinting, potions, and other factors from changing the FOV while flying in creative and spectator mode.
|
* **Disable Field Of View Changing** prevents sprinting, potions, and other factors from changing the FOV while flying in creative and spectator mode.
|
||||||
* **Fly On Ground** lets you fly while touching the ground in creative mode (and also lets you stop flying by tapping Sneak while touching the ground).
|
* **Fly On Ground** lets you fly while touching the ground in creative mode. Stop flying by tapping Sneak while touching the ground.
|
||||||
* **Flight Speed Multiplier** (0.25x - 8x) changes how fast you fly in creative and spectator mode.
|
* **Flight Speed Multiplier** (0.25x - 8x) changes flight speed in creative and spectator mode.
|
||||||
* **Vertical Speed Boost** (up to +300%) adds additional vertical speed boost while flying in creative and spectator mode.
|
* **Vertical Speed Boost** (up to +300%) adds additional vertical flight speed boost in creative and spectator mode.
|
||||||
|
|
||||||
Both speed boosts can be configured separately for sprinting, which will be active when the Sprint key is held. Unlike in vanilla, the sprinting flight boost works in all directions.
|
Both speed boosts can be configured separately for sprinting, which will be active when the Sprint key is held. Unlike in vanilla, the sprinting flight boost works in all directions.
|
||||||
|
|
@@ -6,28 +6,33 @@ Another major difference is the amount and granularity of options. Better Contro
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
The mod adds **Toggle Keybinds** for sprinting, sneaking, flying (creative mode), walking, and jumping. You can use modifier keys (`Control` / `Shift` / `Alt`) for each, including for example setting `Control` to Sneak, and `Control + Y` to Toggle Sneak. If you press the original key, the toggle will be canceled (in the previous example, you can Toggle Sneak by pressing `Control + Y`, and stop sneaking by simply tapping Sneak). Note that the vanilla options for toggling sprinting/sneaking are disabled to avoid conflicts with the custom keybinds.
|
The mod adds **Toggle Keybinds** for sprinting, sneaking, flying (creative mode), walking, and jumping. You can use modifier keys (`Control` / `Shift` / `Alt`) for each, including for example setting `Control` to Sneak, and `Control + Y` to Toggle Sneak. If you press the original key, the toggle will be canceled (in the previous example, you can Toggle Sneak by pressing `Control + Y`, and stop sneaking by simply tapping `Control`). Note that the vanilla options for toggling sprinting/sneaking are disabled to avoid conflicts with the custom keybinds.
|
||||||
|
|
||||||
You can also bind a key that resets all **Toggle Keybinds** at once. That makes it easy to for ex. turn on walking, jumping, and sprinting, and then turn all of them off again by pressing one key instead of three.
|
You can also bind a key that resets all **Toggle Keybinds** at once. That makes it easy to for ex. turn on walking, jumping, and sprinting, and then turn all of them off again by pressing one key instead of three.
|
||||||
|
|
||||||
#### Sprinting
|
#### Sprinting
|
||||||
|
|
||||||
* **Sprint Key Mode** changes how the Sprint key behaves. You can choose between *Tap To Start Sprinting*, *Tap To Start / Stop Sprinting*, and *Hold To Sprint*.
|
* **Sprint Key Mode** changes how the Sprint key behaves. You can choose between *Tap To Start Sprinting*, *Tap To Start / Stop Sprinting*, and *Hold To Sprint*.
|
||||||
* **Double Tap 'Walk Forwards' To Sprint** can be turned off to prevent accidental sprinting.
|
* **Double Tap 'Walk Forwards' To Sprint** can be turned off.
|
||||||
* **Resume Sprinting After Hitting Obstacle** automatically presses the Sprint key once you are no longer touching any blocks (helpful when climbing hills, especially if the previous option is enabled).
|
* **Resume Sprinting After Hitting Obstacle** re-activates sprinting once you are no longer touching any blocks.
|
||||||
|
|
||||||
#### Sneaking
|
#### Sneaking
|
||||||
|
|
||||||
* **Move Camera Smoothly** lets you disable the smooth movement when sneaking or unsneaking.
|
* **Move Camera Smoothly** can be turned off to disable the smooth movement when sneaking or unsneaking.
|
||||||
|
|
||||||
|
#### Gliding
|
||||||
|
|
||||||
|
* **Start a Glide** with a dedicated key.
|
||||||
|
* **Double Tap 'Jump' To Glide** can be turned off.
|
||||||
|
|
||||||
#### Flying
|
#### Flying
|
||||||
|
|
||||||
* **Double Tap 'Jump' To Fly** can be turned off to prevent accidental flight toggling.
|
* **Double Tap 'Jump' To Fly** can be turned off.
|
||||||
* **Disable Flight Inertia** stops you instantly when you stop holding movement keys.
|
* **Flight Inertia Multiplier** changes how quickly you stop moving in the air when you stop holding movement keys.
|
||||||
* **Disable Field Of View Changing** prevents sprinting, potions, and other factors from changing the FOV while flying in creative and spectator mode.
|
* **Disable Field Of View Changing** prevents sprinting, potions, and other factors from changing the FOV while flying in creative and spectator mode.
|
||||||
* **Fly On Ground** lets you fly while touching the ground in creative mode (and also lets you stop flying by tapping Sneak while touching the ground).
|
* **Fly On Ground** lets you fly while touching the ground in creative mode. Stop flying by tapping Sneak while touching the ground.
|
||||||
* **Flight Speed Multiplier** (0.25x - 8x) changes how fast you fly in creative and spectator mode.
|
* **Flight Speed Multiplier** (0.25x - 8x) changes flight speed in creative and spectator mode.
|
||||||
* **Vertical Speed Boost** (up to +300%) adds additional vertical speed boost while flying in creative and spectator mode.
|
* **Vertical Speed Boost** (up to +300%) adds additional vertical flight speed boost in creative and spectator mode.
|
||||||
|
|
||||||
Both speed boosts can be configured separately for sprinting, which will be active when the Sprint key is held. Unlike in vanilla, the sprinting flight boost works in all directions.
|
Both speed boosts can be configured separately for sprinting, which will be active when the Sprint key is held. Unlike in vanilla, the sprinting flight boost works in all directions.
|
||||||
|
|
||||||
|
@@ -3,28 +3,30 @@ modId=bettercontrols
|
|||||||
modName=Better Controls
|
modName=Better Controls
|
||||||
modDescription=Adds many powerful key bindings and options to control your movement.\\n\\nThe features complement vanilla mechanics without giving unfair advantages, so server use should be fine.
|
modDescription=Adds many powerful key bindings and options to control your movement.\\n\\nThe features complement vanilla mechanics without giving unfair advantages, so server use should be fine.
|
||||||
modAuthor=chylex
|
modAuthor=chylex
|
||||||
modVersion=1.4.0
|
modVersion=1.6.1
|
||||||
modLicense=MPL-2.0
|
modLicense=MPL-2.0
|
||||||
modSourcesURL=https://github.com/chylex/Better-Controls
|
modSourcesURL=https://github.com/chylex/Better-Controls
|
||||||
modIssuesURL=https://github.com/chylex/Better-Controls/issues
|
modIssuesURL=https://github.com/chylex/Better-Controls/issues
|
||||||
modSides=client
|
modSides=client
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
minecraftVersion=1.21
|
minecraftVersion=1.21.6
|
||||||
neoForgeVersion=21.0.0-beta
|
neoForgeVersion=21.6.20-beta
|
||||||
neoGradleVersion=7.0.152
|
neoModDevVersion=2.0.103
|
||||||
fabricVersion=0.15.11
|
fabricVersion=0.16.14
|
||||||
loomVersion=1.7
|
loomVersion=1.10
|
||||||
mixinVersion=0.12.5+mixin.0.8.5
|
mixinVersion=0.12.5+mixin.0.8.5
|
||||||
|
mixinExtrasVersion=0.4.1
|
||||||
|
|
||||||
# https://projects.neoforged.net/neoforged/neogradle/
|
# https://projects.neoforged.net/neoforged/neoforge
|
||||||
|
# https://projects.neoforged.net/neoforged/moddevgradle
|
||||||
# https://fabricmc.net/develop/
|
# https://fabricmc.net/develop/
|
||||||
# https://github.com/FabricMC/fabric-loom/releases
|
# https://github.com/FabricMC/fabric-loom/releases
|
||||||
|
|
||||||
# Constraints
|
# Constraints
|
||||||
minimumMinecraftVersion=1.21
|
minimumMinecraftVersion=1.21.6
|
||||||
minimumNeoForgeVersion=21.0.0-beta
|
minimumNeoForgeVersion=21.6.0-beta
|
||||||
minimumFabricVersion=0.12.3
|
minimumFabricVersion=0.15.0
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
org.gradle.jvmargs=-Xmx3G
|
org.gradle.jvmargs=-Xmx3G
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
4
gradlew
vendored
4
gradlew
vendored
@@ -15,6 +15,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -84,7 +86,7 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
@@ -8,10 +8,9 @@ pluginManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
val neoGradleVersion = settings.extra.get("neoGradleVersion") as? String
|
val neoModDevVersion = settings.extra.get("neoModDevVersion") as? String
|
||||||
if (neoGradleVersion != null) {
|
if (neoModDevVersion != null) {
|
||||||
id("net.neoforged.gradle.userdev") version neoGradleVersion
|
id("net.neoforged.moddev") version neoModDevVersion
|
||||||
id("net.neoforged.gradle.mixin") version neoGradleVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val loomVersion = settings.extra.get("loomVersion") as? String
|
val loomVersion = settings.extra.get("loomVersion") as? String
|
||||||
|
@@ -9,7 +9,7 @@ public final class BetterControlsCommon {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setConfig(final BetterControlsConfig config) {
|
static void setConfig(BetterControlsConfig config) {
|
||||||
BetterControlsCommon.config = config;
|
BetterControlsCommon.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
src/main/java/chylex/bettercontrols/Mixins.java
Normal file
42
src/main/java/chylex/bettercontrols/Mixins.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package chylex.bettercontrols;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.mixin.AccessCameraFields;
|
||||||
|
import chylex.bettercontrols.mixin.AccessClientPlayerFields;
|
||||||
|
import chylex.bettercontrols.mixin.AccessKeyMappingFields;
|
||||||
|
import chylex.bettercontrols.mixin.AccessPlayerFields;
|
||||||
|
import chylex.bettercontrols.mixin.AccessToggleKeyMappingFields;
|
||||||
|
import net.minecraft.client.Camera;
|
||||||
|
import net.minecraft.client.KeyMapping;
|
||||||
|
import net.minecraft.client.ToggleKeyMapping;
|
||||||
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
|
@SuppressWarnings("CastToIncompatibleInterface")
|
||||||
|
public final class Mixins {
|
||||||
|
private Mixins() {}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T me(Object object) {
|
||||||
|
return (T) object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccessCameraFields cameraFields(Camera camera) {
|
||||||
|
return (AccessCameraFields) camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccessClientPlayerFields clientPlayerFields(LocalPlayer localPlayer) {
|
||||||
|
return (AccessClientPlayerFields) localPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccessKeyMappingFields keyMappingFields(KeyMapping keyMapping) {
|
||||||
|
return (AccessKeyMappingFields) keyMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccessPlayerFields playerFields(Player player) {
|
||||||
|
return (AccessPlayerFields) player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccessToggleKeyMappingFields toggleKeyMappingFields(ToggleKeyMapping toggleKeyMapping) {
|
||||||
|
return (AccessToggleKeyMappingFields) toggleKeyMapping;
|
||||||
|
}
|
||||||
|
}
|
@@ -5,8 +5,8 @@ import chylex.bettercontrols.input.SprintMode;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public final class BetterControlsConfig {
|
public final class BetterControlsConfig {
|
||||||
public static BetterControlsConfig load(final Path path) {
|
public static BetterControlsConfig load(Path path) {
|
||||||
final BetterControlsConfig cfg = ConfigSerializer.read(path);
|
BetterControlsConfig cfg = ConfigSerializer.read(path);
|
||||||
cfg.path = path;
|
cfg.path = path;
|
||||||
|
|
||||||
if (cfg.wasMigrated) {
|
if (cfg.wasMigrated) {
|
||||||
@@ -27,9 +27,12 @@ public final class BetterControlsConfig {
|
|||||||
public final KeyBindingWithModifier keyToggleSneak = new KeyBindingWithModifier("key.bettercontrols.toggle_sneak");
|
public final KeyBindingWithModifier keyToggleSneak = new KeyBindingWithModifier("key.bettercontrols.toggle_sneak");
|
||||||
public boolean sneakingMovesCameraSmoothly = true;
|
public boolean sneakingMovesCameraSmoothly = true;
|
||||||
|
|
||||||
|
public final KeyBindingWithModifier keyStartGlide = new KeyBindingWithModifier("key.bettercontrols.start_glide");
|
||||||
|
public boolean doubleTapJumpToGlide = true;
|
||||||
|
|
||||||
public final KeyBindingWithModifier keyToggleFlight = new KeyBindingWithModifier("key.bettercontrols.toggle_flight");
|
public final KeyBindingWithModifier keyToggleFlight = new KeyBindingWithModifier("key.bettercontrols.toggle_flight");
|
||||||
public boolean doubleTapJumpToToggleFlight = true;
|
public boolean doubleTapJumpToToggleFlight = true;
|
||||||
public boolean disableFlightInertia = false;
|
public float flightInertiaMultiplier = 1F;
|
||||||
public boolean disableChangingFovWhileFlying = false;
|
public boolean disableChangingFovWhileFlying = false;
|
||||||
public boolean flyOnGroundInCreative = false;
|
public boolean flyOnGroundInCreative = false;
|
||||||
public float flightHorizontalSpeedMpCreativeDefault = 1F;
|
public float flightHorizontalSpeedMpCreativeDefault = 1F;
|
||||||
|
@@ -30,8 +30,8 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js
|
|||||||
private ConfigSerializer() {}
|
private ConfigSerializer() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonElement serialize(final BetterControlsConfig cfg, final Type typeOfSrc, final JsonSerializationContext context) {
|
public JsonElement serialize(BetterControlsConfig cfg, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
final JsonObject obj = new JsonObject();
|
JsonObject obj = new JsonObject();
|
||||||
|
|
||||||
Json.writeKeyBinding(obj, "Sprint.KeyToggle", cfg.keyToggleSprint);
|
Json.writeKeyBinding(obj, "Sprint.KeyToggle", cfg.keyToggleSprint);
|
||||||
Json.setEnum(obj, "Sprint.Mode", cfg.sprintMode);
|
Json.setEnum(obj, "Sprint.Mode", cfg.sprintMode);
|
||||||
@@ -41,9 +41,12 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js
|
|||||||
Json.writeKeyBinding(obj, "Sneak.KeyToggle", cfg.keyToggleSneak);
|
Json.writeKeyBinding(obj, "Sneak.KeyToggle", cfg.keyToggleSneak);
|
||||||
Json.setBool(obj, "Sneak.SmoothCamera", cfg.sneakingMovesCameraSmoothly);
|
Json.setBool(obj, "Sneak.SmoothCamera", cfg.sneakingMovesCameraSmoothly);
|
||||||
|
|
||||||
|
Json.writeKeyBinding(obj, "Glide.KeyStart", cfg.keyStartGlide);
|
||||||
|
Json.setBool(obj, "Glide.DoubleTapJump", cfg.doubleTapJumpToGlide);
|
||||||
|
|
||||||
Json.writeKeyBinding(obj, "Flight.KeyToggle.Creative", cfg.keyToggleFlight);
|
Json.writeKeyBinding(obj, "Flight.KeyToggle.Creative", cfg.keyToggleFlight);
|
||||||
Json.setBool(obj, "Flight.DoubleTapJump", cfg.doubleTapJumpToToggleFlight);
|
Json.setBool(obj, "Flight.DoubleTapJump", cfg.doubleTapJumpToToggleFlight);
|
||||||
Json.setBool(obj, "Flight.DisableInertia", cfg.disableFlightInertia);
|
Json.setFloat(obj, "Flight.InertiaMultiplier", cfg.flightInertiaMultiplier);
|
||||||
Json.setBool(obj, "Flight.DisableChangingFOV", cfg.disableChangingFovWhileFlying);
|
Json.setBool(obj, "Flight.DisableChangingFOV", cfg.disableChangingFovWhileFlying);
|
||||||
Json.setBool(obj, "Flight.FlyOnGround.Creative", cfg.flyOnGroundInCreative);
|
Json.setBool(obj, "Flight.FlyOnGround.Creative", cfg.flyOnGroundInCreative);
|
||||||
Json.setFloat(obj, "Flight.SpeedMp.Creative.Default", cfg.flightHorizontalSpeedMpCreativeDefault);
|
Json.setFloat(obj, "Flight.SpeedMp.Creative.Default", cfg.flightHorizontalSpeedMpCreativeDefault);
|
||||||
@@ -64,14 +67,18 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BetterControlsConfig deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
|
public BetterControlsConfig deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
final BetterControlsConfig cfg = new BetterControlsConfig();
|
BetterControlsConfig cfg = new BetterControlsConfig();
|
||||||
final JsonObject obj = json.getAsJsonObject();
|
JsonObject obj = json.getAsJsonObject();
|
||||||
|
|
||||||
if (obj.has("Sprint.TapToStop") && obj.get("Sprint.TapToStop").getAsBoolean()) {
|
if (obj.has("Sprint.TapToStop") && obj.get("Sprint.TapToStop").getAsBoolean()) {
|
||||||
cfg.sprintMode = SprintMode.TAP_TO_TOGGLE;
|
cfg.sprintMode = SprintMode.TAP_TO_TOGGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (obj.has("Flight.DisableInertia") && obj.get("Flight.DisableInertia").getAsBoolean()) {
|
||||||
|
cfg.flightInertiaMultiplier = 0F;
|
||||||
|
}
|
||||||
|
|
||||||
Json.readKeyBinding(obj, "Sprint.KeyToggle", cfg.keyToggleSprint);
|
Json.readKeyBinding(obj, "Sprint.KeyToggle", cfg.keyToggleSprint);
|
||||||
cfg.sprintMode = Json.getEnum(obj, "Sprint.Mode", cfg.sprintMode, SprintMode.class);
|
cfg.sprintMode = Json.getEnum(obj, "Sprint.Mode", cfg.sprintMode, SprintMode.class);
|
||||||
cfg.doubleTapForwardToSprint = Json.getBool(obj, "Sprint.DoubleTapForward", cfg.doubleTapForwardToSprint);
|
cfg.doubleTapForwardToSprint = Json.getBool(obj, "Sprint.DoubleTapForward", cfg.doubleTapForwardToSprint);
|
||||||
@@ -80,9 +87,12 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js
|
|||||||
Json.readKeyBinding(obj, "Sneak.KeyToggle", cfg.keyToggleSneak);
|
Json.readKeyBinding(obj, "Sneak.KeyToggle", cfg.keyToggleSneak);
|
||||||
cfg.sneakingMovesCameraSmoothly = Json.getBool(obj, "Sneak.SmoothCamera", cfg.sneakingMovesCameraSmoothly);
|
cfg.sneakingMovesCameraSmoothly = Json.getBool(obj, "Sneak.SmoothCamera", cfg.sneakingMovesCameraSmoothly);
|
||||||
|
|
||||||
|
Json.readKeyBinding(obj, "Glide.KeyStart", cfg.keyStartGlide);
|
||||||
|
cfg.doubleTapJumpToGlide = Json.getBool(obj, "Glide.DoubleTapJump", cfg.doubleTapJumpToGlide);
|
||||||
|
|
||||||
Json.readKeyBinding(obj, "Flight.KeyToggle.Creative", cfg.keyToggleFlight);
|
Json.readKeyBinding(obj, "Flight.KeyToggle.Creative", cfg.keyToggleFlight);
|
||||||
cfg.doubleTapJumpToToggleFlight = Json.getBool(obj, "Flight.DoubleTapJump", cfg.doubleTapJumpToToggleFlight);
|
cfg.doubleTapJumpToToggleFlight = Json.getBool(obj, "Flight.DoubleTapJump", cfg.doubleTapJumpToToggleFlight);
|
||||||
cfg.disableFlightInertia = Json.getBool(obj, "Flight.DisableInertia", cfg.disableFlightInertia);
|
cfg.flightInertiaMultiplier = Json.getFloat(obj, "Flight.InertiaMultiplier", cfg.flightInertiaMultiplier, 0F, 1F);
|
||||||
cfg.disableChangingFovWhileFlying = Json.getBool(obj, "Flight.DisableChangingFOV", cfg.disableChangingFovWhileFlying);
|
cfg.disableChangingFovWhileFlying = Json.getBool(obj, "Flight.DisableChangingFOV", cfg.disableChangingFovWhileFlying);
|
||||||
cfg.flyOnGroundInCreative = Json.getBool(obj, "Flight.FlyOnGround.Creative", cfg.flyOnGroundInCreative);
|
cfg.flyOnGroundInCreative = Json.getBool(obj, "Flight.FlyOnGround.Creative", cfg.flyOnGroundInCreative);
|
||||||
cfg.flightHorizontalSpeedMpCreativeDefault = readHorizontalSpeedMultiplier(obj, "Flight.SpeedMp.Creative.Default", cfg.flightHorizontalSpeedMpCreativeDefault);
|
cfg.flightHorizontalSpeedMpCreativeDefault = readHorizontalSpeedMultiplier(obj, "Flight.SpeedMp.Creative.Default", cfg.flightHorizontalSpeedMpCreativeDefault);
|
||||||
@@ -102,18 +112,18 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js
|
|||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float readHorizontalSpeedMultiplier(final JsonObject obj, final String key, final float defaultValue) {
|
private static float readHorizontalSpeedMultiplier(JsonObject obj, String key, float defaultValue) {
|
||||||
return Json.getFloat(obj, key, defaultValue, 0.25F, 8F);
|
return Json.getFloat(obj, key, defaultValue, 0.25F, 8F);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float readVerticalSpeedMultiplier(final JsonObject obj, final BetterControlsConfig cfg, final String newKey, final String legacyBoostKey, final float defaultValue) {
|
private static float readVerticalSpeedMultiplier(JsonObject obj, BetterControlsConfig cfg, String newKey, String legacyBoostKey, float defaultValue) {
|
||||||
if (obj.has(newKey)) {
|
if (obj.has(newKey)) {
|
||||||
return Json.getFloat(obj, newKey, defaultValue, 0.25F, 8F);
|
return Json.getFloat(obj, newKey, defaultValue, 0.25F, 8F);
|
||||||
}
|
}
|
||||||
else if (obj.has(legacyBoostKey)) {
|
else if (obj.has(legacyBoostKey)) {
|
||||||
cfg.wasMigrated = true;
|
cfg.wasMigrated = true;
|
||||||
|
|
||||||
final float value = 1F + Json.getFloat(obj, legacyBoostKey, 0F, 0F, 3F);
|
float value = 1F + Json.getFloat(obj, legacyBoostKey, 0F, 0F, 3F);
|
||||||
// 1.25x, 1.75x, 2.5x, and 3.5x are not supported
|
// 1.25x, 1.75x, 2.5x, and 3.5x are not supported
|
||||||
if (Mth.equal(value, 1.25F) || Mth.equal(value, 1.75F)) {
|
if (Mth.equal(value, 1.25F) || Mth.equal(value, 1.75F)) {
|
||||||
return 1.5F;
|
return 1.5F;
|
||||||
@@ -133,19 +143,19 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write(final Path path, final BetterControlsConfig config) {
|
static void write(Path path, BetterControlsConfig config) {
|
||||||
try (final JsonWriter writer = gson.newJsonWriter(Files.newBufferedWriter(path, StandardCharsets.UTF_8))) {
|
try (JsonWriter writer = gson.newJsonWriter(Files.newBufferedWriter(path, StandardCharsets.UTF_8))) {
|
||||||
gson.getAdapter(BetterControlsConfig.class).write(writer, config);
|
gson.getAdapter(BetterControlsConfig.class).write(writer, config);
|
||||||
} catch (final IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error saving BetterControls configuration file!", e);
|
logger.error("Error saving BetterControls configuration file!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BetterControlsConfig read(final Path path) {
|
static BetterControlsConfig read(Path path) {
|
||||||
try (final JsonReader jsonReader = new JsonReader(Files.newBufferedReader(path, StandardCharsets.UTF_8))) {
|
try (JsonReader jsonReader = new JsonReader(Files.newBufferedReader(path, StandardCharsets.UTF_8))) {
|
||||||
return gson.getAdapter(BetterControlsConfig.class).read(jsonReader);
|
return gson.getAdapter(BetterControlsConfig.class).read(jsonReader);
|
||||||
} catch (final FileNotFoundException | NoSuchFileException ignored) {
|
} catch (FileNotFoundException | NoSuchFileException ignored) {
|
||||||
} catch (final IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error reading BetterControls configuration file!", e);
|
logger.error("Error reading BetterControls configuration file!", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,40 +8,40 @@ import com.mojang.blaze3d.platform.InputConstants;
|
|||||||
final class Json {
|
final class Json {
|
||||||
private Json() {}
|
private Json() {}
|
||||||
|
|
||||||
static void setFloat(final JsonObject obj, final String key, final float value) {
|
static void setFloat(JsonObject obj, String key, float value) {
|
||||||
obj.addProperty(key, Float.valueOf(value));
|
obj.addProperty(key, Float.valueOf(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
static float getFloat(final JsonObject obj, final String key, final float defaultValue, final float minValue, final float maxValue) {
|
static float getFloat(JsonObject obj, String key, float defaultValue, float minValue, float maxValue) {
|
||||||
final float value = obj.has(key) ? obj.get(key).getAsFloat() : defaultValue;
|
float value = obj.has(key) ? obj.get(key).getAsFloat() : defaultValue;
|
||||||
return Math.max(minValue, Math.min(maxValue, value));
|
return Math.max(minValue, Math.min(maxValue, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setBool(final JsonObject obj, final String key, final boolean value) {
|
static void setBool(JsonObject obj, String key, boolean value) {
|
||||||
obj.addProperty(key, Boolean.valueOf(value));
|
obj.addProperty(key, Boolean.valueOf(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean getBool(final JsonObject obj, final String key, final boolean defaultValue) {
|
static boolean getBool(JsonObject obj, String key, boolean defaultValue) {
|
||||||
return obj.has(key) ? obj.get(key).getAsBoolean() : defaultValue;
|
return obj.has(key) ? obj.get(key).getAsBoolean() : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
@SuppressWarnings("SameParameterValue")
|
||||||
static <T extends Enum<T>> void setEnum(final JsonObject obj, final String key, final T value) {
|
static <T extends Enum<T>> void setEnum(JsonObject obj, String key, T value) {
|
||||||
obj.addProperty(key, value.name());
|
obj.addProperty(key, value.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
@SuppressWarnings("SameParameterValue")
|
||||||
static <T extends Enum<T>> T getEnum(final JsonObject obj, final String key, final T defaultValue, final Class<T> enumClass) {
|
static <T extends Enum<T>> T getEnum(JsonObject obj, String key, T defaultValue, Class<T> enumClass) {
|
||||||
if (!obj.has(key)) {
|
if (!obj.has(key)) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final T[] constants = enumClass.getEnumConstants();
|
T[] constants = enumClass.getEnumConstants();
|
||||||
|
|
||||||
if (constants != null) {
|
if (constants != null) {
|
||||||
final String value = obj.get(key).getAsString();
|
String value = obj.get(key).getAsString();
|
||||||
|
|
||||||
for (final T constant : constants) {
|
for (T constant : constants) {
|
||||||
if (constant.name().equalsIgnoreCase(value)) {
|
if (constant.name().equalsIgnoreCase(value)) {
|
||||||
return constant;
|
return constant;
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ final class Json {
|
|||||||
private static final String KEY_SUFFIX = ".Key";
|
private static final String KEY_SUFFIX = ".Key";
|
||||||
private static final String MOD_SUFFIX = ".Mod";
|
private static final String MOD_SUFFIX = ".Mod";
|
||||||
|
|
||||||
static void writeKeyBinding(final JsonObject obj, final String key, final KeyBindingWithModifier keyBinding) {
|
static void writeKeyBinding(JsonObject obj, String key, KeyBindingWithModifier keyBinding) {
|
||||||
obj.addProperty(key + KEY_SUFFIX, keyBinding.saveString());
|
obj.addProperty(key + KEY_SUFFIX, keyBinding.saveString());
|
||||||
|
|
||||||
if (keyBinding.getModifier() != null) {
|
if (keyBinding.getModifier() != null) {
|
||||||
@@ -62,11 +62,11 @@ final class Json {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void readKeyBinding(final JsonObject obj, final String key, final KeyBindingWithModifier keyBinding) {
|
static void readKeyBinding(JsonObject obj, String key, KeyBindingWithModifier keyBinding) {
|
||||||
if (obj.has(key + KEY_SUFFIX)) {
|
if (obj.has(key + KEY_SUFFIX)) {
|
||||||
try {
|
try {
|
||||||
keyBinding.setKey(InputConstants.getKey(obj.get(key + KEY_SUFFIX).getAsString()));
|
keyBinding.setKey(InputConstants.getKey(obj.get(key + KEY_SUFFIX).getAsString()));
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
e.printStackTrace(); // let's not crash if the config file has garbage, okay?
|
e.printStackTrace(); // let's not crash if the config file has garbage, okay?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ import static chylex.bettercontrols.gui.elements.TextWidget.CENTER;
|
|||||||
import static net.minecraft.network.chat.Component.literal;
|
import static net.minecraft.network.chat.Component.literal;
|
||||||
|
|
||||||
public class BetterControlsScreen extends OptionsSubScreen {
|
public class BetterControlsScreen extends OptionsSubScreen {
|
||||||
private static Component text(final String text) {
|
private static Component text(String text) {
|
||||||
return literal(text);
|
return literal(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +53,8 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
|
|
||||||
// Options
|
// Options
|
||||||
|
|
||||||
private int generateSprintingOptions(int y, final List<GuiEventListener> elements) {
|
private int generateSprintingOptions(int y, List<GuiEventListener> elements) {
|
||||||
final BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
||||||
|
|
||||||
generateKeyBindingWithModifierRow(y, elements, text("Toggle Sprint"), cfg.keyToggleSprint);
|
generateKeyBindingWithModifierRow(y, elements, text("Toggle Sprint"), cfg.keyToggleSprint);
|
||||||
y += ROW_HEIGHT;
|
y += ROW_HEIGHT;
|
||||||
@@ -71,8 +71,8 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int generateSneakingOptions(int y, final List<GuiEventListener> elements) {
|
private int generateSneakingOptions(int y, List<GuiEventListener> elements) {
|
||||||
final BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
||||||
|
|
||||||
generateKeyBindingWithModifierRow(y, elements, text("Toggle Sneak"), cfg.keyToggleSneak);
|
generateKeyBindingWithModifierRow(y, elements, text("Toggle Sneak"), cfg.keyToggleSneak);
|
||||||
y += ROW_HEIGHT;
|
y += ROW_HEIGHT;
|
||||||
@@ -83,10 +83,30 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int generateFlightOptions(int y, final List<GuiEventListener> elements) {
|
private int generateGlidingOptions(int y, List<GuiEventListener> elements) {
|
||||||
final BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
||||||
|
|
||||||
final ImmutableList<Option<Float>> flightSpeedOptions = ImmutableList.of(
|
generateKeyBindingWithModifierRow(y, elements, text("Start a Glide"), cfg.keyStartGlide);
|
||||||
|
y += ROW_HEIGHT;
|
||||||
|
|
||||||
|
generateBooleanOptionRow(y, elements, text("Double Tap 'Jump' To Start a Glide"), cfg.doubleTapJumpToGlide, value -> cfg.doubleTapJumpToGlide = value);
|
||||||
|
y += ROW_HEIGHT;
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int generateFlightOptions(int y, List<GuiEventListener> elements) {
|
||||||
|
BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
||||||
|
|
||||||
|
ImmutableList<Option<Float>> flightInertiaOptions = ImmutableList.of(
|
||||||
|
new Option<>(Float.valueOf(0.00F), text("0x")),
|
||||||
|
new Option<>(Float.valueOf(0.25F), text("0.25x")),
|
||||||
|
new Option<>(Float.valueOf(0.50F), text("0.5x")),
|
||||||
|
new Option<>(Float.valueOf(0.75F), text("0.75x")),
|
||||||
|
new Option<>(Float.valueOf(1.00F), text("1x"))
|
||||||
|
);
|
||||||
|
|
||||||
|
ImmutableList<Option<Float>> flightSpeedOptions = ImmutableList.of(
|
||||||
new Option<>(Float.valueOf(0.25F), text("0.25x")),
|
new Option<>(Float.valueOf(0.25F), text("0.25x")),
|
||||||
new Option<>(Float.valueOf(0.50F), text("0.5x")),
|
new Option<>(Float.valueOf(0.50F), text("0.5x")),
|
||||||
new Option<>(Float.valueOf(0.75F), text("0.75x")),
|
new Option<>(Float.valueOf(0.75F), text("0.75x")),
|
||||||
@@ -107,13 +127,14 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
generateBooleanOptionRow(y, elements, text("Double Tap 'Jump' To Fly (Creative)"), cfg.doubleTapJumpToToggleFlight, value -> cfg.doubleTapJumpToToggleFlight = value);
|
generateBooleanOptionRow(y, elements, text("Double Tap 'Jump' To Fly (Creative)"), cfg.doubleTapJumpToToggleFlight, value -> cfg.doubleTapJumpToToggleFlight = value);
|
||||||
y += ROW_HEIGHT;
|
y += ROW_HEIGHT;
|
||||||
|
|
||||||
generateBooleanOptionRow(y, elements, text("Disable Flight Inertia"), cfg.disableFlightInertia, value -> cfg.disableFlightInertia = value);
|
generateLeftSideText(y, elements, text("Flight Inertia Multiplier"));
|
||||||
|
elements.add(new DiscreteValueSliderWidget<>(col2(1), y, COL2_W, text("Flight Inertia Multiplier"), flightInertiaOptions, cfg.flightInertiaMultiplier, value -> cfg.flightInertiaMultiplier = value));
|
||||||
y += ROW_HEIGHT;
|
y += ROW_HEIGHT;
|
||||||
|
|
||||||
generateBooleanOptionRow(y, elements, text("Disable Field Of View Changing"), cfg.disableChangingFovWhileFlying, value -> cfg.disableChangingFovWhileFlying = value);
|
generateBooleanOptionRow(y, elements, text("Disable Field Of View Changing"), cfg.disableChangingFovWhileFlying, value -> cfg.disableChangingFovWhileFlying = value);
|
||||||
y += ROW_HEIGHT;
|
y += ROW_HEIGHT;
|
||||||
|
|
||||||
generateBooleanOptionRow(y, elements, text("Fly On Ground (Creative Mode)"), cfg.flyOnGroundInCreative, value -> cfg.flyOnGroundInCreative = value);
|
generateBooleanOptionRow(y, elements, text("Fly On Ground (Creative)"), cfg.flyOnGroundInCreative, value -> cfg.flyOnGroundInCreative = value);
|
||||||
y += ROW_HEIGHT;
|
y += ROW_HEIGHT;
|
||||||
|
|
||||||
y += ROW_HEIGHT / 3;
|
y += ROW_HEIGHT / 3;
|
||||||
@@ -144,8 +165,8 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int generateMiscellaneousOptions(int y, final List<GuiEventListener> elements) {
|
private int generateMiscellaneousOptions(int y, List<GuiEventListener> elements) {
|
||||||
final BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
BetterControlsConfig cfg = BetterControlsCommon.getConfig();
|
||||||
|
|
||||||
generateKeyBindingWithModifierRow(y, elements, text("Toggle Walk Forwards"), cfg.keyToggleWalkForward);
|
generateKeyBindingWithModifierRow(y, elements, text("Toggle Walk Forwards"), cfg.keyToggleWalkForward);
|
||||||
y += ROW_HEIGHT;
|
y += ROW_HEIGHT;
|
||||||
@@ -171,9 +192,9 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
new Option<>(ModifierKey.ALT, text("Alt"))
|
new Option<>(ModifierKey.ALT, text("Alt"))
|
||||||
);
|
);
|
||||||
|
|
||||||
private void generateKeyBindingWithModifierRow(final int y, final List<GuiEventListener> elements, final Component text, final KeyBindingWithModifier binding) {
|
private void generateKeyBindingWithModifierRow(int y, List<GuiEventListener> elements, Component text, KeyBindingWithModifier binding) {
|
||||||
final var modifierButton = Option.button(col4(2), y, COL4_W, text.plainCopy().append(" Modifier"), MODIFIER_OPTIONS, binding.getModifier(), binding::setModifier);
|
var modifierButton = Option.button(col4(2), y, COL4_W, text.plainCopy().append(" Modifier"), MODIFIER_OPTIONS, binding.getModifier(), binding::setModifier);
|
||||||
final var bindingButton = new KeyBindingWidget(col4(3), y, COL4_W, text, binding, this::startEditingKeyBinding);
|
var bindingButton = new KeyBindingWidget(col4(3), y, COL4_W, text, binding, this::startEditingKeyBinding);
|
||||||
bindingButton.linkButtonToBoundState(modifierButton);
|
bindingButton.linkButtonToBoundState(modifierButton);
|
||||||
|
|
||||||
generateLeftSideText(y, elements, text);
|
generateLeftSideText(y, elements, text);
|
||||||
@@ -182,12 +203,12 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
allKeyBindings.add(bindingButton);
|
allKeyBindings.add(bindingButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> void generateCycleOptionRow(final int y, final List<GuiEventListener> elements, final Component text, final List<Option<T>> options, final T initialValue, final Consumer<T> onValueChanged) {
|
private static <T> void generateCycleOptionRow(int y, List<GuiEventListener> elements, Component text, List<Option<T>> options, T initialValue, Consumer<T> onValueChanged) {
|
||||||
generateLeftSideText(y, elements, text);
|
generateLeftSideText(y, elements, text);
|
||||||
elements.add(Option.button(col2(1), y, COL2_W, text, options, initialValue, onValueChanged));
|
elements.add(Option.button(col2(1), y, COL2_W, text, options, initialValue, onValueChanged));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void generateBooleanOptionRow(final int y, final List<GuiEventListener> elements, final Component text, final boolean initialValue, final BooleanConsumer onValueChanged) {
|
private static void generateBooleanOptionRow(int y, List<GuiEventListener> elements, Component text, boolean initialValue, BooleanConsumer onValueChanged) {
|
||||||
generateLeftSideText(y, elements, text);
|
generateLeftSideText(y, elements, text);
|
||||||
elements.add(CycleButton.onOffBuilder()
|
elements.add(CycleButton.onOffBuilder()
|
||||||
.displayOnlyValue()
|
.displayOnlyValue()
|
||||||
@@ -195,7 +216,7 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
.create(col2(1), y, COL2_W, 20, text, (btn, newValue) -> onValueChanged.accept(newValue.booleanValue())));
|
.create(col2(1), y, COL2_W, 20, text, (btn, newValue) -> onValueChanged.accept(newValue.booleanValue())));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void generateLeftSideText(final int y, final List<GuiEventListener> elements, final Component text) {
|
private static void generateLeftSideText(int y, List<GuiEventListener> elements, Component text) {
|
||||||
elements.add(new TextWidget(col2(0), y, COL2_W - TEXT_PADDING_RIGHT, text));
|
elements.add(new TextWidget(col2(0), y, COL2_W - TEXT_PADDING_RIGHT, text));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,15 +227,15 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
private final List<KeyBindingWidget> allKeyBindings = new ArrayList<>();
|
private final List<KeyBindingWidget> allKeyBindings = new ArrayList<>();
|
||||||
|
|
||||||
@SuppressWarnings("DataFlowIssue")
|
@SuppressWarnings("DataFlowIssue")
|
||||||
public BetterControlsScreen(final Minecraft mc, @Nullable final Screen parentScreen) {
|
public BetterControlsScreen(@Nullable Screen parentScreen) {
|
||||||
super(parentScreen, mc.options, TITLE);
|
super(parentScreen, Minecraft.getInstance().options, TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addContents() {
|
protected void addContents() {
|
||||||
allKeyBindings.clear();
|
allKeyBindings.clear();
|
||||||
|
|
||||||
final List<GuiEventListener> elements = new ArrayList<>();
|
List<GuiEventListener> elements = new ArrayList<>();
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
|
||||||
elements.add(new TextWidget(0, y, ROW_WIDTH, ROW_HEIGHT, text("Sprinting"), CENTER));
|
elements.add(new TextWidget(0, y, ROW_WIDTH, ROW_HEIGHT, text("Sprinting"), CENTER));
|
||||||
@@ -223,6 +244,9 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
elements.add(new TextWidget(0, y, ROW_WIDTH, ROW_HEIGHT, text("Sneaking"), CENTER));
|
elements.add(new TextWidget(0, y, ROW_WIDTH, ROW_HEIGHT, text("Sneaking"), CENTER));
|
||||||
y = generateSneakingOptions(y + ROW_HEIGHT, elements) + TITLE_MARGIN_TOP;
|
y = generateSneakingOptions(y + ROW_HEIGHT, elements) + TITLE_MARGIN_TOP;
|
||||||
|
|
||||||
|
elements.add(new TextWidget(0, y, ROW_WIDTH, ROW_HEIGHT, text("Gliding"), CENTER));
|
||||||
|
y = generateGlidingOptions(y + ROW_HEIGHT, elements) + TITLE_MARGIN_TOP;
|
||||||
|
|
||||||
elements.add(new TextWidget(0, y, ROW_WIDTH, ROW_HEIGHT, text("Flying"), CENTER));
|
elements.add(new TextWidget(0, y, ROW_WIDTH, ROW_HEIGHT, text("Flying"), CENTER));
|
||||||
y = generateFlightOptions(y + ROW_HEIGHT, elements) + TITLE_MARGIN_TOP;
|
y = generateFlightOptions(y + ROW_HEIGHT, elements) + TITLE_MARGIN_TOP;
|
||||||
|
|
||||||
@@ -249,7 +273,7 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
BetterControlsCommon.getConfig().save();
|
BetterControlsCommon.getConfig().save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startEditingKeyBinding(final KeyBindingWidget widget) {
|
private void startEditingKeyBinding(KeyBindingWidget widget) {
|
||||||
if (editingKeyBinding != null) {
|
if (editingKeyBinding != null) {
|
||||||
editingKeyBinding.stopEditing();
|
editingKeyBinding.stopEditing();
|
||||||
}
|
}
|
||||||
@@ -258,7 +282,7 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mouseClicked(final double mouseX, final double mouseY, final int button) {
|
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||||
if (editingKeyBinding != null) {
|
if (editingKeyBinding != null) {
|
||||||
editingKeyBinding.bindAndStopEditing(InputConstants.Type.MOUSE.getOrCreate(button));
|
editingKeyBinding.bindAndStopEditing(InputConstants.Type.MOUSE.getOrCreate(button));
|
||||||
onKeyBindingEditingFinished();
|
onKeyBindingEditingFinished();
|
||||||
@@ -270,7 +294,7 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean keyPressed(final int keyCode, final int scanCode, final int modifiers) {
|
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||||
if (editingKeyBinding != null) {
|
if (editingKeyBinding != null) {
|
||||||
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
||||||
editingKeyBinding.bindAndStopEditing(InputConstants.UNKNOWN);
|
editingKeyBinding.bindAndStopEditing(InputConstants.UNKNOWN);
|
||||||
@@ -291,7 +315,7 @@ public class BetterControlsScreen extends OptionsSubScreen {
|
|||||||
editingKeyBinding = null;
|
editingKeyBinding = null;
|
||||||
KeyMapping.resetMapping();
|
KeyMapping.resetMapping();
|
||||||
|
|
||||||
for (final KeyBindingWidget widget : allKeyBindings) {
|
for (KeyBindingWidget widget : allKeyBindings) {
|
||||||
widget.updateKeyBindingText();
|
widget.updateKeyBindingText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,12 +9,11 @@ import net.minecraft.client.gui.components.Renderable;
|
|||||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
import net.minecraft.client.gui.narration.NarratableEntry;
|
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import java.util.ArrayList;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import static java.util.stream.Collectors.toMap;
|
||||||
|
|
||||||
public final class OptionListWidget extends ContainerObjectSelectionList<Entry> {
|
public final class OptionListWidget extends ContainerObjectSelectionList<Entry> {
|
||||||
public static final int ROW_WIDTH = 408;
|
public static final int ROW_WIDTH = 408;
|
||||||
@@ -25,38 +24,42 @@ public final class OptionListWidget extends ContainerObjectSelectionList<Entry>
|
|||||||
public static final int COL2_W = (ROW_WIDTH / 2) - ROW_HORIZONTAL_PADDING;
|
public static final int COL2_W = (ROW_WIDTH / 2) - ROW_HORIZONTAL_PADDING;
|
||||||
public static final int COL4_W = (ROW_WIDTH / 4) - ROW_HORIZONTAL_PADDING;
|
public static final int COL4_W = (ROW_WIDTH / 4) - ROW_HORIZONTAL_PADDING;
|
||||||
|
|
||||||
public static int col2(final int column) {
|
public static int col2(int column) {
|
||||||
return (column * ROW_WIDTH) / 2;
|
return (column * ROW_WIDTH) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int col4(final int column) {
|
public static int col4(int column) {
|
||||||
return (column * ROW_WIDTH) / 4;
|
return (column * ROW_WIDTH) / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Offset getElementOffset(final GuiEventListener element) {
|
private static Offset getElementOffset(GuiEventListener element) {
|
||||||
if (element instanceof final OptionWidget widget) {
|
if (element instanceof OptionWidget widget) {
|
||||||
return new Offset(widget.getX(), widget.getY());
|
return new Offset(widget.getX(), widget.getY(), widget.getHeight());
|
||||||
}
|
}
|
||||||
else if (element instanceof final AbstractWidget widget) {
|
else if (element instanceof AbstractWidget widget) {
|
||||||
return new Offset(widget.getX(), widget.getY());
|
return new Offset(widget.getX(), widget.getY(), widget.getHeight());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new Offset(0, 0);
|
return new Offset(0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OptionWidget extends GuiEventListener, Renderable {
|
public interface OptionWidget extends GuiEventListener, Renderable {
|
||||||
int getX();
|
|
||||||
int getY();
|
|
||||||
void setX(int x);
|
void setX(int x);
|
||||||
|
int getX();
|
||||||
|
|
||||||
void setY(int y);
|
void setY(int y);
|
||||||
|
int getY();
|
||||||
|
|
||||||
|
int getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Offset(int x, int y) {}
|
private record Offset(int x, int y, int height) {}
|
||||||
|
|
||||||
public OptionListWidget(final int width, final int height, final int top, final int innerHeight, final List<GuiEventListener> widgets) {
|
@SuppressWarnings("ThisEscapedInObjectConstruction")
|
||||||
|
public OptionListWidget(int width, int height, int top, int innerHeight, List<GuiEventListener> widgets) {
|
||||||
super(Minecraft.getInstance(), width, height, top, innerHeight);
|
super(Minecraft.getInstance(), width, height, top, innerHeight);
|
||||||
addEntry(new Entry(widgets));
|
addEntry(new Entry(this, widgets));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,54 +73,73 @@ public final class OptionListWidget extends ContainerObjectSelectionList<Entry>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getScrollbarPosition() {
|
protected int scrollBarX() {
|
||||||
return (width + ROW_WIDTH) / 2 + 4;
|
return (width + ROW_WIDTH) / 2 + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mouseScrolled(final double x, final double y, final double xAmount, final double yAmount) {
|
public boolean mouseScrolled(double x, double y, double xAmount, double yAmount) {
|
||||||
setScrollAmount(getScrollAmount() - yAmount * SCROLL_MULTIPLIER);
|
setScrollAmount(scrollAmount() - yAmount * SCROLL_MULTIPLIER);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void ensureVisible(@NotNull Entry entry) {
|
||||||
|
// Scrolling to focused item is implemented in Entry.
|
||||||
|
}
|
||||||
|
|
||||||
protected static final class Entry extends ContainerObjectSelectionList.Entry<Entry> {
|
protected static final class Entry extends ContainerObjectSelectionList.Entry<Entry> {
|
||||||
|
private final OptionListWidget parentWidget;
|
||||||
private final List<GuiEventListener> elements;
|
private final List<GuiEventListener> elements;
|
||||||
private final List<NarratableEntry> narratables;
|
private final List<NarratableEntry> narratables;
|
||||||
private final Map<GuiEventListener, Offset> offsets;
|
private final Map<GuiEventListener, Offset> offsets;
|
||||||
|
|
||||||
public Entry(final List<GuiEventListener> elements) {
|
public Entry(OptionListWidget parentWidget, List<GuiEventListener> elements) {
|
||||||
this.elements = new ArrayList<>(elements);
|
this.parentWidget = parentWidget;
|
||||||
this.narratables = elements.stream().filter(e -> e instanceof NarratableEntry).map(e -> (NarratableEntry)e).collect(Collectors.toList());
|
this.elements = List.copyOf(elements);
|
||||||
this.offsets = elements.stream().collect(Collectors.toMap(Function.identity(), OptionListWidget::getElementOffset));
|
this.narratables = elements.stream().filter(e -> e instanceof NarratableEntry).map(e -> (NarratableEntry)e).toList();
|
||||||
|
this.offsets = elements.stream().collect(toMap(Function.identity(), OptionListWidget::getElementOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(@Nullable GuiEventListener element) {
|
||||||
|
super.setFocused(element);
|
||||||
|
|
||||||
|
if (Minecraft.getInstance().getLastInputType().isKeyboard()) {
|
||||||
|
Offset offset = offsets.get(element);
|
||||||
|
if (offset != null) {
|
||||||
|
parentWidget.setScrollAmount(offset.y + (offset.height * 0.5F) - (parentWidget.getHeight() * 0.5F) + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<? extends GuiEventListener> children() {
|
public List<? extends GuiEventListener> children() {
|
||||||
return Collections.unmodifiableList(elements);
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<? extends NarratableEntry> narratables() {
|
public List<? extends NarratableEntry> narratables() {
|
||||||
return Collections.unmodifiableList(narratables);
|
return narratables;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(final @NotNull GuiGraphics graphics, final int index, final int y, final int x, final int entryWidth, final int entryHeight, final int mouseX, final int mouseY, final boolean hovered, final float tickDelta) {
|
public void render(@NotNull GuiGraphics graphics, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
|
||||||
for (final GuiEventListener element : elements) {
|
for (GuiEventListener element : elements) {
|
||||||
final Offset offset = offsets.get(element);
|
Offset offset = offsets.get(element);
|
||||||
|
|
||||||
if (element instanceof final AbstractWidget widget) {
|
if (element instanceof AbstractWidget widget) {
|
||||||
widget.setX(x + offset.x);
|
widget.setX(x + offset.x);
|
||||||
widget.setY(y + offset.y);
|
widget.setY(y + offset.y);
|
||||||
}
|
}
|
||||||
else if (element instanceof final OptionWidget widget) {
|
else if (element instanceof OptionWidget widget) {
|
||||||
widget.setX(x + offset.x);
|
widget.setX(x + offset.x);
|
||||||
widget.setY(y + offset.y);
|
widget.setY(y + offset.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element instanceof final Renderable renderable) {
|
if (element instanceof Renderable renderable) {
|
||||||
renderable.render(graphics, mouseX, mouseY, tickDelta);
|
renderable.render(graphics, mouseX, mouseY, tickDelta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,12 @@ package chylex.bettercontrols.gui.elements;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import net.minecraft.client.gui.components.AbstractSliderButton;
|
import net.minecraft.client.gui.components.AbstractSliderButton;
|
||||||
|
import net.minecraft.client.gui.navigation.CommonInputs;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.chat.MutableComponent;
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public final class DiscreteValueSliderWidget<T> extends AbstractSliderButton {
|
public final class DiscreteValueSliderWidget<T> extends AbstractSliderButton {
|
||||||
@@ -14,20 +16,24 @@ public final class DiscreteValueSliderWidget<T> extends AbstractSliderButton {
|
|||||||
private final Consumer<T> onChanged;
|
private final Consumer<T> onChanged;
|
||||||
private T selectedValue;
|
private T selectedValue;
|
||||||
|
|
||||||
public DiscreteValueSliderWidget(final int x, final int y, final int width, final int height, final Component narration, final ImmutableList<Option<T>> options, final T selectedValue, final Consumer<T> onChanged) {
|
public DiscreteValueSliderWidget(int x, int y, int width, int height, Component narration, ImmutableList<Option<T>> options, T selectedValue, Consumer<T> onChanged) {
|
||||||
super(x, y, width, height, Option.find(options, selectedValue).text(), options.indexOf(Option.find(options, selectedValue)) / (options.size() - 1.0));
|
super(x, y, width, height, Option.find(options, selectedValue).text(), getOptionValue(options, options.indexOf(Option.find(options, selectedValue))));
|
||||||
this.narration = narration;
|
this.narration = narration;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.selectedValue = selectedValue;
|
this.selectedValue = selectedValue;
|
||||||
this.onChanged = onChanged;
|
this.onChanged = onChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscreteValueSliderWidget(final int x, final int y, final int width, final Component narration, final ImmutableList<Option<T>> options, final T selectedValue, final Consumer<T> onChanged) {
|
public DiscreteValueSliderWidget(int x, int y, int width, Component narration, ImmutableList<Option<T>> options, T selectedValue, Consumer<T> onChanged) {
|
||||||
this(x, y, width, 20, narration, options, selectedValue, onChanged);
|
this(x, y, width, 20, narration, options, selectedValue, onChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Option<T> getSelectedOption() {
|
public Option<T> getSelectedOption() {
|
||||||
return options.get(Mth.floor(Mth.clampedLerp(0.0, options.size() - 1.0, value)));
|
return options.get(getSelectedOptionIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSelectedOptionIndex() {
|
||||||
|
return Mth.floor(Mth.clampedLerp(0.0, options.size() - 1.0, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -35,9 +41,32 @@ public final class DiscreteValueSliderWidget<T> extends AbstractSliderButton {
|
|||||||
setMessage(getSelectedOption().text());
|
setMessage(getSelectedOption().text());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||||
|
if (CommonInputs.selected(keyCode)) {
|
||||||
|
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyCode == GLFW.GLFW_KEY_LEFT || keyCode == GLFW.GLFW_KEY_RIGHT) {
|
||||||
|
int newOptionIndex = keyCode == GLFW.GLFW_KEY_LEFT
|
||||||
|
? getSelectedOptionIndex() - 1
|
||||||
|
: getSelectedOptionIndex() + 1;
|
||||||
|
|
||||||
|
if (newOptionIndex >= 0 && newOptionIndex < options.size()) {
|
||||||
|
value = getOptionValue(options, newOptionIndex);
|
||||||
|
applyValue();
|
||||||
|
updateMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void applyValue() {
|
protected void applyValue() {
|
||||||
final T newSelectedValue = getSelectedOption().value();
|
T newSelectedValue = getSelectedOption().value();
|
||||||
|
|
||||||
if (selectedValue != newSelectedValue) {
|
if (selectedValue != newSelectedValue) {
|
||||||
selectedValue = newSelectedValue;
|
selectedValue = newSelectedValue;
|
||||||
@@ -50,4 +79,8 @@ public final class DiscreteValueSliderWidget<T> extends AbstractSliderButton {
|
|||||||
protected MutableComponent createNarrationMessage() {
|
protected MutableComponent createNarrationMessage() {
|
||||||
return Component.translatable("gui.narrate.slider", narration.plainCopy().append(" ").append(getMessage()));
|
return Component.translatable("gui.narrate.slider", narration.plainCopy().append(" ").append(getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> double getOptionValue(ImmutableList<Option<T>> options, int optionIndex) {
|
||||||
|
return optionIndex / (options.size() - 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ public final class KeyBindingWidget extends Button {
|
|||||||
private final Consumer<KeyBindingWidget> onEditingStarted;
|
private final Consumer<KeyBindingWidget> onEditingStarted;
|
||||||
private boolean isEditing;
|
private boolean isEditing;
|
||||||
|
|
||||||
public KeyBindingWidget(final int x, final int y, final int width, final int height, final Component bindingName, final KeyMapping binding, final Consumer<KeyBindingWidget> onEditingStarted) {
|
public KeyBindingWidget(int x, int y, int width, int height, Component bindingName, KeyMapping binding, Consumer<KeyBindingWidget> onEditingStarted) {
|
||||||
super(x, y, width, height, Component.empty(), btn -> {}, DEFAULT_NARRATION);
|
super(x, y, width, height, Component.empty(), btn -> {}, DEFAULT_NARRATION);
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
this.bindingName = bindingName;
|
this.bindingName = bindingName;
|
||||||
@@ -30,11 +30,11 @@ public final class KeyBindingWidget extends Button {
|
|||||||
updateKeyBindingText();
|
updateKeyBindingText();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyBindingWidget(final int x, final int y, final int width, final Component bindingName, final KeyMapping binding, final Consumer<KeyBindingWidget> onEditingStarted) {
|
public KeyBindingWidget(int x, int y, int width, Component bindingName, KeyMapping binding, Consumer<KeyBindingWidget> onEditingStarted) {
|
||||||
this(x, y, width, 20, bindingName, binding, onEditingStarted);
|
this(x, y, width, 20, bindingName, binding, onEditingStarted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void linkButtonToBoundState(final AbstractButton button) {
|
public void linkButtonToBoundState(AbstractButton button) {
|
||||||
linkedButtons.add(button);
|
linkedButtons.add(button);
|
||||||
button.active = !binding.isUnbound();
|
button.active = !binding.isUnbound();
|
||||||
}
|
}
|
||||||
@@ -52,11 +52,11 @@ public final class KeyBindingWidget extends Button {
|
|||||||
updateKeyBindingText();
|
updateKeyBindingText();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindAndStopEditing(final InputConstants.Key key) {
|
public void bindAndStopEditing(InputConstants.Key key) {
|
||||||
binding.setKey(key);
|
binding.setKey(key);
|
||||||
stopEditing();
|
stopEditing();
|
||||||
|
|
||||||
for (final AbstractButton button : linkedButtons) {
|
for (AbstractButton button : linkedButtons) {
|
||||||
button.active = !binding.isUnbound();
|
button.active = !binding.isUnbound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ public final class KeyBindingWidget extends Button {
|
|||||||
boolean hasConflict = false;
|
boolean hasConflict = false;
|
||||||
|
|
||||||
if (!binding.isUnbound()) {
|
if (!binding.isUnbound()) {
|
||||||
for (final KeyMapping other : Minecraft.getInstance().options.keyMappings) {
|
for (KeyMapping other : Minecraft.getInstance().options.keyMappings) {
|
||||||
if (binding != other && binding.same(other)) {
|
if (binding != other && binding.same(other)) {
|
||||||
hasConflict = true;
|
hasConflict = true;
|
||||||
break;
|
break;
|
||||||
|
@@ -7,11 +7,11 @@ import java.util.Objects;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public record Option<T>(T value, Component text) {
|
public record Option<T>(T value, Component text) {
|
||||||
public static <T> Option<T> find(final List<Option<T>> options, final T value) {
|
public static <T> Option<T> find(List<Option<T>> options, T value) {
|
||||||
return options.stream().filter(it -> Objects.equals(it.value, value)).findFirst().orElseGet(() -> options.get(0));
|
return options.stream().filter(it -> Objects.equals(it.value, value)).findFirst().orElseGet(() -> options.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> CycleButton<Option<T>> button(final int x, final int y, final int width, final Component text, final List<Option<T>> options, final T initialValue, final Consumer<T> onValueChanged) {
|
public static <T> CycleButton<Option<T>> button(int x, int y, int width, Component text, List<Option<T>> options, T initialValue, Consumer<T> onValueChanged) {
|
||||||
return CycleButton.<Option<T>>builder(Option::text)
|
return CycleButton.<Option<T>>builder(Option::text)
|
||||||
.displayOnlyValue()
|
.displayOnlyValue()
|
||||||
.withValues(options)
|
.withValues(options)
|
||||||
|
@@ -13,7 +13,7 @@ public final class TextWidget implements OptionWidget {
|
|||||||
public static final int LEFT = 0;
|
public static final int LEFT = 0;
|
||||||
public static final int CENTER = 1;
|
public static final int CENTER = 1;
|
||||||
|
|
||||||
public static final int WHITE = 0xFF_FF_FF;
|
public static final int WHITE = 0xFF_FF_FF_FF;
|
||||||
|
|
||||||
private final Component text;
|
private final Component text;
|
||||||
private int x;
|
private int x;
|
||||||
@@ -22,7 +22,7 @@ public final class TextWidget implements OptionWidget {
|
|||||||
private final int height;
|
private final int height;
|
||||||
private final int align;
|
private final int align;
|
||||||
|
|
||||||
public TextWidget(final int x, final int y, final int width, final int height, final Component text, final int align) {
|
public TextWidget(int x, int y, int width, int height, Component text, int align) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
@@ -31,36 +31,41 @@ public final class TextWidget implements OptionWidget {
|
|||||||
this.align = align;
|
this.align = align;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextWidget(final int x, final int y, final int width, final Component text, final int align) {
|
public TextWidget(int x, int y, int width, Component text, int align) {
|
||||||
this(x, y, width, 20, text, align);
|
this(x, y, width, 20, text, align);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextWidget(final int x, final int y, final int width, final Component text) {
|
public TextWidget(int x, int y, int width, Component text) {
|
||||||
this(x, y, width, 20, text, LEFT);
|
this(x, y, width, 20, text, LEFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setX(int x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getX() {
|
public int getX() {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setY(int y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getY() {
|
public int getY() {
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setX(final int x) {
|
public int getHeight() {
|
||||||
this.x = x;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setY(final int y) {
|
public void setFocused(boolean focused) {}
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFocused(final boolean focused) {}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFocused() {
|
public boolean isFocused() {
|
||||||
@@ -68,13 +73,13 @@ public final class TextWidget implements OptionWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(final @NotNull GuiGraphics graphics, final int mouseX, final int mouseY, final float delta) {
|
public void render(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float delta) {
|
||||||
final Font textRenderer = Minecraft.getInstance().font;
|
Font textRenderer = Minecraft.getInstance().font;
|
||||||
final List<FormattedCharSequence> lines = textRenderer.split(text, width);
|
List<FormattedCharSequence> lines = textRenderer.split(text, width);
|
||||||
final int lineHeight = textRenderer.lineHeight + 1;
|
final int lineHeight = textRenderer.lineHeight + 1;
|
||||||
|
|
||||||
final int finalX = align == CENTER ? x + (width / 2) - (lines.stream().mapToInt(textRenderer::width).max().orElse(0) / 2) : x;
|
int finalX = align == CENTER ? x + (width / 2) - (lines.stream().mapToInt(textRenderer::width).max().orElse(0) / 2) : x;
|
||||||
final int finalY = y + (height / 2) - (lineHeight * lines.size() / 2) + 1;
|
int finalY = y + (height / 2) - (lineHeight * lines.size() / 2) + 1;
|
||||||
|
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
graphics.drawString(textRenderer, lines.get(i), finalX, finalY + (i * lineHeight), WHITE);
|
graphics.drawString(textRenderer, lines.get(i), finalX, finalY + (i * lineHeight), WHITE);
|
||||||
|
@@ -10,11 +10,11 @@ public class KeyBindingWithModifier extends KeyMapping {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private ModifierKey modifier = null;
|
private ModifierKey modifier = null;
|
||||||
|
|
||||||
public KeyBindingWithModifier(final String translationKey) {
|
public KeyBindingWithModifier(String translationKey) {
|
||||||
super(translationKey, Type.KEYSYM, -1, CATEGORY);
|
super(translationKey, Type.KEYSYM, -1, CATEGORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setModifier(@Nullable final ModifierKey modifier) {
|
public void setModifier(@Nullable ModifierKey modifier) {
|
||||||
this.modifier = modifier;
|
this.modifier = modifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,13 +26,13 @@ public enum ModifierKey {
|
|||||||
|
|
||||||
public final int id;
|
public final int id;
|
||||||
|
|
||||||
ModifierKey(final int id) {
|
ModifierKey(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean isPressed();
|
public abstract boolean isPressed();
|
||||||
|
|
||||||
public static ModifierKey getById(final int id) {
|
public static ModifierKey getById(int id) {
|
||||||
return switch (id) {
|
return switch (id) {
|
||||||
case 0 -> CONTROL;
|
case 0 -> CONTROL;
|
||||||
case 1 -> SHIFT;
|
case 1 -> SHIFT;
|
||||||
|
@@ -6,15 +6,19 @@ public class ToggleTracker {
|
|||||||
protected final KeyMapping bindingToggle;
|
protected final KeyMapping bindingToggle;
|
||||||
protected final KeyMapping bindingReset;
|
protected final KeyMapping bindingReset;
|
||||||
|
|
||||||
protected boolean isToggled;
|
private boolean isToggled;
|
||||||
|
|
||||||
private boolean waitForRelease;
|
private boolean waitForRelease;
|
||||||
private boolean hasToggledWhileHoldingReset;
|
private boolean hasToggledWhileHoldingReset;
|
||||||
private boolean skipNextToggle;
|
private boolean skipNextToggle;
|
||||||
|
|
||||||
public ToggleTracker(final KeyMapping bindingToggle, final KeyMapping bindingReset) {
|
protected ToggleTracker(KeyMapping bindingToggle, KeyMapping bindingReset, boolean initialState) {
|
||||||
this.bindingToggle = bindingToggle;
|
this.bindingToggle = bindingToggle;
|
||||||
this.bindingReset = bindingReset;
|
this.bindingReset = bindingReset;
|
||||||
|
this.isToggled = initialState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToggleTracker(KeyMapping bindingToggle, KeyMapping bindingReset) {
|
||||||
|
this(bindingToggle, bindingReset, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -42,7 +46,7 @@ public class ToggleTracker {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public boolean tick() {
|
public boolean tick() {
|
||||||
final boolean isHoldingReset = isResetKeyPressed();
|
boolean isHoldingReset = isResetKeyPressed();
|
||||||
|
|
||||||
if (bindingToggle.isDown()) {
|
if (bindingToggle.isDown()) {
|
||||||
if (!waitForRelease) {
|
if (!waitForRelease) {
|
||||||
|
@@ -1,36 +1,36 @@
|
|||||||
package chylex.bettercontrols.input;
|
package chylex.bettercontrols.input;
|
||||||
|
|
||||||
import chylex.bettercontrols.mixin.AccessKeyBindingFields;
|
import chylex.bettercontrols.Mixins;
|
||||||
import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
|
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
|
import net.minecraft.client.OptionInstance;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public final class ToggleTrackerForStickyKey extends ToggleTracker {
|
public final class ToggleTrackerForStickyKey extends ToggleTracker {
|
||||||
private static final Set<KeyMapping> enabledOverrides = new HashSet<>();
|
private static final Set<KeyMapping> enabledOverrides = new HashSet<>();
|
||||||
|
|
||||||
public static boolean isOverrideEnabled(final KeyMapping binding) {
|
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
|
||||||
|
public static boolean isOverrideEnabled(KeyMapping binding) {
|
||||||
return enabledOverrides.contains(binding);
|
return enabledOverrides.contains(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BooleanConsumer setToggleState;
|
private final OptionInstance<Boolean> toggleOption;
|
||||||
|
|
||||||
public ToggleTrackerForStickyKey(final KeyMapping bindingToggle, final KeyMapping bindingStickyReset, final BooleanConsumer setToggleState) {
|
public ToggleTrackerForStickyKey(KeyMapping bindingToggle, KeyMapping bindingStickyReset, OptionInstance<Boolean> toggleOption) {
|
||||||
super(bindingToggle, bindingStickyReset);
|
super(bindingToggle, bindingStickyReset, toggleOption.get().booleanValue());
|
||||||
this.setToggleState = setToggleState;
|
this.toggleOption = toggleOption;
|
||||||
this.setToggleState.accept(false);
|
|
||||||
enabledOverrides.add(bindingStickyReset);
|
enabledOverrides.add(bindingStickyReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tick() {
|
public boolean tick() {
|
||||||
final boolean isToggled = super.tick();
|
boolean isToggled = super.tick();
|
||||||
setToggleState.accept(isToggled);
|
toggleOption.set(Boolean.valueOf(isToggled));
|
||||||
return isToggled;
|
return isToggled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isResetKeyPressed() {
|
protected boolean isResetKeyPressed() {
|
||||||
return ((AccessKeyBindingFields)bindingReset).isPressedField();
|
return Mixins.keyMappingFields(bindingReset).isPressedField();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,8 @@ import org.spongepowered.asm.mixin.gen.Accessor;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Mixin(KeyMapping.class)
|
@Mixin(KeyMapping.class)
|
||||||
public interface AccessKeyBindingFields {
|
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
|
||||||
|
public interface AccessKeyMappingFields {
|
||||||
@Accessor("CATEGORY_SORT_ORDER")
|
@Accessor("CATEGORY_SORT_ORDER")
|
||||||
static Map<String, Integer> getCategoryOrderMap() {
|
static Map<String, Integer> getCategoryOrderMap() {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
@@ -7,11 +7,11 @@ import org.spongepowered.asm.mixin.gen.Accessor;
|
|||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
@Mixin(ToggleKeyMapping.class)
|
@Mixin(ToggleKeyMapping.class)
|
||||||
public interface AccessStickyKeyBindingStateGetter {
|
public interface AccessToggleKeyMappingFields {
|
||||||
@Accessor
|
@Accessor
|
||||||
BooleanSupplier getNeedsToggle();
|
BooleanSupplier getNeedsToggle();
|
||||||
|
|
||||||
@Accessor
|
@Accessor
|
||||||
@Mutable
|
@Mutable
|
||||||
void setNeedsToggle(final BooleanSupplier toggleGetter);
|
void setNeedsToggle(BooleanSupplier toggleGetter);
|
||||||
}
|
}
|
@@ -1,26 +1,27 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.Mixins;
|
||||||
import chylex.bettercontrols.player.PlayerTicker;
|
import chylex.bettercontrols.player.PlayerTicker;
|
||||||
import net.minecraft.client.Minecraft;
|
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||||
import net.minecraft.client.player.AbstractClientPlayer;
|
import net.minecraft.client.player.AbstractClientPlayer;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Slice;
|
|
||||||
|
|
||||||
@Mixin(AbstractClientPlayer.class)
|
@Mixin(AbstractClientPlayer.class)
|
||||||
public abstract class HookClientPlayerFOV {
|
public abstract class HookClientPlayerFOV {
|
||||||
@Redirect(
|
@ModifyExpressionValue(
|
||||||
method = "getFieldOfViewModifier",
|
method = "getFieldOfViewModifier",
|
||||||
at = @At(value = "INVOKE", target = "Ljava/lang/Float;isNaN(F)Z"),
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Abilities;getWalkingSpeed()F")
|
||||||
slice = @Slice(
|
|
||||||
from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Abilities;getWalkingSpeed()F"),
|
|
||||||
to = @At(value = "INVOKE", target = "Ljava/lang/Float;isInfinite(F)Z")
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
private boolean resetFOV(final float movementSpeed) {
|
private float overrideWalkingSpeed(float walkingSpeed) {
|
||||||
final LocalPlayer player = Minecraft.getInstance().player;
|
AbstractClientPlayer me = Mixins.me(this);
|
||||||
return (player != null && PlayerTicker.get(player).shouldResetFOV(player)) || Float.isNaN(movementSpeed);
|
|
||||||
|
if (me instanceof LocalPlayer localPlayer && PlayerTicker.shouldResetFOV(localPlayer)) {
|
||||||
|
return 0F;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return walkingSpeed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ package chylex.bettercontrols.mixin;
|
|||||||
|
|
||||||
import chylex.bettercontrols.player.PlayerTicker;
|
import chylex.bettercontrols.player.PlayerTicker;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.player.Input;
|
|
||||||
import net.minecraft.client.player.KeyboardInput;
|
import net.minecraft.client.player.KeyboardInput;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
@@ -12,16 +11,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
import static org.spongepowered.asm.mixin.injection.At.Shift.AFTER;
|
import static org.spongepowered.asm.mixin.injection.At.Shift.AFTER;
|
||||||
|
|
||||||
@Mixin(KeyboardInput.class)
|
@Mixin(KeyboardInput.class)
|
||||||
@SuppressWarnings("UnreachableCode")
|
@SuppressWarnings("MethodMayBeStatic")
|
||||||
public abstract class HookClientPlayerInputTick {
|
public abstract class HookClientPlayerInputTick {
|
||||||
@Inject(method = "tick(ZF)V", at = @At(value = "FIELD", target = "Lnet/minecraft/client/player/KeyboardInput;up:Z", ordinal = 0, shift = AFTER))
|
@Inject(
|
||||||
private void afterInputTick(final CallbackInfo info) {
|
method = "tick",
|
||||||
@SuppressWarnings("ConstantConditions")
|
at = @At(value = "FIELD", target = "Lnet/minecraft/client/player/KeyboardInput;keyPresses:Lnet/minecraft/world/entity/player/Input;", ordinal = 0, shift = AFTER)
|
||||||
final Input input = (Input)(Object)this;
|
)
|
||||||
final LocalPlayer player = Minecraft.getInstance().player;
|
private void afterInputTick(CallbackInfo info) {
|
||||||
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
|
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
PlayerTicker.get(player).afterInputAssignsPressingForward(input);
|
PlayerTicker.get(player).afterKeyboardInputAssigned(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.Mixins;
|
||||||
import chylex.bettercontrols.player.PlayerTicker;
|
import chylex.bettercontrols.player.PlayerTicker;
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
@@ -12,30 +13,26 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
import static org.spongepowered.asm.mixin.injection.At.Shift.AFTER;
|
import static org.spongepowered.asm.mixin.injection.At.Shift.AFTER;
|
||||||
|
|
||||||
@Mixin(LocalPlayer.class)
|
@Mixin(LocalPlayer.class)
|
||||||
@SuppressWarnings("UnreachableCode")
|
|
||||||
public abstract class HookClientPlayerTick extends AbstractClientPlayer {
|
public abstract class HookClientPlayerTick extends AbstractClientPlayer {
|
||||||
protected HookClientPlayerTick(final ClientLevel world, final GameProfile profile) {
|
protected HookClientPlayerTick(ClientLevel world, GameProfile profile) {
|
||||||
super(world, profile);
|
super(world, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "aiStep()V", at = @At("HEAD"))
|
@Inject(method = "aiStep()V", at = @At("HEAD"))
|
||||||
private void atHead(final CallbackInfo info) {
|
private void atHead(CallbackInfo info) {
|
||||||
@SuppressWarnings("ConstantConditions")
|
LocalPlayer player = Mixins.me(this);
|
||||||
final LocalPlayer player = (LocalPlayer)(Object)this;
|
|
||||||
PlayerTicker.get(player).atHead(player);
|
PlayerTicker.get(player).atHead(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "aiStep()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/Input;tick(ZF)V", ordinal = 0, shift = AFTER))
|
@Inject(method = "aiStep()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/ClientInput;tick()V", ordinal = 0, shift = AFTER))
|
||||||
private void afterInputTick(final CallbackInfo info) {
|
private void afterInputTick(CallbackInfo info) {
|
||||||
@SuppressWarnings("ConstantConditions")
|
LocalPlayer player = Mixins.me(this);
|
||||||
final LocalPlayer player = (LocalPlayer)(Object)this;
|
|
||||||
PlayerTicker.get(player).afterInputTick(player);
|
PlayerTicker.get(player).afterInputTick(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "aiStep()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/AbstractClientPlayer;aiStep()V", ordinal = 0, shift = AFTER))
|
@Inject(method = "aiStep()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/AbstractClientPlayer;aiStep()V", ordinal = 0, shift = AFTER))
|
||||||
private void afterSuperCall(final CallbackInfo info) {
|
private void afterSuperCall(CallbackInfo info) {
|
||||||
@SuppressWarnings("ConstantConditions")
|
LocalPlayer player = Mixins.me(this);
|
||||||
final LocalPlayer player = (LocalPlayer)(Object)this;
|
|
||||||
PlayerTicker.get(player).afterSuperCall(player);
|
PlayerTicker.get(player).afterSuperCall(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,24 +1,23 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.Mixins;
|
||||||
import chylex.bettercontrols.player.FlightHelper;
|
import chylex.bettercontrols.player.FlightHelper;
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.player.Abilities;
|
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Slice;
|
import org.spongepowered.asm.mixin.injection.Slice;
|
||||||
|
|
||||||
@Mixin(LocalPlayer.class)
|
@Mixin(LocalPlayer.class)
|
||||||
@SuppressWarnings({ "SameReturnValue", "UnreachableCode" })
|
|
||||||
public abstract class HookClientPlayerVerticalFlightSpeed extends LivingEntity {
|
public abstract class HookClientPlayerVerticalFlightSpeed extends LivingEntity {
|
||||||
protected HookClientPlayerVerticalFlightSpeed(final EntityType<? extends LivingEntity> type, final Level world) {
|
protected HookClientPlayerVerticalFlightSpeed(EntityType<? extends LivingEntity> type, Level world) {
|
||||||
super(type, world);
|
super(type, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(
|
@ModifyExpressionValue(
|
||||||
method = "aiStep",
|
method = "aiStep",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Abilities;getFlyingSpeed()F"),
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Abilities;getFlyingSpeed()F"),
|
||||||
slice = @Slice(
|
slice = @Slice(
|
||||||
@@ -26,9 +25,8 @@ public abstract class HookClientPlayerVerticalFlightSpeed extends LivingEntity {
|
|||||||
to = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;setDeltaMovement(Lnet/minecraft/world/phys/Vec3;)V")
|
to = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;setDeltaMovement(Lnet/minecraft/world/phys/Vec3;)V")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private float modifyVerticalFlightSpeed(final Abilities abilities) {
|
private float modifyVerticalFlightSpeed(float flyingSpeed) {
|
||||||
@SuppressWarnings("ConstantConditions")
|
LocalPlayer me = Mixins.me(this);
|
||||||
final LocalPlayer me = (LocalPlayer)(Object)this;
|
return flyingSpeed * FlightHelper.getVerticalSpeedMultiplier(me);
|
||||||
return abilities.getFlyingSpeed() * FlightHelper.getVerticalSpeedMultiplier(me);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
import chylex.bettercontrols.BetterControlsCommon;
|
import chylex.bettercontrols.BetterControlsCommon;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.Options;
|
import net.minecraft.client.Options;
|
||||||
@@ -10,16 +12,16 @@ import net.minecraft.client.gui.screens.options.controls.KeyBindsList.Entry;
|
|||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
@Mixin(KeyBindsList.class)
|
@Mixin(KeyBindsList.class)
|
||||||
|
@SuppressWarnings("MethodMayBeStatic")
|
||||||
public abstract class HookControlsListWidget extends ContainerObjectSelectionList<Entry> {
|
public abstract class HookControlsListWidget extends ContainerObjectSelectionList<Entry> {
|
||||||
public HookControlsListWidget(final Minecraft client, final int width, final int height, final int top, final int itemHeight) {
|
public HookControlsListWidget(Minecraft client, int width, int height, int top, int itemHeight) {
|
||||||
super(client, width, height, top, itemHeight);
|
super(client, width, height, top, itemHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Options;keyMappings:[Lnet/minecraft/client/KeyMapping;"))
|
@WrapOperation(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Options;keyMappings:[Lnet/minecraft/client/KeyMapping;"))
|
||||||
private KeyMapping[] excludeOwnKeyBindings(final Options options) {
|
private KeyMapping[] excludeOwnKeyBindings(Options options, Operation<KeyMapping[]> operation) {
|
||||||
return ArrayUtils.removeElements(options.keyMappings, BetterControlsCommon.getConfig().getAllKeyBindings());
|
return ArrayUtils.removeElements(operation.call(options), BetterControlsCommon.getConfig().getAllKeyBindings());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.Mixins;
|
||||||
import chylex.bettercontrols.gui.BetterControlsScreen;
|
import chylex.bettercontrols.gui.BetterControlsScreen;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.Options;
|
import net.minecraft.client.Options;
|
||||||
@@ -17,26 +18,22 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Mixin(ControlsScreen.class)
|
@Mixin(ControlsScreen.class)
|
||||||
@SuppressWarnings("UnreachableCode")
|
|
||||||
public abstract class HookControlsScreen extends OptionsSubScreen {
|
public abstract class HookControlsScreen extends OptionsSubScreen {
|
||||||
public HookControlsScreen(final Screen parentScreen, final Options options, final Component title) {
|
public HookControlsScreen(Screen parentScreen, Options options, Component title) {
|
||||||
super(parentScreen, options, title);
|
super(parentScreen, options, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "addOptions", at = @At("RETURN"))
|
@Inject(method = "addOptions", at = @At("RETURN"))
|
||||||
public void afterAddOptions(final CallbackInfo ci) {
|
public void afterAddOptions(CallbackInfo ci) {
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
final ControlsScreen screen = (ControlsScreen)(Object)this;
|
|
||||||
|
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
final MutableComponent buttonTitle = BetterControlsScreen.TITLE.plainCopy().append("...");
|
ControlsScreen screen = Mixins.me(this);
|
||||||
|
MutableComponent buttonTitle = BetterControlsScreen.TITLE.plainCopy().append("...");
|
||||||
list.addSmall(List.of(Button.builder(buttonTitle, btn -> showOptionsScreen(screen)).build()));
|
list.addSmall(List.of(Button.builder(buttonTitle, btn -> showOptionsScreen(screen)).build()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static void showOptionsScreen(final ControlsScreen screen) {
|
private static void showOptionsScreen(ControlsScreen screen) {
|
||||||
final Minecraft mc = Minecraft.getInstance();
|
Minecraft.getInstance().setScreen(new BetterControlsScreen(screen));
|
||||||
mc.setScreen(new BetterControlsScreen(mc, screen));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,18 +26,18 @@ public abstract class HookLoadGameOptions {
|
|||||||
public KeyMapping[] keyMappings;
|
public KeyMapping[] keyMappings;
|
||||||
|
|
||||||
@Inject(method = "load()V", at = @At("HEAD"))
|
@Inject(method = "load()V", at = @At("HEAD"))
|
||||||
private void load(final CallbackInfo info) {
|
private void load(CallbackInfo info) {
|
||||||
if (hasLoaded) {
|
if (hasLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final BetterControlsConfig config = BetterControlsCommon.getConfig();
|
BetterControlsConfig config = BetterControlsCommon.getConfig();
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLoaded = true;
|
hasLoaded = true;
|
||||||
keyMappings = ArrayUtils.addAll(keyMappings, config.getAllKeyBindings());
|
keyMappings = ArrayUtils.addAll(keyMappings, config.getAllKeyBindings());
|
||||||
AccessKeyBindingFields.getCategoryOrderMap().put(KeyBindingWithModifier.CATEGORY, Integer.valueOf(Integer.MAX_VALUE));
|
AccessKeyMappingFields.getCategoryOrderMap().put(KeyBindingWithModifier.CATEGORY, Integer.valueOf(Integer.MAX_VALUE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.player.FlightHelper;
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||||
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Slice;
|
||||||
|
|
||||||
|
@Mixin(LocalPlayer.class)
|
||||||
|
@SuppressWarnings("MethodMayBeStatic")
|
||||||
|
public abstract class HookPlayerGliding {
|
||||||
|
@ModifyExpressionValue(
|
||||||
|
method = "aiStep",
|
||||||
|
at = @At(value = "INVOKE:LAST", target = "Lnet/minecraft/world/entity/player/Input;jump()Z"),
|
||||||
|
slice = @Slice(
|
||||||
|
to = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;tryToStartFallFlying()Z")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private boolean shouldStartGliding(boolean isHoldingJump) {
|
||||||
|
return FlightHelper.shouldStartGliding(isHoldingJump);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,9 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.Mixins;
|
||||||
import chylex.bettercontrols.player.FlightHelper;
|
import chylex.bettercontrols.player.FlightHelper;
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
@@ -8,20 +11,16 @@ import net.minecraft.world.entity.player.Player;
|
|||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Slice;
|
import org.spongepowered.asm.mixin.injection.Slice;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
@Mixin(Player.class)
|
@Mixin(Player.class)
|
||||||
@SuppressWarnings({ "SameReturnValue", "UnreachableCode" })
|
|
||||||
public abstract class HookPlayerHorizontalFlightSpeed extends LivingEntity {
|
public abstract class HookPlayerHorizontalFlightSpeed extends LivingEntity {
|
||||||
protected HookPlayerHorizontalFlightSpeed(final EntityType<? extends LivingEntity> type, final Level world) {
|
protected HookPlayerHorizontalFlightSpeed(EntityType<? extends LivingEntity> type, Level world) {
|
||||||
super(type, world);
|
super(type, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("SimplifiableIfStatement")
|
@SuppressWarnings("SimplifiableIfStatement")
|
||||||
@Redirect(
|
@ModifyExpressionValue(
|
||||||
method = "getFlyingSpeed",
|
method = "getFlyingSpeed",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSprinting()Z"),
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSprinting()Z"),
|
||||||
slice = @Slice(
|
slice = @Slice(
|
||||||
@@ -29,23 +28,26 @@ public abstract class HookPlayerHorizontalFlightSpeed extends LivingEntity {
|
|||||||
to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Abilities;getFlyingSpeed()F")
|
to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Abilities;getFlyingSpeed()F")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private boolean disableVanillaSprintBoost(final Player player) {
|
private boolean disableVanillaSprintBoost(boolean isSprinting) {
|
||||||
if (player instanceof LocalPlayer) {
|
Player me = Mixins.me(this);
|
||||||
|
|
||||||
|
if (me instanceof LocalPlayer localPlayer && FlightHelper.isFlyingCreativeOrSpectator(localPlayer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return player.isSprinting();
|
return isSprinting;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "getFlyingSpeed", at = @At("RETURN"), cancellable = true)
|
@ModifyReturnValue(method = "getFlyingSpeed", at = @At("RETURN"))
|
||||||
private void modifyHorizontalFlyingSpeed(final CallbackInfoReturnable<Float> cir) {
|
private float modifyHorizontalFlyingSpeed(float flyingSpeed) {
|
||||||
@SuppressWarnings("ConstantConditions")
|
Player me = Mixins.me(this);
|
||||||
final Player me = (Player)(Object)this;
|
|
||||||
|
|
||||||
if (me instanceof final LocalPlayer localPlayer && localPlayer.getAbilities().flying) {
|
if (me instanceof LocalPlayer localPlayer && localPlayer.getAbilities().flying) {
|
||||||
final float multiplier = FlightHelper.getHorizontalSpeedMultiplier(localPlayer);
|
return flyingSpeed * FlightHelper.getHorizontalSpeedMultiplier(localPlayer);
|
||||||
cir.setReturnValue(Float.valueOf(cir.getReturnValueF() * multiplier));
|
}
|
||||||
|
else {
|
||||||
|
return flyingSpeed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.Mixins;
|
||||||
import chylex.bettercontrols.input.ToggleTrackerForStickyKey;
|
import chylex.bettercontrols.input.ToggleTrackerForStickyKey;
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
import net.minecraft.client.ToggleKeyMapping;
|
import net.minecraft.client.ToggleKeyMapping;
|
||||||
@@ -17,14 +18,14 @@ public abstract class HookStickyKeyBindingState extends KeyMapping {
|
|||||||
@Final
|
@Final
|
||||||
private BooleanSupplier needsToggle;
|
private BooleanSupplier needsToggle;
|
||||||
|
|
||||||
public HookStickyKeyBindingState(final String translationKey, final int code, final String category) {
|
public HookStickyKeyBindingState(String translationKey, int code, String category) {
|
||||||
super(translationKey, code, category);
|
super(translationKey, code, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "setDown", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "setDown", at = @At("HEAD"), cancellable = true)
|
||||||
public void setPressed(final boolean pressed, final CallbackInfo info) {
|
public void setPressed(boolean pressed, CallbackInfo info) {
|
||||||
if (ToggleTrackerForStickyKey.isOverrideEnabled(this)) {
|
if (ToggleTrackerForStickyKey.isOverrideEnabled(this)) {
|
||||||
((AccessKeyBindingFields)this).setPressedField(pressed);
|
Mixins.keyMappingFields(this).setPressedField(pressed);
|
||||||
info.cancel();
|
info.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
|
import chylex.bettercontrols.Mixins;
|
||||||
import net.minecraft.client.OptionInstance;
|
import net.minecraft.client.OptionInstance;
|
||||||
import net.minecraft.client.Options;
|
import net.minecraft.client.Options;
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
@@ -10,12 +11,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@Mixin(OptionInstance.class)
|
@Mixin(OptionInstance.class)
|
||||||
@SuppressWarnings("UnreachableCode")
|
|
||||||
public abstract class HookToggleOptionButtons {
|
public abstract class HookToggleOptionButtons {
|
||||||
@Inject(method = "createButton(Lnet/minecraft/client/Options;IIILjava/util/function/Consumer;)Lnet/minecraft/client/gui/components/AbstractWidget;", at = @At("RETURN"))
|
@Inject(method = "createButton(Lnet/minecraft/client/Options;IIILjava/util/function/Consumer;)Lnet/minecraft/client/gui/components/AbstractWidget;", at = @At("RETURN"))
|
||||||
private <T> void disableToggleOptions(final Options options, final int x, final int y, final int width, final Consumer<T> callback, final CallbackInfoReturnable<AbstractWidget> cir) {
|
private <T> void disableToggleOptions(Options options, int x, int y, int width, Consumer<T> callback, CallbackInfoReturnable<AbstractWidget> cir) {
|
||||||
@SuppressWarnings("ConstantConditions")
|
OptionInstance<?> me = Mixins.me(this);
|
||||||
final OptionInstance<?> me = (OptionInstance<?>)(Object)this;
|
|
||||||
|
|
||||||
if (me == options.toggleCrouch() || me == options.toggleSprint()) {
|
if (me == options.toggleCrouch() || me == options.toggleSprint()) {
|
||||||
cir.getReturnValue().active = false;
|
cir.getReturnValue().active = false;
|
||||||
|
@@ -1,10 +1,9 @@
|
|||||||
package chylex.bettercontrols.player;
|
package chylex.bettercontrols.player;
|
||||||
|
|
||||||
import chylex.bettercontrols.BetterControlsCommon;
|
|
||||||
import chylex.bettercontrols.config.BetterControlsConfig;
|
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
|
import static chylex.bettercontrols.BetterControlsCommon.getConfig;
|
||||||
|
|
||||||
public final class FlightHelper {
|
public final class FlightHelper {
|
||||||
private FlightHelper() {}
|
private FlightHelper() {}
|
||||||
@@ -15,36 +14,36 @@ public final class FlightHelper {
|
|||||||
return KEY_SPRINT.isDown();
|
return KEY_SPRINT.isDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BetterControlsConfig cfg() {
|
public static boolean shouldStartGliding(boolean isHoldingJump) {
|
||||||
return BetterControlsCommon.getConfig();
|
return getConfig().keyStartGlide.isDown() || (getConfig().doubleTapJumpToGlide && isHoldingJump);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isFlyingCreativeOrSpectator(final LocalPlayer player) {
|
public static boolean isFlyingCreativeOrSpectator(LocalPlayer player) {
|
||||||
return player.getAbilities().flying && (player.isCreative() || player.isSpectator());
|
return player.getAbilities().flying && (player.isCreative() || player.isSpectator());
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean shouldFlyOnGround(final LocalPlayer player) {
|
static boolean shouldFlyOnGround(LocalPlayer player) {
|
||||||
return cfg().flyOnGroundInCreative && player.isCreative() && player.getAbilities().flying;
|
return getConfig().flyOnGroundInCreative && player.isCreative() && player.getAbilities().flying;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getHorizontalSpeedMultiplier(final LocalPlayer player) {
|
public static float getHorizontalSpeedMultiplier(LocalPlayer player) {
|
||||||
if (player.isCreative()) {
|
if (player.isCreative()) {
|
||||||
return isSprinting() ? cfg().flightHorizontalSpeedMpCreativeSprinting : cfg().flightHorizontalSpeedMpCreativeDefault;
|
return isSprinting() ? getConfig().flightHorizontalSpeedMpCreativeSprinting : getConfig().flightHorizontalSpeedMpCreativeDefault;
|
||||||
}
|
}
|
||||||
else if (player.isSpectator()) {
|
else if (player.isSpectator()) {
|
||||||
return isSprinting() ? cfg().flightHorizontalSpeedMpSpectatorSprinting : cfg().flightHorizontalSpeedMpSpectatorDefault;
|
return isSprinting() ? getConfig().flightHorizontalSpeedMpSpectatorSprinting : getConfig().flightHorizontalSpeedMpSpectatorDefault;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return 1F;
|
return 1F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getVerticalSpeedMultiplier(final LocalPlayer player) {
|
public static float getVerticalSpeedMultiplier(LocalPlayer player) {
|
||||||
if (player.isCreative()) {
|
if (player.isCreative()) {
|
||||||
return isSprinting() ? cfg().flightVerticalSpeedMpCreativeSprinting : cfg().flightVerticalSpeedMpCreativeDefault;
|
return isSprinting() ? getConfig().flightVerticalSpeedMpCreativeSprinting : getConfig().flightVerticalSpeedMpCreativeDefault;
|
||||||
}
|
}
|
||||||
else if (player.isSpectator()) {
|
else if (player.isSpectator()) {
|
||||||
return isSprinting() ? cfg().flightVerticalSpeedMpSpectatorSprinting : cfg().flightVerticalSpeedMpSpectatorDefault;
|
return isSprinting() ? getConfig().flightVerticalSpeedMpSpectatorSprinting : getConfig().flightVerticalSpeedMpSpectatorDefault;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return 1F;
|
return 1F;
|
||||||
|
@@ -1,36 +1,35 @@
|
|||||||
package chylex.bettercontrols.player;
|
package chylex.bettercontrols.player;
|
||||||
|
|
||||||
import chylex.bettercontrols.BetterControlsCommon;
|
import chylex.bettercontrols.Mixins;
|
||||||
import chylex.bettercontrols.config.BetterControlsConfig;
|
|
||||||
import chylex.bettercontrols.gui.BetterControlsScreen;
|
import chylex.bettercontrols.gui.BetterControlsScreen;
|
||||||
import chylex.bettercontrols.input.SprintMode;
|
import chylex.bettercontrols.input.SprintMode;
|
||||||
import chylex.bettercontrols.input.ToggleTracker;
|
import chylex.bettercontrols.input.ToggleTracker;
|
||||||
import chylex.bettercontrols.input.ToggleTrackerForStickyKey;
|
import chylex.bettercontrols.input.ToggleTrackerForStickyKey;
|
||||||
import chylex.bettercontrols.mixin.AccessCameraFields;
|
import chylex.bettercontrols.mixin.AccessToggleKeyMappingFields;
|
||||||
import chylex.bettercontrols.mixin.AccessClientPlayerFields;
|
|
||||||
import chylex.bettercontrols.mixin.AccessPlayerFields;
|
|
||||||
import chylex.bettercontrols.mixin.AccessStickyKeyBindingStateGetter;
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.Options;
|
import net.minecraft.client.Options;
|
||||||
import net.minecraft.client.player.Input;
|
import net.minecraft.client.ToggleKeyMapping;
|
||||||
|
import net.minecraft.client.player.ClientInput;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
|
import net.minecraft.world.entity.player.Input;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
import static chylex.bettercontrols.BetterControlsCommon.getConfig;
|
||||||
|
|
||||||
public final class PlayerTicker {
|
public final class PlayerTicker {
|
||||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
||||||
private static final Options OPTIONS = MINECRAFT.options;
|
private static final Options OPTIONS = MINECRAFT.options;
|
||||||
|
|
||||||
private static final KeyMapping KEY_SPRINT = OPTIONS.keySprint;
|
private static final ToggleKeyMapping KEY_SPRINT = (ToggleKeyMapping) OPTIONS.keySprint;
|
||||||
private static final KeyMapping KEY_SNEAK = OPTIONS.keyShift;
|
private static final KeyMapping KEY_SNEAK = OPTIONS.keyShift;
|
||||||
private static final KeyMapping KEY_FORWARD = OPTIONS.keyUp;
|
private static final KeyMapping KEY_FORWARD = OPTIONS.keyUp;
|
||||||
private static final KeyMapping KEY_JUMP = OPTIONS.keyJump;
|
private static final KeyMapping KEY_JUMP = OPTIONS.keyJump;
|
||||||
|
|
||||||
private static PlayerTicker ticker = new PlayerTicker(null);
|
private static PlayerTicker ticker = new PlayerTicker(null);
|
||||||
|
|
||||||
public static PlayerTicker get(final LocalPlayer player) {
|
public static PlayerTicker get(LocalPlayer player) {
|
||||||
if (ticker.ref.get() != player) {
|
if (ticker.ref.get() != player) {
|
||||||
ticker = new PlayerTicker(player);
|
ticker = new PlayerTicker(player);
|
||||||
}
|
}
|
||||||
@@ -38,23 +37,19 @@ public final class PlayerTicker {
|
|||||||
return ticker;
|
return ticker;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BetterControlsConfig cfg() {
|
|
||||||
return BetterControlsCommon.getConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final WeakReference<LocalPlayer> ref;
|
private final WeakReference<LocalPlayer> ref;
|
||||||
|
|
||||||
private PlayerTicker(final LocalPlayer player) {
|
private PlayerTicker(LocalPlayer player) {
|
||||||
this.ref = new WeakReference<>(player);
|
this.ref = new WeakReference<>(player);
|
||||||
setup();
|
setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logic
|
// Logic
|
||||||
|
|
||||||
private final ToggleTracker toggleSprint = new ToggleTrackerForStickyKey(cfg().keyToggleSprint, KEY_SPRINT, OPTIONS.toggleSprint()::set);
|
private final ToggleTracker toggleSprint = new ToggleTrackerForStickyKey(getConfig().keyToggleSprint, KEY_SPRINT, OPTIONS.toggleSprint());
|
||||||
private final ToggleTracker toggleSneak = new ToggleTrackerForStickyKey(cfg().keyToggleSneak, KEY_SNEAK, OPTIONS.toggleCrouch()::set);
|
private final ToggleTracker toggleSneak = new ToggleTrackerForStickyKey(getConfig().keyToggleSneak, KEY_SNEAK, OPTIONS.toggleCrouch());
|
||||||
private final ToggleTracker toggleWalkForward = new ToggleTracker(cfg().keyToggleWalkForward, KEY_FORWARD);
|
private final ToggleTracker toggleWalkForward = new ToggleTracker(getConfig().keyToggleWalkForward, KEY_FORWARD);
|
||||||
private final ToggleTracker toggleJump = new ToggleTracker(cfg().keyToggleJump, KEY_JUMP);
|
private final ToggleTracker toggleJump = new ToggleTracker(getConfig().keyToggleJump, KEY_JUMP);
|
||||||
|
|
||||||
private boolean waitingForSprintKeyRelease = false;
|
private boolean waitingForSprintKeyRelease = false;
|
||||||
private boolean stopSprintingAfterReleasingSprintKey = false;
|
private boolean stopSprintingAfterReleasingSprintKey = false;
|
||||||
@@ -68,38 +63,38 @@ public final class PlayerTicker {
|
|||||||
private int temporaryFlyOnGroundTimer = 0;
|
private int temporaryFlyOnGroundTimer = 0;
|
||||||
|
|
||||||
private void setup() {
|
private void setup() {
|
||||||
final AccessStickyKeyBindingStateGetter sprint = (AccessStickyKeyBindingStateGetter)KEY_SPRINT;
|
AccessToggleKeyMappingFields sprint = Mixins.toggleKeyMappingFields(KEY_SPRINT);
|
||||||
BooleanSupplier getter = sprint.getNeedsToggle();
|
BooleanSupplier getter = sprint.getNeedsToggle();
|
||||||
|
|
||||||
if (getter instanceof final SprintPressGetter g) {
|
if (getter instanceof SprintPressGetter g) {
|
||||||
getter = g.wrapped();
|
getter = g.wrapped();
|
||||||
}
|
}
|
||||||
|
|
||||||
sprint.setNeedsToggle(new SprintPressGetter(getter, () -> temporarySprintTimer > 0));
|
sprint.setNeedsToggle(new SprintPressGetter(getter, () -> temporarySprintTimer > 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void atHead(final LocalPlayer player) {
|
public void atHead(LocalPlayer player) {
|
||||||
if (FlightHelper.shouldFlyOnGround(player)) {
|
if (FlightHelper.shouldFlyOnGround(player)) {
|
||||||
player.setOnGround(false);
|
player.setOnGround(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cfg().doubleTapForwardToSprint) {
|
if (!getConfig().doubleTapForwardToSprint) {
|
||||||
((AccessClientPlayerFields)player).setSprintTriggerTime(0);
|
Mixins.clientPlayerFields(player).setSprintTriggerTime(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cfg().doubleTapJumpToToggleFlight) {
|
if (!getConfig().doubleTapJumpToToggleFlight) {
|
||||||
((AccessPlayerFields)player).setJumpTriggerTime(0);
|
Mixins.playerFields(player).setJumpTriggerTime(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
final SprintMode sprintMode = cfg().sprintMode;
|
SprintMode sprintMode = getConfig().sprintMode;
|
||||||
final boolean wasSprintToggled = Boolean.TRUE.equals(OPTIONS.toggleSprint().get());
|
boolean wasSprintToggled = Boolean.TRUE.equals(OPTIONS.toggleSprint().get());
|
||||||
final boolean isSprintToggled = toggleSprint.tick();
|
boolean isSprintToggled = toggleSprint.tick();
|
||||||
|
|
||||||
if (temporarySprintTimer > 0) {
|
if (temporarySprintTimer > 0) {
|
||||||
stopSprintingAfterReleasingSprintKey = false;
|
stopSprintingAfterReleasingSprintKey = false;
|
||||||
waitingForSprintKeyRelease = false;
|
waitingForSprintKeyRelease = false;
|
||||||
|
|
||||||
final int nextTemporarySprintTimer = temporarySprintTimer - 1;
|
int nextTemporarySprintTimer = temporarySprintTimer - 1;
|
||||||
temporarySprintTimer = 0;
|
temporarySprintTimer = 0;
|
||||||
|
|
||||||
if (!KEY_SPRINT.isDown() && KEY_FORWARD.isDown()) {
|
if (!KEY_SPRINT.isDown() && KEY_FORWARD.isDown()) {
|
||||||
@@ -148,22 +143,28 @@ public final class PlayerTicker {
|
|||||||
toggleSneak.tick();
|
toggleSneak.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void afterInputAssignsPressingForward(final Input input) {
|
public void afterKeyboardInputAssigned(LocalPlayer player) {
|
||||||
if (MINECRAFT.screen == null) {
|
if (MINECRAFT.screen == null && toggleWalkForward.tick()) {
|
||||||
//noinspection NonShortCircuitBooleanExpression
|
ClientInput input = player.input;
|
||||||
input.up |= toggleWalkForward.tick();
|
|
||||||
|
input.keyPresses = new Input(
|
||||||
|
true,
|
||||||
|
input.keyPresses.backward(),
|
||||||
|
input.keyPresses.left(),
|
||||||
|
input.keyPresses.right(),
|
||||||
|
input.keyPresses.jump(),
|
||||||
|
input.keyPresses.shift(),
|
||||||
|
input.keyPresses.sprint()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void afterInputTick(final LocalPlayer player) {
|
public void afterInputTick(LocalPlayer player) {
|
||||||
final Input input = player.input;
|
if (MINECRAFT.screen == null && !player.getAbilities().flying && toggleJump.tick()) {
|
||||||
|
player.input.makeJump();
|
||||||
if (MINECRAFT.screen == null && !player.getAbilities().flying) {
|
|
||||||
//noinspection NonShortCircuitBooleanExpression
|
|
||||||
input.jumping |= toggleJump.tick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg().resumeSprintingAfterHittingObstacle) {
|
if (getConfig().resumeSprintingAfterHittingObstacle) {
|
||||||
if (wasHittingObstacle != player.horizontalCollision) {
|
if (wasHittingObstacle != player.horizontalCollision) {
|
||||||
if (!wasHittingObstacle) {
|
if (!wasHittingObstacle) {
|
||||||
wasSprintingBeforeHittingObstacle = player.isSprinting() || KEY_SPRINT.isDown();
|
wasSprintingBeforeHittingObstacle = player.isSprinting() || KEY_SPRINT.isDown();
|
||||||
@@ -183,10 +184,10 @@ public final class PlayerTicker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void afterSuperCall(final LocalPlayer player) {
|
public void afterSuperCall(LocalPlayer player) {
|
||||||
if (FlightHelper.shouldFlyOnGround(player)) {
|
if (FlightHelper.shouldFlyOnGround(player)) {
|
||||||
final boolean isSneaking = player.isShiftKeyDown();
|
boolean isSneaking = player.isShiftKeyDown();
|
||||||
final boolean isOnGround = player.onGround();
|
boolean isOnGround = player.onGround();
|
||||||
|
|
||||||
if (!isSneaking) {
|
if (!isSneaking) {
|
||||||
wasSneakingBeforeTouchingGround = false;
|
wasSneakingBeforeTouchingGround = false;
|
||||||
@@ -222,21 +223,27 @@ public final class PlayerTicker {
|
|||||||
holdingSneakWhileTouchingGround = false;
|
holdingSneakWhileTouchingGround = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlightHelper.isFlyingCreativeOrSpectator(player) && cfg().disableFlightInertia) {
|
if (FlightHelper.isFlyingCreativeOrSpectator(player)) {
|
||||||
final Input input = player.input;
|
float inertiaMultiplier = getConfig().flightInertiaMultiplier;
|
||||||
|
|
||||||
if (input.forwardImpulse == 0F && input.leftImpulse == 0F) {
|
if (inertiaMultiplier < 1F) {
|
||||||
player.setDeltaMovement(player.getDeltaMovement().multiply(0.0, 1.0, 0.0));
|
ClientInput input = player.input;
|
||||||
}
|
Input keyPresses = input.keyPresses;
|
||||||
|
double inertiaMultiplierSqrt = Math.sqrt(inertiaMultiplier);
|
||||||
if (!input.jumping && !input.shiftKeyDown) {
|
|
||||||
player.setDeltaMovement(player.getDeltaMovement().multiply(1.0, 0.0, 1.0));
|
if (!keyPresses.forward() && !keyPresses.backward() && !keyPresses.left() && !keyPresses.right()) {
|
||||||
|
player.setDeltaMovement(player.getDeltaMovement().multiply(inertiaMultiplierSqrt, 1.0, inertiaMultiplierSqrt));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyPresses.jump() && !keyPresses.shift()) {
|
||||||
|
player.setDeltaMovement(player.getDeltaMovement().multiply(1.0, inertiaMultiplierSqrt, 1.0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.isCreative()) {
|
if (player.isCreative()) {
|
||||||
if (cfg().keyToggleFlight.consumeClick()) {
|
if (getConfig().keyToggleFlight.consumeClick()) {
|
||||||
final boolean isFlying = !player.getAbilities().flying;
|
boolean isFlying = !player.getAbilities().flying;
|
||||||
|
|
||||||
player.getAbilities().flying = isFlying;
|
player.getAbilities().flying = isFlying;
|
||||||
player.onUpdateAbilities();
|
player.onUpdateAbilities();
|
||||||
@@ -260,27 +267,27 @@ public final class PlayerTicker {
|
|||||||
temporaryFlyOnGroundTimer = 0;
|
temporaryFlyOnGroundTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cfg().sneakingMovesCameraSmoothly) {
|
if (!getConfig().sneakingMovesCameraSmoothly) {
|
||||||
final Camera camera = MINECRAFT.gameRenderer.getMainCamera();
|
Camera camera = MINECRAFT.gameRenderer.getMainCamera();
|
||||||
|
|
||||||
if (camera.getEntity() == player) {
|
if (camera.getEntity() == player) {
|
||||||
((AccessCameraFields)camera).setEyeHeight(player.getEyeHeight());
|
Mixins.cameraFields(camera).setEyeHeight(player.getEyeHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg().keyResetAllToggles.consumeClick()) {
|
if (getConfig().keyResetAllToggles.consumeClick()) {
|
||||||
toggleSprint.reset();
|
toggleSprint.reset();
|
||||||
toggleSneak.reset();
|
toggleSneak.reset();
|
||||||
toggleWalkForward.reset();
|
toggleWalkForward.reset();
|
||||||
toggleJump.reset();
|
toggleJump.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg().keyOpenMenu.isDown()) {
|
if (getConfig().keyOpenMenu.isDown()) {
|
||||||
MINECRAFT.setScreen(new BetterControlsScreen(MINECRAFT, null));
|
MINECRAFT.setScreen(new BetterControlsScreen(null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean shouldResetFOV(final LocalPlayer player) {
|
public static boolean shouldResetFOV(LocalPlayer player) {
|
||||||
return cfg().disableChangingFovWhileFlying && FlightHelper.isFlyingCreativeOrSpectator(player);
|
return getConfig().disableChangingFovWhileFlying && FlightHelper.isFlyingCreativeOrSpectator(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,9 +7,9 @@
|
|||||||
"client": [
|
"client": [
|
||||||
"AccessCameraFields",
|
"AccessCameraFields",
|
||||||
"AccessClientPlayerFields",
|
"AccessClientPlayerFields",
|
||||||
"AccessKeyBindingFields",
|
"AccessKeyMappingFields",
|
||||||
"AccessPlayerFields",
|
"AccessPlayerFields",
|
||||||
"AccessStickyKeyBindingStateGetter",
|
"AccessToggleKeyMappingFields",
|
||||||
"HookClientPlayerFOV",
|
"HookClientPlayerFOV",
|
||||||
"HookClientPlayerInputTick",
|
"HookClientPlayerInputTick",
|
||||||
"HookClientPlayerTick",
|
"HookClientPlayerTick",
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
"HookControlsListWidget",
|
"HookControlsListWidget",
|
||||||
"HookControlsScreen",
|
"HookControlsScreen",
|
||||||
"HookLoadGameOptions",
|
"HookLoadGameOptions",
|
||||||
|
"HookPlayerGliding",
|
||||||
"HookPlayerHorizontalFlightSpeed",
|
"HookPlayerHorizontalFlightSpeed",
|
||||||
"HookStickyKeyBindingState",
|
"HookStickyKeyBindingState",
|
||||||
"HookToggleOptionButtons"
|
"HookToggleOptionButtons"
|
||||||
|
Reference in New Issue
Block a user