forked from Clones/Controlify
Merge branch 'feature/radial-menu' into 1.20.x/dev
# Conflicts: # src/main/java/dev/isxander/controlify/config/ControlifyConfig.java # src/main/java/dev/isxander/controlify/controller/ControllerConfig.java # src/main/resources/assets/controlify/lang/en_us.json
This commit is contained in:
@ -3,12 +3,14 @@ package dev.isxander.controlify.api.bind;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dev.isxander.controlify.bindings.BindContext;
|
import dev.isxander.controlify.bindings.BindContext;
|
||||||
import dev.isxander.controlify.bindings.IBind;
|
import dev.isxander.controlify.bindings.IBind;
|
||||||
|
import dev.isxander.controlify.bindings.RadialIcons;
|
||||||
import dev.isxander.yacl3.api.Option;
|
import dev.isxander.yacl3.api.Option;
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
@ -43,6 +45,8 @@ public interface ControllerBinding {
|
|||||||
*/
|
*/
|
||||||
boolean justReleased();
|
boolean justReleased();
|
||||||
|
|
||||||
|
void fakePress();
|
||||||
|
|
||||||
Component name();
|
Component name();
|
||||||
Component description();
|
Component description();
|
||||||
Component category();
|
Component category();
|
||||||
@ -66,6 +70,10 @@ public interface ControllerBinding {
|
|||||||
|
|
||||||
JsonObject toJson();
|
JsonObject toJson();
|
||||||
|
|
||||||
|
void tick();
|
||||||
|
|
||||||
|
Optional<ResourceLocation> radialIcon();
|
||||||
|
|
||||||
record KeyMappingOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
|
record KeyMappingOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package dev.isxander.controlify.api.bind;
|
package dev.isxander.controlify.api.bind;
|
||||||
|
|
||||||
import dev.isxander.controlify.bindings.BindContext;
|
import dev.isxander.controlify.bindings.*;
|
||||||
import dev.isxander.controlify.bindings.ControllerBindingImpl;
|
|
||||||
import dev.isxander.controlify.bindings.GamepadBinds;
|
|
||||||
import dev.isxander.controlify.bindings.IBind;
|
|
||||||
import dev.isxander.controlify.controller.Controller;
|
import dev.isxander.controlify.controller.Controller;
|
||||||
import dev.isxander.controlify.controller.ControllerState;
|
import dev.isxander.controlify.controller.ControllerState;
|
||||||
import net.minecraft.client.KeyMapping;
|
import net.minecraft.client.KeyMapping;
|
||||||
@ -76,6 +73,8 @@ public interface ControllerBindingBuilder<T extends ControllerState> {
|
|||||||
|
|
||||||
ControllerBindingBuilder<T> context(BindContext... contexts);
|
ControllerBindingBuilder<T> context(BindContext... contexts);
|
||||||
|
|
||||||
|
ControllerBindingBuilder<T> radialCandidate(ResourceLocation icon);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies are vanilla override for the binding.
|
* Specifies are vanilla override for the binding.
|
||||||
* Will emulate presses of the vanilla keybind when the controller binding is pressed.
|
* Will emulate presses of the vanilla keybind when the controller binding is pressed.
|
||||||
|
@ -25,10 +25,7 @@ import net.minecraft.resources.ResourceLocation;
|
|||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
public class ControllerBindingImpl<T extends ControllerState> implements ControllerBinding {
|
public class ControllerBindingImpl<T extends ControllerState> implements ControllerBinding {
|
||||||
@ -39,11 +36,14 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
|||||||
private final ResourceLocation id;
|
private final ResourceLocation id;
|
||||||
private final Component name, description, category;
|
private final Component name, description, category;
|
||||||
private final Set<BindContext> contexts;
|
private final Set<BindContext> contexts;
|
||||||
|
private final ResourceLocation radialIcon;
|
||||||
private final KeyMappingOverride override;
|
private final KeyMappingOverride override;
|
||||||
|
|
||||||
private static final Map<Controller<?, ?>, Set<IBind<?>>> pressedBinds = new HashMap<>();
|
private static final Map<Controller<?, ?>, Set<IBind<?>>> pressedBinds = new HashMap<>();
|
||||||
|
|
||||||
private ControllerBindingImpl(Controller<T, ?> controller, IBind<T> defaultBind, ResourceLocation id, KeyMappingOverride vanillaOverride, Component name, Component description, Component category, Set<BindContext> contexts) {
|
private int fakePressState = 0;
|
||||||
|
|
||||||
|
private ControllerBindingImpl(Controller<T, ?> controller, IBind<T> defaultBind, ResourceLocation id, KeyMappingOverride vanillaOverride, Component name, Component description, Component category, Set<BindContext> contexts, ResourceLocation icon) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
this.bind = this.defaultBind = defaultBind;
|
this.bind = this.defaultBind = defaultBind;
|
||||||
this.renderer = new BindRendererImpl(bind);
|
this.renderer = new BindRendererImpl(bind);
|
||||||
@ -53,33 +53,38 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
|||||||
this.description = description;
|
this.description = description;
|
||||||
this.category = category;
|
this.category = category;
|
||||||
this.contexts = ImmutableSet.copyOf(contexts);
|
this.contexts = ImmutableSet.copyOf(contexts);
|
||||||
|
this.radialIcon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float state() {
|
public float state() {
|
||||||
|
if (fakePressState == 1)
|
||||||
|
return 1f;
|
||||||
return bind.state(controller.state());
|
return bind.state(controller.state());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float prevState() {
|
public float prevState() {
|
||||||
|
if (fakePressState == 2)
|
||||||
|
return 1f;
|
||||||
return bind.state(controller.prevState());
|
return bind.state(controller.prevState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean held() {
|
public boolean held() {
|
||||||
return bind.held(controller.state());
|
return fakePressState == 2 || bind.held(controller.state());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean prevHeld() {
|
public boolean prevHeld() {
|
||||||
return bind.held(controller.prevState());
|
return fakePressState == 3 || bind.held(controller.prevState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean justPressed() {
|
public boolean justPressed() {
|
||||||
if (hasBindPressed(this)) return false;
|
if (hasBindPressed(this)) return false;
|
||||||
|
|
||||||
if (held() && !prevHeld()) {
|
if ((held() && !prevHeld()) || fakePressState == 2) {
|
||||||
addPressedBind(this);
|
addPressedBind(this);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -91,7 +96,7 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
|||||||
public boolean justReleased() {
|
public boolean justReleased() {
|
||||||
if (hasBindPressed(this)) return false;
|
if (hasBindPressed(this)) return false;
|
||||||
|
|
||||||
if (!held() && prevHeld()) {
|
if ((!held() && prevHeld()) || fakePressState == 3) {
|
||||||
addPressedBind(this);
|
addPressedBind(this);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -99,6 +104,24 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fakePress() {
|
||||||
|
this.fakePressState = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
if (fakePressState > 0)
|
||||||
|
fakePressState++;
|
||||||
|
if (fakePressState >= 4)
|
||||||
|
fakePressState = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ResourceLocation> radialIcon() {
|
||||||
|
return Optional.ofNullable(this.radialIcon);
|
||||||
|
}
|
||||||
|
|
||||||
public IBind<T> currentBind() {
|
public IBind<T> currentBind() {
|
||||||
return bind;
|
return bind;
|
||||||
}
|
}
|
||||||
@ -214,6 +237,7 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
|||||||
private Component name = null, description = null, category = null;
|
private Component name = null, description = null, category = null;
|
||||||
private KeyMappingOverride override = null;
|
private KeyMappingOverride override = null;
|
||||||
private final Set<BindContext> contexts = new HashSet<>();
|
private final Set<BindContext> contexts = new HashSet<>();
|
||||||
|
private ResourceLocation radialIcon = null;
|
||||||
|
|
||||||
public ControllerBindingBuilderImpl(Controller<T, ?> controller) {
|
public ControllerBindingBuilderImpl(Controller<T, ?> controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
@ -271,6 +295,12 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ControllerBindingBuilder<T> radialCandidate(ResourceLocation icon) {
|
||||||
|
this.radialIcon = icon;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ControllerBindingBuilder<T> vanillaOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
|
public ControllerBindingBuilder<T> vanillaOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
|
||||||
this.override = new KeyMappingOverride(keyMapping, toggleable);
|
this.override = new KeyMappingOverride(keyMapping, toggleable);
|
||||||
@ -299,7 +329,7 @@ public class ControllerBindingImpl<T extends ControllerState> implements Control
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ControllerBindingImpl<>(controller, bind, id, override, name, description, category, contexts);
|
return new ControllerBindingImpl<>(controller, bind, id, override, name, description, category, contexts, radialIcon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ import net.minecraft.client.Minecraft;
|
|||||||
import net.minecraft.client.ToggleKeyMapping;
|
import net.minecraft.client.ToggleKeyMapping;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.effect.MobEffects;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
@ -37,6 +39,7 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
public static final Component VMOUSE_CATEGORY = Component.translatable("controlify.binding_category.vmouse");
|
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 GUI_CATEGORY = Component.translatable("controlify.binding_category.gui");
|
||||||
public static final Component MISC_CATEGORY = Component.translatable("key.categories.misc");
|
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
|
public final ControllerBinding
|
||||||
WALK_FORWARD, WALK_BACKWARD, WALK_LEFT, WALK_RIGHT,
|
WALK_FORWARD, WALK_BACKWARD, WALK_LEFT, WALK_RIGHT,
|
||||||
@ -59,6 +62,7 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
PICK_BLOCK,
|
PICK_BLOCK,
|
||||||
TOGGLE_HUD_VISIBILITY,
|
TOGGLE_HUD_VISIBILITY,
|
||||||
SHOW_PLAYER_LIST,
|
SHOW_PLAYER_LIST,
|
||||||
|
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_MOVE_UP, VMOUSE_MOVE_DOWN, VMOUSE_MOVE_LEFT, VMOUSE_MOVE_RIGHT,
|
||||||
VMOUSE_LCLICK, VMOUSE_RCLICK, VMOUSE_SHIFT_CLICK,
|
VMOUSE_LCLICK, VMOUSE_RCLICK, VMOUSE_SHIFT_CLICK,
|
||||||
VMOUSE_SCROLL_UP, VMOUSE_SCROLL_DOWN,
|
VMOUSE_SCROLL_UP, VMOUSE_SCROLL_DOWN,
|
||||||
@ -138,6 +142,7 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
.defaultBind(GamepadBinds.A_BUTTON)
|
.defaultBind(GamepadBinds.A_BUTTON)
|
||||||
.category(MOVEMENT_CATEGORY)
|
.category(MOVEMENT_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getEffect(MobEffects.JUMP))
|
||||||
.build());
|
.build());
|
||||||
register(SPRINT = ControllerBindingBuilder.create(controller)
|
register(SPRINT = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "sprint")
|
.identifier("controlify", "sprint")
|
||||||
@ -171,12 +176,14 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
.defaultBind(GamepadBinds.DPAD_DOWN)
|
.defaultBind(GamepadBinds.DPAD_DOWN)
|
||||||
.category(GAMEPLAY_CATEGORY)
|
.category(GAMEPLAY_CATEGORY)
|
||||||
.context(BindContexts.INGAME, BindContexts.INVENTORY)
|
.context(BindContexts.INGAME, BindContexts.INVENTORY)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.BARRIER))
|
||||||
.build());
|
.build());
|
||||||
register(DROP_STACK = ControllerBindingBuilder.create(controller)
|
register(DROP_STACK = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "drop_stack")
|
.identifier("controlify", "drop_stack")
|
||||||
.defaultBind(new EmptyBind<>())
|
.defaultBind(new EmptyBind<>())
|
||||||
.category(GAMEPLAY_CATEGORY)
|
.category(GAMEPLAY_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.TNT))
|
||||||
.build());
|
.build());
|
||||||
register(NEXT_SLOT = ControllerBindingBuilder.create(controller)
|
register(NEXT_SLOT = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "next_slot")
|
.identifier("controlify", "next_slot")
|
||||||
@ -195,30 +202,35 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
.defaultBind(GamepadBinds.START)
|
.defaultBind(GamepadBinds.START)
|
||||||
.category(GAMEPLAY_CATEGORY)
|
.category(GAMEPLAY_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.STRUCTURE_VOID))
|
||||||
.build());
|
.build());
|
||||||
register(INVENTORY = ControllerBindingBuilder.create(controller)
|
register(INVENTORY = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "inventory")
|
.identifier("controlify", "inventory")
|
||||||
.defaultBind(GamepadBinds.Y_BUTTON)
|
.defaultBind(GamepadBinds.Y_BUTTON)
|
||||||
.category(INVENTORY_CATEGORY)
|
.category(INVENTORY_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.CHEST))
|
||||||
.build());
|
.build());
|
||||||
register(CHANGE_PERSPECTIVE = ControllerBindingBuilder.create(controller)
|
register(CHANGE_PERSPECTIVE = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "change_perspective")
|
.identifier("controlify", "change_perspective")
|
||||||
.defaultBind(GamepadBinds.BACK)
|
.defaultBind(GamepadBinds.BACK)
|
||||||
.category(GAMEPLAY_CATEGORY)
|
.category(GAMEPLAY_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.PAINTING))
|
||||||
.build());
|
.build());
|
||||||
register(SWAP_HANDS = ControllerBindingBuilder.create(controller)
|
register(SWAP_HANDS = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "swap_hands")
|
.identifier("controlify", "swap_hands")
|
||||||
.defaultBind(GamepadBinds.X_BUTTON)
|
.defaultBind(GamepadBinds.X_BUTTON)
|
||||||
.category(INVENTORY_CATEGORY)
|
.category(INVENTORY_CATEGORY)
|
||||||
.context(BindContexts.INGAME, BindContexts.INVENTORY)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.BONE))
|
||||||
.build());
|
.build());
|
||||||
register(OPEN_CHAT = ControllerBindingBuilder.create(controller)
|
register(OPEN_CHAT = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "open_chat")
|
.identifier("controlify", "open_chat")
|
||||||
.defaultBind(GamepadBinds.DPAD_UP)
|
.defaultBind(GamepadBinds.DPAD_UP)
|
||||||
.category(MISC_CATEGORY)
|
.category(MISC_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.WRITABLE_BOOK))
|
||||||
.vanillaOverride(options.keyChat, () -> false)
|
.vanillaOverride(options.keyChat, () -> false)
|
||||||
.build());
|
.build());
|
||||||
register(GUI_PRESS = ControllerBindingBuilder.create(controller)
|
register(GUI_PRESS = ControllerBindingBuilder.create(controller)
|
||||||
@ -280,6 +292,7 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
.defaultBind(GamepadBinds.DPAD_LEFT)
|
.defaultBind(GamepadBinds.DPAD_LEFT)
|
||||||
.category(GAMEPLAY_CATEGORY)
|
.category(GAMEPLAY_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.STICK))
|
||||||
.vanillaOverride(options.keyPickItem, () -> false)
|
.vanillaOverride(options.keyPickItem, () -> false)
|
||||||
.build());
|
.build());
|
||||||
register(TOGGLE_HUD_VISIBILITY = ControllerBindingBuilder.create(controller)
|
register(TOGGLE_HUD_VISIBILITY = ControllerBindingBuilder.create(controller)
|
||||||
@ -287,12 +300,44 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
.defaultBind(new EmptyBind<>())
|
.defaultBind(new EmptyBind<>())
|
||||||
.category(MISC_CATEGORY)
|
.category(MISC_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getEffect(MobEffects.INVISIBILITY))
|
||||||
.build());
|
.build());
|
||||||
register(SHOW_PLAYER_LIST = ControllerBindingBuilder.create(controller)
|
register(SHOW_PLAYER_LIST = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "show_player_list")
|
.identifier("controlify", "show_player_list")
|
||||||
.defaultBind(GamepadBinds.DPAD_RIGHT)
|
.defaultBind(new EmptyBind<>())
|
||||||
.category(MISC_CATEGORY)
|
.category(MISC_CATEGORY)
|
||||||
.context(BindContexts.INGAME)
|
.context(BindContexts.INGAME)
|
||||||
|
.radialCandidate(RadialIcons.getItem(Items.PLAYER_HEAD))
|
||||||
|
.build());
|
||||||
|
register(RADIAL_MENU = ControllerBindingBuilder.create(controller)
|
||||||
|
.identifier("controlify", "radial_menu")
|
||||||
|
.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());
|
.build());
|
||||||
register(VMOUSE_MOVE_UP = ControllerBindingBuilder.create(controller)
|
register(VMOUSE_MOVE_UP = ControllerBindingBuilder.create(controller)
|
||||||
.identifier("controlify", "vmouse_move_up")
|
.identifier("controlify", "vmouse_move_up")
|
||||||
@ -408,6 +453,9 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
this.imitateVanillaClick();
|
this.imitateVanillaClick();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||||
|
registry().values().forEach(ControllerBinding::tick);
|
||||||
|
});
|
||||||
|
|
||||||
ControlifyEvents.INPUT_MODE_CHANGED.register(mode -> KeyMapping.releaseAll());
|
ControlifyEvents.INPUT_MODE_CHANGED.register(mode -> KeyMapping.releaseAll());
|
||||||
}
|
}
|
||||||
@ -503,6 +551,7 @@ public class ControllerBindings<T extends ControllerState> {
|
|||||||
.name(Component.translatable(keyMapping.getName()))
|
.name(Component.translatable(keyMapping.getName()))
|
||||||
.description(Component.translatable("controlify.custom_binding.vanilla_description").withStyle(ChatFormatting.GRAY))
|
.description(Component.translatable("controlify.custom_binding.vanilla_description").withStyle(ChatFormatting.GRAY))
|
||||||
.category(Component.translatable(keyMapping.getCategory()))
|
.category(Component.translatable(keyMapping.getCategory()))
|
||||||
|
.radialCandidate(RadialIcons.FABRIC_ICON)
|
||||||
.vanillaOverride(keyMapping, toggleOverride)
|
.vanillaOverride(keyMapping, toggleOverride)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
package dev.isxander.controlify.bindings;
|
||||||
|
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.resources.MobEffectTextureManager;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.effect.MobEffect;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
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-resource-loader-v0", "icon.png");
|
||||||
|
|
||||||
|
private static final Map<ResourceLocation, Icon> icons = Util.make(() -> {
|
||||||
|
Map<ResourceLocation, Icon> map = new HashMap<>();
|
||||||
|
|
||||||
|
map.put(EMPTY, (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, 32, 32);
|
||||||
|
graphics.pose().popPose();
|
||||||
|
});
|
||||||
|
addItems(map);
|
||||||
|
addPotionEffects(map);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
|
||||||
|
public static Map<ResourceLocation, Icon> getIcons() {
|
||||||
|
return icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceLocation getItem(Item item) {
|
||||||
|
return prefixLocation("item", BuiltInRegistries.ITEM.getKey(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceLocation getEffect(MobEffect effect) {
|
||||||
|
return prefixLocation("effect", BuiltInRegistries.MOB_EFFECT.getKey(effect));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addItems(Map<ResourceLocation, Icon> map) {
|
||||||
|
BuiltInRegistries.ITEM.entrySet().forEach(entry -> {
|
||||||
|
ResourceKey<Item> key = entry.getKey();
|
||||||
|
ItemStack stack = entry.getValue().getDefaultInstance();
|
||||||
|
|
||||||
|
map.put(prefixLocation("item", key.location()), (graphics, x, y) -> {
|
||||||
|
graphics.renderItem(stack, x, y);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addPotionEffects(Map<ResourceLocation, Icon> map) {
|
||||||
|
MobEffectTextureManager mobEffectTextureManager = minecraft.getMobEffectTextures();
|
||||||
|
|
||||||
|
BuiltInRegistries.MOB_EFFECT.entrySet().forEach(entry -> {
|
||||||
|
ResourceKey<MobEffect> key = entry.getKey();
|
||||||
|
MobEffect effect = entry.getValue();
|
||||||
|
|
||||||
|
TextureAtlasSprite sprite = mobEffectTextureManager.get(effect);
|
||||||
|
map.put(prefixLocation("effect", key.location()), (graphics, x, y) -> {
|
||||||
|
graphics.pose().pushPose();
|
||||||
|
graphics.pose().translate(x, y, 0);
|
||||||
|
graphics.pose().scale(0.88f, 0.88f, 1f);
|
||||||
|
|
||||||
|
graphics.blit(0, 0, 0, 18, 18, sprite);
|
||||||
|
|
||||||
|
graphics.pose().popPose();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResourceLocation prefixLocation(String prefix, ResourceLocation location) {
|
||||||
|
return new ResourceLocation(location.getNamespace(), prefix + "/" + location.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Icon {
|
||||||
|
void draw(GuiGraphics graphics, int x, int y);
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ import dev.isxander.controlify.utils.Log;
|
|||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.loader.api.Version;
|
import net.fabricmc.loader.api.Version;
|
||||||
import net.fabricmc.loader.api.VersionParsingException;
|
import net.fabricmc.loader.api.VersionParsingException;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -29,6 +30,7 @@ public class ControlifyConfig {
|
|||||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||||
.registerTypeHierarchyAdapter(Class.class, new TypeAdapters.ClassTypeAdapter())
|
.registerTypeHierarchyAdapter(Class.class, new TypeAdapters.ClassTypeAdapter())
|
||||||
.registerTypeHierarchyAdapter(Version.class, new TypeAdapters.VersionTypeAdapter())
|
.registerTypeHierarchyAdapter(Version.class, new TypeAdapters.VersionTypeAdapter())
|
||||||
|
.registerTypeHierarchyAdapter(ResourceLocation.class, new ResourceLocation.Serializer())
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
private final Controlify controlify;
|
private final Controlify controlify;
|
||||||
@ -175,8 +177,8 @@ public class ControlifyConfig {
|
|||||||
|
|
||||||
private void applyControllerConfig(Controller<?, ?> controller, JsonObject object) {
|
private void applyControllerConfig(Controller<?, ?> controller, JsonObject object) {
|
||||||
try {
|
try {
|
||||||
controller.setConfig(GSON, object.getAsJsonObject("config"));
|
|
||||||
dirty |= !controller.bindings().fromJson(object.getAsJsonObject("bindings"));
|
dirty |= !controller.bindings().fromJson(object.getAsJsonObject("bindings"));
|
||||||
|
controller.setConfig(GSON, object.getAsJsonObject("config"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.LOGGER.error("Failed to load controller data for " + controller.uid() + ". Resetting to default!", e);
|
Log.LOGGER.error("Failed to load controller data for " + controller.uid() + ". Resetting to default!", e);
|
||||||
controller.resetConfig();
|
controller.resetConfig();
|
||||||
|
@ -120,6 +120,7 @@ public abstract class AbstractController<S extends ControllerState, C extends Co
|
|||||||
this.config = SerializationUtils.clone(defaultConfig());
|
this.config = SerializationUtils.clone(defaultConfig());
|
||||||
Controlify.instance().config().setDirty();
|
Controlify.instance().config().setDirty();
|
||||||
}
|
}
|
||||||
|
this.config.validateRadialActions(bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2,7 +2,11 @@ package dev.isxander.controlify.controller;
|
|||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import dev.isxander.controlify.Controlify;
|
||||||
|
import dev.isxander.controlify.bindings.ControllerBindings;
|
||||||
|
import dev.isxander.controlify.gui.screen.RadialMenuScreen;
|
||||||
import dev.isxander.controlify.rumble.RumbleSource;
|
import dev.isxander.controlify.rumble.RumbleSource;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@ -36,6 +40,10 @@ public abstract class ControllerConfig implements Serializable {
|
|||||||
|
|
||||||
public boolean mixedInput = false;
|
public boolean mixedInput = false;
|
||||||
|
|
||||||
|
public ResourceLocation[] radialActions = new ResourceLocation[]{
|
||||||
|
null, null, null, null, null, null, null, null,
|
||||||
|
};
|
||||||
|
|
||||||
public boolean dontShowControllerSubmission = false;
|
public boolean dontShowControllerSubmission = false;
|
||||||
|
|
||||||
public abstract void setDeadzone(int axis, float deadzone);
|
public abstract void setDeadzone(int axis, float deadzone);
|
||||||
@ -48,4 +56,33 @@ public abstract class ControllerConfig implements Serializable {
|
|||||||
public void setRumbleStrength(RumbleSource source, float strength) {
|
public void setRumbleStrength(RumbleSource source, float strength) {
|
||||||
vibrationStrengths.addProperty(source.id().toString(), strength);
|
vibrationStrengths.addProperty(source.id().toString(), strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean validateRadialActions(ControllerBindings<?> 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,11 @@ public class InGameButtonGuide implements IngameGuideRegistry {
|
|||||||
return Optional.of(Component.translatable("controlify.guide.ingame.inventory"));
|
return Optional.of(Component.translatable("controlify.guide.ingame.inventory"));
|
||||||
return Optional.empty();
|
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) -> {
|
registerGuideAction(controller.bindings().ATTACK, ActionLocation.RIGHT, (ctx) -> {
|
||||||
var hitResult = ctx.hitResult();
|
var hitResult = ctx.hitResult();
|
||||||
if (hitResult.getType() == HitResult.Type.ENTITY)
|
if (hitResult.getType() == HitResult.Type.ENTITY)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package dev.isxander.controlify.gui.layout;
|
package dev.isxander.controlify.gui.layout;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.Renderable;
|
import net.minecraft.client.gui.components.Renderable;
|
||||||
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
|
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||||
|
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||||
import org.joml.Vector2ic;
|
import org.joml.Vector2ic;
|
||||||
|
|
||||||
public class PositionedComponent<T extends RenderComponent> implements Renderable {
|
public class PositionedComponent<T extends RenderComponent> implements Renderable, GuiEventListener, NarratableEntry {
|
||||||
private final T component;
|
private final T component;
|
||||||
|
|
||||||
private int x, y;
|
private int x, y;
|
||||||
@ -52,4 +54,24 @@ public class PositionedComponent<T extends RenderComponent> implements Renderabl
|
|||||||
public T getComponent() {
|
public T getComponent() {
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(boolean focused) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFocused() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NarrationPriority narrationPriority() {
|
||||||
|
return NarrationPriority.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateNarration(NarrationElementOutput builder) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package dev.isxander.controlify.gui.screen;
|
package dev.isxander.controlify.gui.screen;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import dev.isxander.controlify.Controlify;
|
import dev.isxander.controlify.Controlify;
|
||||||
import dev.isxander.controlify.api.bind.ControllerBinding;
|
import dev.isxander.controlify.api.bind.ControllerBinding;
|
||||||
import dev.isxander.controlify.bindings.BindContext;
|
import dev.isxander.controlify.bindings.BindContext;
|
||||||
@ -16,10 +17,7 @@ import dev.isxander.controlify.rumble.BasicRumbleEffect;
|
|||||||
import dev.isxander.controlify.rumble.RumbleSource;
|
import dev.isxander.controlify.rumble.RumbleSource;
|
||||||
import dev.isxander.controlify.rumble.RumbleState;
|
import dev.isxander.controlify.rumble.RumbleState;
|
||||||
import dev.isxander.yacl3.api.*;
|
import dev.isxander.yacl3.api.*;
|
||||||
import dev.isxander.yacl3.api.controller.BooleanControllerBuilder;
|
import dev.isxander.yacl3.api.controller.*;
|
||||||
import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder;
|
|
||||||
import dev.isxander.yacl3.api.controller.StringControllerBuilder;
|
|
||||||
import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder;
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
@ -334,6 +332,20 @@ public class ControllerConfigScreenFactory {
|
|||||||
.name(Component.translatable("controlify.gui.group.controls"));
|
.name(Component.translatable("controlify.gui.group.controls"));
|
||||||
|
|
||||||
List<OptionBindPair> optionBinds = new ArrayList<>();
|
List<OptionBindPair> optionBinds = new ArrayList<>();
|
||||||
|
|
||||||
|
ButtonOption editRadialButton = ButtonOption.createBuilder()
|
||||||
|
.name(Component.translatable("controlify.gui.radial_menu"))
|
||||||
|
.description(OptionDescription.of(Component.translatable("controlify.gui.radial_menu.tooltip")))
|
||||||
|
.action((screen, opt) -> Minecraft.getInstance().setScreen(new RadialMenuScreen(controller, true, screen)))
|
||||||
|
.text(Component.translatable("controlify.gui.radial_menu.btn_text"))
|
||||||
|
.build();
|
||||||
|
Option<?> radialBind = controller.bindings().RADIAL_MENU.startYACLOption()
|
||||||
|
.listener((opt, val) -> updateConflictingBinds(optionBinds))
|
||||||
|
.build();
|
||||||
|
optionBinds.add(new OptionBindPair(radialBind, controller.bindings().RADIAL_MENU));
|
||||||
|
category.option(editRadialButton);
|
||||||
|
category.option(radialBind);
|
||||||
|
|
||||||
groupBindings(controller.bindings().registry().values()).forEach((categoryName, bindGroup) -> {
|
groupBindings(controller.bindings().registry().values()).forEach((categoryName, bindGroup) -> {
|
||||||
var controlsGroup = OptionGroup.createBuilder()
|
var controlsGroup = OptionGroup.createBuilder()
|
||||||
.name(categoryName);
|
.name(categoryName);
|
||||||
|
@ -0,0 +1,522 @@
|
|||||||
|
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.gui.guide.GuideAction;
|
||||||
|
import dev.isxander.controlify.gui.guide.GuideActionRenderer;
|
||||||
|
import dev.isxander.controlify.gui.layout.AnchorPoint;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
||||||
|
import net.minecraft.network.chat.CommonComponents;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class RadialMenuScreen extends Screen implements ScreenControllerEventListener, ScreenProcessorProvider {
|
||||||
|
public static final ResourceLocation EMPTY_ACTION = new ResourceLocation("controlify", "empty_action");
|
||||||
|
|
||||||
|
private final Controller<?, ?> controller;
|
||||||
|
private final boolean editMode;
|
||||||
|
private final Screen parent;
|
||||||
|
|
||||||
|
private final RadialButton[] buttons = new RadialButton[8];
|
||||||
|
private int selectedButton = -1;
|
||||||
|
private int idleTicks;
|
||||||
|
private boolean isEditing;
|
||||||
|
|
||||||
|
private ActionSelectList actionSelectList;
|
||||||
|
|
||||||
|
private final Processor processor = new Processor(this);
|
||||||
|
|
||||||
|
public RadialMenuScreen(Controller<?, ?> controller, boolean editMode, Screen parent) {
|
||||||
|
super(Component.empty());
|
||||||
|
this.controller = controller;
|
||||||
|
this.editMode = editMode;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
int centerX = this.width / 2;
|
||||||
|
int centerY = this.height / 2;
|
||||||
|
|
||||||
|
RadialButton button;
|
||||||
|
addRenderableWidget(buttons[0] = button = new RadialButton(0, centerX - 16, centerY - 64 - 8));
|
||||||
|
addRenderableWidget(buttons[1] = button = new RadialButton(1, button.x + 32 + 8, button.y + 16));
|
||||||
|
addRenderableWidget(buttons[2] = button = new RadialButton(2, button.x + 16, button.y + 32 + 8));
|
||||||
|
addRenderableWidget(buttons[3] = button = new RadialButton(3, button.x - 16, button.y + 32 + 8));
|
||||||
|
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));
|
||||||
|
|
||||||
|
Animator.AnimationInstance animation = new Animator.AnimationInstance(5, Easings::easeOutQuad);
|
||||||
|
for (RadialButton radialButton : buttons) {
|
||||||
|
animation.addConsumer(radialButton::setX, centerX - 16, (float) radialButton.getX());
|
||||||
|
animation.addConsumer(radialButton::setY, centerY - 16, (float) radialButton.getY());
|
||||||
|
}
|
||||||
|
Animator.INSTANCE.play(animation);
|
||||||
|
|
||||||
|
if (editMode) {
|
||||||
|
var exitGuide = addRenderableWidget(new PositionedComponent<>(
|
||||||
|
new GuideActionRenderer<>(
|
||||||
|
new GuideAction<>(
|
||||||
|
controller.bindings().GUI_BACK,
|
||||||
|
obj -> Optional.of(CommonComponents.GUI_DONE)
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
AnchorPoint.BOTTOM_CENTER,
|
||||||
|
0, -10,
|
||||||
|
AnchorPoint.BOTTOM_CENTER
|
||||||
|
));
|
||||||
|
|
||||||
|
exitGuide.getComponent().updateName(null);
|
||||||
|
exitGuide.updatePosition(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onControllerInput(Controller<?, ?> controller) {
|
||||||
|
if (this.controller != controller) return;
|
||||||
|
|
||||||
|
if (!editMode && !controller.bindings().RADIAL_MENU.held()) {
|
||||||
|
if (selectedButton != -1 && buttons[selectedButton].invoke()) {
|
||||||
|
playClickSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editMode && controller.bindings().GUI_BACK.justPressed()) {
|
||||||
|
playClickSound();
|
||||||
|
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) {
|
||||||
|
float angle = Mth.wrapDegrees(Mth.RAD_TO_DEG * (float) Mth.atan2(y, x) - 90f) + 180f;
|
||||||
|
float each = 360f / buttons.length;
|
||||||
|
|
||||||
|
int newSelected = Mth.floor((angle + each / 2f) / each) % buttons.length;
|
||||||
|
if (newSelected != selectedButton) {
|
||||||
|
selectedButton = newSelected;
|
||||||
|
minecraft.getSoundManager().play(SimpleSoundInstance.forUI(ControlifySounds.SCREEN_FOCUS_CHANGE, 1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buttons.length; i++) {
|
||||||
|
boolean selected = i == selectedButton;
|
||||||
|
buttons[i].setFocused(selected);
|
||||||
|
if (selected) {
|
||||||
|
this.setFocused(buttons[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idleTicks = 0;
|
||||||
|
} else if (!editMode) {
|
||||||
|
idleTicks++;
|
||||||
|
if (idleTicks >= 20) {
|
||||||
|
selectedButton = -1;
|
||||||
|
for (RadialButton button : buttons) {
|
||||||
|
button.setFocused(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
|
||||||
|
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 - 39,
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playClickSound() {
|
||||||
|
minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishEditing() {
|
||||||
|
isEditing = false;
|
||||||
|
removeWidget(actionSelectList);
|
||||||
|
this.setFocused(null);
|
||||||
|
actionSelectList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose() {
|
||||||
|
Controlify.instance().config().saveIfDirty();
|
||||||
|
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");
|
||||||
|
|
||||||
|
private int x, y;
|
||||||
|
private float translateX, translateY;
|
||||||
|
private boolean focused;
|
||||||
|
private ControllerBinding binding;
|
||||||
|
private MultiLineLabel name;
|
||||||
|
private RadialIcons.Icon icon;
|
||||||
|
|
||||||
|
private RadialButton(int index, float x, float y) {
|
||||||
|
this.setX(x);
|
||||||
|
this.setY(y);
|
||||||
|
|
||||||
|
this.setAction(controller.config().radialActions[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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();
|
||||||
|
|
||||||
|
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)
|
||||||
|
name.renderCentered(graphics, width / 2, height / 2 - font.lineHeight / 2 - ((name.getLineCount() - 1) * font.lineHeight / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean invoke() {
|
||||||
|
if (binding != null) {
|
||||||
|
binding.fakePress();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAction(ResourceLocation binding) {
|
||||||
|
if (!EMPTY_ACTION.equals(binding)) {
|
||||||
|
this.binding = controller.bindings().get(binding);
|
||||||
|
this.icon = RadialIcons.getIcons().get(this.binding.radialIcon().orElseThrow());
|
||||||
|
this.name = MultiLineLabel.create(font, this.binding.name(), 76);
|
||||||
|
} else {
|
||||||
|
this.binding = null;
|
||||||
|
this.name = MultiLineLabel.EMPTY;
|
||||||
|
this.icon = RadialIcons.getIcons().get(RadialIcons.EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(float x) {
|
||||||
|
this.x = (int) x;
|
||||||
|
this.translateX = x - this.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(float y) {
|
||||||
|
this.y = (int) y;
|
||||||
|
this.translateY = y - this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFocused() {
|
||||||
|
return focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(boolean focused) {
|
||||||
|
this.focused = focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller<?, ?> controller) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NarrationPriority narrationPriority() {
|
||||||
|
return isFocused() ? NarrationPriority.FOCUSED : NarrationPriority.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateNarration(NarrationElementOutput builder) {
|
||||||
|
if (binding != null)
|
||||||
|
builder.add(NarratedElementType.TITLE, binding.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenRectangle getRectangle() {
|
||||||
|
return new ScreenRectangle(x, y, 32, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActionSelectList implements Renderable, ContainerEventHandler, NarratableEntry, ComponentProcessor {
|
||||||
|
private final int radialIndex;
|
||||||
|
|
||||||
|
private int x, y;
|
||||||
|
private int width, height;
|
||||||
|
private final int itemHeight = 10;
|
||||||
|
private int scrollOffset;
|
||||||
|
|
||||||
|
private boolean focused;
|
||||||
|
private ActionEntry focusedEntry;
|
||||||
|
|
||||||
|
private final List<ActionEntry> children = new ArrayList<>();
|
||||||
|
|
||||||
|
public ActionSelectList(int index, int x, int y, int width, int height) {
|
||||||
|
this.radialIndex = index;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
|
||||||
|
controller.bindings().registry().entrySet().stream()
|
||||||
|
.filter(entry -> entry.getValue().radialIcon().isPresent())
|
||||||
|
.map(Map.Entry::getKey)
|
||||||
|
.forEach(id -> children.add(new ActionEntry(id)));
|
||||||
|
|
||||||
|
var selectedBind = controller.config().radialActions[radialIndex];
|
||||||
|
children.stream()
|
||||||
|
.filter(action -> action.binding.equals(selectedBind))
|
||||||
|
.findAny()
|
||||||
|
.ifPresent(this::setFocused);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
|
||||||
|
graphics.fill(x, y, x + width, y + height, 0x80000000);
|
||||||
|
|
||||||
|
graphics.enableScissor(x, y, x + width, y + height);
|
||||||
|
int y = this.y - scrollOffset;
|
||||||
|
for (ActionEntry child : children) {
|
||||||
|
child.render(graphics, x, y, width, itemHeight, mouseX, mouseY, delta);
|
||||||
|
y += itemHeight;
|
||||||
|
}
|
||||||
|
graphics.disableScissor();
|
||||||
|
|
||||||
|
graphics.renderOutline(x - 1, this.y - 1, width + 2, height + 2, 0x80ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller<?, ?> controller) {
|
||||||
|
if (controller == RadialMenuScreen.this.controller) {
|
||||||
|
if (controller.bindings().GUI_BACK.justPressed()) {
|
||||||
|
finishEditing();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ActionEntry> children() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDragging() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDragging(boolean dragging) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ActionEntry getFocused() {
|
||||||
|
return focusedEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(@Nullable GuiEventListener child) {
|
||||||
|
ActionEntry focus = (ActionEntry) child;
|
||||||
|
this.focusedEntry = focus;
|
||||||
|
|
||||||
|
if (focus != null) {
|
||||||
|
int index = children().indexOf(child);
|
||||||
|
if (index != -1) {
|
||||||
|
int focusY = index * itemHeight - scrollOffset;
|
||||||
|
if (focusY < 0)
|
||||||
|
scrollOffset = Mth.clamp(index * itemHeight, 0, children().size() * itemHeight - height);
|
||||||
|
else if (focusY + itemHeight > height)
|
||||||
|
scrollOffset = Mth.clamp(index * itemHeight + itemHeight - height, 0, children().size() * itemHeight - height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(boolean focused) {
|
||||||
|
this.focused = focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFocused() {
|
||||||
|
return focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NarrationPriority narrationPriority() {
|
||||||
|
return focused ? NarrationPriority.FOCUSED : NarrationPriority.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateNarration(NarrationElementOutput builder) {
|
||||||
|
if (getFocused() != null) {
|
||||||
|
builder.add(NarratedElementType.TITLE, getFocused().name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActionEntry implements GuiEventListener, ComponentProcessor {
|
||||||
|
private int x, y;
|
||||||
|
private boolean focused;
|
||||||
|
private final ResourceLocation binding;
|
||||||
|
private final Component name;
|
||||||
|
|
||||||
|
public ActionEntry(ResourceLocation binding) {
|
||||||
|
this.binding = binding;
|
||||||
|
this.name = controller.bindings().get(binding).name();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(GuiGraphics graphics, int x, int y, int width, int itemHeight, int mouseX, int mouseY, float delta) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
|
||||||
|
if (focused)
|
||||||
|
graphics.fill(x, y, x + width, y + itemHeight, 0xff000000);
|
||||||
|
graphics.drawString(RadialMenuScreen.this.font, name, x + 2, y + 1, focused ? -1 : 0xffa6a6a6);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(boolean focused) {
|
||||||
|
this.focused = focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFocused() {
|
||||||
|
return focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ComponentPath nextFocusPath(FocusNavigationEvent event) {
|
||||||
|
return !focused ? ComponentPath.leaf(this) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenRectangle getRectangle() {
|
||||||
|
return new ScreenRectangle(x, y, width, itemHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean overrideControllerButtons(ScreenProcessor<?> screen, Controller<?, ?> controller) {
|
||||||
|
if (controller == RadialMenuScreen.this.controller) {
|
||||||
|
if (controller.bindings().GUI_PRESS.justPressed()) {
|
||||||
|
controller.config().radialActions[radialIndex] = binding;
|
||||||
|
Controlify.instance().config().setDirty();
|
||||||
|
|
||||||
|
buttons[radialIndex].setAction(binding);
|
||||||
|
|
||||||
|
playClickSound();
|
||||||
|
finishEditing();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Processor extends ScreenProcessor<RadialMenuScreen> {
|
||||||
|
public Processor(RadialMenuScreen screen) {
|
||||||
|
super(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VirtualMouseBehaviour virtualMouseBehaviour() {
|
||||||
|
return VirtualMouseBehaviour.DISABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import dev.isxander.controlify.controller.Controller;
|
|||||||
import dev.isxander.controlify.api.event.ControlifyEvents;
|
import dev.isxander.controlify.api.event.ControlifyEvents;
|
||||||
import dev.isxander.controlify.controller.gamepad.GamepadController;
|
import dev.isxander.controlify.controller.gamepad.GamepadController;
|
||||||
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
||||||
|
import dev.isxander.controlify.gui.screen.RadialMenuScreen;
|
||||||
import dev.isxander.controlify.utils.Animator;
|
import dev.isxander.controlify.utils.Animator;
|
||||||
import dev.isxander.controlify.utils.Easings;
|
import dev.isxander.controlify.utils.Easings;
|
||||||
import dev.isxander.controlify.utils.NavigationHelper;
|
import dev.isxander.controlify.utils.NavigationHelper;
|
||||||
@ -105,7 +106,13 @@ public class InGameInputHandler {
|
|||||||
minecraft.options.hideGui = !minecraft.options.hideGui;
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handlePlayerLookInput() {
|
protected void handlePlayerLookInput() {
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package dev.isxander.controlify.mixins.feature.bind;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.WrapWithCondition;
|
||||||
|
import dev.isxander.controlify.gui.screen.RadialMenuScreen;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.Gui;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
||||||
|
@Mixin(Gui.class)
|
||||||
|
public class GuiMixin {
|
||||||
|
@Shadow @Final private Minecraft minecraft;
|
||||||
|
|
||||||
|
@WrapWithCondition(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/Gui;renderCrosshair(Lnet/minecraft/client/gui/GuiGraphics;)V"))
|
||||||
|
private boolean shouldRenderCrosshair(Gui instance, GuiGraphics graphics) {
|
||||||
|
return !(minecraft.screen instanceof RadialMenuScreen);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package dev.isxander.controlify.mixins.feature.screenop;
|
||||||
|
|
||||||
|
import dev.isxander.controlify.screenop.CustomFocus;
|
||||||
|
import net.minecraft.client.gui.components.events.ContainerEventHandler;
|
||||||
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
@Mixin(ContainerEventHandler.class)
|
||||||
|
public interface ContainerEventHandlerMixin extends CustomFocus {
|
||||||
|
@Shadow
|
||||||
|
@Nullable GuiEventListener getFocused();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default GuiEventListener getCustomFocus() {
|
||||||
|
return this.getFocused();
|
||||||
|
}
|
||||||
|
}
|
@ -113,6 +113,9 @@
|
|||||||
"controlify.gui.test_vibration": "Test Vibration",
|
"controlify.gui.test_vibration": "Test Vibration",
|
||||||
"controlify.gui.test_vibration.tooltip": "Test the vibration of your controller.",
|
"controlify.gui.test_vibration.tooltip": "Test the vibration of your controller.",
|
||||||
|
|
||||||
|
"controlify.gui.radial_menu": "Radial Menu",
|
||||||
|
"controlify.gui.radial_menu.tooltip": "Open up the radial menu to configure what each button does.",
|
||||||
|
"controlify.gui.radial_menu.btn_text": "CONFIGURE",
|
||||||
"controlify.gui.group.controls": "Controls",
|
"controlify.gui.group.controls": "Controls",
|
||||||
"controlify.gui.group.controls.tooltip": "Adjust the controller controls.",
|
"controlify.gui.group.controls.tooltip": "Adjust the controller controls.",
|
||||||
"controlify.gui.bind_input_awaiting": "Press any button",
|
"controlify.gui.bind_input_awaiting": "Press any button",
|
||||||
@ -218,6 +221,7 @@
|
|||||||
"controlify.binding.controlify.pick_block": "Pick Block",
|
"controlify.binding.controlify.pick_block": "Pick Block",
|
||||||
"controlify.binding.controlify.toggle_hud_visibility": "Toggle HUD Visibility",
|
"controlify.binding.controlify.toggle_hud_visibility": "Toggle HUD Visibility",
|
||||||
"controlify.binding.controlify.show_player_list": "Show Player List",
|
"controlify.binding.controlify.show_player_list": "Show Player List",
|
||||||
|
"controlify.binding.controlify.radial_menu": "Radial Menu",
|
||||||
"controlify.binding.controlify.vmouse_move_up": "VMouse Move Up",
|
"controlify.binding.controlify.vmouse_move_up": "VMouse Move Up",
|
||||||
"controlify.binding.controlify.vmouse_move_down": "VMouse Move Down",
|
"controlify.binding.controlify.vmouse_move_down": "VMouse Move Down",
|
||||||
"controlify.binding.controlify.vmouse_move_left": "VMouse Move Left",
|
"controlify.binding.controlify.vmouse_move_left": "VMouse Move Left",
|
||||||
@ -286,6 +290,8 @@
|
|||||||
"controlify.calibration.later": "Maybe Later",
|
"controlify.calibration.later": "Maybe Later",
|
||||||
"controlify.calibration.later.tooltip": "You must calibrate to use the controller. Pressing this will deactivate the controller and you will have to use it again to calibrate.",
|
"controlify.calibration.later.tooltip": "You must calibrate to use the controller. Pressing this will deactivate the controller and you will have to use it again to calibrate.",
|
||||||
|
|
||||||
|
"controlify.radial_menu.configure_hint": "Configure actions in the controller settings.",
|
||||||
|
|
||||||
"controlify.controller_submission.title": "Unknown Controller Submission",
|
"controlify.controller_submission.title": "Unknown Controller Submission",
|
||||||
"controlify.controller_submission.message": "Please submit some of your controller info to Controlify's database to get it added in a future update.\n\nControlify sends the following information:\n- Your controller's vendor & product IDs\n- Your controller's GUID\n- The name of your controller (in the box below)\n- The version of Controlify you are currently on\n\nThis is completely anonymous and doesn't store any of your personal or account information.",
|
"controlify.controller_submission.message": "Please submit some of your controller info to Controlify's database to get it added in a future update.\n\nControlify sends the following information:\n- Your controller's vendor & product IDs\n- Your controller's GUID\n- The name of your controller (in the box below)\n- The version of Controlify you are currently on\n\nThis is completely anonymous and doesn't store any of your personal or account information.",
|
||||||
"controlify.controller_submission.operational_checkbox": "Does your controller work with Controlify?",
|
"controlify.controller_submission.operational_checkbox": "Does your controller work with Controlify?",
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 261 B |
@ -27,6 +27,7 @@
|
|||||||
"core.MouseHandlerMixin",
|
"core.MouseHandlerMixin",
|
||||||
"feature.accessibility.LocalPlayerMixin",
|
"feature.accessibility.LocalPlayerMixin",
|
||||||
"feature.autoswitch.ToastComponentAccessor",
|
"feature.autoswitch.ToastComponentAccessor",
|
||||||
|
"feature.bind.GuiMixin",
|
||||||
"feature.bind.KeyMappingAccessor",
|
"feature.bind.KeyMappingAccessor",
|
||||||
"feature.bind.KeyMappingMixin",
|
"feature.bind.KeyMappingMixin",
|
||||||
"feature.bind.ToggleKeyMappingAccessor",
|
"feature.bind.ToggleKeyMappingAccessor",
|
||||||
@ -52,6 +53,7 @@
|
|||||||
"feature.rumble.levelevents.LevelRendererMixin",
|
"feature.rumble.levelevents.LevelRendererMixin",
|
||||||
"feature.rumble.useitem.LivingEntityMixin",
|
"feature.rumble.useitem.LivingEntityMixin",
|
||||||
"feature.rumble.useitem.LocalPlayerMixin",
|
"feature.rumble.useitem.LocalPlayerMixin",
|
||||||
|
"feature.screenop.ContainerEventHandlerMixin",
|
||||||
"feature.screenop.GameRendererMixin",
|
"feature.screenop.GameRendererMixin",
|
||||||
"feature.screenop.MinecraftMixin",
|
"feature.screenop.MinecraftMixin",
|
||||||
"feature.screenop.ScreenAccessor",
|
"feature.screenop.ScreenAccessor",
|
||||||
|
Reference in New Issue
Block a user