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;
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;
@ -14,6 +23,17 @@ 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)
@ -27,6 +47,60 @@ public class BlockMovementTraits {
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,7 +110,7 @@ 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) {

View file

@ -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<BlockPos, BlockState> 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);
}
}
}

View file

@ -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);

View file

@ -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))

View file

@ -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();