From 89f8528466cfcd347c081ea5dd4b896a007a4ee7 Mon Sep 17 00:00:00 2001 From: Michael Hillcox Date: Sun, 26 Feb 2023 12:52:29 +0000 Subject: [PATCH] fix: #396 #601 #713 #718 also added player to the use context --- .../client/renders/BuildRender.java | 2 +- .../common/items/GadgetBuilding.java | 2 +- .../common/items/GadgetExchanger.java | 2 +- .../common/items/modes/AbstractMode.java | 26 +++++---- .../common/items/modes/StairMode.java | 55 ++++++++++++++++--- .../common/items/modes/SurfaceMode.java | 4 +- .../items/modes/VerticalColumnMode.java | 4 +- .../common/items/modes/VerticalWallMode.java | 7 +-- .../common/util/GadgetUtils.java | 2 +- 9 files changed, 73 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/direwolf20/buildinggadgets/client/renders/BuildRender.java b/src/main/java/com/direwolf20/buildinggadgets/client/renders/BuildRender.java index 13fdb95c..05bd9062 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/client/renders/BuildRender.java +++ b/src/main/java/com/direwolf20/buildinggadgets/client/renders/BuildRender.java @@ -71,7 +71,7 @@ public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) List coordinates = anchor.orElseGet(() -> { AbstractMode mode = !this.isExchanger ? GadgetBuilding.getToolMode(heldItem).getMode() : GadgetExchanger.getToolMode(heldItem).getMode(); return mode.getCollection( - new AbstractMode.UseContext(player.level, renderBlockState, lookingAt.getBlockPos(), heldItem, lookingAt.getDirection(), !this.isExchanger && GadgetBuilding.shouldPlaceAtop(heldItem), !this.isExchanger ? GadgetBuilding.getConnectedArea(heldItem) : GadgetExchanger.getConnectedArea(heldItem)), + new AbstractMode.UseContext(player.level, player, renderBlockState, lookingAt.getBlockPos(), heldItem, lookingAt.getDirection(), !this.isExchanger && GadgetBuilding.shouldPlaceAtop(heldItem), !this.isExchanger ? GadgetBuilding.getConnectedArea(heldItem) : GadgetExchanger.getConnectedArea(heldItem)), player ); }); diff --git a/src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetBuilding.java b/src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetBuilding.java index dc88e13d..848253e5 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetBuilding.java +++ b/src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetBuilding.java @@ -210,7 +210,7 @@ private void build(ServerPlayer player, ItemStack stack) { Direction sideHit = lookingAt.getDirection(); coords = getToolMode(stack).getMode().getCollection( - new AbstractMode.UseContext(world, blockData.getState(), lookingAt.getBlockPos(), heldItem, sideHit, placeAtop(stack), getConnectedArea(stack)), + new AbstractMode.UseContext(world, player, blockData.getState(), lookingAt.getBlockPos(), heldItem, sideHit, placeAtop(stack), getConnectedArea(stack)), player ); } else //If we do have an anchor, erase it (Even if the build fails) diff --git a/src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetExchanger.java b/src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetExchanger.java index 90bd2701..1e6f20ab 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetExchanger.java +++ b/src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetExchanger.java @@ -224,7 +224,7 @@ private void exchange(ServerPlayer player, ItemStack stack) { // Get the anchor or build the collection Optional> anchor = GadgetUtils.getAnchor(stack); List coords = anchor.orElseGet( - () -> getToolMode(stack).getMode().getCollection(new AbstractMode.UseContext(world, blockData.getState(), lookingAt.getBlockPos(), heldItem, lookingAt.getDirection(), getConnectedArea(heldItem)), player) + () -> getToolMode(stack).getMode().getCollection(new AbstractMode.UseContext(world, player, blockData.getState(), lookingAt.getBlockPos(), heldItem, lookingAt.getDirection(), getConnectedArea(heldItem)), player) ); if (anchor.isPresent()) { diff --git a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/AbstractMode.java b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/AbstractMode.java index 33540624..759be2bc 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/AbstractMode.java +++ b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/AbstractMode.java @@ -38,7 +38,7 @@ public AbstractMode(boolean isExchanging) { * method from having to handle the world etc. */ public List getCollection(UseContext context, Player player) { - BlockPos startPos = this.withOffset(context.getStartPos(), context.getHitSide(), context.isPlaceOnTop()); + BlockPos startPos = this.withOffset(context); // We don't need this unless we're using the exchanger but I also don't want to // have to remake the state for every block. @@ -47,7 +47,7 @@ public List getCollection(UseContext context, Player player) { // We alternate the validator as the exchanger requires a more in-depth validation process. return collect(context, player, startPos) .stream() - .filter(e -> isExchanging ? this.exchangingValidator(e, lookingAtState, context) : this.validator(player, e, context)) + .filter(e -> isExchanging ? this.exchangingValidator(e, lookingAtState, context) : this.validator(e, context)) .sorted(Comparator.comparing((BlockPos pos) -> player.blockPosition().distSqr(pos))) .collect(Collectors.toList()); } @@ -58,8 +58,8 @@ public List getCollection(UseContext context, Player player) { * @param context the use context instance * @return if the block is valid */ - public boolean validator(Player player, BlockPos pos, UseContext context) { - if (!context.getWorldState(pos).canBeReplaced(context.createBlockUseContext(player))) + public boolean validator(BlockPos pos, UseContext context) { + if (!context.getWorldState(pos).canBeReplaced(context.createBlockUseContext())) return false; if (context.world.isOutsideBuildHeight(pos)) @@ -109,8 +109,8 @@ private boolean exchangingValidator(BlockPos pos, BlockState lookingAtState, Use return hasSingeValid; } - public BlockPos withOffset(BlockPos pos, Direction side, boolean placeOnTop) { - return placeOnTop ? pos.relative(side, 1) : pos; + public BlockPos withOffset(UseContext context) { + return context.placeOnTop ? context.startPos.relative(context.hitSide, 1) : context.startPos; } public boolean isExchanging() { @@ -122,6 +122,7 @@ public static class UseContext { private final BlockState setState; private final BlockPos startPos; private final Direction hitSide; + private final Player player; private final boolean isFuzzy; private final boolean placeOnTop; @@ -129,10 +130,11 @@ public static class UseContext { private final boolean rayTraceFluid; private final boolean isConnected; - public UseContext(Level world, BlockState setState, BlockPos startPos, ItemStack gadget, Direction hitSide, boolean placeOnTop, boolean isConnected) { + public UseContext(Level world, Player player, BlockState setState, BlockPos startPos, ItemStack gadget, Direction hitSide, boolean placeOnTop, boolean isConnected) { this.world = world; this.setState = setState; this.startPos = startPos; + this.player = player; this.range = GadgetUtils.getToolRange(gadget); this.isFuzzy = AbstractGadget.getFuzzy(gadget); @@ -143,11 +145,11 @@ public UseContext(Level world, BlockState setState, BlockPos startPos, ItemStack this.placeOnTop = placeOnTop; } - public UseContext(Level world, BlockState setState, BlockPos startPos, ItemStack gadget, Direction hitSide, boolean isConnected) { - this(world, setState, startPos, gadget, hitSide, false, isConnected); + public UseContext(Level world, Player player, BlockState setState, BlockPos startPos, ItemStack gadget, Direction hitSide, boolean isConnected) { + this(world, player, setState, startPos, gadget, hitSide, false, isConnected); } - public BlockPlaceContext createBlockUseContext(Player player) { + public BlockPlaceContext createBlockUseContext() { return new BlockPlaceContext( new UseOnContext( player, @@ -197,6 +199,10 @@ public Direction getHitSide() { return this.hitSide; } + public Player getPlayer() { + return player; + } + @Override public String toString() { return "UseContext{" + diff --git a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/StairMode.java b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/StairMode.java index 55cf4e4b..26920c21 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/StairMode.java +++ b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/StairMode.java @@ -7,6 +7,17 @@ import java.util.ArrayList; import java.util.List; +/** + * This implementation is pretty complicate due the convenience settings for placement. We do a relative simple math + * equation to start with, stairs are simple, take the look vector, then in a single direction x + 1, then increase the height. + *

+ * It gets complicated though as we need to support the following options + * - If the player is higher than the target block we should build the stairs in reverse up to the player + * - If the player is looking at the block one under their feet, and we're on a horizontal facing plane, we should build + * outwards away from the facing plain. + * - If we're looking at the top or bottom of a block, we correct the positioning to build as if we were looking at a face + * of a block that wasn't the top or bottom. + */ public class StairMode extends AbstractMode { public StairMode() { super(false); } @@ -18,24 +29,50 @@ List collect(UseContext context, Player player, BlockPos start) { if (XYZ.isAxisY(side)) side = player.getDirection().getOpposite(); - XYZ facingXYZ = XYZ.fromFacing(side); for( int i = 0; i < context.getRange(); i ++ ) { - // Check to see if we should build up or down from the player - int tmp = start.getY() > player.getY() + 1 ? (i + 1) * -1 : i; + var hitSide = context.getHitSide(); + int shiftAxis = (i + 1) * (hitSide == Direction.EAST || hitSide == Direction.SOUTH ? 1 : -1); - if( facingXYZ == XYZ.X ) - coordinates.add(new BlockPos(start.getX() + (tmp * (side == Direction.EAST ? -1 : 1)), start.getY() + tmp, start.getZ())); + // Special case for looking at block under your feet and looking at the horizontal axis + if (context.getStartPos().getY() < player.getY() && hitSide.getAxis().isHorizontal()) { + boolean mutateXAxis = hitSide.getAxis() == Direction.Axis.X; + boolean mutateZAxis = hitSide.getAxis() == Direction.Axis.Z; - if( facingXYZ == XYZ.Z ) - coordinates.add(new BlockPos(start.getX(), start.getY() + tmp, start.getZ() + (tmp * (side == Direction.SOUTH ? -1 : 1)))); + coordinates.add(context.getStartPos().offset( + mutateXAxis ? shiftAxis : 0, // If we're hitting at the X axis we should shift it + context.isPlaceOnTop() ? -i : -(i + 1), // If place on top is on, we should place aside the block, otherwise, in the current one + mutateZAxis ? shiftAxis : 0 // If we're hitting at the Z axis we should shift the Z axis of the block. + )); + + continue; + } + + shiftAxis = i * (side == Direction.EAST || side == Direction.SOUTH ? -1 : 1); + if (start.getY() < player.getY() - 2) { + shiftAxis = shiftAxis * -1; + } + + coordinates.add(start.offset( + side.getAxis() == Direction.Axis.X ? shiftAxis : 0, + start.getY() > (player.getY() + 1) ? i * -1 : i, // Check to see if we should build up or down from the player + side.getAxis() == Direction.Axis.Z ? shiftAxis : 0 + )); } return coordinates; } @Override - public BlockPos withOffset(BlockPos pos, Direction side, boolean placeOnTop) { + public BlockPos withOffset(UseContext context) { + var side = context.getHitSide(); + var placeOnTop = context.isPlaceOnTop(); + var pos = context.getStartPos(); + // Is top / bottom? Do as normal. Not? then place on top or inside :D - return XYZ.isAxisY(side) ? super.withOffset(pos, side, placeOnTop) : (placeOnTop ? pos.relative(Direction.UP) : pos); + if (side == Direction.DOWN) { + return placeOnTop ? pos.relative(side, 1) : pos; + } + + return XYZ.isAxisY(side) ? super.withOffset(context) : (placeOnTop ? pos.relative(Direction.UP) : pos); } } diff --git a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/SurfaceMode.java b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/SurfaceMode.java index 514a69c1..e10eb443 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/SurfaceMode.java +++ b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/SurfaceMode.java @@ -38,9 +38,9 @@ List collect(UseContext context, Player player, BlockPos start) { } @Override - public boolean validator(Player player, BlockPos pos, UseContext context) { + public boolean validator(BlockPos pos, UseContext context) { // Do our default checks, then do our more complex fuzzy aware checks. - boolean topRow = super.validator(player, pos, context); + boolean topRow = super.validator(pos, context); if( this.isExchanging() ) return topRow; diff --git a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalColumnMode.java b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalColumnMode.java index 3c0fe7b2..2afb45c1 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalColumnMode.java +++ b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalColumnMode.java @@ -44,8 +44,8 @@ List collect(UseContext context, Player player, BlockPos start) { * and ignore placeOnTop as this mode does the action by default. */ @Override - public BlockPos withOffset(BlockPos pos, Direction side, boolean placeOnTop) { - return XYZ.isAxisY(side) ? super.withOffset(pos, side, placeOnTop) : pos; + public BlockPos withOffset(UseContext context) { + return XYZ.isAxisY(context.getHitSide()) ? super.withOffset(context) : context.getStartPos(); } } diff --git a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalWallMode.java b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalWallMode.java index c0924b7a..40e91f38 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalWallMode.java +++ b/src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalWallMode.java @@ -1,8 +1,7 @@ package com.direwolf20.buildinggadgets.common.items.modes; -import net.minecraft.world.entity.player.Player; -import net.minecraft.core.Direction; import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; import java.util.ArrayList; import java.util.List; @@ -59,7 +58,7 @@ List collect(UseContext context, Player player, BlockPos start) { * and ignore placeOnTop as this mode does the action by default. */ @Override - public BlockPos withOffset(BlockPos pos, Direction side, boolean placeOnTop) { - return XYZ.isAxisY(side) ? super.withOffset(pos, side, placeOnTop) : pos; + public BlockPos withOffset(UseContext context) { + return XYZ.isAxisY(context.getHitSide()) ? super.withOffset(context) : context.getStartPos(); } } diff --git a/src/main/java/com/direwolf20/buildinggadgets/common/util/GadgetUtils.java b/src/main/java/com/direwolf20/buildinggadgets/common/util/GadgetUtils.java index 26381dcc..204a4cb5 100644 --- a/src/main/java/com/direwolf20/buildinggadgets/common/util/GadgetUtils.java +++ b/src/main/java/com/direwolf20/buildinggadgets/common/util/GadgetUtils.java @@ -240,7 +240,7 @@ public static boolean anchorBlocks(Player player, ItemStack stack) { return false; BlockData blockData = getToolBlock(stack); - AbstractMode.UseContext context = new AbstractMode.UseContext(player.level, blockData.getState(), startBlock, stack, sideHit, stack.getItem() instanceof GadgetBuilding && GadgetBuilding.shouldPlaceAtop(stack), stack.getItem() instanceof GadgetBuilding ? GadgetBuilding.getConnectedArea(stack) : GadgetExchanger.getConnectedArea(stack)); + AbstractMode.UseContext context = new AbstractMode.UseContext(player.level, player, blockData.getState(), startBlock, stack, sideHit, stack.getItem() instanceof GadgetBuilding && GadgetBuilding.shouldPlaceAtop(stack), stack.getItem() instanceof GadgetBuilding ? GadgetBuilding.getConnectedArea(stack) : GadgetExchanger.getConnectedArea(stack)); List coords = stack.getItem() instanceof GadgetBuilding ? GadgetBuilding.getToolMode(stack).getMode().getCollection(context, player)