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,14 +89,11 @@ 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))
if (info.state.getCollisionShape(world, offsetPos).isEmpty())
continue;
if (blocks.containsKey(offsetPos)
&& !blocks.get(offsetPos).state.getCollisionShape(world, offsetPos).isEmpty())
continue;
hasNext = true;
break;
}
if (!hasNext)
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,18 +158,19 @@ public abstract class Contraption {
frontier.add(prevPos);
}
if (state.getBlock() instanceof SlimeBlock)
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)
if (offset == forcedDirection && isSlimeBlock)
return false;
continue;
}
if (!visited.contains(offsetPos))
if (!visited.contains(offsetPos)
&& (isSlimeBlock || BlockMovementTraits.isBlockAttachedTowards(blockState, offset.getOpposite())))
frontier.add(offsetPos);
}
@ -382,7 +379,11 @@ public abstract class Contraption {
public void removeBlocksFromWorld(IWorld world, BlockPos offset, BiPredicate<BlockPos, BlockState> customRemoval) {
storage.values().forEach(MountedStorage::empty);
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;
@ -390,6 +391,7 @@ public abstract class Contraption {
world.setBlockState(add, Blocks.AIR.getDefaultState(), 67);
}
}
}
public void disassemble(World world, BlockPos offset, Vec3d rotation,
BiPredicate<BlockPos, BlockState> customPlacement) {
@ -397,7 +399,11 @@ public abstract class Contraption {
StructureTransform transform = new StructureTransform(offset, rotation);
for (boolean nonBrittles : Iterate.trueAndFalse) {
for (BlockInfo block : blocks.values()) {
if (nonBrittles == BlockMovementTraits.isBrittle(block.state))
continue;
BlockPos targetPos = transform.apply(block.pos);
BlockState state = transform.apply(block.state);
@ -405,14 +411,19 @@ public abstract class Contraption {
continue;
for (Direction face : Direction.values())
state = state.updatePostPlacement(face, world.getBlockState(targetPos.offset(face)), world, targetPos,
targetPos.offset(face));
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 (world.getBlockState(targetPos).getBlockHardness(world, targetPos) == -1)
BlockState blockState = world.getBlockState(targetPos);
if (blockState.getBlockHardness(world, targetPos) == -1)
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);
TileEntity tileEntity = world.getTileEntity(targetPos);
CompoundNBT tag = block.nbt;
@ -445,6 +456,8 @@ public abstract class Contraption {
}
}
}
}
public void initActors(World world) {

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