diff --git a/src/main/java/chylex/bettercontrols/config/BetterControlsConfig.java b/src/main/java/chylex/bettercontrols/config/BetterControlsConfig.java index e790bb5..9db9f0b 100644 --- a/src/main/java/chylex/bettercontrols/config/BetterControlsConfig.java +++ b/src/main/java/chylex/bettercontrols/config/BetterControlsConfig.java @@ -9,6 +9,8 @@ public final class BetterControlsConfig{ private Path path; + public boolean doubleTapForwardToSprint = true; + BetterControlsConfig(){} private BetterControlsConfig setPath(final Path path){ diff --git a/src/main/java/chylex/bettercontrols/config/ConfigSerializer.java b/src/main/java/chylex/bettercontrols/config/ConfigSerializer.java index fc50a7b..4a6e00e 100644 --- a/src/main/java/chylex/bettercontrols/config/ConfigSerializer.java +++ b/src/main/java/chylex/bettercontrols/config/ConfigSerializer.java @@ -30,6 +30,8 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js public JsonElement serialize(final BetterControlsConfig cfg, final Type typeOfSrc, final JsonSerializationContext context){ final JsonObject obj = new JsonObject(); + Json.setBool(obj, "Sprint.DoubleTapForward", cfg.doubleTapForwardToSprint); + return obj; } @@ -38,6 +40,8 @@ final class ConfigSerializer implements JsonSerializer<BetterControlsConfig>, Js final BetterControlsConfig cfg = new BetterControlsConfig(); final JsonObject obj = json.getAsJsonObject(); + cfg.doubleTapForwardToSprint = Json.getBool(obj, "Sprint.DoubleTapForward", cfg.doubleTapForwardToSprint); + return cfg; } diff --git a/src/main/java/chylex/bettercontrols/gui/BetterControlsScreen.java b/src/main/java/chylex/bettercontrols/gui/BetterControlsScreen.java index cc80a80..233c7e0 100644 --- a/src/main/java/chylex/bettercontrols/gui/BetterControlsScreen.java +++ b/src/main/java/chylex/bettercontrols/gui/BetterControlsScreen.java @@ -1,6 +1,9 @@ package chylex.bettercontrols.gui; import chylex.bettercontrols.BetterControlsMod; +import chylex.bettercontrols.config.BetterControlsConfig; +import chylex.bettercontrols.gui.elements.BooleanValueWidget; import chylex.bettercontrols.gui.elements.KeyBindingWidget; +import chylex.bettercontrols.gui.elements.TextWidget; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.Element; import net.minecraft.client.gui.screen.Screen; @@ -11,9 +14,14 @@ import net.minecraft.client.options.KeyBinding; import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; import org.lwjgl.glfw.GLFW; import java.util.ArrayList; import java.util.List; +import static chylex.bettercontrols.gui.OptionListWidget.COL2_W; +import static chylex.bettercontrols.gui.OptionListWidget.ROW_WIDTH; +import static chylex.bettercontrols.gui.OptionListWidget.col2; +import static chylex.bettercontrols.gui.elements.TextWidget.CENTER; public class BetterControlsScreen extends GameOptionsScreen{ public static final LiteralText TITLE = new LiteralText("Better Controls"); @@ -23,6 +31,26 @@ public class BetterControlsScreen extends GameOptionsScreen{ private static final int TITLE_MARGIN_TOP = 3; private static final int ROW_HEIGHT = 22; + // Options + + private int generateSprintingOptions(int y, final List<Element> elements){ + final BetterControlsConfig cfg = BetterControlsMod.config; + + generateLeftSideText(y, elements, Text.of("Double Tap 'Walk Forwards' To Sprint")); + elements.add(new BooleanValueWidget(col2(1), y, COL2_W, cfg.doubleTapForwardToSprint, value -> cfg.doubleTapForwardToSprint = value)); + + y += ROW_HEIGHT; + return y; + } + + // Helpers + + private static void generateLeftSideText(final int y, final List<Element> elements, final Text text){ + elements.add(new TextWidget(col2(0), y, COL2_W - TEXT_PADDING_RIGHT, text)); + } + + // Instance + private OptionListWidget optionsWidget; private KeyBindingWidget editingKeyBinding; private final List<KeyBindingWidget> allKeyBindings = new ArrayList<>(); @@ -38,6 +66,9 @@ public class BetterControlsScreen extends GameOptionsScreen{ final List<Element> elements = new ArrayList<>(); int y = 0; + elements.add(new TextWidget(0, y, ROW_WIDTH, ROW_HEIGHT, Text.of("Sprinting"), CENTER)); + y = generateSprintingOptions(y + ROW_HEIGHT, elements) + TITLE_MARGIN_TOP; + addButton(new ButtonWidget(width / 2 - 99, height - 29, 200, 20, ScreenTexts.DONE, btn -> client.openScreen(parent))); addChild(optionsWidget = new OptionListWidget(21, height - 32, width, height, elements, y - TITLE_MARGIN_TOP + BOTTOM_PADDING)); } diff --git a/src/main/java/chylex/bettercontrols/mixin/AccessClientPlayerFields.java b/src/main/java/chylex/bettercontrols/mixin/AccessClientPlayerFields.java new file mode 100644 index 0000000..93c1073 --- /dev/null +++ b/src/main/java/chylex/bettercontrols/mixin/AccessClientPlayerFields.java @@ -0,0 +1,10 @@ +package chylex.bettercontrols.mixin; +import net.minecraft.client.network.ClientPlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ClientPlayerEntity.class) +public interface AccessClientPlayerFields{ + @Accessor + void setTicksLeftToDoubleTapSprint(int value); +} diff --git a/src/main/java/chylex/bettercontrols/mixin/HookClientPlayerTick.java b/src/main/java/chylex/bettercontrols/mixin/HookClientPlayerTick.java new file mode 100644 index 0000000..0ca4ea2 --- /dev/null +++ b/src/main/java/chylex/bettercontrols/mixin/HookClientPlayerTick.java @@ -0,0 +1,23 @@ +package chylex.bettercontrols.mixin; +import chylex.bettercontrols.player.PlayerTicker; +import com.mojang.authlib.GameProfile; +import net.minecraft.client.network.AbstractClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientPlayerEntity.class) +public abstract class HookClientPlayerTick extends AbstractClientPlayerEntity{ + protected HookClientPlayerTick(final ClientWorld world, final GameProfile profile){ + super(world, profile); + } + + @Inject(method = "tickMovement()V", at = @At("HEAD")) + private void atHead(final CallbackInfo info){ + final ClientPlayerEntity player = (ClientPlayerEntity)(Object)this; + PlayerTicker.get(player).atHead(player); + } +} diff --git a/src/main/java/chylex/bettercontrols/player/PlayerTicker.java b/src/main/java/chylex/bettercontrols/player/PlayerTicker.java new file mode 100644 index 0000000..4610009 --- /dev/null +++ b/src/main/java/chylex/bettercontrols/player/PlayerTicker.java @@ -0,0 +1,41 @@ +package chylex.bettercontrols.player; +import chylex.bettercontrols.BetterControlsMod; +import chylex.bettercontrols.config.BetterControlsConfig; +import chylex.bettercontrols.mixin.AccessClientPlayerFields; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import java.lang.ref.WeakReference; + +public final class PlayerTicker{ + private static PlayerTicker ticker = new PlayerTicker(null); + + public static PlayerTicker get(final ClientPlayerEntity player){ + if (ticker.ref.get() != player){ + ticker = new PlayerTicker(player); + } + + return ticker; + } + + private static MinecraftClient mc(){ + return MinecraftClient.getInstance(); + } + + private static BetterControlsConfig cfg(){ + return BetterControlsMod.config; + } + + private final WeakReference<ClientPlayerEntity> ref; + + private PlayerTicker(final ClientPlayerEntity player){ + this.ref = new WeakReference<>(player); + } + + // Logic + + public void atHead(final ClientPlayerEntity player){ + if (!cfg().doubleTapForwardToSprint){ + ((AccessClientPlayerFields)player).setTicksLeftToDoubleTapSprint(0); + } + } +} diff --git a/src/main/resources/mixins.json b/src/main/resources/mixins.json index 77d4e93..d4b0038 100644 --- a/src/main/resources/mixins.json +++ b/src/main/resources/mixins.json @@ -4,8 +4,10 @@ "package": "chylex.bettercontrols.mixin", "compatibilityLevel": "JAVA_8", "mixins": [ + "AccessClientPlayerFields", "AccessKeyBindingFields", "AccessScreenButtons", + "HookClientPlayerTick", "HookLoadGameOptions", "HookOpenScreen", "HookStickyKeyBindingState"