From 581757aaee34d7d6416b2c9d4fae2b7dcfeaeb2c Mon Sep 17 00:00:00 2001 From: LambdAurora Date: Sun, 16 Feb 2020 00:46:03 +0100 Subject: [PATCH] :sparkles: Add new recipe book controls and front block outline. --- .../lambdacontrols/client/LambdaInput.java | 23 ++++- .../client/controller/InputHandlers.java | 20 +++- .../client/mixin/MinecraftClientMixin.java | 33 ++++++- .../mixin/RecipeBookWidgetAccessor.java | 34 +++++++ .../client/mixin/WorldRendererMixin.java | 98 +++++++++++++++++++ .../main/resources/lambdacontrols.mixins.json | 4 +- 6 files changed, 204 insertions(+), 8 deletions(-) create mode 100644 fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/RecipeBookWidgetAccessor.java create mode 100644 fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/WorldRendererMixin.java diff --git a/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/LambdaInput.java b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/LambdaInput.java index 72bc3f7..b12db44 100644 --- a/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/LambdaInput.java +++ b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/LambdaInput.java @@ -9,6 +9,7 @@ package me.lambdaurora.lambdacontrols.client; +import me.lambdaurora.lambdacontrols.LambdaControlsFeature; import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding; import me.lambdaurora.lambdacontrols.client.controller.Controller; import me.lambdaurora.lambdacontrols.client.controller.InputManager; @@ -19,8 +20,10 @@ import me.lambdaurora.lambdacontrols.client.mixin.CreativeInventoryScreenAccesso import me.lambdaurora.lambdacontrols.client.mixin.EntryListWidgetAccessor; import me.lambdaurora.lambdacontrols.client.util.ContainerScreenAccessor; import me.lambdaurora.spruceui.SpruceLabelWidget; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.FluidBlock; +import net.minecraft.block.SlabBlock; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.Element; import net.minecraft.client.gui.ParentElement; @@ -37,6 +40,8 @@ import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.container.Slot; import net.minecraft.container.SlotActionType; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.BlockPos; @@ -671,6 +676,8 @@ public class LambdaInput public static @Nullable BlockHitResult tryFrontPlace(@NotNull MinecraftClient client) { + if (!LambdaControlsFeature.FRONT_BLOCK_PLACING.isAvailable()) + return null; if (client.player != null && client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.MISS && client.player.onGround && client.player.pitch > 35.0F) { if (client.player.isRiding()) return null; @@ -686,8 +693,22 @@ public class LambdaInput return null; } - return new BlockHitResult(client.crosshairTarget.getPos(), direction.getOpposite(), blockPos, false); + return new BlockHitResult(client.crosshairTarget.getPos(), direction, blockPos, false); } return null; } + + public static @NotNull BlockHitResult withSideForFrontPlace(@NotNull BlockHitResult result, @Nullable ItemStack stack) + { + if (stack == null || stack.isEmpty() || !(stack.getItem() instanceof BlockItem)) + return result; + return withSideForFrontPlace(result, Block.getBlockFromItem(stack.getItem())); + } + + public static @NotNull BlockHitResult withSideForFrontPlace(@NotNull BlockHitResult result, @NotNull Block block) + { + if (block instanceof SlabBlock) + result = result.withSide(Direction.DOWN); + return result; + } } diff --git a/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/controller/InputHandlers.java b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/controller/InputHandlers.java index be7ac89..3a9e577 100644 --- a/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/controller/InputHandlers.java +++ b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/controller/InputHandlers.java @@ -12,6 +12,7 @@ package me.lambdaurora.lambdacontrols.client.controller; import me.lambdaurora.lambdacontrols.client.ButtonState; import me.lambdaurora.lambdacontrols.client.mixin.AdvancementsScreenAccessor; import me.lambdaurora.lambdacontrols.client.mixin.CreativeInventoryScreenAccessor; +import me.lambdaurora.lambdacontrols.client.mixin.RecipeBookWidgetAccessor; import me.lambdaurora.lambdacontrols.client.util.ContainerScreenAccessor; import me.lambdaurora.lambdacontrols.client.util.KeyBindingAccessor; import net.minecraft.client.MinecraftClient; @@ -19,6 +20,8 @@ import net.minecraft.client.gui.screen.advancement.AdvancementTab; import net.minecraft.client.gui.screen.advancement.AdvancementsScreen; import net.minecraft.client.gui.screen.ingame.ContainerScreen; import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget; import net.minecraft.client.util.ScreenshotUtils; import net.minecraft.container.Slot; import net.minecraft.item.ItemGroup; @@ -59,14 +62,27 @@ public class InputHandlers return true; } else if (client.currentScreen instanceof CreativeInventoryScreen) { CreativeInventoryScreenAccessor inventory = (CreativeInventoryScreenAccessor) client.currentScreen; - int currentSelectedTab = inventory.getSelectedTab(); - int nextTab = currentSelectedTab + (right ? 1 : -1); + int currentTab = inventory.getSelectedTab(); + int nextTab = currentTab + (right ? 1 : -1); if (nextTab < 0) nextTab = ItemGroup.GROUPS.length - 1; else if (nextTab >= ItemGroup.GROUPS.length) nextTab = 0; inventory.lambdacontrols_setSelectedTab(ItemGroup.GROUPS[nextTab]); return true; + } else if (client.currentScreen instanceof InventoryScreen) { + RecipeBookWidgetAccessor recipeBook = (RecipeBookWidgetAccessor) ((InventoryScreen) client.currentScreen).getRecipeBookWidget(); + List tabs = recipeBook.getTabButtons(); + RecipeGroupButtonWidget currentTab = recipeBook.getCurrentTab(); + int nextTab = tabs.indexOf(currentTab) + (right ? 1 : -1); + if (nextTab < 0) + nextTab = tabs.size() - 1; + else if (nextTab >= tabs.size()) + nextTab = 0; + currentTab.setToggled(false); + recipeBook.setCurrentTab(currentTab = tabs.get(nextTab)); + currentTab.setToggled(true); + recipeBook.lambdacontrols_refreshResults(true); } else if (client.currentScreen instanceof AdvancementsScreen) { AdvancementsScreenAccessor screen = (AdvancementsScreenAccessor) client.currentScreen; List tabs = screen.getTabs().values().stream().distinct().collect(Collectors.toList()); diff --git a/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/MinecraftClientMixin.java b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/MinecraftClientMixin.java index 9ce943d..42682f2 100644 --- a/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/MinecraftClientMixin.java +++ b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/MinecraftClientMixin.java @@ -24,6 +24,8 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -59,6 +61,10 @@ public abstract class MinecraftClientMixin @Shadow private int itemUseCooldown; + private BlockPos lambdacontrols_lastTargetPos; + private Direction lambdacontrols_lockedSide; + private int lambdacontrols_lockedSideCooldown; + @Inject(method = "", at = @At("RETURN")) private void onInit(CallbackInfo ci) { @@ -68,14 +74,31 @@ public abstract class MinecraftClientMixin @Inject(method = "tick", at = @At("HEAD")) private void onStartTick(CallbackInfo ci) { - if (this.player != null && this.player.isCreative()) { + if (!LambdaControlsFeature.FAST_BLOCK_INTERACTION.isAvailable()) + return; + if (this.player != null) { int cooldown = this.itemUseCooldown; + BlockHitResult hitResult; if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.BLOCK && this.player.abilities.flying) { - if (cooldown > 1) + hitResult = (BlockHitResult) this.crosshairTarget; + BlockPos targetPos = hitResult.getBlockPos(); + Direction side = hitResult.getSide(); + + if (cooldown > 1 && !targetPos.equals(this.lambdacontrols_lastTargetPos) && (side.equals(this.lambdacontrols_lockedSide) || this.lambdacontrols_lockedSide == null)) { this.itemUseCooldown = 1; + this.lambdacontrols_lockedSide = side; + this.lambdacontrols_lockedSideCooldown = 10; + } else { + if (this.lambdacontrols_lockedSideCooldown == 0) + this.lambdacontrols_lockedSide = null; + else if (this.lambdacontrols_lockedSideCooldown > 0) + this.lambdacontrols_lockedSideCooldown--; + } + + this.lambdacontrols_lastTargetPos = targetPos.toImmutable(); } else if (this.player.isSprinting()) { - BlockHitResult result = LambdaInput.tryFrontPlace(((MinecraftClient) (Object) this)); - if (result != null) { + hitResult = LambdaInput.tryFrontPlace(((MinecraftClient) (Object) this)); + if (hitResult != null) { if (cooldown > 0) this.itemUseCooldown = 0; } @@ -106,6 +129,8 @@ public abstract class MinecraftClientMixin if (hitResult == null) return; + hitResult = LambdaInput.withSideForFrontPlace(hitResult, stackInHand); + int previousStackCount = stackInHand.getCount(); ActionResult result = this.interactionManager.interactBlock(this.player, this.world, hand, hitResult); if (result.isAccepted()) { diff --git a/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/RecipeBookWidgetAccessor.java b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/RecipeBookWidgetAccessor.java new file mode 100644 index 0000000..20d435e --- /dev/null +++ b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/RecipeBookWidgetAccessor.java @@ -0,0 +1,34 @@ +/* + * Copyright © 2020 LambdAurora + * + * This file is part of LambdaControls. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package me.lambdaurora.lambdacontrols.client.mixin; + +import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.List; + +@Mixin(RecipeBookWidget.class) +public interface RecipeBookWidgetAccessor +{ + @Accessor("tabButtons") + List getTabButtons(); + + @Accessor("currentTab") + RecipeGroupButtonWidget getCurrentTab(); + + @Accessor("currentTab") + void setCurrentTab(RecipeGroupButtonWidget currentTab); + + @Invoker("refreshResults") + void lambdacontrols_refreshResults(boolean resetCurrentPage); +} diff --git a/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/WorldRendererMixin.java b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/WorldRendererMixin.java new file mode 100644 index 0000000..323b496 --- /dev/null +++ b/fabric/src/main/java/me/lambdaurora/lambdacontrols/client/mixin/WorldRendererMixin.java @@ -0,0 +1,98 @@ +/* + * Copyright © 2020 LambdAurora + * + * This file is part of LambdaControls. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package me.lambdaurora.lambdacontrols.client.mixin; + +import me.lambdaurora.lambdacontrols.client.LambdaInput; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.SlabBlock; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.*; +import net.minecraft.client.util.math.Matrix4f; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.EntityContext; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUsageContext; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.profiler.Profiler; +import net.minecraft.util.shape.VoxelShape; +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; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +/** + * Represents a mixin to WorldRenderer. + *

+ * Handles the rendering of the block outline of the front block placing. + */ +@Mixin(WorldRenderer.class) +public abstract class WorldRendererMixin +{ + @Shadow + @Final + private MinecraftClient client; + + @Shadow + private ClientWorld world; + + @Shadow + private static void drawShapeOutline(MatrixStack matrixStack, VertexConsumer vertexConsumer, VoxelShape voxelShape, double d, double e, double f, float g, float h, float i, float j) + { + } + + @Inject( + method = "render", + at = @At( + value = "FIELD", + target = "Lnet/minecraft/client/MinecraftClient;crosshairTarget:Lnet/minecraft/util/hit/HitResult;", + ordinal = 1, + shift = At.Shift.AFTER + ), + locals = LocalCapture.CAPTURE_FAILEXCEPTION + ) + private void onOutlineRender(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, + LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci, + Profiler profiler, Vec3d cameraPos, double x, double y, double z, Matrix4f modelMatrix, boolean bl, Frustum frustum2, boolean bl3, + VertexConsumerProvider.Immediate immediate) + { + if (this.client.crosshairTarget == null || this.client.crosshairTarget.getType() != HitResult.Type.MISS) + return; + BlockHitResult result = LambdaInput.tryFrontPlace(client); + if (result == null) + return; + BlockPos blockPos = result.getBlockPos(); + if (this.world.getWorldBorder().contains(blockPos)) { + ItemStack stack = this.client.player.getStackInHand(Hand.MAIN_HAND); + if (stack == null || !(stack.getItem() instanceof BlockItem)) + return; + Block block = ((BlockItem) stack.getItem()).getBlock(); + result = LambdaInput.withSideForFrontPlace(result, block); + ItemPlacementContext context = new ItemPlacementContext(new ItemUsageContext(this.client.player, Hand.MAIN_HAND, result)); + VertexConsumer vertexConsumer = immediate.getBuffer(RenderLayer.getLines()); + BlockState placementState = block.getPlacementState(context); + if (placementState == null) + return; + VoxelShape outlineShape = placementState.getOutlineShape(this.client.world, blockPos, EntityContext.of(camera.getFocusedEntity())); + drawShapeOutline(matrices, vertexConsumer, outlineShape, (double) blockPos.getX() - x, (double) blockPos.getY() - y, (double) blockPos.getZ() - z, 0.0F, 0.0F, 0.0F, 0.4F); + } + } +} diff --git a/fabric/src/main/resources/lambdacontrols.mixins.json b/fabric/src/main/resources/lambdacontrols.mixins.json index efa727f..980489e 100644 --- a/fabric/src/main/resources/lambdacontrols.mixins.json +++ b/fabric/src/main/resources/lambdacontrols.mixins.json @@ -16,7 +16,9 @@ "KeyBindingMixin", "MinecraftClientMixin", "MouseMixin", - "SettingsScreenMixin" + "RecipeBookWidgetAccessor", + "SettingsScreenMixin", + "WorldRendererMixin" ], "injectors": { "defaultRequire": 1