diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/Controlify.java index a0349c6..a5d71a0 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/Controlify.java @@ -15,7 +15,7 @@ import dev.isxander.controlify.screenop.ScreenProcessorProvider; import dev.isxander.controlify.config.ControlifyConfig; import dev.isxander.controlify.controller.hid.ControllerHIDService; import dev.isxander.controlify.api.event.ControlifyEvents; -import dev.isxander.controlify.ingame.guide.InGameButtonGuide; +import dev.isxander.controlify.gui.guide.InGameButtonGuide; import dev.isxander.controlify.ingame.InGameInputHandler; import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; import dev.isxander.controlify.sound.ControlifySounds; @@ -270,7 +270,8 @@ public class Controlify implements ControlifyApi { this.virtualMouseHandler().handleControllerInput(controller); if (minecraft.screen != null) { ScreenProcessorProvider.provide(minecraft.screen).onControllerUpdate(controller); - } else if (minecraft.level != null) { + } + if (minecraft.level != null) { this.inGameInputHandler().ifPresent(InGameInputHandler::inputTick); } diff --git a/src/main/java/dev/isxander/controlify/api/ingameguide/ActionPriority.java b/src/main/java/dev/isxander/controlify/api/guide/ActionPriority.java similarity index 80% rename from src/main/java/dev/isxander/controlify/api/ingameguide/ActionPriority.java rename to src/main/java/dev/isxander/controlify/api/guide/ActionPriority.java index 997198b..8284cc7 100644 --- a/src/main/java/dev/isxander/controlify/api/ingameguide/ActionPriority.java +++ b/src/main/java/dev/isxander/controlify/api/guide/ActionPriority.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.api.ingameguide; +package dev.isxander.controlify.api.guide; /** * Defines how the action is sorted in the list. All default Controlify actions are {@link #NORMAL}. diff --git a/src/main/java/dev/isxander/controlify/api/ingameguide/GuideActionNameSupplier.java b/src/main/java/dev/isxander/controlify/api/guide/GuideActionNameSupplier.java similarity index 66% rename from src/main/java/dev/isxander/controlify/api/ingameguide/GuideActionNameSupplier.java rename to src/main/java/dev/isxander/controlify/api/guide/GuideActionNameSupplier.java index 82e3eab..6dbce29 100644 --- a/src/main/java/dev/isxander/controlify/api/ingameguide/GuideActionNameSupplier.java +++ b/src/main/java/dev/isxander/controlify/api/guide/GuideActionNameSupplier.java @@ -1,4 +1,4 @@ -package dev.isxander.controlify.api.ingameguide; +package dev.isxander.controlify.api.guide; import net.minecraft.network.chat.Component; @@ -11,6 +11,6 @@ import java.util.Optional; * This is supplied once every tick. */ @FunctionalInterface -public interface GuideActionNameSupplier { - Optional supply(IngameGuideContext ctx); +public interface GuideActionNameSupplier { + Optional supply(T ctx); } diff --git a/src/main/java/dev/isxander/controlify/api/ingameguide/IngameGuideRegistry.java b/src/main/java/dev/isxander/controlify/api/ingameguide/IngameGuideRegistry.java index c85033b..60c969d 100644 --- a/src/main/java/dev/isxander/controlify/api/ingameguide/IngameGuideRegistry.java +++ b/src/main/java/dev/isxander/controlify/api/ingameguide/IngameGuideRegistry.java @@ -1,6 +1,8 @@ package dev.isxander.controlify.api.ingameguide; import dev.isxander.controlify.api.bind.ControllerBinding; +import dev.isxander.controlify.api.guide.ActionPriority; +import dev.isxander.controlify.api.guide.GuideActionNameSupplier; /** * Allows you to register your own actions to the button guide. @@ -16,7 +18,7 @@ public interface IngameGuideRegistry { * @param priority the priority of the action, used to sort the list. * @param supplier the supplier for the name of the action. can be empty to hide the action. */ - void registerGuideAction(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier); + void registerGuideAction(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier); /** * Registers a new action to the button guide. @@ -25,5 +27,5 @@ public interface IngameGuideRegistry { * @param location the location of the action, left or right. * @param supplier the supplier for the name of the action. can be empty to hide the action. */ - void registerGuideAction(ControllerBinding binding, ActionLocation location, GuideActionNameSupplier supplier); + void registerGuideAction(ControllerBinding binding, ActionLocation location, GuideActionNameSupplier supplier); } diff --git a/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java b/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java index 308a266..b221cbe 100644 --- a/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java +++ b/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java @@ -194,7 +194,6 @@ public class ControllerBindings { .defaultBind(GamepadBinds.Y_BUTTON) .category(INVENTORY_CATEGORY) .context(BindContexts.INGAME) - .vanillaOverride(options.keyInventory, () -> false) .build()); register(CHANGE_PERSPECTIVE = ControllerBindingBuilder.create(controller) .identifier("controlify", "change_perspective") diff --git a/src/main/java/dev/isxander/controlify/config/gui/YACLHelper.java b/src/main/java/dev/isxander/controlify/config/gui/YACLHelper.java index 2545d63..0fdce07 100644 --- a/src/main/java/dev/isxander/controlify/config/gui/YACLHelper.java +++ b/src/main/java/dev/isxander/controlify/config/gui/YACLHelper.java @@ -5,6 +5,7 @@ import dev.isxander.controlify.Controlify; import dev.isxander.controlify.ControllerManager; import dev.isxander.controlify.api.bind.ControllerBinding; import dev.isxander.controlify.bindings.BindContext; +import dev.isxander.controlify.bindings.EmptyBind; import dev.isxander.controlify.config.GlobalSettings; import dev.isxander.controlify.controller.BatteryLevel; import dev.isxander.controlify.controller.Controller; @@ -297,7 +298,8 @@ public class YACLHelper { .stream() .anyMatch(ctxs::contains); boolean bindMatches = pair.option().pendingValue().equals(opt.option().pendingValue()); - return contextsMatch && bindMatches; + boolean bindIsNotEmpty = !(pair.option().pendingValue() instanceof EmptyBind); + return contextsMatch && bindMatches && bindIsNotEmpty; }).toList(); conflicting.forEach(conflict -> ((AbstractBindController) conflict.option().controller()).setConflicting(true)); diff --git a/src/main/java/dev/isxander/controlify/gui/guide/ContainerButtonGuide.java b/src/main/java/dev/isxander/controlify/gui/guide/ContainerButtonGuide.java new file mode 100644 index 0000000..1b27475 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/gui/guide/ContainerButtonGuide.java @@ -0,0 +1,4 @@ +package dev.isxander.controlify.gui.guide; + +public class ContainerButtonGuide { +} diff --git a/src/main/java/dev/isxander/controlify/gui/guide/ContainerGuideCtx.java b/src/main/java/dev/isxander/controlify/gui/guide/ContainerGuideCtx.java new file mode 100644 index 0000000..1b7cacd --- /dev/null +++ b/src/main/java/dev/isxander/controlify/gui/guide/ContainerGuideCtx.java @@ -0,0 +1,8 @@ +package dev.isxander.controlify.gui.guide; + +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +public record ContainerGuideCtx(@Nullable Slot hoveredSlot, ItemStack holdingItem, boolean cursorOutsideContainer) { +} diff --git a/src/main/java/dev/isxander/controlify/gui/guide/GuideAction.java b/src/main/java/dev/isxander/controlify/gui/guide/GuideAction.java new file mode 100644 index 0000000..f861a1b --- /dev/null +++ b/src/main/java/dev/isxander/controlify/gui/guide/GuideAction.java @@ -0,0 +1,17 @@ +package dev.isxander.controlify.gui.guide; + +import dev.isxander.controlify.api.bind.ControllerBinding; +import dev.isxander.controlify.api.guide.ActionPriority; +import dev.isxander.controlify.api.guide.GuideActionNameSupplier; +import org.jetbrains.annotations.NotNull; + +public record GuideAction(ControllerBinding binding, GuideActionNameSupplier name, ActionPriority priority) implements Comparable> { + public GuideAction(ControllerBinding binding, GuideActionNameSupplier name) { + this(binding, name, ActionPriority.NORMAL); + } + + @Override + public int compareTo(@NotNull GuideAction o) { + return this.priority().compareTo(o.priority()); + } +} diff --git a/src/main/java/dev/isxander/controlify/ingame/guide/GuideActionRenderer.java b/src/main/java/dev/isxander/controlify/gui/guide/GuideActionRenderer.java similarity index 72% rename from src/main/java/dev/isxander/controlify/ingame/guide/GuideActionRenderer.java rename to src/main/java/dev/isxander/controlify/gui/guide/GuideActionRenderer.java index 2f57882..1d34a5b 100644 --- a/src/main/java/dev/isxander/controlify/ingame/guide/GuideActionRenderer.java +++ b/src/main/java/dev/isxander/controlify/gui/guide/GuideActionRenderer.java @@ -1,8 +1,7 @@ -package dev.isxander.controlify.ingame.guide; +package dev.isxander.controlify.gui.guide; import com.mojang.blaze3d.vertex.PoseStack; import dev.isxander.controlify.api.bind.BindRenderer; -import dev.isxander.controlify.api.ingameguide.IngameGuideContext; import dev.isxander.controlify.gui.DrawSize; import dev.isxander.controlify.gui.layout.RenderComponent; import net.minecraft.client.Minecraft; @@ -14,20 +13,22 @@ import org.joml.Vector2ic; import java.util.Optional; -public class GuideActionRenderer implements RenderComponent { - private final GuideAction guideAction; +public class GuideActionRenderer implements RenderComponent { + private final GuideAction guideAction; private final boolean rtl; + private final boolean textContrast; private Optional name = Optional.empty(); - public GuideActionRenderer(GuideAction action, boolean rtl) { + public GuideActionRenderer(GuideAction action, boolean rtl, boolean textContrast) { this.guideAction = action; this.rtl = rtl; + this.textContrast = textContrast; } @Override public void render(PoseStack stack, int x, int y, float deltaTime) { - if (name.isEmpty()) + if (!isVisible()) return; Font font = Minecraft.getInstance().font; @@ -41,7 +42,8 @@ public class GuideActionRenderer implements RenderComponent { int textX = x + (rtl ? 0 : drawSize.width() + 2); int textY = y + drawSize.height() / 2 - font.lineHeight / 2; - GuiComponent.fill(stack, textX - 1, textY - 1, textX + textWidth + 1, textY + font.lineHeight + 1, 0x80000000); + if (textContrast) + GuiComponent.fill(stack, textX - 1, textY - 1, textX + textWidth + 1, textY + font.lineHeight + 1, 0x80000000); font.draw(stack, name.get(), textX, textY, 0xFFFFFF); } @@ -55,10 +57,10 @@ public class GuideActionRenderer implements RenderComponent { @Override public boolean isVisible() { - return name.isPresent(); + return name.isPresent() && !guideAction.binding().isUnbound(); } - public void updateName(IngameGuideContext ctx) { + public void updateName(T ctx) { name = guideAction.name().supply(ctx); } } diff --git a/src/main/java/dev/isxander/controlify/ingame/guide/InGameButtonGuide.java b/src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java similarity index 83% rename from src/main/java/dev/isxander/controlify/ingame/guide/InGameButtonGuide.java rename to src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java index d821509..d478666 100644 --- a/src/main/java/dev/isxander/controlify/ingame/guide/InGameButtonGuide.java +++ b/src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java @@ -1,9 +1,10 @@ -package dev.isxander.controlify.ingame.guide; +package dev.isxander.controlify.gui.guide; import com.mojang.blaze3d.vertex.PoseStack; import dev.isxander.controlify.api.bind.ControllerBinding; +import dev.isxander.controlify.api.guide.ActionPriority; +import dev.isxander.controlify.api.guide.GuideActionNameSupplier; import dev.isxander.controlify.api.ingameguide.*; -import dev.isxander.controlify.bindings.ControllerBindingImpl; import dev.isxander.controlify.compatibility.ControlifyCompat; import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.api.event.ControlifyEvents; @@ -11,8 +12,6 @@ import dev.isxander.controlify.gui.layout.AnchorPoint; import dev.isxander.controlify.gui.layout.ColumnLayoutComponent; import dev.isxander.controlify.gui.layout.PositionedComponent; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiComponent; -import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import net.minecraft.network.chat.Component; import net.minecraft.world.effect.MobEffects; @@ -29,11 +28,11 @@ public class InGameButtonGuide implements IngameGuideRegistry { private final LocalPlayer player; private final Minecraft minecraft = Minecraft.getInstance(); - private final List leftGuides = new ArrayList<>(); - private final List rightGuides = new ArrayList<>(); + private final List> leftGuides = new ArrayList<>(); + private final List> rightGuides = new ArrayList<>(); - private final PositionedComponent> leftLayout; - private final PositionedComponent> rightLayout; + private final PositionedComponent>> leftLayout; + private final PositionedComponent>> rightLayout; public InGameButtonGuide(Controller controller, LocalPlayer localPlayer) { this.controller = controller; @@ -46,11 +45,11 @@ public class InGameButtonGuide implements IngameGuideRegistry { Collections.sort(rightGuides); leftLayout = new PositionedComponent<>( - ColumnLayoutComponent.builder() - .elementPadding(2) + ColumnLayoutComponent.>builder() + .spacing(2) .colPadding(4, 4) .elementPosition(ColumnLayoutComponent.ElementPosition.LEFT) - .elements(leftGuides.stream().map(guide -> new GuideActionRenderer(guide, false)).toList()) + .elements(leftGuides.stream().map(guide -> new GuideActionRenderer<>(guide, false, true)).toList()) .build(), AnchorPoint.TOP_LEFT, 0, 0, @@ -58,11 +57,11 @@ public class InGameButtonGuide implements IngameGuideRegistry { ); rightLayout = new PositionedComponent<>( - ColumnLayoutComponent.builder() - .elementPadding(2) + ColumnLayoutComponent.>builder() + .spacing(2) .colPadding(4, 4) .elementPosition(ColumnLayoutComponent.ElementPosition.RIGHT) - .elements(rightGuides.stream().map(guide -> new GuideActionRenderer(guide, true)).toList()) + .elements(rightGuides.stream().map(guide -> new GuideActionRenderer<>(guide, true, true)).toList()) .build(), AnchorPoint.TOP_RIGHT, 0, 0, @@ -93,16 +92,16 @@ public class InGameButtonGuide implements IngameGuideRegistry { } @Override - public void registerGuideAction(ControllerBinding binding, ActionLocation location, GuideActionNameSupplier supplier) { + public void registerGuideAction(ControllerBinding binding, ActionLocation location, GuideActionNameSupplier supplier) { this.registerGuideAction(binding, location, ActionPriority.NORMAL, supplier); } @Override - public void registerGuideAction(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier) { + public void registerGuideAction(ControllerBinding binding, ActionLocation location, ActionPriority priority, GuideActionNameSupplier supplier) { if (location == ActionLocation.LEFT) - leftGuides.add(new GuideAction(binding, supplier, priority)); + leftGuides.add(new GuideAction<>(binding, supplier, priority)); else - rightGuides.add(new GuideAction(binding, supplier, priority)); + rightGuides.add(new GuideAction<>(binding, supplier, priority)); } private void registerDefaultActions() { @@ -110,18 +109,18 @@ public class InGameButtonGuide implements IngameGuideRegistry { registerGuideAction(controller.bindings().JUMP, ActionLocation.LEFT, (ctx) -> { var player = ctx.player(); if (player.getAbilities().flying) - return Optional.of(Component.translatable("controlify.guide.fly_up")); + return Optional.of(Component.translatable("controlify.guide.ingame.fly_up")); if (player.isOnGround()) return Optional.of(Component.translatable("key.jump")); if (player.isInWater()) - return Optional.of(Component.translatable("controlify.guide.swim_up")); + return Optional.of(Component.translatable("controlify.guide.ingame.swim_up")); if (!player.isOnGround() && !player.isFallFlying() && !player.isInWater() && !player.hasEffect(MobEffects.LEVITATION)) { var chestStack = player.getItemBySlot(EquipmentSlot.CHEST); if (chestStack.is(Items.ELYTRA) && ElytraItem.isFlyEnabled(chestStack)) - return Optional.of(Component.translatable("controlify.guide.start_elytra")); + return Optional.of(Component.translatable("controlify.guide.ingame.start_elytra")); } return Optional.empty(); @@ -129,16 +128,16 @@ public class InGameButtonGuide implements IngameGuideRegistry { registerGuideAction(controller.bindings().SNEAK, ActionLocation.LEFT, (ctx) -> { var player = ctx.player(); if (player.getVehicle() != null) - return Optional.of(Component.translatable("controlify.guide.dismount")); + return Optional.of(Component.translatable("controlify.guide.ingame.dismount")); if (player.getAbilities().flying) - return Optional.of(Component.translatable("controlify.guide.fly_down")); + return Optional.of(Component.translatable("controlify.guide.ingame.fly_down")); if (player.isInWater() && !player.isOnGround()) - return Optional.of(Component.translatable("controlify.guide.swim_down")); + return Optional.of(Component.translatable("controlify.guide.ingame.swim_down")); if (ctx.controller().config().toggleSneak) { - return Optional.of(Component.translatable(player.input.shiftKeyDown ? "controlify.guide.stop_sneaking" : "controlify.guide.start_sneaking")); + return Optional.of(Component.translatable(player.input.shiftKeyDown ? "controlify.guide.ingame.stop_sneaking" : "controlify.guide.ingame.start_sneaking")); } else { if (!player.input.shiftKeyDown) - return Optional.of(Component.translatable("controlify.guide.sneak")); + return Optional.of(Component.translatable("controlify.guide.ingame.sneak")); } return Optional.empty(); }); @@ -147,27 +146,27 @@ public class InGameButtonGuide implements IngameGuideRegistry { if (!options.keySprint.isDown()) { if (!player.input.getMoveVector().equals(Vec2.ZERO)) { if (player.isUnderWater()) - return Optional.of(Component.translatable("controlify.guide.start_swimming")); - return Optional.of(Component.translatable("controlify.guide.start_sprinting")); + return Optional.of(Component.translatable("controlify.guide.ingame.start_swimming")); + return Optional.of(Component.translatable("controlify.guide.ingame.start_sprinting")); } } else if (ctx.controller().config().toggleSprint) { if (player.isUnderWater()) - return Optional.of(Component.translatable("controlify.guide.stop_swimming")); - return Optional.of(Component.translatable("controlify.guide.stop_sprinting")); + return Optional.of(Component.translatable("controlify.guide.ingame.stop_swimming")); + return Optional.of(Component.translatable("controlify.guide.ingame.stop_sprinting")); } return Optional.empty(); }); registerGuideAction(controller.bindings().INVENTORY, ActionLocation.RIGHT, (ctx) -> { if (ctx.client().screen == null) - return Optional.of(Component.translatable("controlify.guide.inventory")); + return Optional.of(Component.translatable("controlify.guide.ingame.inventory")); return Optional.empty(); }); registerGuideAction(controller.bindings().ATTACK, ActionLocation.RIGHT, (ctx) -> { var hitResult = ctx.hitResult(); if (hitResult.getType() == HitResult.Type.ENTITY) - return Optional.of(Component.translatable("controlify.guide.attack")); + return Optional.of(Component.translatable("controlify.guide.ingame.attack")); if (hitResult.getType() == HitResult.Type.BLOCK) - return Optional.of(Component.translatable("controlify.guide.break")); + return Optional.of(Component.translatable("controlify.guide.ingame.break")); return Optional.empty(); }); registerGuideAction(controller.bindings().USE, ActionLocation.RIGHT, (ctx) -> { @@ -175,28 +174,28 @@ public class InGameButtonGuide implements IngameGuideRegistry { var player = ctx.player(); if (hitResult.getType() == HitResult.Type.ENTITY) if (player.isSpectator()) - return Optional.of(Component.translatable("controlify.guide.spectate")); + return Optional.of(Component.translatable("controlify.guide.ingame.spectate")); else - return Optional.of(Component.translatable("controlify.guide.interact")); + return Optional.of(Component.translatable("controlify.guide.ingame.interact")); if (hitResult.getType() == HitResult.Type.BLOCK || player.hasItemInSlot(EquipmentSlot.MAINHAND) || player.hasItemInSlot(EquipmentSlot.OFFHAND)) - return Optional.of(Component.translatable("controlify.guide.use")); + return Optional.of(Component.translatable("controlify.guide.ingame.use")); return Optional.empty(); }); registerGuideAction(controller.bindings().DROP, ActionLocation.RIGHT, (ctx) -> { var player = ctx.player(); if (player.hasItemInSlot(EquipmentSlot.MAINHAND) || player.hasItemInSlot(EquipmentSlot.OFFHAND)) - return Optional.of(Component.translatable("controlify.guide.drop")); + return Optional.of(Component.translatable("controlify.guide.ingame.drop")); return Optional.empty(); }); registerGuideAction(controller.bindings().SWAP_HANDS, ActionLocation.RIGHT, (ctx) -> { var player = ctx.player(); if (player.hasItemInSlot(EquipmentSlot.MAINHAND) || player.hasItemInSlot(EquipmentSlot.OFFHAND)) - return Optional.of(Component.translatable("controlify.guide.swap_hands")); + return Optional.of(Component.translatable("controlify.guide.ingame.swap_hands")); return Optional.empty(); }); registerGuideAction(controller.bindings().PICK_BLOCK, ActionLocation.RIGHT, (ctx) -> { if (ctx.hitResult().getType() == HitResult.Type.BLOCK && ctx.player().isCreative()) - return Optional.of(Component.translatable("controlify.guide.pick_block")); + return Optional.of(Component.translatable("controlify.guide.ingame.pick_block")); return Optional.empty(); }); } diff --git a/src/main/java/dev/isxander/controlify/gui/layout/ColumnLayoutComponent.java b/src/main/java/dev/isxander/controlify/gui/layout/ColumnLayoutComponent.java index 3c306a1..ba85880 100644 --- a/src/main/java/dev/isxander/controlify/gui/layout/ColumnLayoutComponent.java +++ b/src/main/java/dev/isxander/controlify/gui/layout/ColumnLayoutComponent.java @@ -118,7 +118,7 @@ public class ColumnLayoutComponent extends AbstractLa return this; } - public Builder elementPadding(int padding) { + public Builder spacing(int padding) { this.componentPaddingVertical = padding; return this; } diff --git a/src/main/java/dev/isxander/controlify/gui/layout/RowLayoutComponent.java b/src/main/java/dev/isxander/controlify/gui/layout/RowLayoutComponent.java index f0065b5..b2275a8 100644 --- a/src/main/java/dev/isxander/controlify/gui/layout/RowLayoutComponent.java +++ b/src/main/java/dev/isxander/controlify/gui/layout/RowLayoutComponent.java @@ -78,6 +78,12 @@ public class RowLayoutComponent extends AbstractLayou .sum() - elementPaddingHorizontal; } + @Override + public boolean isVisible() { + return this.getChildComponents().stream() + .anyMatch(RenderComponent::isVisible); + } + public static Builder builder() { return new Builder<>(); } @@ -115,7 +121,7 @@ public class RowLayoutComponent extends AbstractLayou return this; } - public Builder elementPadding(int padding) { + public Builder spacing(int padding) { this.elementPaddingHorizontal = padding; return this; } diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java index c4f905e..2bddd7c 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java @@ -7,6 +7,7 @@ import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.controller.gamepad.GamepadController; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.inventory.InventoryScreen; import net.minecraft.client.player.KeyboardInput; import net.minecraft.world.InteractionHand; @@ -41,7 +42,7 @@ public class InGameInputHandler { protected void handleKeybinds() { shouldShowPlayerList = false; - if (Minecraft.getInstance().screen != null) + if (minecraft.screen != null) return; if (controller.bindings().PAUSE.justPressed()) { @@ -62,6 +63,14 @@ public class InGameInputHandler { } } + if (controller.bindings().INVENTORY.justPressed()) { + if (minecraft.gameMode.isServerControlledInventory()) { + minecraft.player.sendOpenInventory(); + } else { + minecraft.getTutorial().onOpenInventory(); + minecraft.setScreen(new InventoryScreen(minecraft.player)); + } + } } if (controller.bindings().TOGGLE_HUD_VISIBILITY.justPressed()) { minecraft.options.hideGui = !minecraft.options.hideGui; @@ -74,19 +83,23 @@ public class InGameInputHandler { var player = this.minecraft.player; var gamepad = controller instanceof GamepadController ? (GamepadController) controller : null; + if (!minecraft.mouseHandler.isMouseGrabbed() || (!minecraft.isWindowActive() && !Controlify.instance().config().globalSettings().outOfFocusInput) || minecraft.screen != null || player == null) { + lookInputX = 0; + lookInputY = 0; + return; + } + // flick stick - turn 90 degrees immediately upon turning // should be paired with gyro controls if (gamepad != null && gamepad.config().flickStick) { - if (player != null) { - var turnAngle = 90 / 0.15f; // Entity#turn multiplies cursor delta by 0.15 to get rotation + var turnAngle = 90 / 0.15f; // Entity#turn multiplies cursor delta by 0.15 to get rotation - player.turn( - (controller.bindings().LOOK_RIGHT.justPressed() ? turnAngle : 0) - - (controller.bindings().LOOK_LEFT.justPressed() ? turnAngle : 0), - (controller.bindings().LOOK_DOWN.justPressed() ? turnAngle : 0) - - (controller.bindings().LOOK_UP.justPressed() ? turnAngle : 0) - ); - } + player.turn( + (controller.bindings().LOOK_RIGHT.justPressed() ? turnAngle : 0) + - (controller.bindings().LOOK_LEFT.justPressed() ? turnAngle : 0), + (controller.bindings().LOOK_DOWN.justPressed() ? turnAngle : 0) + - (controller.bindings().LOOK_UP.justPressed() ? turnAngle : 0) + ); return; } @@ -122,12 +135,8 @@ public class InGameInputHandler { impulseX = lookInputModifier.modifyX(impulseX, controller); impulseY = lookInputModifier.modifyY(impulseY, controller); - if (minecraft.mouseHandler.isMouseGrabbed() && (minecraft.isWindowActive() || Controlify.instance().config().globalSettings().outOfFocusInput) && player != null) { - lookInputX = impulseX * controller.config().horizontalLookSensitivity * 65f; - lookInputY = impulseY * controller.config().verticalLookSensitivity * 65f; - } else { - lookInputX = lookInputY = 0; - } + lookInputX = impulseX * controller.config().horizontalLookSensitivity * 65f; + lookInputY = impulseY * controller.config().verticalLookSensitivity * 65f; } public void processPlayerLook(float deltaTime) { diff --git a/src/main/java/dev/isxander/controlify/ingame/guide/GuideAction.java b/src/main/java/dev/isxander/controlify/ingame/guide/GuideAction.java deleted file mode 100644 index da6a4a4..0000000 --- a/src/main/java/dev/isxander/controlify/ingame/guide/GuideAction.java +++ /dev/null @@ -1,19 +0,0 @@ -package dev.isxander.controlify.ingame.guide; - -import dev.isxander.controlify.api.bind.ControllerBinding; -import dev.isxander.controlify.api.ingameguide.ActionLocation; -import dev.isxander.controlify.api.ingameguide.ActionPriority; -import dev.isxander.controlify.api.ingameguide.GuideActionNameSupplier; -import net.minecraft.network.chat.Component; -import org.jetbrains.annotations.NotNull; - -public record GuideAction(ControllerBinding binding, GuideActionNameSupplier name, ActionPriority priority) implements Comparable { - public GuideAction(ControllerBinding binding, GuideActionNameSupplier name) { - this(binding, name, ActionPriority.NORMAL); - } - - @Override - public int compareTo(@NotNull GuideAction o) { - return this.priority().compareTo(o.priority()); - } -} diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java index b876e5f..274bb48 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/ClientPacketListenerMixin.java @@ -2,7 +2,7 @@ package dev.isxander.controlify.mixins.feature.guide.ingame; import dev.isxander.controlify.Controlify; import dev.isxander.controlify.InputMode; -import dev.isxander.controlify.ingame.guide.InGameButtonGuide; +import dev.isxander.controlify.gui.guide.InGameButtonGuide; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.protocol.game.ClientboundLoginPacket; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/GuiMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/GuiMixin.java index ff1334e..ecf894c 100644 --- a/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/GuiMixin.java +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/ingame/GuiMixin.java @@ -2,8 +2,7 @@ package dev.isxander.controlify.mixins.feature.guide.ingame; import com.mojang.blaze3d.vertex.PoseStack; import dev.isxander.controlify.Controlify; -import dev.isxander.controlify.ingame.InGameInputHandler; -import dev.isxander.controlify.ingame.guide.InGameButtonGuide; +import dev.isxander.controlify.gui.guide.InGameButtonGuide; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; import org.spongepowered.asm.mixin.Final; diff --git a/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractContainerScreenMixin.java b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractContainerScreenMixin.java new file mode 100644 index 0000000..aad12f1 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/mixins/feature/guide/screen/AbstractContainerScreenMixin.java @@ -0,0 +1,153 @@ +package dev.isxander.controlify.mixins.feature.guide.screen; + +import com.mojang.blaze3d.vertex.PoseStack; +import dev.isxander.controlify.InputMode; +import dev.isxander.controlify.api.ControlifyApi; +import dev.isxander.controlify.bindings.ControllerBindings; +import dev.isxander.controlify.controller.Controller; +import dev.isxander.controlify.gui.guide.ContainerGuideCtx; +import dev.isxander.controlify.gui.guide.GuideAction; +import dev.isxander.controlify.gui.guide.GuideActionRenderer; +import dev.isxander.controlify.gui.layout.AnchorPoint; +import dev.isxander.controlify.gui.layout.ColumnLayoutComponent; +import dev.isxander.controlify.gui.layout.PositionedComponent; +import dev.isxander.controlify.gui.layout.RowLayoutComponent; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +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.Optional; + +// TODO: Move out of mixin +@Mixin(AbstractContainerScreen.class) +public abstract class AbstractContainerScreenMixin { + @Shadow @Nullable protected Slot hoveredSlot; + @Shadow @Final protected T menu; + @Shadow protected int leftPos; + @Shadow protected int topPos; + @Shadow protected abstract boolean hasClickedOutside(double mouseX, double mouseY, int left, int top, int button); + + @Unique private PositionedComponent>>> leftLayout; + @Unique private PositionedComponent>>> rightLayout; + + @Inject(method = "init", at = @At("RETURN")) + private void initButtonGuide(CallbackInfo ci) { + ControllerBindings bindings = ControlifyApi.get().getCurrentController() + .map(Controller::bindings) + .orElse(null); + + if (bindings == null) + return; + + leftLayout = new PositionedComponent<>( + ColumnLayoutComponent.>>builder() + .spacing(2) + .colPadding(2, 2) + .elementPosition(ColumnLayoutComponent.ElementPosition.LEFT) + .element(RowLayoutComponent.>builder() + .spacing(5) + .rowPadding(0) + .elementPosition(RowLayoutComponent.ElementPosition.MIDDLE) + .element(new GuideActionRenderer<>( + new GuideAction<>(bindings.VMOUSE_LCLICK, ctx -> { + if (!ctx.holdingItem().isEmpty()) + if (ctx.hoveredSlot() != null && ctx.hoveredSlot().hasItem()) + if (ctx.hoveredSlot().mayPlace(ctx.holdingItem())) + if (ctx.holdingItem().getCount() > 1) + return Optional.of(Component.translatable("controlify.guide.container.place_all")); + else + return Optional.of(Component.translatable("controlify.guide.container.place_one")); + else + return Optional.of(Component.translatable("controlify.guide.container.swap")); + else if (ctx.cursorOutsideContainer()) + return Optional.of(Component.translatable("controlify.guide.container.drop")); + if (ctx.hoveredSlot() != null && ctx.hoveredSlot().hasItem()) + return Optional.of(Component.translatable("controlify.guide.container.take")); + return Optional.empty(); + }), + false, false + )) + .elements(new GuideActionRenderer<>( + new GuideAction<>(bindings.GUI_BACK, ctx -> { + return Optional.of(Component.translatable("controlify.guide.container.exit")); + }), + false, false + )) + .build()) + .build(), + AnchorPoint.BOTTOM_LEFT, + 0, 0, + AnchorPoint.BOTTOM_LEFT + ); + + rightLayout = new PositionedComponent<>( + ColumnLayoutComponent.>>builder() + .spacing(2) + .elementPosition(ColumnLayoutComponent.ElementPosition.RIGHT) + .colPadding(2, 2) + .element(RowLayoutComponent.>builder() + .spacing(5) + .rowPadding(0) + .elementPosition(RowLayoutComponent.ElementPosition.MIDDLE) + .element(new GuideActionRenderer<>( + new GuideAction<>(bindings.VMOUSE_RCLICK, ctx -> { + if (ctx.hoveredSlot() != null && ctx.hoveredSlot().getItem().getCount() > 1 && ctx.holdingItem().isEmpty()) + return Optional.of(Component.translatable("controlify.guide.container.take_half")); + if (ctx.hoveredSlot() != null && !ctx.holdingItem().isEmpty() && ctx.hoveredSlot().mayPlace(ctx.holdingItem())) + return Optional.of(Component.translatable("controlify.guide.container.take_one")); + return Optional.empty(); + }), + true, false + )) + .element(new GuideActionRenderer<>( + new GuideAction<>(bindings.VMOUSE_SHIFT_CLICK, ctx -> { + return Optional.of(Component.translatable("controlify.guide.container.quick_move")); + }), + true, false + )) + .build()) + .build(), + AnchorPoint.BOTTOM_RIGHT, + 0, 0, + AnchorPoint.BOTTOM_RIGHT + ); + } + + @Inject(method = "render", at = @At("RETURN")) + private void renderButtonGuide(PoseStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci) { + if (!ControlifyApi.get().getCurrentController().map(controller -> controller.config().showScreenGuide).orElse(false) + || ControlifyApi.get().currentInputMode() != InputMode.CONTROLLER + ) { + return; + } + + ContainerGuideCtx ctx = new ContainerGuideCtx(hoveredSlot, menu.getCarried(), hasClickedOutside(mouseX, mouseY, this.leftPos, this.topPos, 0)); + + for (var row : leftLayout.getComponent().getChildComponents()) { + for (var element : row.getChildComponents()) { + element.updateName(ctx); + } + } + for (var row : rightLayout.getComponent().getChildComponents()) { + for (var element : row.getChildComponents()) { + element.updateName(ctx); + } + } + + leftLayout.updatePosition(); + rightLayout.updatePosition(); + + leftLayout.render(matrices, delta); + rightLayout.render(matrices, delta); + } +} diff --git a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java b/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java index d607cf3..d6ee2a1 100644 --- a/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java +++ b/src/main/java/dev/isxander/controlify/virtualmouse/VirtualMouseHandler.java @@ -81,7 +81,7 @@ public class VirtualMouseHandler { snapping = false; } - var sensitivity = !snapping ? controller.config().virtualMouseSensitivity : 20f; + var sensitivity = controller.config().virtualMouseSensitivity; // quadratic function to make small movements smaller // abs to keep sign @@ -166,8 +166,9 @@ public class VirtualMouseHandler { lastSnappedPoint = closestSnapPoint; snapping = false; - targetX = closestSnapPoint.position().x() / scaleFactor.x(); - targetY = closestSnapPoint.position().y() / scaleFactor.y(); + targetX = currentX = closestSnapPoint.position().x() / scaleFactor.x(); + targetY = currentY = closestSnapPoint.position().y() / scaleFactor.y(); + ((MouseHandlerAccessor) minecraft.mouseHandler).invokeOnMove(minecraft.getWindow().getWindow(), currentX, currentY); } } diff --git a/src/main/resources/assets/controlify/lang/en_us.json b/src/main/resources/assets/controlify/lang/en_us.json index 10a42c7..1f143e8 100644 --- a/src/main/resources/assets/controlify/lang/en_us.json +++ b/src/main/resources/assets/controlify/lang/en_us.json @@ -194,27 +194,38 @@ "controlify.custom_binding.vanilla_description": "Automatically generated key binding from Fabric API.", - "controlify.guide.inventory": "Open Inventory", - "controlify.guide.swim_up": "Swim Up", - "controlify.guide.start_elytra": "Open Elytra Wings", - "controlify.guide.fly_up": "Fly Up", - "controlify.guide.fly_down": "Fly Down", - "controlify.guide.start_sneaking": "Start Sneaking", - "controlify.guide.stop_sneaking": "Stop Sneaking", - "controlify.guide.start_swimming": "Start Swimming", - "controlify.guide.start_sprinting": "Start Sprinting", - "controlify.guide.stop_swimming": "Stop Swimming", - "controlify.guide.stop_sprinting": "Stop Sprinting", - "controlify.guide.sneak": "Sneak", - "controlify.guide.dismount": "Dismount", - "controlify.guide.swim_down": "Swim Down", - "controlify.guide.drop": "Drop Item", - "controlify.guide.swap_hands": "Swap Hands", - "controlify.guide.attack": "Attack", - "controlify.guide.break": "Break", - "controlify.guide.use": "Use", - "controlify.guide.interact": "Interact", - "controlify.guide.pick_block": "Pick Block", + "controlify.guide.ingame.inventory": "Open Inventory", + "controlify.guide.ingame.swim_up": "Swim Up", + "controlify.guide.ingame.start_elytra": "Open Elytra Wings", + "controlify.guide.ingame.fly_up": "Fly Up", + "controlify.guide.ingame.fly_down": "Fly Down", + "controlify.guide.ingame.start_sneaking": "Start Sneaking", + "controlify.guide.ingame.stop_sneaking": "Stop Sneaking", + "controlify.guide.ingame.start_swimming": "Start Swimming", + "controlify.guide.ingame.start_sprinting": "Start Sprinting", + "controlify.guide.ingame.stop_swimming": "Stop Swimming", + "controlify.guide.ingame.stop_sprinting": "Stop Sprinting", + "controlify.guide.ingame.sneak": "Sneak", + "controlify.guide.ingame.dismount": "Dismount", + "controlify.guide.ingame.swim_down": "Swim Down", + "controlify.guide.ingame.drop": "Drop Item", + "controlify.guide.ingame.swap_hands": "Swap Hands", + "controlify.guide.ingame.attack": "Attack", + "controlify.guide.ingame.break": "Break", + "controlify.guide.ingame.use": "Use", + "controlify.guide.ingame.spectate": "Spectate", + "controlify.guide.ingame.interact": "Interact", + "controlify.guide.ingame.pick_block": "Pick Block", + + "controlify.guide.container.place_all": "Place All", + "controlify.guide.container.place_one": "Place", + "controlify.guide.container.swap": "Swap", + "controlify.guide.container.drop": "Drop", + "controlify.guide.container.take": "Take", + "controlify.guide.container.exit": "Exit", + "controlify.guide.container.take_half": "Take Half", + "controlify.guide.container.take_one": "Place One", + "controlify.guide.container.quick_move": "Quick Move", "controlify.calibration.title": "Controller Calibration for '%s'", "controlify.calibration.info": "This process will optimize settings for your controller to prevent stick drift. Stick drift happens in your controller thumbsticks and outputs slightly wrong values when you aren't touching them at all. Deadzones are used to prevent this.\n\nThis will only take a few seconds.", diff --git a/src/main/resources/assets/controlify/lang/zh_cn.json b/src/main/resources/assets/controlify/lang/zh_cn.json index 7057db0..9d23bff 100644 --- a/src/main/resources/assets/controlify/lang/zh_cn.json +++ b/src/main/resources/assets/controlify/lang/zh_cn.json @@ -180,7 +180,7 @@ "controlify.custom_binding.vanilla_description": "自动从Fabric API生成键绑定。", - "controlify.guide.inventory": "打开物品栏", + "controlify.guide.ingame.inventory": "打开物品栏", "controlify.guide.swim_up": "向上游", "controlify.guide.start_elytra": "打开鞘翅", "controlify.guide.fly_up": "向上飞", diff --git a/src/main/resources/controlify.mixins.json b/src/main/resources/controlify.mixins.json index 2343b23..381ec42 100644 --- a/src/main/resources/controlify.mixins.json +++ b/src/main/resources/controlify.mixins.json @@ -39,6 +39,7 @@ "feature.guide.ingame.ClientPacketListenerMixin", "feature.guide.ingame.GuiMixin", "feature.guide.screen.AbstractButtonMixin", + "feature.guide.screen.AbstractContainerScreenMixin", "feature.guide.screen.AbstractWidgetMixin", "feature.guide.screen.TabNavigationBarMixin", "feature.oofinput.GameRendererMixin",