1
0
forked from Clones/Controlify

Implement basic container guide (unfinished)

This commit is contained in:
isXander
2023-05-12 15:45:11 +01:00
parent 4c9cb11830
commit 45c20bb2dc
22 changed files with 318 additions and 123 deletions

View File

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

View File

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

View File

@ -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<Component> supply(IngameGuideContext ctx);
public interface GuideActionNameSupplier<T> {
Optional<Component> supply(T ctx);
}

View File

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

View File

@ -194,7 +194,6 @@ public class ControllerBindings<T extends ControllerState> {
.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")

View File

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

View File

@ -0,0 +1,4 @@
package dev.isxander.controlify.gui.guide;
public class ContainerButtonGuide {
}

View File

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

View File

@ -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<T>(ControllerBinding binding, GuideActionNameSupplier<T> name, ActionPriority priority) implements Comparable<GuideAction<T>> {
public GuideAction(ControllerBinding binding, GuideActionNameSupplier<T> name) {
this(binding, name, ActionPriority.NORMAL);
}
@Override
public int compareTo(@NotNull GuideAction<T> o) {
return this.priority().compareTo(o.priority());
}
}

View File

@ -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<T> implements RenderComponent {
private final GuideAction<T> guideAction;
private final boolean rtl;
private final boolean textContrast;
private Optional<Component> name = Optional.empty();
public GuideActionRenderer(GuideAction action, boolean rtl) {
public GuideActionRenderer(GuideAction<T> 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);
}
}

View File

@ -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<GuideAction> leftGuides = new ArrayList<>();
private final List<GuideAction> rightGuides = new ArrayList<>();
private final List<GuideAction<IngameGuideContext>> leftGuides = new ArrayList<>();
private final List<GuideAction<IngameGuideContext>> rightGuides = new ArrayList<>();
private final PositionedComponent<ColumnLayoutComponent<GuideActionRenderer>> leftLayout;
private final PositionedComponent<ColumnLayoutComponent<GuideActionRenderer>> rightLayout;
private final PositionedComponent<ColumnLayoutComponent<GuideActionRenderer<IngameGuideContext>>> leftLayout;
private final PositionedComponent<ColumnLayoutComponent<GuideActionRenderer<IngameGuideContext>>> 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.<GuideActionRenderer>builder()
.elementPadding(2)
ColumnLayoutComponent.<GuideActionRenderer<IngameGuideContext>>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.<GuideActionRenderer>builder()
.elementPadding(2)
ColumnLayoutComponent.<GuideActionRenderer<IngameGuideContext>>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<IngameGuideContext> 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<IngameGuideContext> 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();
});
}

View File

@ -118,7 +118,7 @@ public class ColumnLayoutComponent<T extends RenderComponent> extends AbstractLa
return this;
}
public Builder<T> elementPadding(int padding) {
public Builder<T> spacing(int padding) {
this.componentPaddingVertical = padding;
return this;
}

View File

@ -78,6 +78,12 @@ public class RowLayoutComponent<T extends RenderComponent> extends AbstractLayou
.sum() - elementPaddingHorizontal;
}
@Override
public boolean isVisible() {
return this.getChildComponents().stream()
.anyMatch(RenderComponent::isVisible);
}
public static <T extends RenderComponent> Builder<T> builder() {
return new Builder<>();
}
@ -115,7 +121,7 @@ public class RowLayoutComponent<T extends RenderComponent> extends AbstractLayou
return this;
}
public Builder<T> elementPadding(int padding) {
public Builder<T> spacing(int padding) {
this.elementPaddingHorizontal = padding;
return this;
}

View File

@ -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) {

View File

@ -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<GuideAction> {
public GuideAction(ControllerBinding binding, GuideActionNameSupplier name) {
this(binding, name, ActionPriority.NORMAL);
}
@Override
public int compareTo(@NotNull GuideAction o) {
return this.priority().compareTo(o.priority());
}
}

View File

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

View File

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

View File

@ -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<T extends AbstractContainerMenu> {
@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<ColumnLayoutComponent<RowLayoutComponent<GuideActionRenderer<ContainerGuideCtx>>>> leftLayout;
@Unique private PositionedComponent<ColumnLayoutComponent<RowLayoutComponent<GuideActionRenderer<ContainerGuideCtx>>>> 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.<RowLayoutComponent<GuideActionRenderer<ContainerGuideCtx>>>builder()
.spacing(2)
.colPadding(2, 2)
.elementPosition(ColumnLayoutComponent.ElementPosition.LEFT)
.element(RowLayoutComponent.<GuideActionRenderer<ContainerGuideCtx>>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.<RowLayoutComponent<GuideActionRenderer<ContainerGuideCtx>>>builder()
.spacing(2)
.elementPosition(ColumnLayoutComponent.ElementPosition.RIGHT)
.colPadding(2, 2)
.element(RowLayoutComponent.<GuideActionRenderer<ContainerGuideCtx>>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);
}
}

View File

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

View File

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

View File

@ -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": "向上飞",

View File

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