1
0
forked from Clones/Controlify
Files
Controlify/src/main/java/dev/isxander/controlify/bindings/ControllerBindingImpl.java
2023-10-30 19:31:59 +00:00

345 lines
11 KiB
Java

package dev.isxander.controlify.bindings;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonObject;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.api.bind.BindRenderer;
import dev.isxander.controlify.api.bind.ControllerBinding;
import dev.isxander.controlify.api.bind.ControllerBindingBuilder;
import dev.isxander.controlify.gui.controllers.GamepadBindController;
import dev.isxander.controlify.gui.controllers.JoystickBindController;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerState;
import dev.isxander.controlify.controller.gamepad.GamepadController;
import dev.isxander.controlify.controller.gamepad.GamepadState;
import dev.isxander.controlify.controller.joystick.JoystickController;
import dev.isxander.controlify.controller.joystick.JoystickState;
import dev.isxander.controlify.gui.DrawSize;
import dev.isxander.yacl3.api.Option;
import dev.isxander.yacl3.api.OptionDescription;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.locale.Language;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.ApiStatus;
import java.util.*;
import java.util.function.BooleanSupplier;
public class ControllerBindingImpl<T extends ControllerState> implements ControllerBinding {
private final Controller<T, ?> controller;
private IBind<T> bind;
private final IBind<T> defaultBind;
private BindRenderer renderer;
private final ResourceLocation id;
private final Component name, description, category;
private final Set<BindContext> contexts;
private final ResourceLocation radialIcon;
private final KeyMappingOverride override;
private static final Map<Controller<?, ?>, Set<IBind<?>>> pressedBinds = new HashMap<>();
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.bind = this.defaultBind = defaultBind;
this.renderer = new BindRendererImpl(bind);
this.id = id;
this.override = vanillaOverride;
this.name = name;
this.description = description;
this.category = category;
this.contexts = ImmutableSet.copyOf(contexts);
this.radialIcon = icon;
}
@Override
public float state() {
if (fakePressState == 1)
return 1f;
return bind.state(controller.state());
}
@Override
public float prevState() {
if (fakePressState == 2)
return 1f;
return bind.state(controller.prevState());
}
@Override
public boolean held() {
return fakePressState == 2 || bind.held(controller.state());
}
@Override
public boolean prevHeld() {
return fakePressState == 3 || bind.held(controller.prevState());
}
@Override
public boolean justPressed() {
if (hasBindPressed(this)) return false;
if ((held() && !prevHeld()) || fakePressState == 2) {
addPressedBind(this);
return true;
} else {
return false;
}
}
@Override
public boolean justReleased() {
if (hasBindPressed(this)) return false;
if ((!held() && prevHeld()) || fakePressState == 3) {
addPressedBind(this);
return true;
} else {
return false;
}
}
@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 void setCurrentBind(IBind<T> bind) {
this.bind = bind;
this.renderer = new BindRendererImpl(bind);
Controlify.instance().config().setDirty();
}
@Override
public IBind<T> defaultBind() {
return defaultBind;
}
@Override
public void resetBind() {
setCurrentBind(defaultBind());
}
public ResourceLocation id() {
return id;
}
@Override
public Component name() {
return name;
}
@Override
public Component description() {
return description;
}
@Override
public Component category() {
return category;
}
@Override
public Set<BindContext> contexts() {
return contexts;
}
@Override
public IBind<T> getBind() {
return bind;
}
@Override
public boolean isUnbound() {
return bind instanceof EmptyBind;
}
@Override
public KeyMappingOverride override() {
return override;
}
@Override
public JsonObject toJson() {
return getBind().toJson();
}
@Override
public BindRenderer renderer() {
return renderer;
}
@Override
public Option.Builder<?> startYACLOption() {
Option.Builder<IBind<T>> option = Option.<IBind<T>>createBuilder()
.name(name())
.binding(new EmptyBind<>(), this::getBind, this::setCurrentBind)
.description(OptionDescription.of(this.description()));
if (controller instanceof GamepadController gamepad) {
((Option.Builder<IBind<GamepadState>>) (Object) option).customController(opt -> new GamepadBindController(opt, gamepad));
} else if (controller instanceof JoystickController<?> joystick) {
((Option.Builder<IBind<JoystickState>>) (Object) option).customController(opt -> new JoystickBindController(opt, joystick));
} else {
throw new IllegalStateException("Unknown controller type: " + controller.getClass().getName());
}
return option;
}
// 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(ControllerBindingImpl<?> binding) {
var pressed = pressedBinds.getOrDefault(binding.controller, Set.of());
return pressed.containsAll(getBinds(binding.bind));
}
private static void addPressedBind(ControllerBindingImpl<?> binding) {
pressedBinds.computeIfAbsent(binding.controller, c -> new HashSet<>()).addAll(getBinds(binding.bind));
}
private static Set<IBind<?>> getBinds(IBind<?> bind) {
return Set.of(bind);
}
@ApiStatus.Internal
public static final class ControllerBindingBuilderImpl<T extends ControllerState> implements ControllerBindingBuilder<T> {
private final Controller<T, ?> controller;
private IBind<T> bind;
private ResourceLocation id;
private Component name = null, description = null, category = null;
private KeyMappingOverride override = null;
private final Set<BindContext> contexts = new HashSet<>();
private ResourceLocation radialIcon = null;
public ControllerBindingBuilderImpl(Controller<T, ?> controller) {
this.controller = controller;
}
@Override
public ControllerBindingBuilder<T> identifier(ResourceLocation id) {
this.id = id;
return this;
}
@Override
public ControllerBindingBuilder<T> identifier(String namespace, String path) {
return identifier(new ResourceLocation(namespace, path));
}
@Override
public ControllerBindingBuilder<T> defaultBind(IBind<T> bind) {
this.bind = bind;
return this;
}
@Override
@SuppressWarnings("unchecked")
public ControllerBindingBuilder<T> defaultBind(GamepadBinds gamepadBind) {
if (controller instanceof GamepadController gamepad) {
this.bind = (IBind<T>) gamepadBind.forGamepad(gamepad);
} else {
this.bind = new EmptyBind<>();
}
return this;
}
@Override
public ControllerBindingBuilder<T> name(Component name) {
this.name = name;
return this;
}
@Override
public ControllerBindingBuilder<T> description(Component description) {
this.description = description;
return this;
}
@Override
public ControllerBindingBuilder<T> category(Component category) {
this.category = category;
return this;
}
@Override
public ControllerBindingBuilder<T> context(BindContext... contexts) {
this.contexts.addAll(Set.of(contexts));
return this;
}
@Override
public ControllerBindingBuilder<T> radialCandidate(ResourceLocation icon) {
this.radialIcon = icon;
return this;
}
@Override
public ControllerBindingBuilder<T> vanillaOverride(KeyMapping keyMapping, BooleanSupplier toggleable) {
this.override = new KeyMappingOverride(keyMapping, toggleable);
return this;
}
@Override
public ControllerBindingBuilder<T> vanillaOverride(KeyMapping keyMapping) {
return vanillaOverride(keyMapping, () -> false);
}
@Override
public ControllerBinding build() {
Validate.notNull(id, "Identifier must be set");
Validate.notNull(bind, "Default bind must be set");
Validate.notNull(category, "Category must be set");
if (name == null)
name = Component.translatable("controlify.binding." + id.getNamespace() + "." + id.getPath());
if (description == null) {
var descKey = "controlify.binding." + id.getNamespace() + "." + id.getPath() + ".desc";
if (Language.getInstance().has(descKey)) {
description = Component.translatable(descKey);
} else {
description = Component.empty();
}
}
return new ControllerBindingImpl<>(controller, bind, id, override, name, description, category, contexts, radialIcon);
}
}
private record BindRendererImpl(IBind<?> bind) implements BindRenderer {
@Override
public void render(GuiGraphics graphics, int x, int centerY) {
bind.draw(graphics, x, centerY);
}
@Override
public DrawSize size() {
return bind.drawSize();
}
}
}