mirror of
https://github.com/chylex/Better-Controls.git
synced 2025-05-24 10:34:04 +02:00
Add sticky keybind hooks to support better toggling
This commit is contained in:
parent
bee7dee256
commit
482daeba3f
src/main
java/chylex/bettercontrols
input
mixin
resources
83
src/main/java/chylex/bettercontrols/input/ToggleTracker.java
Normal file
83
src/main/java/chylex/bettercontrols/input/ToggleTracker.java
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package chylex.bettercontrols.input;
|
||||||
|
import net.minecraft.client.options.KeyBinding;
|
||||||
|
|
||||||
|
public class ToggleTracker{
|
||||||
|
protected final KeyBinding bindingToggle;
|
||||||
|
protected final KeyBinding bindingReset;
|
||||||
|
|
||||||
|
protected boolean isToggled;
|
||||||
|
|
||||||
|
private boolean waitForRelease;
|
||||||
|
private boolean hasToggledWhileHoldingReset;
|
||||||
|
private boolean skipNextToggle;
|
||||||
|
|
||||||
|
public ToggleTracker(final KeyBinding bindingToggle, final KeyBinding bindingReset){
|
||||||
|
this.bindingToggle = bindingToggle;
|
||||||
|
this.bindingReset = bindingReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assume holding CTRL is used to sprint (reset key) and either G or CTRL + G is used to toggle sprint on.
|
||||||
|
* The toggle modifier actually does not matter, having it just prevents trigger if the key is pressed alone.
|
||||||
|
*
|
||||||
|
* Pressing the toggle key alone switches the toggled state, then the key has to be released.
|
||||||
|
* Holding the reset key while toggled will reset the toggle, but continue the sprint until released.
|
||||||
|
*
|
||||||
|
* Pressing the toggle key while holding reset key switches the toggled state, but allows both keys to be
|
||||||
|
* released without interrupting the toggle (hasToggledWhileHoldingReset).
|
||||||
|
*
|
||||||
|
* Note that holding the reset key and pressing toggle key twice will switch the toggle twice, as expected.
|
||||||
|
*
|
||||||
|
* Holding the reset key while toggled, then pressing the toggle key again, will do nothing (skipNextToggle).
|
||||||
|
* This allows resetting the toggle both by pressing the reset key alone, or with the full toggle key combo.
|
||||||
|
* However, if the toggle key is then pressed again, it will function as usual.
|
||||||
|
*
|
||||||
|
* All of the logic combined allows for complex scenarios such as:
|
||||||
|
*
|
||||||
|
* +CTRL, +G, -CTRL --> toggled on
|
||||||
|
* +CTRL, +G, -CTRL, +CTRL --> toggled off (quick reset)
|
||||||
|
* +CTRL, +G, -CTRL, +CTRL, +G --> toggled off (full combo)
|
||||||
|
* +CTRL, +G, -CTRL, +CTRL, +G, +G --> toggled on
|
||||||
|
*/
|
||||||
|
|
||||||
|
public boolean tick(){
|
||||||
|
final boolean isHoldingReset = isResetKeyPressed();
|
||||||
|
|
||||||
|
if (bindingToggle.isPressed()){
|
||||||
|
if (!waitForRelease){
|
||||||
|
if (skipNextToggle){
|
||||||
|
skipNextToggle = false;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
isToggled = !isToggled;
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForRelease = true;
|
||||||
|
hasToggledWhileHoldingReset = isHoldingReset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
waitForRelease = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isToggled){
|
||||||
|
if (hasToggledWhileHoldingReset && !isHoldingReset){
|
||||||
|
hasToggledWhileHoldingReset = false;
|
||||||
|
}
|
||||||
|
else if (!hasToggledWhileHoldingReset && isHoldingReset){
|
||||||
|
isToggled = false;
|
||||||
|
skipNextToggle = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipNextToggle && !isHoldingReset){
|
||||||
|
skipNextToggle = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isToggled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isResetKeyPressed(){
|
||||||
|
return bindingReset.isPressed();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package chylex.bettercontrols.input;
|
||||||
|
import chylex.bettercontrols.mixin.AccessKeyBindingFields;
|
||||||
|
import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
|
||||||
|
import net.minecraft.client.options.KeyBinding;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class ToggleTrackerForStickyKey extends ToggleTracker{
|
||||||
|
private static final Set<KeyBinding> enabledOverrides = new HashSet<>();
|
||||||
|
|
||||||
|
public static boolean isOverrideEnabled(final KeyBinding binding){
|
||||||
|
return enabledOverrides.contains(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BooleanConsumer setToggleState;
|
||||||
|
|
||||||
|
public ToggleTrackerForStickyKey(final KeyBinding bindingToggle, final KeyBinding bindingStickyReset, final BooleanConsumer setToggleState){
|
||||||
|
super(bindingToggle, bindingStickyReset);
|
||||||
|
this.setToggleState = setToggleState;
|
||||||
|
this.setToggleState.accept(false);
|
||||||
|
enabledOverrides.add(bindingStickyReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tick(){
|
||||||
|
final boolean isToggled = super.tick();
|
||||||
|
setToggleState.accept(isToggled);
|
||||||
|
return isToggled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isResetKeyPressed(){
|
||||||
|
return ((AccessKeyBindingFields)bindingReset).isPressedField();
|
||||||
|
}
|
||||||
|
}
|
@ -10,4 +10,10 @@ public interface AccessKeyBindingFields{
|
|||||||
static Map<String, Integer> getCategoryOrderMap(){
|
static Map<String, Integer> getCategoryOrderMap(){
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Accessor("pressed")
|
||||||
|
boolean isPressedField();
|
||||||
|
|
||||||
|
@Accessor("pressed")
|
||||||
|
void setPressedField(boolean value);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package chylex.bettercontrols.mixin;
|
||||||
|
import chylex.bettercontrols.input.ToggleTrackerForStickyKey;
|
||||||
|
import net.minecraft.client.options.KeyBinding;
|
||||||
|
import net.minecraft.client.options.StickyKeyBinding;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
|
@Mixin(StickyKeyBinding.class)
|
||||||
|
public abstract class HookStickyKeyBindingState extends KeyBinding{
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private BooleanSupplier toggleGetter;
|
||||||
|
|
||||||
|
public HookStickyKeyBindingState(final String translationKey, final int code, final String category){
|
||||||
|
super(translationKey, code, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "setPressed(Z)V", at = @At("HEAD"), cancellable = true)
|
||||||
|
public void setPressed(final boolean pressed, final CallbackInfo info){
|
||||||
|
if (ToggleTrackerForStickyKey.isOverrideEnabled(this)){
|
||||||
|
((AccessKeyBindingFields)this).setPressedField(pressed);
|
||||||
|
info.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPressed(){
|
||||||
|
return super.isPressed() || (ToggleTrackerForStickyKey.isOverrideEnabled(this) && toggleGetter.getAsBoolean());
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,8 @@
|
|||||||
"AccessKeyBindingFields",
|
"AccessKeyBindingFields",
|
||||||
"AccessScreenButtons",
|
"AccessScreenButtons",
|
||||||
"HookLoadGameOptions",
|
"HookLoadGameOptions",
|
||||||
"HookOpenScreen"
|
"HookOpenScreen",
|
||||||
|
"HookStickyKeyBindingState"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
|
Loading…
Reference in New Issue
Block a user