1
0
mirror of https://github.com/chylex/Better-Controls.git synced 2025-04-09 15:15:51 +02:00

Add sticky keybind hooks to support better toggling

This commit is contained in:
chylex 2020-10-17 10:53:01 +02:00
parent bee7dee256
commit 482daeba3f
5 changed files with 161 additions and 1 deletions

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

View File

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

View File

@ -10,4 +10,10 @@ public interface AccessKeyBindingFields{
static Map<String, Integer> getCategoryOrderMap(){
throw new AssertionError();
}
@Accessor("pressed")
boolean isPressedField();
@Accessor("pressed")
void setPressedField(boolean value);
}

View File

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

View File

@ -7,7 +7,8 @@
"AccessKeyBindingFields",
"AccessScreenButtons",
"HookLoadGameOptions",
"HookOpenScreen"
"HookOpenScreen",
"HookStickyKeyBindingState"
],
"injectors": {
"defaultRequire": 1