Allow registering custom block movement checks

Registering block movement checks works by using one of the six static register methods on an instance of an implementation of one of the six corresponding interfaces. Each one takes some arguments and returns a CheckResult, which defines whether to return from the global check or fall through to the next registered check or fallback check.

- Rename BlockMovementTraits to BlockMovementChecks
- Rename some check methods to be consistent
- Pass World instead of IBlockReader to isBlockAttachedTowards
- Make the bottom half of doors attach up and down
- Remove door check from Contrapion#moveBlock as it is unnecessary
This commit is contained in:
PepperBell 2021-05-24 13:32:39 -07:00
parent 6c390977d8
commit 20cd8da377
6 changed files with 203 additions and 61 deletions

View File

@ -1,5 +1,8 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.content.contraptions.components.actors.AttachedActorBlock;
@ -53,15 +56,118 @@ import net.minecraft.state.properties.AttachFace;
import net.minecraft.state.properties.BedPart;
import net.minecraft.state.properties.BellAttachment;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.state.properties.DoubleBlockHalf;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
public class BlockMovementTraits {
public class BlockMovementChecks {
public static boolean movementNecessary(BlockState state, World world, BlockPos pos) {
private static final List<MovementNecessaryCheck> MOVEMENT_NECESSARY_CHECKS = new ArrayList<>();
private static final List<MovementAllowedCheck> MOVEMENT_ALLOWED_CHECKS = new ArrayList<>();
private static final List<BrittleCheck> BRITTLE_CHECKS = new ArrayList<>();
private static final List<AttachedCheck> ATTACHED_CHECKS = new ArrayList<>();
private static final List<NotSupportiveCheck> NOT_SUPPORTIVE_CHECKS = new ArrayList<>();
// Registration
// Add new checks to the front instead of the end
public static void registerMovementNecessaryCheck(MovementNecessaryCheck check) {
MOVEMENT_NECESSARY_CHECKS.add(0, check);
}
public static void registerMovementAllowedCheck(MovementAllowedCheck check) {
MOVEMENT_ALLOWED_CHECKS.add(0, check);
}
public static void registerBrittleCheck(BrittleCheck check) {
BRITTLE_CHECKS.add(0, check);
}
public static void registerAttachedCheck(AttachedCheck check) {
ATTACHED_CHECKS.add(0, check);
}
public static void registerNotSupportiveCheck(NotSupportiveCheck check) {
NOT_SUPPORTIVE_CHECKS.add(0, check);
}
public static void registerAllChecks(AllChecks checks) {
registerMovementNecessaryCheck(checks);
registerMovementAllowedCheck(checks);
registerBrittleCheck(checks);
registerAttachedCheck(checks);
registerNotSupportiveCheck(checks);
}
// Actual check methods
public static boolean isMovementNecessary(BlockState state, World world, BlockPos pos) {
for (MovementNecessaryCheck check : MOVEMENT_NECESSARY_CHECKS) {
CheckResult result = check.isMovementNecessary(state, world, pos);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isMovementNecessaryFallback(state, world, pos);
}
public static boolean isMovementAllowed(BlockState state, World world, BlockPos pos) {
for (MovementAllowedCheck check : MOVEMENT_ALLOWED_CHECKS) {
CheckResult result = check.isMovementAllowed(state, world, pos);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isMovementAllowedFallback(state, world, pos);
}
/**
* Brittle blocks will be collected first, as they may break when other blocks
* are removed before them
*/
public static boolean isBrittle(BlockState state) {
for (BrittleCheck check : BRITTLE_CHECKS) {
CheckResult result = check.isBrittle(state);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isBrittleFallback(state);
}
/**
* Attached blocks will move if blocks they are attached to are moved
*/
public static boolean isBlockAttachedTowards(BlockState state, World world, BlockPos pos,
Direction direction) {
for (AttachedCheck check : ATTACHED_CHECKS) {
CheckResult result = check.isBlockAttachedTowards(state, world, pos, direction);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isBlockAttachedTowardsFallback(state, world, pos, direction);
}
/**
* Non-Supportive blocks will not continue a chain of blocks picked up by e.g. a
* piston
*/
public static boolean isNotSupportive(BlockState state, Direction facing) {
for (NotSupportiveCheck check : NOT_SUPPORTIVE_CHECKS) {
CheckResult result = check.isNotSupportive(state, facing);
if (result != CheckResult.PASS) {
return result.toBoolean();
}
}
return isNotSupportiveFallback(state, facing);
}
// Fallback checks
private static boolean isMovementNecessaryFallback(BlockState state, World world, BlockPos pos) {
if (isBrittle(state))
return true;
if (state.getBlock() instanceof FenceGateBlock)
@ -75,7 +181,7 @@ public class BlockMovementTraits {
return true;
}
public static boolean movementAllowed(BlockState state, World world, BlockPos pos) {
private static boolean isMovementAllowedFallback(BlockState state, World world, BlockPos pos) {
Block block = state.getBlock();
if (block instanceof AbstractChassisBlock)
return true;
@ -115,11 +221,7 @@ public class BlockMovementTraits {
return state.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) {
private static boolean isBrittleFallback(BlockState state) {
Block block = state.getBlock();
if (state.contains(BlockStateProperties.HANGING))
return true;
@ -147,10 +249,7 @@ public class BlockMovementTraits {
return AllBlockTags.BRITTLE.tag.contains(block);
}
/**
* Attached blocks will move if blocks they are attached to are moved
*/
public static boolean isBlockAttachedTowards(IBlockReader world, BlockPos pos, BlockState state,
private static boolean isBlockAttachedTowardsFallback(BlockState state, World world, BlockPos pos,
Direction direction) {
Block block = state.getBlock();
if (block instanceof LadderBlock)
@ -163,13 +262,15 @@ public class BlockMovementTraits {
return direction == Direction.DOWN;
if (block instanceof AbstractPressurePlateBlock)
return direction == Direction.DOWN;
if (block instanceof DoorBlock)
if (block instanceof DoorBlock) {
if (state.get(DoorBlock.HALF) == DoubleBlockHalf.LOWER && direction == Direction.UP)
return true;
return direction == Direction.DOWN;
}
if (block instanceof BedBlock) {
Direction facing = state.get(BedBlock.HORIZONTAL_FACING);
if (state.get(BedBlock.PART) == BedPart.HEAD) {
if (state.get(BedBlock.PART) == BedPart.HEAD)
facing = facing.getOpposite();
}
return direction == facing;
}
if (block instanceof RedstoneLinkBlock)
@ -226,16 +327,12 @@ public class BlockMovementTraits {
return FluidTankConnectivityHandler.isConnected(world, pos, pos.offset(direction));
if (AllBlocks.STICKER.has(state) && state.get(StickerBlock.EXTENDED)) {
return direction == state.get(StickerBlock.FACING)
&& !notSupportive(world.getBlockState(pos.offset(direction)), direction.getOpposite());
&& !isNotSupportive(world.getBlockState(pos.offset(direction)), direction.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) {
private static boolean isNotSupportiveFallback(BlockState state, Direction facing) {
if (AllBlocks.MECHANICAL_DRILL.has(state))
return state.get(BlockStateProperties.FACING) == facing;
if (AllBlocks.MECHANICAL_BEARING.has(state))
@ -266,4 +363,58 @@ public class BlockMovementTraits {
return isBrittle(state);
}
// Check classes
public static interface MovementNecessaryCheck {
public CheckResult isMovementNecessary(BlockState state, World world, BlockPos pos);
}
public static interface MovementAllowedCheck {
public CheckResult isMovementAllowed(BlockState state, World world, BlockPos pos);
}
public static interface BrittleCheck {
/**
* Brittle blocks will be collected first, as they may break when other blocks
* are removed before them
*/
public CheckResult isBrittle(BlockState state);
}
public static interface AttachedCheck {
/**
* Attached blocks will move if blocks they are attached to are moved
*/
public CheckResult isBlockAttachedTowards(BlockState state, World world, BlockPos pos, Direction direction);
}
public static interface NotSupportiveCheck {
/**
* Non-Supportive blocks will not continue a chain of blocks picked up by e.g. a
* piston
*/
public CheckResult isNotSupportive(BlockState state, Direction direction);
}
public static interface AllChecks extends MovementNecessaryCheck, MovementAllowedCheck, BrittleCheck, AttachedCheck, NotSupportiveCheck {
}
public static enum CheckResult {
SUCCESS,
FAIL,
PASS;
public Boolean toBoolean() {
return this == PASS ? null : (this == SUCCESS ? true : false);
}
public static CheckResult of(boolean b) {
return b ? SUCCESS : FAIL;
}
public static CheckResult of(Boolean b) {
return b == null ? PASS : (b ? SUCCESS : FAIL);
}
}
}

View File

@ -73,7 +73,6 @@ import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.ChestBlock;
import net.minecraft.block.DoorBlock;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.block.PressurePlateBlock;
import net.minecraft.block.material.PushReaction;
@ -87,7 +86,6 @@ import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.state.properties.ChestType;
import net.minecraft.state.properties.DoubleBlockHalf;
import net.minecraft.state.properties.PistonType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
@ -212,7 +210,7 @@ public abstract class Contraption {
if (bounds == null)
bounds = new AxisAlignedBB(BlockPos.ZERO);
if (!BlockMovementTraits.isBrittle(world.getBlockState(pos)))
if (!BlockMovementChecks.isBrittle(world.getBlockState(pos)))
frontier.add(pos);
if (!addToInitialFrontier(world, pos, forcedDirection, frontier))
return false;
@ -312,7 +310,7 @@ public abstract class Contraption {
if (isAnchoringBlockAt(pos))
return true;
BlockState state = world.getBlockState(pos);
if (!BlockMovementTraits.movementNecessary(state, world, pos))
if (!BlockMovementChecks.isMovementNecessary(state, world, pos))
return true;
if (!movementAllowed(state, world, pos))
throw AssemblyException.unmovableBlock(pos, state);
@ -336,7 +334,7 @@ public abstract class Contraption {
Direction offset = state.get(StickerBlock.FACING);
BlockPos attached = pos.offset(offset);
if (!visited.contains(attached)
&& !BlockMovementTraits.notSupportive(world.getBlockState(attached), offset.getOpposite()))
&& !BlockMovementChecks.isNotSupportive(world.getBlockState(attached), offset.getOpposite()))
frontier.add(attached);
}
@ -365,13 +363,6 @@ public abstract class Contraption {
if (isPistonHead(state))
movePistonHead(world, pos, frontier, visited, state);
// Doors try to stay whole
if (state.getBlock() instanceof DoorBlock) {
BlockPos otherPartPos = pos.up(state.get(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? 1 : -1);
if (!visited.contains(otherPartPos))
frontier.add(otherPartPos);
}
// Cart assemblers attach themselves
BlockPos posDown = pos.down();
BlockState stateBelow = world.getBlockState(posDown);
@ -395,24 +386,24 @@ public abstract class Contraption {
boolean wasVisited = visited.contains(offsetPos);
boolean faceHasGlue = superglue.containsKey(offset);
boolean blockAttachedTowardsFace =
BlockMovementTraits.isBlockAttachedTowards(world, offsetPos, blockState, offset.getOpposite());
boolean brittle = BlockMovementTraits.isBrittle(blockState);
BlockMovementChecks.isBlockAttachedTowards(blockState, world, offsetPos, offset.getOpposite());
boolean brittle = BlockMovementChecks.isBrittle(blockState);
boolean canStick = !brittle && state.canStickTo(blockState) && blockState.canStickTo(state);
if (canStick) {
if (state.getPushReaction() == PushReaction.PUSH_ONLY
|| blockState.getPushReaction() == PushReaction.PUSH_ONLY) {
canStick = false;
}
if (BlockMovementTraits.notSupportive(state, offset)) {
if (BlockMovementChecks.isNotSupportive(state, offset)) {
canStick = false;
}
if (BlockMovementTraits.notSupportive(blockState, offset.getOpposite())) {
if (BlockMovementChecks.isNotSupportive(blockState, offset.getOpposite())) {
canStick = false;
}
}
if (!wasVisited && (canStick || blockAttachedTowardsFace || faceHasGlue
|| (offset == forcedDirection && !BlockMovementTraits.notSupportive(state, forcedDirection))))
|| (offset == forcedDirection && !BlockMovementChecks.isNotSupportive(state, forcedDirection))))
frontier.add(offsetPos);
if (faceHasGlue)
addGlue(superglue.get(offset));
@ -674,7 +665,7 @@ public abstract class Contraption {
}
protected boolean movementAllowed(BlockState state, World world, BlockPos pos) {
return BlockMovementTraits.movementAllowed(state, world, pos);
return BlockMovementChecks.isMovementAllowed(state, world, pos);
}
protected boolean isAnchoringBlockAt(BlockPos pos) {
@ -944,7 +935,7 @@ public abstract class Contraption {
for (Iterator<BlockInfo> iterator = blocks.values()
.iterator(); iterator.hasNext();) {
BlockInfo block = iterator.next();
if (brittles != BlockMovementTraits.isBrittle(block.state))
if (brittles != BlockMovementChecks.isBrittle(block.state))
continue;
BlockPos add = block.pos.add(anchor)
@ -981,7 +972,7 @@ public abstract class Contraption {
public void addBlocksToWorld(World world, StructureTransform transform) {
for (boolean nonBrittles : Iterate.trueAndFalse) {
for (BlockInfo block : blocks.values()) {
if (nonBrittles == BlockMovementTraits.isBrittle(block.state))
if (nonBrittles == BlockMovementChecks.isBrittle(block.state))
continue;
BlockPos targetPos = transform.apply(block.pos);

View File

@ -11,7 +11,7 @@ import java.util.Queue;
import java.util.Set;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementTraits;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
@ -165,14 +165,14 @@ public class ChassisTileEntity extends SmartTileEntity {
break;
// Ignore replaceable Blocks and Air-like
if (!BlockMovementTraits.movementNecessary(currentState, world, current))
if (!BlockMovementChecks.isMovementNecessary(currentState, world, current))
break;
if (BlockMovementTraits.isBrittle(currentState))
if (BlockMovementChecks.isBrittle(currentState))
break;
positions.add(current);
if (BlockMovementTraits.notSupportive(currentState, facing))
if (BlockMovementChecks.isNotSupportive(currentState, facing))
break;
}
}
@ -206,9 +206,9 @@ public class ChassisTileEntity extends SmartTileEntity {
continue;
if (!searchPos.withinDistance(pos, chassisRange + .5f))
continue;
if (!BlockMovementTraits.movementNecessary(searchedState, world, searchPos))
if (!BlockMovementChecks.isMovementNecessary(searchedState, world, searchPos))
continue;
if (BlockMovementTraits.isBrittle(searchedState))
if (BlockMovementChecks.isBrittle(searchedState))
continue;
localVisited.add(searchPos);
@ -220,7 +220,7 @@ public class ChassisTileEntity extends SmartTileEntity {
continue;
if (searchPos.equals(pos) && offset != facing)
continue;
if (BlockMovementTraits.notSupportive(searchedState, offset))
if (BlockMovementChecks.isNotSupportive(searchedState, offset))
continue;
localFrontier.add(searchPos.offset(offset));

View File

@ -9,7 +9,7 @@ import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.content.contraptions.base.DirectionalKineticBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementTraits;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.BearingBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.AbstractChassisBlock;
import com.simibubi.create.content.schematics.ISpecialEntityItemRequirement;
@ -195,11 +195,11 @@ public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnDat
public static boolean isValidFace(World world, BlockPos pos, Direction direction) {
BlockState state = world.getBlockState(pos);
if (BlockMovementTraits.isBlockAttachedTowards(world, pos, state, direction))
if (BlockMovementChecks.isBlockAttachedTowards(state, world, pos, direction))
return true;
if (!BlockMovementTraits.movementNecessary(state, world, pos))
if (!BlockMovementChecks.isMovementNecessary(state, world, pos))
return false;
if (BlockMovementTraits.notSupportive(state, direction))
if (BlockMovementChecks.isNotSupportive(state, direction))
return false;
return true;
}

View File

@ -15,7 +15,7 @@ import java.util.Queue;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementTraits;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionLighter;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionType;
import com.simibubi.create.content.contraptions.components.structureMovement.TranslatingContraption;
@ -169,13 +169,13 @@ public class PistonContraption extends TranslatingContraption {
if (!world.isBlockPresent(currentPos))
throw AssemblyException.unloadedChunk(currentPos);
BlockState state = world.getBlockState(currentPos);
if (!BlockMovementTraits.movementNecessary(state, world, currentPos))
if (!BlockMovementChecks.isMovementNecessary(state, world, currentPos))
return true;
if (BlockMovementTraits.isBrittle(state) && !(state.getBlock() instanceof CarpetBlock))
if (BlockMovementChecks.isBrittle(state) && !(state.getBlock() instanceof CarpetBlock))
return true;
if (isPistonHead(state) && state.get(FACING) == direction.getOpposite())
return true;
if (!BlockMovementTraits.movementAllowed(state, world, currentPos))
if (!BlockMovementChecks.isMovementAllowed(state, world, currentPos))
if (retracting)
return true;
else
@ -183,7 +183,7 @@ public class PistonContraption extends TranslatingContraption {
if (retracting && state.getPushReaction() == PushReaction.PUSH_ONLY)
return true;
frontier.add(currentPos);
if (BlockMovementTraits.notSupportive(state, orientation))
if (BlockMovementChecks.isNotSupportive(state, orientation))
return true;
}
return true;

View File

@ -2,7 +2,7 @@ package com.simibubi.create.content.contraptions.components.structureMovement.pu
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementTraits;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionCollider;
import com.simibubi.create.content.contraptions.components.structureMovement.ControlledContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.LinearActuatorTileEntity;
@ -186,9 +186,9 @@ public class PulleyTileEntity extends LinearActuatorTileEntity {
BlockPos posBelow = pos.down((int) (offset + getMovementSpeed()) + 1);
BlockState state = world.getBlockState(posBelow);
if (!BlockMovementTraits.movementNecessary(state, world, posBelow))
if (!BlockMovementChecks.isMovementNecessary(state, world, posBelow))
return;
if (BlockMovementTraits.isBrittle(state))
if (BlockMovementChecks.isBrittle(state))
return;
disassemble();