From d0ba456bbbf582c42210dd4198e0e21806f5b1d3 Mon Sep 17 00:00:00 2001 From: zelophed Date: Sat, 4 Jun 2022 01:54:19 +0200 Subject: [PATCH] beam support - allow some girder properties to be toggled by wrench --- .../curiosities/girder/GirderBlock.java | 8 + .../girder/GirderWrenchBehavior.java | 181 ++++++++++++++++++ .../simibubi/create/events/ClientEvents.java | 2 + .../utility/placement/IPlacementHelper.java | 12 +- 4 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/simibubi/create/content/curiosities/girder/GirderWrenchBehavior.java diff --git a/src/main/java/com/simibubi/create/content/curiosities/girder/GirderBlock.java b/src/main/java/com/simibubi/create/content/curiosities/girder/GirderBlock.java index b0c46aa65..41716b8e9 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/girder/GirderBlock.java +++ b/src/main/java/com/simibubi/create/content/curiosities/girder/GirderBlock.java @@ -5,6 +5,7 @@ import static net.minecraft.world.level.block.state.properties.BlockStatePropert import java.util.Random; import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllItems; import com.simibubi.create.AllShapes; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.fluids.pipes.BracketBlock; @@ -100,6 +101,13 @@ public class GirderBlock extends Block implements SimpleWaterloggedBlock, IWrenc return InteractionResult.SUCCESS; } + if (AllItems.WRENCH.isIn(itemInHand) && !pPlayer.isSteppingCarefully()) { + if (GirderWrenchBehavior.handleClick(pLevel, pPos, pState, pHit)) + return InteractionResult.sidedSuccess(pLevel.isClientSide); + + return InteractionResult.FAIL; + } + IPlacementHelper helper = PlacementHelpers.get(placementHelperId); if (helper.matchesItem(itemInHand)) return helper.getOffset(pPlayer, pLevel, pState, pPos, pHit) diff --git a/src/main/java/com/simibubi/create/content/curiosities/girder/GirderWrenchBehavior.java b/src/main/java/com/simibubi/create/content/curiosities/girder/GirderWrenchBehavior.java new file mode 100644 index 000000000..629cf3b18 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/curiosities/girder/GirderWrenchBehavior.java @@ -0,0 +1,181 @@ +package com.simibubi.create.content.curiosities.girder; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllItems; +import com.simibubi.create.CreateClient; +import com.simibubi.create.foundation.utility.Color; +import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.Pair; +import com.simibubi.create.foundation.utility.VecHelper; +import com.simibubi.create.foundation.utility.placement.IPlacementHelper; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; + +public class GirderWrenchBehavior { + + public static void tick() { + Minecraft mc = Minecraft.getInstance(); + if (mc.player == null || mc.level == null || !(mc.hitResult instanceof BlockHitResult result)) + return; + + ClientLevel world = mc.level; + BlockPos pos = result.getBlockPos(); + Player player = mc.player; + ItemStack heldItem = player.getMainHandItem(); + + if (player.isSteppingCarefully()) + return; + + if (!AllBlocks.METAL_GIRDER.has(world.getBlockState(pos))) + return; + + if (!AllItems.WRENCH.isIn(heldItem)) + return; + + Pair dirPair = getDirectionAndAction(result, world, pos); + if (dirPair == null) + return; + + Vec3 center = VecHelper.getCenterOf(pos); + Vec3 edge = center.add(Vec3.atLowerCornerOf(dirPair.getFirst().getNormal()).scale(0.4)); + Direction.Axis[] axes = Arrays.stream(Iterate.axes).filter(axis -> axis != dirPair.getFirst().getAxis()).toArray(Direction.Axis[]::new); + + + double normalMultiplier = dirPair.getSecond() == Action.PAIR ? 3 : 1; + Vec3 corner1 = edge + .add(Vec3.atLowerCornerOf(Direction.fromAxisAndDirection(axes[0], Direction.AxisDirection.POSITIVE).getNormal()).scale(0.3)) + .add(Vec3.atLowerCornerOf(Direction.fromAxisAndDirection(axes[1], Direction.AxisDirection.POSITIVE).getNormal()).scale(0.3)) + .add(Vec3.atLowerCornerOf(dirPair.getFirst().getNormal()).scale(0.1 * normalMultiplier)); + + normalMultiplier = dirPair.getSecond() == Action.HORIZONTAL ? 9 : 1; + Vec3 corner2 = edge + .add(Vec3.atLowerCornerOf(Direction.fromAxisAndDirection(axes[0], Direction.AxisDirection.NEGATIVE).getNormal()).scale(0.3)) + .add(Vec3.atLowerCornerOf(Direction.fromAxisAndDirection(axes[1], Direction.AxisDirection.NEGATIVE).getNormal()).scale(0.3)) + .add(Vec3.atLowerCornerOf(dirPair.getFirst().getOpposite().getNormal()).scale(0.1 * normalMultiplier)); + + CreateClient.OUTLINER.showAABB("girderWrench", new AABB(corner1, corner2)) + .lineWidth(1 / 32f) + .colored(new Color(127, 127, 127)); + } + + @Nullable + private static Pair getDirectionAndAction(BlockHitResult result, Level world, BlockPos pos) { + List> validDirections = getValidDirections(world, pos); + + if (validDirections.isEmpty()) + return null; + + List directions = IPlacementHelper.orderedByDistance(pos, result.getLocation(), validDirections.stream().map(Pair::getFirst).toList()); + + if (directions.isEmpty()) + return null; + + Direction dir = directions.get(0); + return validDirections.stream() + .filter(pair -> pair.getFirst() == dir) + .findFirst() + .orElseGet(() -> Pair.of(dir, Action.SINGLE)); + } + + public static List> getValidDirections(BlockGetter level, BlockPos pos) { + BlockState blockState = level.getBlockState(pos); + + if (!AllBlocks.METAL_GIRDER.has(blockState)) + return Collections.emptyList(); + + return Arrays.stream(Iterate.directions) + .>mapMulti((direction, consumer) -> { + BlockState other = level.getBlockState(pos.relative(direction)); + + // up and down + if (direction.getAxis() == Direction.Axis.Y) { + //no other girder in target dir + if (!AllBlocks.METAL_GIRDER.has(other)) { + if (!blockState.getValue(GirderBlock.X) ^ !blockState.getValue(GirderBlock.Z)) + consumer.accept(Pair.of(direction, Action.SINGLE)); + + return; + } + //this girder is a pole or cross + if (blockState.getValue(GirderBlock.X) == blockState.getValue(GirderBlock.Z)) + return; + //other girder is a pole or cross + if (other.getValue(GirderBlock.X) == other.getValue(GirderBlock.Z)) + return; + //toggle up/down connection for both + consumer.accept(Pair.of(direction, Action.PAIR)); + + return; + } + + if (AllBlocks.METAL_GIRDER.has(other)) + consumer.accept(Pair.of(direction, Action.HORIZONTAL)); + + }).toList(); + } + + public static boolean handleClick(Level level, BlockPos pos, BlockState state, BlockHitResult result) { + + Pair dirPair = getDirectionAndAction(result, level, pos); + if (dirPair == null) + return false; + + if (level.isClientSide) + return true; + + Direction dir = dirPair.getFirst(); + + BlockPos otherPos = pos.relative(dir); + BlockState other = level.getBlockState(otherPos); + + if (dir == Direction.UP) { + level.setBlock(pos, state.cycle(GirderBlock.TOP), 2 | 16); + if (dirPair.getSecond() == Action.PAIR && AllBlocks.METAL_GIRDER.has(other)) { + level.setBlock(otherPos, other.cycle(GirderBlock.BOTTOM), 2 | 16); + } + return true; + } + + if (dir == Direction.DOWN) { + level.setBlock(pos, state.cycle(GirderBlock.BOTTOM), 2 | 16); + if (dirPair.getSecond() == Action.PAIR && AllBlocks.METAL_GIRDER.has(other)) { + level.setBlock(otherPos, other.cycle(GirderBlock.TOP), 2 | 16); + } + return true; + } + + if (dirPair.getSecond() == Action.HORIZONTAL) { + BooleanProperty property = dir.getAxis() == Direction.Axis.X ? GirderBlock.X : GirderBlock.Z; + level.setBlock(pos, state.cycle(property), 2 | 16); + + return true; + } + + return true; + } + + private enum Action { + SINGLE, + PAIR, + HORIZONTAL + } + +} diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index 8525c75e9..486e07763 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -25,6 +25,7 @@ import com.simibubi.create.content.contraptions.components.turntable.TurntableHa import com.simibubi.create.content.contraptions.itemAssembly.SequencedAssemblyRecipe; import com.simibubi.create.content.contraptions.relays.belt.item.BeltConnectorHandler; import com.simibubi.create.content.curiosities.armor.CopperBacktankArmorLayer; +import com.simibubi.create.content.curiosities.girder.GirderWrenchBehavior; import com.simibubi.create.content.curiosities.toolbox.ToolboxHandlerClient; import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer; import com.simibubi.create.content.curiosities.tools.ExtendoGripRenderHandler; @@ -143,6 +144,7 @@ public class ClientEvents { ScrollValueRenderer.tick(); ChassisRangeDisplay.tick(); EdgeInteractionRenderer.tick(); + GirderWrenchBehavior.tick(); WorldshaperRenderHandler.tick(); CouplingHandlerClient.tick(); CouplingRenderer.tickDebugModeRenders(); diff --git a/src/main/java/com/simibubi/create/foundation/utility/placement/IPlacementHelper.java b/src/main/java/com/simibubi/create/foundation/utility/placement/IPlacementHelper.java index 11ad3322e..9689da935 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/placement/IPlacementHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/placement/IPlacementHelper.java @@ -1,6 +1,7 @@ package com.simibubi.create.foundation.utility.placement; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.function.Function; @@ -76,7 +77,7 @@ public interface IPlacementHelper { * @param pos the position of the Block the player is looking at or clicked on * @param state the Blockstate of the Block that the player is looking at or clicked on * @param ray the exact raytrace result - * @param offset the PlacementOffset returned by {@link #getOffset(PlayerEntity, World, BlockState, BlockPos, BlockRayTraceResult)}
+ * @param offset the PlacementOffset returned by {@link #getOffset(Player, Level, BlockState, BlockPos, BlockHitResult)}
* the offset will always be successful if this method is called */ default void renderAt(BlockPos pos, BlockState state, BlockHitResult ray, PlacementOffset offset) { @@ -147,6 +148,15 @@ public interface IPlacementHelper { .collect(Collectors.toList()); } + static List orderedByDistance(BlockPos pos, Vec3 hit, Collection directions) { + Vec3 centerToHit = hit.subtract(VecHelper.getCenterOf(pos)); + return directions.stream() + .map(dir -> Pair.of(dir, Vec3.atLowerCornerOf(dir.getNormal()).distanceTo(centerToHit))) + .sorted(Comparator.comparingDouble(Pair::getSecond)) + .map(Pair::getFirst) + .toList(); + } + default boolean matchesItem(ItemStack item) { return getItemPredicate().test(item); }