diff --git a/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java b/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java index 0767292..ef0603b 100644 --- a/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java +++ b/src/main/java/dev/isxander/controlify/bindings/ControllerBindings.java @@ -39,6 +39,7 @@ public class ControllerBindings { public static final Component VMOUSE_CATEGORY = Component.translatable("controlify.binding_category.vmouse"); public static final Component GUI_CATEGORY = Component.translatable("controlify.binding_category.gui"); public static final Component MISC_CATEGORY = Component.translatable("key.categories.misc"); + public static final Component RADIAL_CATEGORY = Component.translatable("controlify.gui.radial_menu"); public final ControllerBinding WALK_FORWARD, WALK_BACKWARD, WALK_LEFT, WALK_RIGHT, @@ -61,7 +62,7 @@ public class ControllerBindings { PICK_BLOCK, TOGGLE_HUD_VISIBILITY, SHOW_PLAYER_LIST, - RADIAL_MENU, + RADIAL_MENU, RADIAL_AXIS_UP, RADIAL_AXIS_DOWN, RADIAL_AXIS_LEFT, RADIAL_AXIS_RIGHT, VMOUSE_MOVE_UP, VMOUSE_MOVE_DOWN, VMOUSE_MOVE_LEFT, VMOUSE_MOVE_RIGHT, VMOUSE_LCLICK, VMOUSE_RCLICK, VMOUSE_SHIFT_CLICK, VMOUSE_SCROLL_UP, VMOUSE_SCROLL_DOWN, @@ -222,7 +223,7 @@ public class ControllerBindings { .identifier("controlify", "swap_hands") .defaultBind(GamepadBinds.X_BUTTON) .category(INVENTORY_CATEGORY) - .context(BindContexts.INGAME, BindContexts.INVENTORY) + .context(BindContexts.INGAME) .radialCandidate(RadialIcons.getItem(Items.BONE)) .build()); register(OPEN_CHAT = ControllerBindingBuilder.create(controller) @@ -304,17 +305,41 @@ public class ControllerBindings { .build()); register(SHOW_PLAYER_LIST = ControllerBindingBuilder.create(controller) .identifier("controlify", "show_player_list") - .defaultBind(GamepadBinds.DPAD_RIGHT) + .defaultBind(new EmptyBind<>()) .category(MISC_CATEGORY) .context(BindContexts.INGAME) .radialCandidate(RadialIcons.getItem(Items.PLAYER_HEAD)) .build()); register(RADIAL_MENU = ControllerBindingBuilder.create(controller) .identifier("controlify", "radial_menu") - .defaultBind(GamepadBinds.DPAD_DOWN) - .category(MISC_CATEGORY) + .defaultBind(GamepadBinds.DPAD_RIGHT) + .category(RADIAL_CATEGORY) .context(BindContexts.INGAME) .build()); + register(RADIAL_AXIS_UP = ControllerBindingBuilder.create(controller) + .identifier("controlify", "radial_axis_up") + .defaultBind(GamepadBinds.RIGHT_STICK_FORWARD) + .category(RADIAL_CATEGORY) + .context(BindContexts.GUI) + .build()); + register(RADIAL_AXIS_DOWN = ControllerBindingBuilder.create(controller) + .identifier("controlify", "radial_axis_down") + .defaultBind(GamepadBinds.RIGHT_STICK_BACKWARD) + .category(RADIAL_CATEGORY) + .context(BindContexts.GUI) + .build()); + register(RADIAL_AXIS_LEFT = ControllerBindingBuilder.create(controller) + .identifier("controlify", "radial_axis_left") + .defaultBind(GamepadBinds.RIGHT_STICK_LEFT) + .category(RADIAL_CATEGORY) + .context(BindContexts.GUI) + .build()); + register(RADIAL_AXIS_RIGHT = ControllerBindingBuilder.create(controller) + .identifier("controlify", "radial_axis_right") + .defaultBind(GamepadBinds.RIGHT_STICK_RIGHT) + .category(RADIAL_CATEGORY) + .context(BindContexts.GUI) + .build()); register(VMOUSE_MOVE_UP = ControllerBindingBuilder.create(controller) .identifier("controlify", "vmouse_move_up") .defaultBind(GamepadBinds.LEFT_STICK_FORWARD) diff --git a/src/main/java/dev/isxander/controlify/bindings/RadialIcons.java b/src/main/java/dev/isxander/controlify/bindings/RadialIcons.java index 413a762..300d384 100644 --- a/src/main/java/dev/isxander/controlify/bindings/RadialIcons.java +++ b/src/main/java/dev/isxander/controlify/bindings/RadialIcons.java @@ -19,19 +19,19 @@ public final class RadialIcons { private static final Minecraft minecraft = Minecraft.getInstance(); public static final ResourceLocation EMPTY = new ResourceLocation("controlify", "empty"); - public static final ResourceLocation FABRIC_ICON = new ResourceLocation("fabric", "icon"); + public static final ResourceLocation FABRIC_ICON = new ResourceLocation("fabric-resource-loader-v0", "icon.png"); private static final Map icons = Util.make(() -> { Map map = new HashMap<>(); map.put(EMPTY, (graphics, x, y) -> {}); - map.put(FABRIC_ICON, ((graphics, x, y) -> { + map.put(FABRIC_ICON, (graphics, x, y) -> { graphics.pose().pushPose(); graphics.pose().translate(x, y, 0); graphics.pose().scale(0.5f, 0.5f, 1f); graphics.blit(FABRIC_ICON, 0, 0, 0, 0, 32, 32); graphics.pose().popPose(); - })); + }); addItems(map); addPotionEffects(map); diff --git a/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java b/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java index 47fda1b..2ee5e09 100644 --- a/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java +++ b/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java @@ -155,8 +155,8 @@ public class ControlifyConfig { private void applyControllerConfig(Controller controller, JsonObject object) { try { - controller.setConfig(GSON, object.getAsJsonObject("config")); dirty |= !controller.bindings().fromJson(object.getAsJsonObject("bindings")); + controller.setConfig(GSON, object.getAsJsonObject("config")); } catch (Exception e) { Log.LOGGER.error("Failed to load controller data for " + controller.uid() + ". Resetting to default!", e); controller.resetConfig(); diff --git a/src/main/java/dev/isxander/controlify/controller/AbstractController.java b/src/main/java/dev/isxander/controlify/controller/AbstractController.java index 260bc6c..8a55622 100644 --- a/src/main/java/dev/isxander/controlify/controller/AbstractController.java +++ b/src/main/java/dev/isxander/controlify/controller/AbstractController.java @@ -120,6 +120,7 @@ public abstract class AbstractController bindings) { + boolean changed = false; + for (int i = 0; i < radialActions.length; i++) { + ResourceLocation action = radialActions[i]; + if (!RadialMenuScreen.EMPTY_ACTION.equals(action) && (action == null || !bindings.registry().containsKey(action) || bindings.registry().get(action).radialIcon().isEmpty())) { + setDefaultRadialAction(bindings, i); + changed = true; + } + } + if (changed) + Controlify.instance().config().setDirty(); + + return !changed; + } + + private void setDefaultRadialAction(ControllerBindings bindings, int index) { + radialActions[index] = switch (index) { + case 0 -> bindings.TOGGLE_HUD_VISIBILITY.id(); + case 1 -> bindings.CHANGE_PERSPECTIVE.id(); + case 2 -> bindings.DROP_STACK.id(); + case 3 -> bindings.OPEN_CHAT.id(); + case 4 -> bindings.SWAP_HANDS.id(); + case 5 -> bindings.PICK_BLOCK.id(); + case 6 -> bindings.PAUSE.id(); + case 7 -> bindings.SHOW_PLAYER_LIST.id(); + default -> RadialMenuScreen.EMPTY_ACTION; + }; + } } diff --git a/src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java b/src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java index 6baafe0..f4b3f3b 100644 --- a/src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java +++ b/src/main/java/dev/isxander/controlify/gui/guide/InGameButtonGuide.java @@ -193,6 +193,11 @@ public class InGameButtonGuide implements IngameGuideRegistry { return Optional.of(Component.translatable("controlify.guide.ingame.inventory")); return Optional.empty(); }); + registerGuideAction(controller.bindings().RADIAL_MENU, ActionLocation.RIGHT, ctx -> { + if (ctx.client().screen == null) + return Optional.of(Component.translatable("controlify.gui.radial_menu")); + return Optional.empty(); + }); registerGuideAction(controller.bindings().ATTACK, ActionLocation.RIGHT, (ctx) -> { var hitResult = ctx.hitResult(); if (hitResult.getType() == HitResult.Type.ENTITY) diff --git a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java b/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java index 849c42a..449a6a5 100644 --- a/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java +++ b/src/main/java/dev/isxander/controlify/gui/screen/RadialMenuScreen.java @@ -1,10 +1,10 @@ package dev.isxander.controlify.gui.screen; import dev.isxander.controlify.Controlify; +import dev.isxander.controlify.api.bind.BindRenderer; import dev.isxander.controlify.api.bind.ControllerBinding; import dev.isxander.controlify.bindings.RadialIcons; import dev.isxander.controlify.controller.Controller; -import dev.isxander.controlify.controller.gamepad.GamepadState; import dev.isxander.controlify.gui.guide.GuideAction; import dev.isxander.controlify.gui.guide.GuideActionRenderer; import dev.isxander.controlify.gui.layout.AnchorPoint; @@ -12,9 +12,11 @@ import dev.isxander.controlify.gui.layout.PositionedComponent; import dev.isxander.controlify.screenop.ComponentProcessor; import dev.isxander.controlify.screenop.ScreenControllerEventListener; import dev.isxander.controlify.screenop.ScreenProcessor; +import dev.isxander.controlify.screenop.ScreenProcessorProvider; import dev.isxander.controlify.sound.ControlifySounds; import dev.isxander.controlify.utils.Animator; import dev.isxander.controlify.utils.Easings; +import dev.isxander.controlify.virtualmouse.VirtualMouseBehaviour; import net.minecraft.client.gui.ComponentPath; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.MultiLineLabel; @@ -22,6 +24,7 @@ import net.minecraft.client.gui.components.Renderable; import net.minecraft.client.gui.components.events.ContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.gui.narration.NarratedElementType; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.navigation.FocusNavigationEvent; import net.minecraft.client.gui.navigation.ScreenRectangle; @@ -39,7 +42,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; -public class RadialMenuScreen extends Screen implements ScreenControllerEventListener { +public class RadialMenuScreen extends Screen implements ScreenControllerEventListener, ScreenProcessorProvider { public static final ResourceLocation EMPTY_ACTION = new ResourceLocation("controlify", "empty_action"); private final Controller controller; @@ -53,6 +56,8 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis private ActionSelectList actionSelectList; + private final Processor processor = new Processor(this); + public RadialMenuScreen(Controller controller, boolean editMode, Screen parent) { super(Component.empty()); this.controller = controller; @@ -73,7 +78,7 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis addRenderableWidget(buttons[4] = button = new RadialButton(4, button.x - 32 - 8, button.y + 16)); addRenderableWidget(buttons[5] = button = new RadialButton(5, button.x - 32 - 8, button.y - 16)); addRenderableWidget(buttons[6] = button = new RadialButton(6, button.x - 16, button.y - 32 - 8)); - addRenderableWidget(buttons[7] = new RadialButton(7, button.x + 16, button.y - 32 - 8)); + addRenderableWidget(buttons[7] = new RadialButton(7, button.x + 16, button.y - 32 - 8)); Animator.AnimationInstance animation = new Animator.AnimationInstance(5, Easings::easeOutQuad); for (RadialButton radialButton : buttons) { @@ -114,9 +119,13 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis onClose(); } - if (!isEditing && controller.state() instanceof GamepadState state) { - float x = state.gamepadAxes().rightStickX(); - float y = state.gamepadAxes().rightStickY(); + if (editMode && controller.bindings().GUI_BACK.justPressed()) { + onClose(); + } + + if (!isEditing) { + float x = controller.bindings().RADIAL_AXIS_RIGHT.state() - controller.bindings().RADIAL_AXIS_LEFT.state(); + float y = controller.bindings().RADIAL_AXIS_DOWN.state() - controller.bindings().RADIAL_AXIS_UP.state(); float threshold = controller.config().buttonActivationThreshold; if (Math.abs(x) >= threshold || Math.abs(y) >= threshold) { @@ -152,13 +161,19 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { - if (minecraft.level == null) + if (editMode) renderDirtBackground(graphics); super.render(graphics, mouseX, mouseY, delta); if (!editMode) { - graphics.drawCenteredString(font, Component.translatable("controlify.radial_menu.configure_hint"), width / 2, height - 10 - font.lineHeight, -1); + graphics.drawCenteredString( + font, + Component.translatable("controlify.radial_menu.configure_hint"), + width / 2, + height - 39, + -1 + ); } } @@ -178,6 +193,16 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis minecraft.setScreen(parent); } + @Override + public boolean isPauseScreen() { + return editMode; + } + + @Override + public ScreenProcessor screenProcessor() { + return this.processor; + } + public class RadialButton implements Renderable, GuiEventListener, NarratableEntry, ComponentProcessor { public static final ResourceLocation TEXTURE = Controlify.id("textures/gui/radial-buttons.png"); @@ -199,14 +224,23 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { graphics.pose().pushPose(); graphics.pose().translate(x + translateX, y + translateY, 0); + + graphics.pose().pushPose(); graphics.pose().scale(2, 2, 1); graphics.blit(TEXTURE, 0, 0, focused ? 16 : 0, 0, 16, 16, 32, 16); graphics.pose().popPose(); - graphics.pose().pushPose(); - graphics.pose().translate(x + translateX + 4, y + translateY + 4, 0); - graphics.pose().scale(1.5f, 1.5f, 1); - this.icon.draw(graphics, 0, 0); + if (!editMode || !focused) { + graphics.pose().pushPose(); + graphics.pose().translate(4, 4, 0); + graphics.pose().scale(1.5f, 1.5f, 1); + this.icon.draw(graphics, 0, 0); + graphics.pose().popPose(); + } else { + BindRenderer renderer = controller.bindings().GUI_PRESS.renderer(); + renderer.render(graphics, 16 - renderer.size().width() / 2, 16); + } + graphics.pose().popPose(); if (focused) @@ -263,16 +297,14 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis @Override public boolean overrideControllerButtons(ScreenProcessor screen, Controller controller) { - if (controller == RadialMenuScreen.this.controller) { - if (controller.bindings().GUI_PRESS.justPressed()) { - RadialButton button = buttons[selectedButton]; - int x = button.x < width / 2 ? button.x - 110 : button.x + 42; - actionSelectList = new ActionSelectList(selectedButton, x, button.y, 100, 80); - addRenderableWidget(actionSelectList); - RadialMenuScreen.this.setFocused(actionSelectList); - isEditing = true; - return true; - } + if (editMode && controller == RadialMenuScreen.this.controller && controller.bindings().GUI_PRESS.justPressed()) { + RadialButton button = buttons[selectedButton]; + int x = button.x < width / 2 ? button.x - 110 : button.x + 42; + actionSelectList = new ActionSelectList(selectedButton, x, button.y, 100, 80); + addRenderableWidget(actionSelectList); + RadialMenuScreen.this.setFocused(actionSelectList); + isEditing = true; + return true; } return false; } @@ -284,7 +316,8 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis @Override public void updateNarration(NarrationElementOutput builder) { - + if (binding != null) + builder.add(NarratedElementType.TITLE, binding.name()); } @Override @@ -370,7 +403,7 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis @Nullable @Override - public GuiEventListener getFocused() { + public ActionEntry getFocused() { return focusedEntry; } @@ -408,7 +441,9 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis @Override public void updateNarration(NarrationElementOutput builder) { - + if (getFocused() != null) { + builder.add(NarratedElementType.TITLE, getFocused().name); + } } public class ActionEntry implements GuiEventListener, ComponentProcessor { @@ -471,4 +506,15 @@ public class RadialMenuScreen extends Screen implements ScreenControllerEventLis } } } + + public static class Processor extends ScreenProcessor { + public Processor(RadialMenuScreen screen) { + super(screen); + } + + @Override + public VirtualMouseBehaviour virtualMouseBehaviour() { + return VirtualMouseBehaviour.DISABLED; + } + } } diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java index c6f97ad..fe49484 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java @@ -106,7 +106,9 @@ public class InGameInputHandler { minecraft.options.hideGui = !minecraft.options.hideGui; } - shouldShowPlayerList = controller.bindings().SHOW_PLAYER_LIST.held(); + if (controller.bindings().SHOW_PLAYER_LIST.justPressed()) { + shouldShowPlayerList = !shouldShowPlayerList; + } if (controller.bindings().RADIAL_MENU.justPressed()) { minecraft.setScreen(new RadialMenuScreen(controller, false, null));