forked from Clones/Controlify
compound binds, complete vanilla compat, better vmouse screen handling, controller uuid, beta notice screen, configurable movement keys, vmouse shift key, icon, optimize controller save/load
This commit is contained in:
@ -1,21 +1,25 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.ControllerState;
|
||||
import dev.isxander.controlify.gui.ButtonRenderer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
public enum Bind {
|
||||
public enum Bind implements IBind {
|
||||
A_BUTTON(state -> state.buttons().a(), "a_button"),
|
||||
B_BUTTON(state -> state.buttons().b(), "b_button"),
|
||||
X_BUTTON(state -> state.buttons().x(), "x_button"),
|
||||
Y_BUTTON(state -> state.buttons().y(), "y_button"),
|
||||
LEFT_BUMPER(state -> state.buttons().leftBumper(), "left_bumper"),
|
||||
RIGHT_BUMPER(state -> state.buttons().rightBumper(), "right_bumper"),
|
||||
LEFT_STICK(state -> state.buttons().leftStick(), "left_stick"),
|
||||
RIGHT_STICK(state -> state.buttons().rightStick(), "right_stick"),
|
||||
LEFT_STICK_PRESS(state -> state.buttons().leftStick(), "left_stick_press"),
|
||||
RIGHT_STICK_PRESS(state -> state.buttons().rightStick(), "right_stick_press"),
|
||||
START(state -> state.buttons().start(), "start"),
|
||||
BACK(state -> state.buttons().back(), "back"),
|
||||
GUIDE(state -> state.buttons().guide(), "guide"), // the middle button
|
||||
@ -23,23 +27,42 @@ public enum Bind {
|
||||
DPAD_DOWN(state -> state.buttons().dpadDown(), "dpad_down"),
|
||||
DPAD_LEFT(state -> state.buttons().dpadLeft(), "dpad_left"),
|
||||
DPAD_RIGHT(state -> state.buttons().dpadRight(), "dpad_right"),
|
||||
LEFT_TRIGGER((state, controller) -> state.axes().leftTrigger() >= controller.config().leftTriggerActivationThreshold, "left_trigger"),
|
||||
RIGHT_TRIGGER((state, controller) -> state.axes().rightTrigger() >= controller.config().rightTriggerActivationThreshold, "right_trigger");
|
||||
LEFT_TRIGGER((state, controller) -> state.axes().leftTrigger(), "left_trigger"),
|
||||
RIGHT_TRIGGER((state, controller) -> state.axes().rightTrigger(), "right_trigger"),
|
||||
LEFT_STICK_FORWARD((state, controller) -> -Math.min(0, state.axes().leftStickY()), "left_stick_up"),
|
||||
LEFT_STICK_BACKWARD((state, controller) -> Math.max(0, state.axes().leftStickY()), "left_stick_down"),
|
||||
LEFT_STICK_LEFT((state, controller) -> -Math.min(0, state.axes().leftStickX()), "left_stick_left"),
|
||||
LEFT_STICK_RIGHT((state, controller) -> Math.max(0, state.axes().leftStickX()), "left_stick_right"),
|
||||
RIGHT_STICK_FORWARD((state, controller) -> -Math.min(0, state.axes().rightStickY()), "right_stick_up"),
|
||||
RIGHT_STICK_BACKWARD((state, controller) -> Math.max(0, state.axes().rightStickY()), "right_stick_down"),
|
||||
RIGHT_STICK_LEFT((state, controller) -> -Math.min(0, state.axes().rightStickX()), "right_stick_left"),
|
||||
RIGHT_STICK_RIGHT((state, controller) -> Math.max(0, state.axes().rightStickX()), "right_stick_right");
|
||||
|
||||
private final BiFunction<ControllerState, Controller, Boolean> state;
|
||||
private final BiFunction<ControllerState, Controller, Float> state;
|
||||
private final String identifier;
|
||||
|
||||
Bind(BiFunction<ControllerState, Controller, Boolean> state, String identifier) {
|
||||
Bind(BiFunction<ControllerState, Controller, Float> state, String identifier) {
|
||||
this.state = state;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
Bind(Function<ControllerState, Boolean> state, String identifier) {
|
||||
this((state1, controller) -> state.apply(state1), identifier);
|
||||
this((state1, controller) -> state.apply(state1) ? 1f : 0f, identifier);
|
||||
}
|
||||
|
||||
public boolean state(ControllerState controllerState, Controller controller) {
|
||||
return state.apply(controllerState, controller);
|
||||
@Override
|
||||
public float state(ControllerState state, Controller controller) {
|
||||
return this.state.apply(state, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(PoseStack matrices, int x, int centerY, Controller controller) {
|
||||
ButtonRenderer.drawButton(this, controller, matrices, x, centerY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ButtonRenderer.DrawSize drawSize() {
|
||||
return new ButtonRenderer.DrawSize(22, 22);
|
||||
}
|
||||
|
||||
public String identifier() {
|
||||
@ -50,6 +73,11 @@ public enum Bind {
|
||||
return new ResourceLocation("controlify", "textures/gui/buttons/" + controller.config().theme.id() + "/" + identifier + ".png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson() {
|
||||
return new JsonPrimitive(identifier);
|
||||
}
|
||||
|
||||
public static Bind fromIdentifier(String identifier) {
|
||||
for (Bind bind : values()) {
|
||||
if (bind.identifier.equals(identifier)) return bind;
|
||||
|
@ -0,0 +1,77 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.ControllerState;
|
||||
import dev.isxander.controlify.gui.ButtonRenderer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CompoundBind implements IBind {
|
||||
private final Set<Bind> binds;
|
||||
|
||||
CompoundBind(Bind... binds) {
|
||||
this.binds = new LinkedHashSet<>(Arrays.asList(binds));
|
||||
}
|
||||
|
||||
public Set<Bind> binds() {
|
||||
return ImmutableSet.copyOf(binds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float state(ControllerState state, Controller controller) {
|
||||
return held(state, controller) ? 1f : 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean held(ControllerState state, Controller controller) {
|
||||
return binds.stream().allMatch(bind -> bind.held(state, controller));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(PoseStack matrices, int x, int centerY, Controller controller) {
|
||||
var font = Minecraft.getInstance().font;
|
||||
|
||||
var iterator = binds.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
var bind = iterator.next();
|
||||
|
||||
bind.draw(matrices, x, centerY, controller);
|
||||
x += bind.drawSize().width();
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
font.drawShadow(matrices, "+", x + 1, centerY - font.lineHeight / 2f, 0xFFFFFF);
|
||||
x += font.width("+") + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ButtonRenderer.DrawSize drawSize() {
|
||||
return new ButtonRenderer.DrawSize(
|
||||
binds.stream().map(IBind::drawSize).mapToInt(ButtonRenderer.DrawSize::width).sum() + (binds.size() - 1) * (2 + Minecraft.getInstance().font.width("+")),
|
||||
binds.stream().map(IBind::drawSize).mapToInt(ButtonRenderer.DrawSize::height).max().orElse(0)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson() {
|
||||
var list = new JsonArray();
|
||||
for (IBind bind : binds) {
|
||||
list.add(bind.toJson());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof CompoundBind compoundBind && compoundBind.binds.equals(binds)
|
||||
|| obj instanceof Bind bind && Set.of(bind).equals(binds);
|
||||
}
|
||||
}
|
@ -1,52 +1,76 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.ControllerState;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.locale.Language;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class ControllerBinding {
|
||||
private final Controller controller;
|
||||
private Bind bind;
|
||||
private final Bind defaultBind;
|
||||
private IBind bind;
|
||||
private final IBind defaultBind;
|
||||
private final ResourceLocation id;
|
||||
private final Component name, description;
|
||||
private final KeyMapping override;
|
||||
|
||||
public ControllerBinding(Controller controller, Bind defaultBind, ResourceLocation id, Component description, KeyMapping override) {
|
||||
private static final Map<Controller, Set<Bind>> pressedBinds = new HashMap<>();
|
||||
|
||||
public ControllerBinding(Controller controller, IBind defaultBind, ResourceLocation id, KeyMapping override) {
|
||||
this.controller = controller;
|
||||
this.bind = this.defaultBind = defaultBind;
|
||||
this.id = id;
|
||||
this.name = Component.translatable("controlify.binding." + id.getNamespace() + "." + id.getPath());
|
||||
this.description = description;
|
||||
var descKey = "controlify.binding." + id.getNamespace() + "." + id.getPath() + ".desc";
|
||||
this.description = Language.getInstance().has(descKey) ? Component.translatable(descKey) : Component.empty();
|
||||
this.override = override;
|
||||
}
|
||||
|
||||
public ControllerBinding(Controller controller, Bind defaultBind, ResourceLocation id, KeyMapping override) {
|
||||
this(controller, defaultBind, id, Component.empty(), override);
|
||||
}
|
||||
|
||||
public boolean held() {
|
||||
public float state() {
|
||||
return bind.state(controller.state(), controller);
|
||||
}
|
||||
|
||||
public boolean held() {
|
||||
return bind.held(controller.state(), controller);
|
||||
}
|
||||
|
||||
public boolean justPressed() {
|
||||
return held() && !bind.state(controller.prevState(), controller);
|
||||
if (hasBindPressed(this)) return false;
|
||||
|
||||
if (held() && !bind.held(controller.prevState(), controller)) {
|
||||
addPressedBind(this);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean justReleased() {
|
||||
return !held() && bind.state(controller.prevState(), controller);
|
||||
if (hasBindPressed(this)) return false;
|
||||
|
||||
if (!held() && bind.held(controller.prevState(), controller)) {
|
||||
addPressedBind(this);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Bind currentBind() {
|
||||
public IBind currentBind() {
|
||||
return bind;
|
||||
}
|
||||
|
||||
public void setCurrentBind(Bind bind) {
|
||||
public void setCurrentBind(IBind bind) {
|
||||
this.bind = bind;
|
||||
}
|
||||
|
||||
public Bind defaultBind() {
|
||||
public IBind defaultBind() {
|
||||
return defaultBind;
|
||||
}
|
||||
|
||||
@ -65,4 +89,29 @@ public class ControllerBinding {
|
||||
public KeyMapping override() {
|
||||
return override;
|
||||
}
|
||||
|
||||
// FIXME: very hack solution please remove me
|
||||
|
||||
public static void clearPressedBinds(Controller controller) {
|
||||
if (pressedBinds.containsKey(controller)) {
|
||||
pressedBinds.get(controller).clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasBindPressed(ControllerBinding binding) {
|
||||
var pressed = pressedBinds.getOrDefault(binding.controller, Set.of());
|
||||
return pressed.containsAll(getBinds(binding.bind));
|
||||
}
|
||||
|
||||
private static void addPressedBind(ControllerBinding binding) {
|
||||
pressedBinds.computeIfAbsent(binding.controller, c -> new HashSet<>()).addAll(getBinds(binding.bind));
|
||||
}
|
||||
|
||||
private static Set<Bind> getBinds(IBind bind) {
|
||||
if (bind instanceof CompoundBind compoundBind) {
|
||||
return compoundBind.binds();
|
||||
} else {
|
||||
return Set.of((Bind) bind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import java.util.*;
|
||||
|
||||
public class ControllerBindings {
|
||||
public final ControllerBinding
|
||||
WALK_FORWARD, WALK_BACKWARD, WALK_LEFT, WALK_RIGHT,
|
||||
JUMP, SNEAK,
|
||||
ATTACK, USE,
|
||||
SPRINT,
|
||||
@ -24,18 +25,24 @@ public class ControllerBindings {
|
||||
CHANGE_PERSPECTIVE,
|
||||
OPEN_CHAT,
|
||||
GUI_PRESS, GUI_BACK,
|
||||
VMOUSE_LCLICK, VMOUSE_RCLICK, VMOUSE_MCLICK, VMOUSE_ESCAPE, VMOUSE_TOGGLE;
|
||||
GUI_NEXT_TAB, GUI_PREV_TAB,
|
||||
VMOUSE_LCLICK, VMOUSE_RCLICK, VMOUSE_MCLICK, VMOUSE_ESCAPE, VMOUSE_SHIFT, VMOUSE_TOGGLE,
|
||||
TOGGLE_DEBUG_MENU;
|
||||
|
||||
private final Map<ResourceLocation, ControllerBinding> registry = new LinkedHashMap<>();
|
||||
|
||||
public ControllerBindings(Controller controller) {
|
||||
var options = Minecraft.getInstance().options;
|
||||
|
||||
register(WALK_FORWARD = new ControllerBinding(controller, Bind.LEFT_STICK_FORWARD, new ResourceLocation("controlify", "walk_forward"), null));
|
||||
register(WALK_BACKWARD = new ControllerBinding(controller, Bind.LEFT_STICK_BACKWARD, new ResourceLocation("controlify", "walk_backward"), null));
|
||||
register(WALK_LEFT = new ControllerBinding(controller, Bind.LEFT_STICK_LEFT, new ResourceLocation("controlify", "strafe_left"), null));
|
||||
register(WALK_RIGHT = new ControllerBinding(controller, Bind.LEFT_STICK_RIGHT, new ResourceLocation("controlify", "strafe_right"), null));
|
||||
register(JUMP = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "jump"), options.keyJump));
|
||||
register(SNEAK = new ControllerBinding(controller, Bind.RIGHT_STICK, new ResourceLocation("controlify", "sneak"), options.keyShift));
|
||||
register(SNEAK = new ControllerBinding(controller, Bind.RIGHT_STICK_PRESS, new ResourceLocation("controlify", "sneak"), options.keyShift));
|
||||
register(ATTACK = new ControllerBinding(controller, Bind.RIGHT_TRIGGER, new ResourceLocation("controlify", "attack"), options.keyAttack));
|
||||
register(USE = new ControllerBinding(controller, Bind.LEFT_TRIGGER, new ResourceLocation("controlify", "use"), options.keyUse));
|
||||
register(SPRINT = new ControllerBinding(controller, Bind.LEFT_STICK, new ResourceLocation("controlify", "sprint"), options.keySprint));
|
||||
register(SPRINT = new ControllerBinding(controller, Bind.LEFT_STICK_PRESS, new ResourceLocation("controlify", "sprint"), options.keySprint));
|
||||
register(DROP = new ControllerBinding(controller, Bind.DPAD_DOWN, new ResourceLocation("controlify", "drop"), options.keyDrop));
|
||||
register(NEXT_SLOT = new ControllerBinding(controller, Bind.RIGHT_BUMPER, new ResourceLocation("controlify", "next_slot"), null));
|
||||
register(PREV_SLOT = new ControllerBinding(controller, Bind.LEFT_BUMPER, new ResourceLocation("controlify", "prev_slot"), null));
|
||||
@ -45,11 +52,15 @@ public class ControllerBindings {
|
||||
register(OPEN_CHAT = new ControllerBinding(controller, Bind.DPAD_UP, new ResourceLocation("controlify", "open_chat"), options.keyChat));
|
||||
register(GUI_PRESS = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "gui_press"), null));
|
||||
register(GUI_BACK = new ControllerBinding(controller, Bind.B_BUTTON, new ResourceLocation("controlify", "gui_back"), null));
|
||||
register(GUI_NEXT_TAB = new ControllerBinding(controller, Bind.RIGHT_BUMPER, new ResourceLocation("controlify", "gui_next_tab"), null));
|
||||
register(GUI_PREV_TAB = new ControllerBinding(controller, Bind.LEFT_BUMPER, new ResourceLocation("controlify", "gui_prev_tab"), null));
|
||||
register(VMOUSE_LCLICK = new ControllerBinding(controller, Bind.A_BUTTON, new ResourceLocation("controlify", "vmouse_lclick"), null));
|
||||
register(VMOUSE_RCLICK = new ControllerBinding(controller, Bind.X_BUTTON, new ResourceLocation("controlify", "vmouse_rclick"), null));
|
||||
register(VMOUSE_MCLICK = new ControllerBinding(controller, Bind.Y_BUTTON, new ResourceLocation("controlify", "vmouse_mclick"), null));
|
||||
register(VMOUSE_ESCAPE = new ControllerBinding(controller, Bind.B_BUTTON, new ResourceLocation("controlify", "vmouse_escape"), null));
|
||||
register(VMOUSE_SHIFT = new ControllerBinding(controller, Bind.LEFT_STICK_PRESS, new ResourceLocation("controlify", "vmouse_shift"), null));
|
||||
register(VMOUSE_TOGGLE = new ControllerBinding(controller, Bind.BACK, new ResourceLocation("controlify", "vmouse_toggle"), null));
|
||||
register(TOGGLE_DEBUG_MENU = new ControllerBinding(controller, new CompoundBind(Bind.BACK, Bind.START), new ResourceLocation("controlify", "toggle_debug_menu"), null));
|
||||
|
||||
ControlifyEvents.CONTROLLER_BIND_REGISTRY.invoker().onRegisterControllerBinds(this, controller);
|
||||
|
||||
@ -73,7 +84,7 @@ public class ControllerBindings {
|
||||
public JsonObject toJson() {
|
||||
JsonObject json = new JsonObject();
|
||||
for (var binding : registry().values()) {
|
||||
json.addProperty(binding.id().toString(), binding.currentBind().identifier());
|
||||
json.add(binding.id().toString(), binding.currentBind().toJson());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
@ -82,13 +93,17 @@ public class ControllerBindings {
|
||||
for (var binding : registry().values()) {
|
||||
var bind = json.get(binding.id().toString());
|
||||
if (bind == null) continue;
|
||||
binding.setCurrentBind(Bind.fromIdentifier(bind.getAsString()));
|
||||
binding.setCurrentBind(IBind.fromJson(bind));
|
||||
}
|
||||
}
|
||||
|
||||
private void imitateVanillaClick(Controller controller) {
|
||||
ControllerBinding.clearPressedBinds(controller);
|
||||
|
||||
if (Controlify.instance().currentInputMode() != InputMode.CONTROLLER)
|
||||
return;
|
||||
if (Minecraft.getInstance().screen != null && !Minecraft.getInstance().screen.passEvents)
|
||||
return;
|
||||
|
||||
for (var binding : registry().values()) {
|
||||
KeyMapping vanillaKey = binding.override();
|
||||
|
38
src/main/java/dev/isxander/controlify/bindings/IBind.java
Normal file
38
src/main/java/dev/isxander/controlify/bindings/IBind.java
Normal file
@ -0,0 +1,38 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.ControllerState;
|
||||
import dev.isxander.controlify.gui.ButtonRenderer;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface IBind {
|
||||
float state(ControllerState state, Controller controller);
|
||||
default boolean held(ControllerState state, Controller controller) {
|
||||
return state(state, controller) > controller.config().buttonActivationThreshold;
|
||||
}
|
||||
|
||||
void draw(PoseStack matrices, int x, int centerY, Controller controller);
|
||||
ButtonRenderer.DrawSize drawSize();
|
||||
|
||||
JsonElement toJson();
|
||||
|
||||
static IBind fromJson(JsonElement json) {
|
||||
if (json.isJsonArray()) {
|
||||
return new CompoundBind(json.getAsJsonArray().asList().stream().map(element -> Bind.fromIdentifier(element.getAsString())).toArray(Bind[]::new));
|
||||
} else {
|
||||
return Bind.fromIdentifier(json.getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
static IBind create(Collection<Bind> binds) {
|
||||
if (binds.size() == 1) return binds.stream().findAny().orElseThrow();
|
||||
return new CompoundBind(binds.toArray(new Bind[0]));
|
||||
}
|
||||
static IBind create(Bind... binds) {
|
||||
if (binds.length == 1) return binds[0];
|
||||
return new CompoundBind(binds);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user