1
0
forked from Clones/Controlify

compound joysticks, button guide in screens, improve API

This commit is contained in:
isXander
2023-03-26 18:13:02 +01:00
parent de210df84f
commit 0d9321e3ba
55 changed files with 1188 additions and 287 deletions

View File

@ -9,6 +9,11 @@ import org.jetbrains.annotations.NotNull;
/**
* Interface with Controlify in a manner where you don't need to worry about updates
* breaking! This is the recommended way to interact with Controlify.
* <p>
* Alternatively, to use Controlify directly, you can use {@link Controlify#instance()}. Though
* beware, things may break at any time!
* <p>
* Anything that is asked for from this API is safe to use, even if it is not in the API package.
*/
public interface ControlifyApi {
/**
@ -16,6 +21,9 @@ public interface ControlifyApi {
*/
@NotNull Controller<?, ?> currentController();
/**
* Get the current input mode for the game.
*/
@NotNull InputMode currentInputMode();
void setInputMode(@NotNull InputMode mode);

View File

@ -1,5 +1,6 @@
package dev.isxander.controlify.api.bind;
import dev.isxander.controlify.api.ControlifyApi;
import dev.isxander.controlify.bindings.BindingSupplier;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.bindings.GamepadBinds;
@ -8,12 +9,17 @@ import net.minecraft.resources.ResourceLocation;
import java.util.function.BooleanSupplier;
/**
* Handles registering new bindings for controllers.
* <p>
* Should be called within {@link dev.isxander.controlify.api.entrypoint.ControlifyEntrypoint#onControlifyPreInit(ControlifyApi)}
*/
public interface ControlifyBindingsApi {
/**
* Registers a custom binding for all available controllers.
* If the controller is not a gamepad, the binding with be empty by default.
*
* @param bind the default gamepad bind
* @param bind the default gamepad bind - joysticks are unset by default
* @param id the identifier for the binding, the namespace should be your modid.
* @return the binding supplier to fetch the binding for a specific controller.
*/

View File

@ -0,0 +1,34 @@
package dev.isxander.controlify.api.buttonguide;
import dev.isxander.controlify.bindings.ControllerBinding;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.gui.ButtonGuideRenderer;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.screens.Screen;
import java.util.function.Function;
/**
* Adds a guide to a button. This does not invoke the button press on binding trigger, only renders the guide.
* This should be called every time a button is initialised, like in {@link Screen#init()}
*/
public interface ButtonGuideApi {
/**
* Makes the button render the image of the binding specified.
* This does not invoke the button press on binding trigger, only renders the guide.
* Custom behaviour should be handled inside a {@link dev.isxander.controlify.screenop.ScreenProcessor} or {@link dev.isxander.controlify.screenop.ComponentProcessor}
*
* @param button button to render the guide for
* @param binding gets the binding to render
* @param position where the guide should be rendered relative to the button
* @param renderPredicate whether the guide should be rendered
*/
static <T extends AbstractButton> void addGuideToButton(
T button,
Function<ControllerBindings<?>, ControllerBinding<?>> binding,
ButtonRenderPosition position,
ButtonGuidePredicate<T> renderPredicate) {
ButtonGuideRenderer.registerBindingForButton(button, binding, position, renderPredicate);
}
}

View File

@ -0,0 +1,13 @@
package dev.isxander.controlify.api.buttonguide;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.screens.Screen;
@FunctionalInterface
public interface ButtonGuidePredicate<T extends AbstractButton> {
boolean shouldDisplay(T button);
ButtonGuidePredicate<AbstractButton> FOCUS_ONLY = AbstractWidget::isFocused;
ButtonGuidePredicate<AbstractButton> ALWAYS = btn -> true;
}

View File

@ -0,0 +1,19 @@
package dev.isxander.controlify.api.buttonguide;
/**
* Where the guide should be rendered relative to the button.
*/
public enum ButtonRenderPosition {
/**
* Renders outside the button the left.
*/
LEFT,
/**
* Renders outside the button the right.
*/
RIGHT,
/**
* Renders inside the button on the left of the button text.
*/
TEXT
}

View File

@ -1,25 +0,0 @@
package dev.isxander.controlify.api.buttonguide;
import dev.isxander.controlify.controller.Controller;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.chat.Component;
import net.minecraft.world.phys.HitResult;
import java.util.Optional;
/**
* Supplies the text to display for a guide action based on the current context.
* If return is empty, the action will not be displayed.
*/
@FunctionalInterface
public interface GuideActionNameSupplier {
Optional<Component> supply(
Minecraft client,
LocalPlayer player,
ClientLevel level,
HitResult hitResult,
Controller<?, ?> controller
);
}

View File

@ -0,0 +1,20 @@
package dev.isxander.controlify.api.entrypoint;
import dev.isxander.controlify.api.ControlifyApi;
public interface ControlifyEntrypoint {
/**
* Called once Controlify has been fully initialised. And all controllers have
* been discovered and loaded.
* Due to the nature of the resource-pack system, this is called
* very late in the game's lifecycle (once the resources have been reloaded).
*/
void onControllersDiscovered(ControlifyApi controlify);
/**
* Called once Controlify has initialised some systems but controllers
* have not yet been discovered and constructed. This is the ideal
* time to register events in preparation for controller discovery.
*/
void onControlifyPreInit(ControlifyApi controlify);
}

View File

@ -3,7 +3,7 @@ package dev.isxander.controlify.api.event;
import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.api.buttonguide.ButtonGuideRegistry;
import dev.isxander.controlify.api.ingameguide.IngameGuideRegistry;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@ -29,9 +29,9 @@ public final class ControlifyEvents {
/**
* Triggers when the button guide entries are being populated, so you can add more of your own.
*/
public static final Event<ButtonGuideRegistryEvent> BUTTON_GUIDE_REGISTRY = EventFactory.createArrayBacked(ButtonGuideRegistryEvent.class, callbacks -> (bindings, registry) -> {
for (ButtonGuideRegistryEvent callback : callbacks) {
callback.onRegisterButtonGuide(bindings, registry);
public static final Event<IngameGuideRegistryEvent> INGAME_GUIDE_REGISTRY = EventFactory.createArrayBacked(IngameGuideRegistryEvent.class, callbacks -> (bindings, registry) -> {
for (IngameGuideRegistryEvent callback : callbacks) {
callback.onRegisterIngameGuide(bindings, registry);
}
});
@ -55,8 +55,8 @@ public final class ControlifyEvents {
}
@FunctionalInterface
public interface ButtonGuideRegistryEvent {
void onRegisterButtonGuide(ControllerBindings<?> bindings, ButtonGuideRegistry registry);
public interface IngameGuideRegistryEvent {
void onRegisterIngameGuide(ControllerBindings<?> bindings, IngameGuideRegistry registry);
}
@FunctionalInterface

View File

@ -1,4 +1,4 @@
package dev.isxander.controlify.api.buttonguide;
package dev.isxander.controlify.api.ingameguide;
/**
* Whether the action should be on the left or right list.

View File

@ -1,4 +1,4 @@
package dev.isxander.controlify.api.buttonguide;
package dev.isxander.controlify.api.ingameguide;
/**
* Defines how the action is sorted in the list. All default Controlify actions are {@link #NORMAL}.

View File

@ -0,0 +1,16 @@
package dev.isxander.controlify.api.ingameguide;
import net.minecraft.network.chat.Component;
import java.util.Optional;
/**
* Supplies the text to display for a guide action based on the current context.
* If return is empty, the action will not be displayed.
* <p>
* This is supplied once every tick.
*/
@FunctionalInterface
public interface GuideActionNameSupplier {
Optional<Component> supply(IngameGuideContext ctx);
}

View File

@ -0,0 +1,14 @@
package dev.isxander.controlify.api.ingameguide;
import dev.isxander.controlify.controller.Controller;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.phys.HitResult;
public record IngameGuideContext(Minecraft client,
LocalPlayer player,
ClientLevel level,
HitResult hitResult,
Controller<?, ?> controller) {
}

View File

@ -1,13 +1,13 @@
package dev.isxander.controlify.api.buttonguide;
package dev.isxander.controlify.api.ingameguide;
import dev.isxander.controlify.bindings.ControllerBinding;
/**
* Allows you to register your own actions to the button guide.
* This should be called through {@link dev.isxander.controlify.api.event.ControlifyEvents#BUTTON_GUIDE_REGISTRY} as
* This should be called through {@link dev.isxander.controlify.api.event.ControlifyEvents#INGAME_GUIDE_REGISTRY} as
* these should be called every time the guide is initialised.
*/
public interface ButtonGuideRegistry {
public interface IngameGuideRegistry {
/**
* Registers a new action to the button guide.
*

View File

@ -4,6 +4,7 @@ import java.util.Set;
/**
* An interface to implement by gui components to define snap points for virtual mouse snapping.
* Can also be implemented in a mixin to improve compatibility.
*/
public interface ISnapBehaviour {
Set<SnapPoint> getSnapPoints();