1
0
mirror of https://github.com/chylex/Better-Controls.git synced 2026-02-15 15:06:12 +01:00

19 Commits

Author SHA1 Message Date
a8cc4e115b Release v1.6.4 for Minecraft 1.21.11+ 2026-01-16 05:19:35 +01:00
13fc85ff1c Fix broken slider options
Closes #48
2026-01-16 05:15:21 +01:00
1c7c5402ad Release v1.6.3 for Minecraft 1.21.11+ 2025-12-21 18:24:26 +01:00
cdb9dadaec Update key binding button UI to match vanilla 2025-12-21 06:27:25 +01:00
76afa6bc40 Update for Minecraft 1.21.11 2025-12-21 05:52:22 +01:00
cbf5adb4a7 Release v1.6.2 for Minecraft 1.21.9+ 2025-10-12 22:06:49 +02:00
5a67786d0b Update for Minecraft 1.21.9 2025-10-01 12:34:15 +02:00
2e19fffe44 Release v1.6.1 for Minecraft 1.21.6+ 2025-07-20 16:28:02 +02:00
32f6a6e7fe Update for Minecraft 1.21.6 2025-07-20 16:28:02 +02:00
dfefab3482 Fix typo in filename 2025-05-25 14:43:55 +02:00
ffd56a4407 Release v1.6.0 for Minecraft 1.21.5+ 2025-05-25 13:36:03 +02:00
6b574f8568 Fix not scrolling to focused option when using Tab 2025-05-25 08:42:35 +02:00
76a1353dbf Jump to previous/next option when pressing left/right arrow key in discrete value sliders 2025-05-25 06:40:00 +02:00
0757d30f12 Replace flight inertia toggle with a multiplier slider
Closes #17
2025-05-25 06:19:11 +02:00
64c4b51f0e Update for Minecraft 1.21.5
Closes #40
2025-05-23 12:27:49 +02:00
5111ac5f7e Release v1.5.0 for Minecraft 1.21.4+ 2025-01-31 04:09:02 +01:00
ee3db91f0c Do not reset Toggle Sprint/Sneak after switching worlds to maintain consistency with vanilla toggles
Closes #30
2025-01-31 00:46:26 +01:00
9da758937e Refactor mixins 2025-01-30 23:53:10 +01:00
aa499bead5 Add keybinding to start a glide & option to turn off double tapping 'Jump' key to glide
Closes #18
2025-01-30 22:46:36 +01:00
33 changed files with 420 additions and 199 deletions

View File

@@ -15,7 +15,7 @@ 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.getDist() == 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, () -> BetterControlsMod::createOptionsScreen); ModLoadingContext.get().registerExtensionPoint(IConfigScreenFactory.class, () -> BetterControlsMod::createOptionsScreen);
} }

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -3,18 +3,18 @@ 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.4
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.4 minecraftVersion=1.21.11
neoForgeVersion=21.4.77-beta neoForgeVersion=21.11.12-beta
neoModDevVersion=2.0.76 neoModDevVersion=2.0.110
fabricVersion=0.16.10 fabricVersion=0.17.2
loomVersion=1.9 loomVersion=1.10
mixinVersion=0.12.5+mixin.0.8.5 mixinVersion=0.12.5+mixin.0.8.5
mixinExtrasVersion=0.4.1 mixinExtrasVersion=0.4.1
@@ -24,8 +24,8 @@ mixinExtrasVersion=0.4.1
# https://github.com/FabricMC/fabric-loom/releases # https://github.com/FabricMC/fabric-loom/releases
# Constraints # Constraints
minimumMinecraftVersion=1.21.4 minimumMinecraftVersion=1.21.11
minimumNeoForgeVersion=21.4.0-beta minimumNeoForgeVersion=21.11.0-beta
minimumFabricVersion=0.15.0 minimumFabricVersion=0.15.0
# Gradle # Gradle

View 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;
}
}

View File

@@ -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;

View File

@@ -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);
@@ -72,6 +75,10 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js
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);

View File

@@ -18,7 +18,10 @@ import net.minecraft.client.gui.components.CycleButton;
import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.options.OptionsSubScreen; import net.minecraft.client.gui.screens.options.OptionsSubScreen;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.util.ArrayList; import java.util.ArrayList;
@@ -83,9 +86,30 @@ public class BetterControlsScreen extends OptionsSubScreen {
return y; return y;
} }
private int generateGlidingOptions(int y, List<GuiEventListener> elements) {
BetterControlsConfig cfg = BetterControlsCommon.getConfig();
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;
}
@SuppressWarnings({ "AutoBoxing", "AutoUnboxing" })
private int generateFlightOptions(int y, List<GuiEventListener> elements) { private int generateFlightOptions(int y, List<GuiEventListener> elements) {
BetterControlsConfig cfg = BetterControlsCommon.getConfig(); 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( 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")),
@@ -107,13 +131,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;
@@ -189,9 +214,8 @@ public class BetterControlsScreen extends OptionsSubScreen {
private static void generateBooleanOptionRow(int y, List<GuiEventListener> elements, Component text, boolean initialValue, 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(initialValue)
.displayOnlyValue() .displayOnlyValue()
.withInitialValue(Boolean.valueOf(initialValue))
.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())));
} }
@@ -223,6 +247,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;
@@ -258,32 +285,32 @@ public class BetterControlsScreen extends OptionsSubScreen {
} }
@Override @Override
public boolean mouseClicked(double mouseX, double mouseY, int button) { public boolean mouseClicked(@NotNull MouseButtonEvent event, boolean isDoubleClick) {
if (editingKeyBinding != null) { if (editingKeyBinding != null) {
editingKeyBinding.bindAndStopEditing(InputConstants.Type.MOUSE.getOrCreate(button)); editingKeyBinding.bindAndStopEditing(InputConstants.Type.MOUSE.getOrCreate(event.button()));
onKeyBindingEditingFinished(); onKeyBindingEditingFinished();
return true; return true;
} }
else { else {
return super.mouseClicked(mouseX, mouseY, button); return super.mouseClicked(event, isDoubleClick);
} }
} }
@Override @Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) { public boolean keyPressed(@NotNull KeyEvent event) {
if (editingKeyBinding != null) { if (editingKeyBinding != null) {
if (keyCode == GLFW.GLFW_KEY_ESCAPE) { if (event.key() == GLFW.GLFW_KEY_ESCAPE) {
editingKeyBinding.bindAndStopEditing(InputConstants.UNKNOWN); editingKeyBinding.bindAndStopEditing(InputConstants.UNKNOWN);
} }
else { else {
editingKeyBinding.bindAndStopEditing(InputConstants.getKey(keyCode, scanCode)); editingKeyBinding.bindAndStopEditing(InputConstants.getKey(event));
} }
onKeyBindingEditingFinished(); onKeyBindingEditingFinished();
return true; return true;
} }
else { else {
return super.keyPressed(keyCode, scanCode, modifiers); return super.keyPressed(event);
} }
} }

View File

@@ -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;
@@ -35,28 +34,32 @@ public final class OptionListWidget extends ContainerObjectSelectionList<Entry>
private static Offset getElementOffset(GuiEventListener element) { private static Offset getElementOffset(GuiEventListener element) {
if (element instanceof 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 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) {}
@SuppressWarnings("ThisEscapedInObjectConstruction")
public OptionListWidget(int width, int height, int top, int innerHeight, List<GuiEventListener> widgets) { 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
@@ -80,41 +83,60 @@ public final class OptionListWidget extends ContainerObjectSelectionList<Entry>
return true; return true;
} }
@Override
protected void scrollToEntry(@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(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(@NotNull GuiGraphics graphics, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { public void renderContent(@NotNull GuiGraphics graphics, int mouseX, int mouseY, boolean hovered, float tickDelta) {
for (GuiEventListener element : elements) { for (GuiEventListener element : elements) {
Offset offset = offsets.get(element); Offset offset = offsets.get(element);
if (element instanceof AbstractWidget widget) { if (element instanceof AbstractWidget widget) {
widget.setX(x + offset.x); widget.setX(getX() + offset.x);
widget.setY(y + offset.y); widget.setY(getY() + offset.y);
} }
else if (element instanceof OptionWidget widget) { else if (element instanceof OptionWidget widget) {
widget.setX(x + offset.x); widget.setX(getX() + offset.x);
widget.setY(y + offset.y); widget.setY(getY() + offset.y);
} }
if (element instanceof Renderable renderable) { if (element instanceof Renderable renderable) {

View File

@@ -2,9 +2,9 @@ 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.input.KeyEvent;
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 org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -15,7 +15,7 @@ public final class DiscreteValueSliderWidget<T> extends AbstractSliderButton {
private T selectedValue; private T selectedValue;
public DiscreteValueSliderWidget(int x, int y, int width, int height, Component narration, ImmutableList<Option<T>> options, T selectedValue, 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;
@@ -27,7 +27,11 @@ public final class DiscreteValueSliderWidget<T> extends AbstractSliderButton {
} }
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 getOptionIndex(value, options.size());
} }
@Override @Override
@@ -35,6 +39,29 @@ public final class DiscreteValueSliderWidget<T> extends AbstractSliderButton {
setMessage(getSelectedOption().text()); setMessage(getSelectedOption().text());
} }
@Override
public boolean keyPressed(KeyEvent keyEvent) {
if (keyEvent.isSelection()) {
return super.keyPressed(keyEvent);
}
if (keyEvent.isLeft() || keyEvent.isRight()) {
int newOptionIndex = keyEvent.isLeft()
? 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() {
T newSelectedValue = getSelectedOption().value(); T newSelectedValue = getSelectedOption().value();
@@ -50,4 +77,20 @@ 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()));
} }
public static int getOptionIndex(double value, int optionCount) {
if (value < 0.0) {
return 0;
}
else if (value > 1.0) {
return optionCount - 1;
}
else {
return (int) (value * (optionCount - 1));
}
}
private static <T> double getOptionValue(ImmutableList<Option<T>> options, int optionIndex) {
return optionIndex / (options.size() - 1.0);
}
} }

View File

@@ -6,6 +6,8 @@ import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.input.InputWithModifiers;
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 org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -13,7 +15,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
public final class KeyBindingWidget extends Button { public final class KeyBindingWidget extends Button.Plain {
private final KeyMapping binding; private final KeyMapping binding;
private final Component bindingName; private final Component bindingName;
@@ -42,11 +44,13 @@ public final class KeyBindingWidget extends Button {
@NotNull @NotNull
@Override @Override
protected MutableComponent createNarrationMessage() { protected MutableComponent createNarrationMessage() {
return binding.isUnbound() ? Component.translatable("narrator.controls.unbound", bindingName) : Component.translatable("narrator.controls.bound", bindingName, super.createNarrationMessage()); return binding.isUnbound()
? Component.translatable("narrator.controls.unbound", bindingName)
: Component.translatable("narrator.controls.bound", bindingName, super.createNarrationMessage());
} }
@Override @Override
public void onPress() { public void onPress(@NotNull InputWithModifiers input) {
isEditing = true; isEditing = true;
onEditingStarted.accept(this); onEditingStarted.accept(this);
updateKeyBindingText(); updateKeyBindingText();
@@ -68,24 +72,59 @@ public final class KeyBindingWidget extends Button {
public void updateKeyBindingText() { public void updateKeyBindingText() {
boolean hasConflict = false; boolean hasConflict = false;
MutableComponent conflictText = Component.empty();
if (!binding.isUnbound()) { if (!binding.isUnbound()) {
for (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)) {
if (hasConflict) {
conflictText.append(", ");
}
hasConflict = true; hasConflict = true;
break; conflictText.append(Component.translatable(other.getName()));
} }
} }
} }
if (isEditing) { if (hasConflict) {
setMessage(Component.literal("> ").append(binding.getTranslatedKeyMessage().copy().withStyle(ChatFormatting.YELLOW)).append(" <").withStyle(ChatFormatting.YELLOW)); setMessage(getMessageWithConflict(binding));
} setTooltip(Tooltip.create(Component.translatable("controls.keybinds.duplicateKeybinds", conflictText)));
else if (hasConflict) {
setMessage(binding.getTranslatedKeyMessage().copy().withStyle(ChatFormatting.RED));
} }
else { else {
setMessage(binding.isUnbound() ? Component.literal("(No Binding)") : binding.getTranslatedKeyMessage()); setMessage(getMessageWithoutConflict(binding));
setTooltip(null);
}
if (isEditing) {
setMessage(getEditingMessage(getMessage()));
} }
} }
private static MutableComponent getMessageWithConflict(KeyMapping binding) {
return Component
.literal("[ ")
.append(binding.getTranslatedKeyMessage().copy().withStyle(ChatFormatting.WHITE))
.append(" ]")
.withStyle(ChatFormatting.YELLOW);
}
private static Component getMessageWithoutConflict(KeyMapping binding) {
if (binding.isUnbound()) {
return Component
.literal("(")
.append(Component.translatable("key.keyboard.unknown"))
.append(")");
}
return binding.getTranslatedKeyMessage();
}
private static MutableComponent getEditingMessage(Component originalMessage) {
return Component
.literal("> ")
.append(originalMessage.copy().withStyle(ChatFormatting.WHITE, ChatFormatting.UNDERLINE))
.append(" <")
.withStyle(ChatFormatting.YELLOW);
}
} }

View File

@@ -8,14 +8,13 @@ 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(List<Option<T>> options, 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::getFirst);
} }
public static <T> CycleButton<Option<T>> button(int x, int y, int width, Component text, List<Option<T>> options, T initialValue, 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.builder(Option::text, find(options, initialValue))
.displayOnlyValue() .displayOnlyValue()
.withValues(options) .withValues(options)
.withInitialValue(find(options, initialValue))
.create(x, y, width, 20, text, (btn, newValue) -> onValueChanged.accept(newValue.value())); .create(x, y, width, 20, text, (btn, newValue) -> onValueChanged.accept(newValue.value()));
} }
} }

View File

@@ -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;
@@ -39,24 +39,29 @@ public final class TextWidget implements OptionWidget {
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(int x) { public int getHeight() {
this.x = x; return height;
}
@Override
public void setY(int y) {
this.y = y;
} }
@Override @Override

View File

@@ -2,13 +2,15 @@ package chylex.bettercontrols.input;
import com.mojang.blaze3d.platform.InputConstants.Type; import com.mojang.blaze3d.platform.InputConstants.Type;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import net.minecraft.resources.Identifier;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class KeyBindingWithModifier extends KeyMapping { public class KeyBindingWithModifier extends KeyMapping {
public static final String CATEGORY = "key.categories.bettercontrols"; @SuppressWarnings("SpellCheckingInspection")
public static final Category CATEGORY = new Category(Identifier.fromNamespaceAndPath("bettercontrols", "all"));
@Nullable @Nullable
private ModifierKey modifier = null; private ModifierKey modifier;
public KeyBindingWithModifier(String translationKey) { public KeyBindingWithModifier(String translationKey) {
super(translationKey, Type.KEYSYM, -1, CATEGORY); super(translationKey, Type.KEYSYM, -1, CATEGORY);

View File

@@ -1,26 +1,26 @@
package chylex.bettercontrols.input; package chylex.bettercontrols.input;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.Minecraft;
public enum ModifierKey { public enum ModifierKey {
CONTROL(0) { CONTROL(0) {
@Override @Override
public boolean isPressed() { public boolean isPressed() {
return Screen.hasControlDown(); return Minecraft.getInstance().hasControlDown();
} }
}, },
SHIFT(1) { SHIFT(1) {
@Override @Override
public boolean isPressed() { public boolean isPressed() {
return Screen.hasShiftDown(); return Minecraft.getInstance().hasShiftDown();
} }
}, },
ALT(2) { ALT(2) {
@Override @Override
public boolean isPressed() { public boolean isPressed() {
return Screen.hasAltDown(); return Minecraft.getInstance().hasAltDown();
} }
}; };

View File

@@ -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(KeyMapping bindingToggle, 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);
} }
/* /*

View File

@@ -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<>();
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
public static boolean isOverrideEnabled(KeyMapping binding) { 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(KeyMapping bindingToggle, KeyMapping bindingStickyReset, 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() {
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();
} }
} }

View File

@@ -3,15 +3,10 @@ package chylex.bettercontrols.mixin;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
@Mixin(KeyMapping.class) @Mixin(KeyMapping.class)
public interface AccessKeyBindingFields { @SuppressWarnings("StaticMethodOnlyUsedInOneClass")
@Accessor("CATEGORY_SORT_ORDER") public interface AccessKeyMappingFields {
static Map<String, Integer> getCategoryOrderMap() {
throw new AssertionError();
}
@Accessor("isDown") @Accessor("isDown")
boolean isPressedField(); boolean isPressedField();

View File

@@ -7,7 +7,7 @@ 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();

View File

@@ -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.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.player.AbstractClientPlayer;
@@ -14,9 +15,9 @@ public abstract class HookClientPlayerFOV {
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Abilities;getWalkingSpeed()F") at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Abilities;getWalkingSpeed()F")
) )
private float overrideWalkingSpeed(float walkingSpeed) { private float overrideWalkingSpeed(float walkingSpeed) {
AbstractClientPlayer me = (AbstractClientPlayer)(Object)this; AbstractClientPlayer me = Mixins.me(this);
if (me instanceof LocalPlayer localPlayer && PlayerTicker.get(localPlayer).shouldResetFOV(localPlayer)) { if (me instanceof LocalPlayer localPlayer && PlayerTicker.shouldResetFOV(localPlayer)) {
return 0F; return 0F;
} }
else { else {

View File

@@ -11,7 +11,7 @@ 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({ "MethodMayBeStatic", "UnreachableCode" }) @SuppressWarnings("MethodMayBeStatic")
public abstract class HookClientPlayerInputTick { public abstract class HookClientPlayerInputTick {
@Inject( @Inject(
method = "tick", method = "tick",

View File

@@ -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,7 +13,6 @@ 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(ClientLevel world, GameProfile profile) { protected HookClientPlayerTick(ClientLevel world, GameProfile profile) {
super(world, profile); super(world, profile);
@@ -20,22 +20,19 @@ public abstract class HookClientPlayerTick extends AbstractClientPlayer {
@Inject(method = "aiStep()V", at = @At("HEAD")) @Inject(method = "aiStep()V", at = @At("HEAD"))
private void atHead(CallbackInfo info) { private void atHead(CallbackInfo info) {
@SuppressWarnings("ConstantConditions") LocalPlayer player = Mixins.me(this);
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/ClientInput;tick()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(CallbackInfo info) { private void afterInputTick(CallbackInfo info) {
@SuppressWarnings("ConstantConditions") LocalPlayer player = Mixins.me(this);
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(CallbackInfo info) { private void afterSuperCall(CallbackInfo info) {
@SuppressWarnings("ConstantConditions") LocalPlayer player = Mixins.me(this);
LocalPlayer player = (LocalPlayer)(Object)this;
PlayerTicker.get(player).afterSuperCall(player); PlayerTicker.get(player).afterSuperCall(player);
} }
} }

View File

@@ -1,5 +1,6 @@
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.ModifyExpressionValue;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
@@ -11,7 +12,6 @@ import org.spongepowered.asm.mixin.injection.At;
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(EntityType<? extends LivingEntity> type, Level world) { protected HookClientPlayerVerticalFlightSpeed(EntityType<? extends LivingEntity> type, Level world) {
super(type, world); super(type, world);
@@ -26,8 +26,7 @@ public abstract class HookClientPlayerVerticalFlightSpeed extends LivingEntity {
) )
) )
private float modifyVerticalFlightSpeed(float flyingSpeed) { private float modifyVerticalFlightSpeed(float flyingSpeed) {
@SuppressWarnings("ConstantConditions") LocalPlayer me = Mixins.me(this);
LocalPlayer me = (LocalPlayer)(Object)this;
return flyingSpeed * FlightHelper.getVerticalSpeedMultiplier(me); return flyingSpeed * FlightHelper.getVerticalSpeedMultiplier(me);
} }
} }

View File

@@ -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,7 +18,6 @@ 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(Screen parentScreen, Options options, Component title) { public HookControlsScreen(Screen parentScreen, Options options, Component title) {
super(parentScreen, options, title); super(parentScreen, options, title);
@@ -26,8 +26,7 @@ public abstract class HookControlsScreen extends OptionsSubScreen {
@Inject(method = "addOptions", at = @At("RETURN")) @Inject(method = "addOptions", at = @At("RETURN"))
public void afterAddOptions(CallbackInfo ci) { public void afterAddOptions(CallbackInfo ci) {
if (list != null) { if (list != null) {
@SuppressWarnings("ConstantConditions") ControlsScreen screen = Mixins.me(this);
ControlsScreen screen = (ControlsScreen)(Object)this;
MutableComponent buttonTitle = BetterControlsScreen.TITLE.plainCopy().append("..."); 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()));
} }

View File

@@ -2,7 +2,6 @@ package chylex.bettercontrols.mixin;
import chylex.bettercontrols.BetterControlsCommon; import chylex.bettercontrols.BetterControlsCommon;
import chylex.bettercontrols.config.BetterControlsConfig; import chylex.bettercontrols.config.BetterControlsConfig;
import chylex.bettercontrols.input.KeyBindingWithModifier;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import net.minecraft.client.Options; import net.minecraft.client.Options;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
@@ -38,6 +37,5 @@ public abstract class HookLoadGameOptions {
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));
} }
} }

View File

@@ -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);
}
}

View File

@@ -1,5 +1,6 @@
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.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue;
@@ -13,7 +14,6 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.Slice;
@Mixin(Player.class) @Mixin(Player.class)
@SuppressWarnings({ "SameReturnValue", "UnreachableCode" })
public abstract class HookPlayerHorizontalFlightSpeed extends LivingEntity { public abstract class HookPlayerHorizontalFlightSpeed extends LivingEntity {
protected HookPlayerHorizontalFlightSpeed(EntityType<? extends LivingEntity> type, Level world) { protected HookPlayerHorizontalFlightSpeed(EntityType<? extends LivingEntity> type, Level world) {
super(type, world); super(type, world);
@@ -29,8 +29,7 @@ public abstract class HookPlayerHorizontalFlightSpeed extends LivingEntity {
) )
) )
private boolean disableVanillaSprintBoost(boolean isSprinting) { private boolean disableVanillaSprintBoost(boolean isSprinting) {
@SuppressWarnings("ConstantConditions") Player me = Mixins.me(this);
Player me = (Player)(Object)this;
if (me instanceof LocalPlayer localPlayer && FlightHelper.isFlyingCreativeOrSpectator(localPlayer)) { if (me instanceof LocalPlayer localPlayer && FlightHelper.isFlyingCreativeOrSpectator(localPlayer)) {
return false; return false;
@@ -42,8 +41,7 @@ public abstract class HookPlayerHorizontalFlightSpeed extends LivingEntity {
@ModifyReturnValue(method = "getFlyingSpeed", at = @At("RETURN")) @ModifyReturnValue(method = "getFlyingSpeed", at = @At("RETURN"))
private float modifyHorizontalFlyingSpeed(float flyingSpeed) { private float modifyHorizontalFlyingSpeed(float flyingSpeed) {
@SuppressWarnings("ConstantConditions") Player me = Mixins.me(this);
Player me = (Player)(Object)this;
if (me instanceof LocalPlayer localPlayer && localPlayer.getAbilities().flying) { if (me instanceof LocalPlayer localPlayer && localPlayer.getAbilities().flying) {
return flyingSpeed * FlightHelper.getHorizontalSpeedMultiplier(localPlayer); return flyingSpeed * FlightHelper.getHorizontalSpeedMultiplier(localPlayer);

View File

@@ -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(String translationKey, int code, String category) { public HookStickyKeyBindingState(String translationKey, int code, KeyMapping.Category 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(boolean pressed, 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();
} }
} }

View File

@@ -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(Options options, int x, int y, int width, Consumer<T> callback, 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);
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;

View File

@@ -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,8 +14,8 @@ 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);
} }
public static boolean isFlyingCreativeOrSpectator(LocalPlayer player) { public static boolean isFlyingCreativeOrSpectator(LocalPlayer player) {
@@ -24,15 +23,15 @@ public final class FlightHelper {
} }
static boolean shouldFlyOnGround(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(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;
@@ -41,10 +40,10 @@ public final class FlightHelper {
public static float getVerticalSpeedMultiplier(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;

View File

@@ -1,30 +1,28 @@
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.ToggleKeyMapping;
import net.minecraft.client.player.ClientInput; 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 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;
@@ -39,10 +37,6 @@ 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(LocalPlayer player) { private PlayerTicker(LocalPlayer player) {
@@ -52,10 +46,10 @@ public final class PlayerTicker {
// 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;
@@ -69,7 +63,7 @@ public final class PlayerTicker {
private int temporaryFlyOnGroundTimer = 0; private int temporaryFlyOnGroundTimer = 0;
private void setup() { private void setup() {
AccessStickyKeyBindingStateGetter sprint = (AccessStickyKeyBindingStateGetter)KEY_SPRINT; AccessToggleKeyMappingFields sprint = Mixins.toggleKeyMappingFields(KEY_SPRINT);
BooleanSupplier getter = sprint.getNeedsToggle(); BooleanSupplier getter = sprint.getNeedsToggle();
if (getter instanceof SprintPressGetter g) { if (getter instanceof SprintPressGetter g) {
@@ -84,16 +78,16 @@ public final class PlayerTicker {
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);
} }
SprintMode sprintMode = cfg().sprintMode; SprintMode sprintMode = getConfig().sprintMode;
boolean wasSprintToggled = Boolean.TRUE.equals(OPTIONS.toggleSprint().get()); boolean wasSprintToggled = OPTIONS.toggleSprint().get().booleanValue();
boolean isSprintToggled = toggleSprint.tick(); boolean isSprintToggled = toggleSprint.tick();
if (temporarySprintTimer > 0) { if (temporarySprintTimer > 0) {
@@ -170,7 +164,7 @@ public final class PlayerTicker {
player.input.makeJump(); player.input.makeJump();
} }
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();
@@ -229,20 +223,26 @@ public final class PlayerTicker {
holdingSneakWhileTouchingGround = false; holdingSneakWhileTouchingGround = false;
} }
if (FlightHelper.isFlyingCreativeOrSpectator(player) && cfg().disableFlightInertia) { if (FlightHelper.isFlyingCreativeOrSpectator(player)) {
ClientInput 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.keyPresses.jump() && !input.keyPresses.shift()) {
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()) {
boolean isFlying = !player.getAbilities().flying; boolean isFlying = !player.getAbilities().flying;
player.getAbilities().flying = isFlying; player.getAbilities().flying = isFlying;
@@ -267,27 +267,27 @@ public final class PlayerTicker {
temporaryFlyOnGroundTimer = 0; temporaryFlyOnGroundTimer = 0;
} }
if (!cfg().sneakingMovesCameraSmoothly) { if (!getConfig().sneakingMovesCameraSmoothly) {
Camera camera = MINECRAFT.gameRenderer.getMainCamera(); Camera camera = MINECRAFT.gameRenderer.getMainCamera();
if (camera.getEntity() == player) { if (camera.entity() == 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(null)); MINECRAFT.setScreen(new BetterControlsScreen(null));
} }
} }
public boolean shouldResetFOV(LocalPlayer player) { public static boolean shouldResetFOV(LocalPlayer player) {
return cfg().disableChangingFovWhileFlying && FlightHelper.isFlyingCreativeOrSpectator(player); return getConfig().disableChangingFovWhileFlying && FlightHelper.isFlyingCreativeOrSpectator(player);
} }
} }

View File

@@ -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"