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

@ -1,4 +1,4 @@
package dev.isxander.controlify.mixins.feature.guide;
package dev.isxander.controlify.mixins.feature.guide.ingame;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;

View File

@ -1,4 +1,4 @@
package dev.isxander.controlify.mixins.feature.guide;
package dev.isxander.controlify.mixins.feature.guide.ingame;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.Controlify;

View File

@ -0,0 +1,86 @@
package dev.isxander.controlify.mixins.feature.guide.screen;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.api.buttonguide.ButtonRenderPosition;
import dev.isxander.controlify.bindings.IBind;
import dev.isxander.controlify.gui.ButtonGuideRenderer;
import dev.isxander.controlify.screenop.ComponentProcessor;
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.AbstractButtonComponentProcessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.contents.TranslatableContents;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Set;
@Mixin(AbstractButton.class)
public abstract class AbstractButtonMixin extends AbstractWidgetMixin implements ButtonGuideRenderer<AbstractButton> {
@Unique private RenderData<AbstractButton> renderData = null;
@Inject(method = "renderString", at = @At("RETURN"))
private void renderButtonGuide(PoseStack matrices, Font renderer, int color, CallbackInfo ci) {
if (shouldRender()) {
switch (renderData.position()) {
case LEFT -> getBind().draw(matrices, getX() - getBind().drawSize().width() - 1, getY() + getHeight() / 2);
case RIGHT -> getBind().draw(matrices, getX() + getWidth() + 1, getY() + getHeight() / 2);
case TEXT -> {
Font font = Minecraft.getInstance().font;
int x;
if (font.width(getMessage()) > getWidth()) {
x = getX();
} else {
x = getX() + getWidth() / 2 - font.width(getMessage()) / 2 - getBind().drawSize().width();
}
getBind().draw(matrices, x, getY() + getHeight() / 2);
}
}
}
}
@Inject(method = "renderString", at = @At("HEAD"))
private void shiftXOffset(PoseStack matrices, Font renderer, int color, CallbackInfo ci) {
matrices.pushPose();
if (!shouldRender() || Minecraft.getInstance().font.width(getMessage()) > getWidth() || renderData.position() != ButtonRenderPosition.TEXT) return;
matrices.translate(getBind().drawSize().width() / 2f, 0, 0);
}
@Inject(method = "renderString", at = @At("RETURN"))
private void finishShiftXOffset(PoseStack matrices, Font renderer, int color, CallbackInfo ci) {
matrices.popPose();
}
@Override
protected int shiftDrawSize(int x) {
if (!shouldRender() || Minecraft.getInstance().font.width(getMessage()) < getWidth() || renderData.position() != ButtonRenderPosition.TEXT) return x;
return x + getBind().drawSize().width();
}
@Override
public void setButtonGuide(RenderData<AbstractButton> renderData) {
this.renderData = renderData;
}
private boolean shouldRender() {
return renderData != null
&& this.isActive()
&& Controlify.instance().currentInputMode() == InputMode.CONTROLLER
&& Controlify.instance().currentController().config().showScreenGuide
&& !renderData.binding().apply(Controlify.instance().currentController().bindings()).unbound()
&& renderData.renderPredicate().shouldDisplay((AbstractButton) (Object) this);
}
private IBind<?> getBind() {
return renderData.binding().apply(Controlify.instance().currentController().bindings()).currentBind();
}
}

View File

@ -0,0 +1,32 @@
package dev.isxander.controlify.mixins.feature.guide.screen;
import dev.isxander.controlify.gui.DrawSize;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.network.chat.Component;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(AbstractWidget.class)
public abstract class AbstractWidgetMixin extends GuiComponent {
@Shadow public abstract int getX();
@Shadow public abstract int getY();
@Shadow public abstract int getHeight();
@Shadow public abstract Component getMessage();
@Shadow public abstract int getWidth();
@Shadow public abstract boolean isActive();
@ModifyArg(method = "renderScrollingString(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/gui/Font;II)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/AbstractWidget;renderScrollingString(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/gui/Font;Lnet/minecraft/network/chat/Component;IIIII)V"), index = 3)
protected int shiftDrawSize(int x) {
return x;
}
}

View File

@ -0,0 +1,57 @@
package dev.isxander.controlify.mixins.feature.guide.screen;
import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.InputMode;
import dev.isxander.controlify.bindings.IBind;
import dev.isxander.controlify.compatibility.ControlifyCompat;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.gui.DrawSize;
import net.minecraft.client.gui.components.TabButton;
import net.minecraft.client.gui.components.tabs.TabNavigationBar;
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;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TabNavigationBar.class)
public class TabNavigationBarMixin {
@Shadow @Final private ImmutableList<TabButton> tabButtons;
@Shadow private int width;
@Inject(method = "render", at = @At("RETURN"))
private void renderControllerButtonOverlay(PoseStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (Controlify.instance().currentInputMode() == InputMode.CONTROLLER) {
var controller = Controlify.instance().currentController();
if (controller.config().showScreenGuide) {
this.renderControllerButtonOverlay(matrices, controller);
}
}
}
private void renderControllerButtonOverlay(PoseStack matrices, Controller<?, ?> controller) {
ControlifyCompat.ifBeginHudBatching();
TabButton firstTab = tabButtons.get(0);
TabButton lastTab = tabButtons.get(tabButtons.size() - 1);
IBind<?> prevBind = controller.bindings().GUI_PREV_TAB.currentBind();
DrawSize prevBindDrawSize = prevBind.drawSize();
int firstButtonX = Math.max(firstTab.getX() - 2 - prevBindDrawSize.width(), firstTab.getX() / 2 - prevBindDrawSize.width() / 2);
int firstButtonY = 12;
prevBind.draw(matrices, firstButtonX, firstButtonY);
IBind<?> nextBind = controller.bindings().GUI_NEXT_TAB.currentBind();
DrawSize nextBindDrawSize = nextBind.drawSize();
int lastButtonEnd = lastTab.getX() + lastTab.getWidth();
int lastButtonX = Math.min(lastTab.getX() + lastTab.getWidth() + 2, lastButtonEnd + (width - lastButtonEnd) / 2 - nextBindDrawSize.width() / 2);
int lastButtonY = 12;
nextBind.draw(matrices, lastButtonX, lastButtonY);
ControlifyCompat.ifEndHudBatching();
}
}

View File

@ -0,0 +1,40 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate;
import dev.isxander.controlify.api.buttonguide.ButtonRenderPosition;
import dev.isxander.controlify.gui.ButtonGuideRenderer;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.CreateWorldScreenProcessor;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.layouts.LayoutElement;
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(CreateWorldScreen.class)
public abstract class CreateWorldScreenMixin implements ScreenProcessorProvider {
@Shadow protected abstract void onCreate();
@Unique private final ScreenProcessor<CreateWorldScreen> processor = new CreateWorldScreenProcessor((CreateWorldScreen) (Object) this, this::onCreate);
@ModifyArg(method = "init()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/layouts/GridLayout$RowHelper;addChild(Lnet/minecraft/client/gui/layouts/LayoutElement;)Lnet/minecraft/client/gui/layouts/LayoutElement;", ordinal = 1))
private LayoutElement modifyCancelButton(LayoutElement button) {
ButtonGuideRenderer.registerBindingForButton((AbstractButton) button, bindings -> bindings.GUI_BACK, ButtonRenderPosition.TEXT, ButtonGuidePredicate.ALWAYS);
return button;
}
@ModifyArg(method = "init()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/layouts/GridLayout$RowHelper;addChild(Lnet/minecraft/client/gui/layouts/LayoutElement;)Lnet/minecraft/client/gui/layouts/LayoutElement;", ordinal = 0))
private LayoutElement modifyCreateButton(LayoutElement button) {
ButtonGuideRenderer.registerBindingForButton((AbstractButton) button, bindings -> bindings.GUI_ABSTRACT_ACTION_1, ButtonRenderPosition.TEXT, ButtonGuidePredicate.ALWAYS);
return button;
}
@Override
public ScreenProcessor<?> screenProcessor() {
return processor;
}
}

View File

@ -1,10 +1,18 @@
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
import dev.isxander.controlify.api.buttonguide.ButtonGuidePredicate;
import dev.isxander.controlify.api.buttonguide.ButtonRenderPosition;
import dev.isxander.controlify.gui.ButtonGuideRenderer;
import dev.isxander.controlify.screenop.ScreenProcessor;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.screenop.compat.vanilla.SelectWorldScreenProcessor;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(SelectWorldScreen.class)
public class SelectWorldScreenMixin implements ScreenProcessorProvider {
@ -14,4 +22,16 @@ public class SelectWorldScreenMixin implements ScreenProcessorProvider {
public ScreenProcessor<?> screenProcessor() {
return controlify$processor;
}
@ModifyArg(method = "init()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/worldselection/SelectWorldScreen;addRenderableWidget(Lnet/minecraft/client/gui/components/events/GuiEventListener;)Lnet/minecraft/client/gui/components/events/GuiEventListener;", ordinal = 5))
private <T extends GuiEventListener & Renderable> T modifyCancelButton(T button) {
ButtonGuideRenderer.registerBindingForButton((AbstractButton) button, bindings -> bindings.GUI_BACK, ButtonRenderPosition.TEXT, ButtonGuidePredicate.ALWAYS);
return button;
}
@ModifyArg(method = "init()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/worldselection/SelectWorldScreen;addRenderableWidget(Lnet/minecraft/client/gui/components/events/GuiEventListener;)Lnet/minecraft/client/gui/components/events/GuiEventListener;", ordinal = 1))
private <T extends GuiEventListener & Renderable> T modifyCreateButton(T button) {
ButtonGuideRenderer.registerBindingForButton((AbstractButton) button, bindings -> bindings.GUI_ABSTRACT_ACTION_1, ButtonRenderPosition.TEXT, ButtonGuidePredicate.ALWAYS);
return button;
}
}