diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/BlockMovementTraits.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/BlockMovementTraits.java index 411eda950..7fb1827cf 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/BlockMovementTraits.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/BlockMovementTraits.java @@ -1,19 +1,39 @@ package com.simibubi.create.modules.contraptions.components.contraptions; import com.simibubi.create.AllBlocks; +import com.simibubi.create.modules.contraptions.components.actors.HarvesterBlock; import com.simibubi.create.modules.contraptions.components.actors.PortableStorageInterfaceBlock; import com.simibubi.create.modules.contraptions.components.contraptions.chassis.AbstractChassisBlock; +import net.minecraft.block.AbstractRailBlock; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; +import net.minecraft.block.HorizontalFaceBlock; +import net.minecraft.block.LadderBlock; +import net.minecraft.block.RedstoneWallTorchBlock; +import net.minecraft.block.TorchBlock; +import net.minecraft.block.WallTorchBlock; import net.minecraft.block.material.PushReaction; +import net.minecraft.state.properties.AttachFace; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class BlockMovementTraits { - + + public static boolean movementNecessary(World world, BlockPos pos) { + BlockState state = world.getBlockState(pos); + if (isBrittle(state)) + return true; + if (state.getMaterial().isReplaceable()) + return false; + if (state.getCollisionShape(world, pos).isEmpty()) + return false; + return true; + } + public static boolean movementAllowed(World world, BlockPos pos) { BlockState blockState = world.getBlockState(pos); if (blockState.getBlock() instanceof AbstractChassisBlock) @@ -26,7 +46,61 @@ public class BlockMovementTraits { return true; return blockState.getPushReaction() != PushReaction.BLOCK; } - + + /** + * Brittle blocks will be collected first, as they may break when other blocks + * are removed before them + */ + public static boolean isBrittle(BlockState state) { + Block block = state.getBlock(); + if (state.has(BlockStateProperties.HANGING)) + return true; + if (block instanceof LadderBlock) + return true; + if (block instanceof TorchBlock) + return true; + if (block instanceof HorizontalFaceBlock) + return true; + if (block instanceof AbstractRailBlock) + return true; + return false; + } + + /** + * Attached blocks will move if blocks they are attached to are moved + */ + public static boolean isBlockAttachedTowards(BlockState state, Direction direction) { + Block block = state.getBlock(); + if (block instanceof LadderBlock) + return state.get(LadderBlock.FACING) == direction.getOpposite(); + if (block instanceof WallTorchBlock) + return state.get(WallTorchBlock.HORIZONTAL_FACING) == direction.getOpposite(); + if (block instanceof RedstoneWallTorchBlock) + return state.get(RedstoneWallTorchBlock.FACING) == direction.getOpposite(); + if (block instanceof TorchBlock) + return direction == Direction.DOWN; + if (block instanceof HorizontalFaceBlock) { + AttachFace attachFace = state.get(HorizontalFaceBlock.FACE); + if (attachFace == AttachFace.CEILING) + return direction == Direction.UP; + if (attachFace == AttachFace.FLOOR) + return direction == Direction.DOWN; + if (attachFace == AttachFace.WALL) + return direction.getOpposite() == state.get(HorizontalFaceBlock.HORIZONTAL_FACING); + } + if (state.has(BlockStateProperties.HANGING)) + return direction == (state.get(BlockStateProperties.HANGING) ? Direction.UP : Direction.DOWN); + if (block instanceof AbstractRailBlock) + return direction == Direction.DOWN; + if (block instanceof HarvesterBlock) + return direction == state.get(HarvesterBlock.HORIZONTAL_FACING).getOpposite(); + return false; + } + + /** + * Non-Supportive blocks will not continue a chain of blocks picked up by e.g. a + * piston + */ public static boolean notSupportive(BlockState state, Direction facing) { if (AllBlocks.DRILL.typeOf(state)) return state.get(BlockStateProperties.FACING) == facing; @@ -36,9 +110,9 @@ public class BlockMovementTraits { return state.get(PortableStorageInterfaceBlock.FACING) == facing; if (AllBlocks.HARVESTER.typeOf(state)) return state.get(BlockStateProperties.HORIZONTAL_FACING) == facing; - return false; + return isBrittle(state); } - + public static boolean movementIgnored(BlockState state) { if (AllBlocks.MECHANICAL_PISTON.typeOf(state)) return true; diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/Contraption.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/Contraption.java index 1aca3b434..2997aeb0c 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/Contraption.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/Contraption.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.tuple.Pair; import com.simibubi.create.AllBlocks; import com.simibubi.create.config.AllConfigs; +import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.WrappedWorld; @@ -88,15 +89,12 @@ public abstract class Contraption { for (BlockInfo info : blocks.values()) { BlockPos offsetPos = info.pos.offset(movementDirection); - boolean hasNext = false; - for (BlockInfo otherInfo : blocks.values()) { - if (!otherInfo.pos.equals(offsetPos)) - continue; - hasNext = true; - break; - } - if (!hasNext) - cachedColliders.add(info.pos); + if (info.state.getCollisionShape(world, offsetPos).isEmpty()) + continue; + if (blocks.containsKey(offsetPos) + && !blocks.get(offsetPos).state.getCollisionShape(world, offsetPos).isEmpty()) + continue; + cachedColliders.add(info.pos); } } @@ -142,13 +140,11 @@ public abstract class Contraption { if (!world.isBlockPresent(pos)) return false; - BlockState state = world.getBlockState(pos); - if (state.getMaterial().isReplaceable()) - return true; - if (state.getCollisionShape(world, pos).isEmpty()) + if (!BlockMovementTraits.movementNecessary(world, pos)) return true; if (!BlockMovementTraits.movementAllowed(world, pos)) return false; + BlockState state = world.getBlockState(pos); if (isChassis(state) && !moveChassis(world, pos, forcedDirection, frontier, visited)) return false; if (AllBlocks.FLEXCRATE.typeOf(state)) @@ -162,20 +158,21 @@ public abstract class Contraption { frontier.add(prevPos); } - if (state.getBlock() instanceof SlimeBlock) - for (Direction offset : Direction.values()) { - BlockPos offsetPos = pos.offset(offset); - BlockState blockState = world.getBlockState(offsetPos); - if (BlockMovementTraits.movementIgnored(blockState)) - continue; - if (!BlockMovementTraits.movementAllowed(world, offsetPos)) { - if (offset == forcedDirection) - return false; - continue; - } - if (!visited.contains(offsetPos)) - frontier.add(offsetPos); + boolean isSlimeBlock = state.getBlock() instanceof SlimeBlock; + for (Direction offset : Direction.values()) { + BlockPos offsetPos = pos.offset(offset); + BlockState blockState = world.getBlockState(offsetPos); + if (BlockMovementTraits.movementIgnored(blockState)) + continue; + if (!BlockMovementTraits.movementAllowed(world, offsetPos)) { + if (offset == forcedDirection && isSlimeBlock) + return false; + continue; } + if (!visited.contains(offsetPos) + && (isSlimeBlock || BlockMovementTraits.isBlockAttachedTowards(blockState, offset.getOpposite()))) + frontier.add(offsetPos); + } add(pos, capture(world, pos)); return true; @@ -382,12 +379,17 @@ public abstract class Contraption { public void removeBlocksFromWorld(IWorld world, BlockPos offset, BiPredicate customRemoval) { storage.values().forEach(MountedStorage::empty); - for (BlockInfo block : blocks.values()) { - BlockPos add = block.pos.add(anchor).add(offset); - if (customRemoval.test(add, block.state)) - continue; - world.getWorld().removeTileEntity(add); - world.setBlockState(add, Blocks.AIR.getDefaultState(), 67); + for (boolean brittles : Iterate.trueAndFalse) { + for (BlockInfo block : blocks.values()) { + if (brittles != BlockMovementTraits.isBrittle(block.state)) + continue; + + BlockPos add = block.pos.add(anchor).add(offset); + if (customRemoval.test(add, block.state)) + continue; + world.getWorld().removeTileEntity(add); + world.setBlockState(add, Blocks.AIR.getDefaultState(), 67); + } } } @@ -397,51 +399,62 @@ public abstract class Contraption { StructureTransform transform = new StructureTransform(offset, rotation); - for (BlockInfo block : blocks.values()) { - BlockPos targetPos = transform.apply(block.pos); - BlockState state = transform.apply(block.state); + for (boolean nonBrittles : Iterate.trueAndFalse) { + for (BlockInfo block : blocks.values()) { + if (nonBrittles == BlockMovementTraits.isBrittle(block.state)) + continue; - if (customPlacement.test(targetPos, state)) - continue; + BlockPos targetPos = transform.apply(block.pos); + BlockState state = transform.apply(block.state); - for (Direction face : Direction.values()) - state = state.updatePostPlacement(face, world.getBlockState(targetPos.offset(face)), world, targetPos, - targetPos.offset(face)); - if (AllBlocks.SAW.typeOf(state)) - state = state.with(SawBlock.RUNNING, false); + if (customPlacement.test(targetPos, state)) + continue; - if (world.getBlockState(targetPos).getBlockHardness(world, targetPos) == -1) - continue; - world.destroyBlock(targetPos, world.getBlockState(targetPos).getCollisionShape(world, targetPos).isEmpty()); - world.setBlockState(targetPos, state, 3 | BlockFlags.IS_MOVING); - TileEntity tileEntity = world.getTileEntity(targetPos); - CompoundNBT tag = block.nbt; - if (tileEntity != null && tag != null) { - tag.putInt("x", targetPos.getX()); - tag.putInt("y", targetPos.getY()); - tag.putInt("z", targetPos.getZ()); + for (Direction face : Direction.values()) + state = state.updatePostPlacement(face, world.getBlockState(targetPos.offset(face)), world, + targetPos, targetPos.offset(face)); + if (AllBlocks.SAW.typeOf(state)) + state = state.with(SawBlock.RUNNING, false); - if (tileEntity instanceof BeltTileEntity) { - tag.remove("Length"); - tag.remove("Index"); - tag.putBoolean("DontClearAttachments", true); + BlockState blockState = world.getBlockState(targetPos); + if (blockState.getBlockHardness(world, targetPos) == -1) + continue; + if (state.getCollisionShape(world, targetPos).isEmpty() + && !blockState.getCollisionShape(world, targetPos).isEmpty()) + continue; + + world.destroyBlock(targetPos, blockState.getCollisionShape(world, targetPos).isEmpty()); + world.setBlockState(targetPos, state, 3 | BlockFlags.IS_MOVING); + TileEntity tileEntity = world.getTileEntity(targetPos); + CompoundNBT tag = block.nbt; + if (tileEntity != null && tag != null) { + tag.putInt("x", targetPos.getX()); + tag.putInt("y", targetPos.getY()); + tag.putInt("z", targetPos.getZ()); + + if (tileEntity instanceof BeltTileEntity) { + tag.remove("Length"); + tag.remove("Index"); + tag.putBoolean("DontClearAttachments", true); + } + + tileEntity.read(tag); + + if (tileEntity instanceof KineticTileEntity) { + KineticTileEntity kineticTileEntity = (KineticTileEntity) tileEntity; + kineticTileEntity.source = null; + kineticTileEntity.setSpeed(0); + kineticTileEntity.network = null; + kineticTileEntity.attachKinetics(); + } + + if (storage.containsKey(block.pos)) { + MountedStorage mountedStorage = storage.get(block.pos); + if (mountedStorage.isWorking()) + mountedStorage.fill(tileEntity); + } } - tileEntity.read(tag); - - if (tileEntity instanceof KineticTileEntity) { - KineticTileEntity kineticTileEntity = (KineticTileEntity) tileEntity; - kineticTileEntity.source = null; - kineticTileEntity.setSpeed(0); - kineticTileEntity.network = null; - kineticTileEntity.attachKinetics(); - } - - if (storage.containsKey(block.pos)) { - MountedStorage mountedStorage = storage.get(block.pos); - if (mountedStorage.isWorking()) - mountedStorage.fill(tileEntity); - } } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/chassis/ChassisTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/chassis/ChassisTileEntity.java index 5659bab6e..d19225985 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/chassis/ChassisTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/chassis/ChassisTileEntity.java @@ -165,9 +165,7 @@ public class ChassisTileEntity extends SmartTileEntity { break; // Ignore replaceable Blocks and Air-like - if (currentState.getMaterial().isReplaceable()) - break; - if (currentState.getCollisionShape(world, current).isEmpty()) + if (!BlockMovementTraits.movementNecessary(world, current)) break; if (AllBlocks.MECHANICAL_PISTON_HEAD.typeOf(currentState)) break; @@ -212,9 +210,7 @@ public class ChassisTileEntity extends SmartTileEntity { continue; if (!searchPos.withinDistance(pos, chassisRange + .5f)) continue; - if (searchedState.getMaterial().isReplaceable()) - continue; - if (searchedState.getCollisionShape(world, searchPos).isEmpty()) + if (!BlockMovementTraits.movementNecessary(world, searchPos)) continue; localVisited.add(searchPos); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/piston/PistonContraption.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/piston/PistonContraption.java index ec1bbba64..cb2118760 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/piston/PistonContraption.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/piston/PistonContraption.java @@ -135,11 +135,9 @@ public class PistonContraption extends Contraption { BlockPos currentPos = pos.offset(orientation, offset + initialExtensionProgress); if (!world.isBlockPresent(currentPos)) return false; + if (!BlockMovementTraits.movementNecessary(world, currentPos)) + return true; BlockState state = world.getBlockState(currentPos); - if (state.getMaterial().isReplaceable()) - return true; - if (state.getCollisionShape(world, currentPos).isEmpty()) - return true; if (AllBlocks.MECHANICAL_PISTON_HEAD.typeOf(state) && state.get(FACING) == direction.getOpposite()) return true; if (!BlockMovementTraits.movementAllowed(world, currentPos)) diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/pulley/PulleyTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/pulley/PulleyTileEntity.java index be4597827..8edfd3583 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/pulley/PulleyTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/pulley/PulleyTileEntity.java @@ -5,11 +5,11 @@ import com.simibubi.create.AllTileEntities; import com.simibubi.create.config.AllConfigs; import com.simibubi.create.foundation.behaviour.CenteredSideValueBoxTransform; import com.simibubi.create.foundation.behaviour.ValueBoxTransform; +import com.simibubi.create.modules.contraptions.components.contraptions.BlockMovementTraits; import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionCollider; import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity; import com.simibubi.create.modules.contraptions.components.contraptions.piston.LinearActuatorTileEntity; -import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.Direction; @@ -129,8 +129,7 @@ public class PulleyTileEntity extends LinearActuatorTileEntity { return; BlockPos posBelow = pos.down((int) (offset + getMovementSpeed()) + 1); - BlockState stateBelow = world.getBlockState(posBelow); - if (stateBelow.getMaterial().isReplaceable() || stateBelow.getShape(world, posBelow).isEmpty()) + if (!BlockMovementTraits.movementNecessary(world, posBelow)) return; disassemble();