forked from Clones/Controlify
➕ Implement basic container guide (unfinished)
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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}.
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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));
|
||||
|
@ -0,0 +1,4 @@
|
||||
package dev.isxander.controlify.gui.guide;
|
||||
|
||||
public class ContainerButtonGuide {
|
||||
}
|
@ -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) {
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
});
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.",
|
||||
|
@ -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": "向上飞",
|
||||
|
@ -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",
|
||||
|
Reference in New Issue
Block a user