Portable Attached Blocks

- Moved Structures now pick up attached blocks such as torches, ladders or rails if the block they sit on is moved
This commit is contained in:
simibubi 2020-03-14 17:23:44 +01:00
parent 2f5d2adc9f
commit cb983bb017
5 changed files with 168 additions and 88 deletions

View file

@ -1,12 +1,21 @@
package com.simibubi.create.modules.contraptions.components.contraptions; package com.simibubi.create.modules.contraptions.components.contraptions;
import com.simibubi.create.AllBlocks; 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.actors.PortableStorageInterfaceBlock;
import com.simibubi.create.modules.contraptions.components.contraptions.chassis.AbstractChassisBlock; 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.BlockState;
import net.minecraft.block.Blocks; 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.block.material.PushReaction;
import net.minecraft.state.properties.AttachFace;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -14,6 +23,17 @@ import net.minecraft.world.World;
public class BlockMovementTraits { 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) { public static boolean movementAllowed(World world, BlockPos pos) {
BlockState blockState = world.getBlockState(pos); BlockState blockState = world.getBlockState(pos);
if (blockState.getBlock() instanceof AbstractChassisBlock) if (blockState.getBlock() instanceof AbstractChassisBlock)
@ -27,6 +47,60 @@ public class BlockMovementTraits {
return blockState.getPushReaction() != PushReaction.BLOCK; 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) { public static boolean notSupportive(BlockState state, Direction facing) {
if (AllBlocks.DRILL.typeOf(state)) if (AllBlocks.DRILL.typeOf(state))
return state.get(BlockStateProperties.FACING) == facing; return state.get(BlockStateProperties.FACING) == facing;
@ -36,7 +110,7 @@ public class BlockMovementTraits {
return state.get(PortableStorageInterfaceBlock.FACING) == facing; return state.get(PortableStorageInterfaceBlock.FACING) == facing;
if (AllBlocks.HARVESTER.typeOf(state)) if (AllBlocks.HARVESTER.typeOf(state))
return state.get(BlockStateProperties.HORIZONTAL_FACING) == facing; return state.get(BlockStateProperties.HORIZONTAL_FACING) == facing;
return false; return isBrittle(state);
} }
public static boolean movementIgnored(BlockState state) { public static boolean movementIgnored(BlockState state) {

View file

@ -18,6 +18,7 @@ import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.config.AllConfigs; 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.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.WrappedWorld; import com.simibubi.create.foundation.utility.WrappedWorld;
@ -88,14 +89,11 @@ public abstract class Contraption {
for (BlockInfo info : blocks.values()) { for (BlockInfo info : blocks.values()) {
BlockPos offsetPos = info.pos.offset(movementDirection); BlockPos offsetPos = info.pos.offset(movementDirection);
boolean hasNext = false; if (info.state.getCollisionShape(world, offsetPos).isEmpty())
for (BlockInfo otherInfo : blocks.values()) { continue;
if (!otherInfo.pos.equals(offsetPos)) if (blocks.containsKey(offsetPos)
&& !blocks.get(offsetPos).state.getCollisionShape(world, offsetPos).isEmpty())
continue; continue;
hasNext = true;
break;
}
if (!hasNext)
cachedColliders.add(info.pos); cachedColliders.add(info.pos);
} }
@ -142,13 +140,11 @@ public abstract class Contraption {
if (!world.isBlockPresent(pos)) if (!world.isBlockPresent(pos))
return false; return false;
BlockState state = world.getBlockState(pos); if (!BlockMovementTraits.movementNecessary(world, pos))
if (state.getMaterial().isReplaceable())
return true;
if (state.getCollisionShape(world, pos).isEmpty())
return true; return true;
if (!BlockMovementTraits.movementAllowed(world, pos)) if (!BlockMovementTraits.movementAllowed(world, pos))
return false; return false;
BlockState state = world.getBlockState(pos);
if (isChassis(state) && !moveChassis(world, pos, forcedDirection, frontier, visited)) if (isChassis(state) && !moveChassis(world, pos, forcedDirection, frontier, visited))
return false; return false;
if (AllBlocks.FLEXCRATE.typeOf(state)) if (AllBlocks.FLEXCRATE.typeOf(state))
@ -162,18 +158,19 @@ public abstract class Contraption {
frontier.add(prevPos); frontier.add(prevPos);
} }
if (state.getBlock() instanceof SlimeBlock) boolean isSlimeBlock = state.getBlock() instanceof SlimeBlock;
for (Direction offset : Direction.values()) { for (Direction offset : Direction.values()) {
BlockPos offsetPos = pos.offset(offset); BlockPos offsetPos = pos.offset(offset);
BlockState blockState = world.getBlockState(offsetPos); BlockState blockState = world.getBlockState(offsetPos);
if (BlockMovementTraits.movementIgnored(blockState)) if (BlockMovementTraits.movementIgnored(blockState))
continue; continue;
if (!BlockMovementTraits.movementAllowed(world, offsetPos)) { if (!BlockMovementTraits.movementAllowed(world, offsetPos)) {
if (offset == forcedDirection) if (offset == forcedDirection && isSlimeBlock)
return false; return false;
continue; continue;
} }
if (!visited.contains(offsetPos)) if (!visited.contains(offsetPos)
&& (isSlimeBlock || BlockMovementTraits.isBlockAttachedTowards(blockState, offset.getOpposite())))
frontier.add(offsetPos); frontier.add(offsetPos);
} }
@ -382,7 +379,11 @@ public abstract class Contraption {
public void removeBlocksFromWorld(IWorld world, BlockPos offset, BiPredicate<BlockPos, BlockState> customRemoval) { public void removeBlocksFromWorld(IWorld world, BlockPos offset, BiPredicate<BlockPos, BlockState> customRemoval) {
storage.values().forEach(MountedStorage::empty); storage.values().forEach(MountedStorage::empty);
for (boolean brittles : Iterate.trueAndFalse) {
for (BlockInfo block : blocks.values()) { for (BlockInfo block : blocks.values()) {
if (brittles != BlockMovementTraits.isBrittle(block.state))
continue;
BlockPos add = block.pos.add(anchor).add(offset); BlockPos add = block.pos.add(anchor).add(offset);
if (customRemoval.test(add, block.state)) if (customRemoval.test(add, block.state))
continue; continue;
@ -390,6 +391,7 @@ public abstract class Contraption {
world.setBlockState(add, Blocks.AIR.getDefaultState(), 67); world.setBlockState(add, Blocks.AIR.getDefaultState(), 67);
} }
} }
}
public void disassemble(World world, BlockPos offset, Vec3d rotation, public void disassemble(World world, BlockPos offset, Vec3d rotation,
BiPredicate<BlockPos, BlockState> customPlacement) { BiPredicate<BlockPos, BlockState> customPlacement) {
@ -397,7 +399,11 @@ public abstract class Contraption {
StructureTransform transform = new StructureTransform(offset, rotation); StructureTransform transform = new StructureTransform(offset, rotation);
for (boolean nonBrittles : Iterate.trueAndFalse) {
for (BlockInfo block : blocks.values()) { for (BlockInfo block : blocks.values()) {
if (nonBrittles == BlockMovementTraits.isBrittle(block.state))
continue;
BlockPos targetPos = transform.apply(block.pos); BlockPos targetPos = transform.apply(block.pos);
BlockState state = transform.apply(block.state); BlockState state = transform.apply(block.state);
@ -405,14 +411,19 @@ public abstract class Contraption {
continue; continue;
for (Direction face : Direction.values()) for (Direction face : Direction.values())
state = state.updatePostPlacement(face, world.getBlockState(targetPos.offset(face)), world, targetPos, state = state.updatePostPlacement(face, world.getBlockState(targetPos.offset(face)), world,
targetPos.offset(face)); targetPos, targetPos.offset(face));
if (AllBlocks.SAW.typeOf(state)) if (AllBlocks.SAW.typeOf(state))
state = state.with(SawBlock.RUNNING, false); state = state.with(SawBlock.RUNNING, false);
if (world.getBlockState(targetPos).getBlockHardness(world, targetPos) == -1) BlockState blockState = world.getBlockState(targetPos);
if (blockState.getBlockHardness(world, targetPos) == -1)
continue; continue;
world.destroyBlock(targetPos, world.getBlockState(targetPos).getCollisionShape(world, targetPos).isEmpty()); 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); world.setBlockState(targetPos, state, 3 | BlockFlags.IS_MOVING);
TileEntity tileEntity = world.getTileEntity(targetPos); TileEntity tileEntity = world.getTileEntity(targetPos);
CompoundNBT tag = block.nbt; CompoundNBT tag = block.nbt;
@ -445,6 +456,8 @@ public abstract class Contraption {
} }
} }
}
} }
public void initActors(World world) { public void initActors(World world) {

View file

@ -165,9 +165,7 @@ public class ChassisTileEntity extends SmartTileEntity {
break; break;
// Ignore replaceable Blocks and Air-like // Ignore replaceable Blocks and Air-like
if (currentState.getMaterial().isReplaceable()) if (!BlockMovementTraits.movementNecessary(world, current))
break;
if (currentState.getCollisionShape(world, current).isEmpty())
break; break;
if (AllBlocks.MECHANICAL_PISTON_HEAD.typeOf(currentState)) if (AllBlocks.MECHANICAL_PISTON_HEAD.typeOf(currentState))
break; break;
@ -212,9 +210,7 @@ public class ChassisTileEntity extends SmartTileEntity {
continue; continue;
if (!searchPos.withinDistance(pos, chassisRange + .5f)) if (!searchPos.withinDistance(pos, chassisRange + .5f))
continue; continue;
if (searchedState.getMaterial().isReplaceable()) if (!BlockMovementTraits.movementNecessary(world, searchPos))
continue;
if (searchedState.getCollisionShape(world, searchPos).isEmpty())
continue; continue;
localVisited.add(searchPos); localVisited.add(searchPos);

View file

@ -135,11 +135,9 @@ public class PistonContraption extends Contraption {
BlockPos currentPos = pos.offset(orientation, offset + initialExtensionProgress); BlockPos currentPos = pos.offset(orientation, offset + initialExtensionProgress);
if (!world.isBlockPresent(currentPos)) if (!world.isBlockPresent(currentPos))
return false; return false;
if (!BlockMovementTraits.movementNecessary(world, currentPos))
return true;
BlockState state = world.getBlockState(currentPos); 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()) if (AllBlocks.MECHANICAL_PISTON_HEAD.typeOf(state) && state.get(FACING) == direction.getOpposite())
return true; return true;
if (!BlockMovementTraits.movementAllowed(world, currentPos)) if (!BlockMovementTraits.movementAllowed(world, currentPos))

View file

@ -5,11 +5,11 @@ import com.simibubi.create.AllTileEntities;
import com.simibubi.create.config.AllConfigs; import com.simibubi.create.config.AllConfigs;
import com.simibubi.create.foundation.behaviour.CenteredSideValueBoxTransform; import com.simibubi.create.foundation.behaviour.CenteredSideValueBoxTransform;
import com.simibubi.create.foundation.behaviour.ValueBoxTransform; 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.ContraptionCollider;
import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity; import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity;
import com.simibubi.create.modules.contraptions.components.contraptions.piston.LinearActuatorTileEntity; import com.simibubi.create.modules.contraptions.components.contraptions.piston.LinearActuatorTileEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
@ -129,8 +129,7 @@ public class PulleyTileEntity extends LinearActuatorTileEntity {
return; return;
BlockPos posBelow = pos.down((int) (offset + getMovementSpeed()) + 1); BlockPos posBelow = pos.down((int) (offset + getMovementSpeed()) + 1);
BlockState stateBelow = world.getBlockState(posBelow); if (!BlockMovementTraits.movementNecessary(world, posBelow))
if (stateBelow.getMaterial().isReplaceable() || stateBelow.getShape(world, posBelow).isEmpty())
return; return;
disassemble(); disassemble();