filename:
common/src/main/java/rearth/oritech/block/blocks/pipes/AbstractPipeBlock.java
branch:
1.21
back to repo
package rearth.oritech.block.blocks.pipes;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.ShapeContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import org.apache.commons.lang3.function.TriFunction;
import org.jetbrains.annotations.Nullable;
import rearth.oritech.Oritech;
import rearth.oritech.block.entity.pipes.GenericPipeInterfaceEntity;
public abstract class AbstractPipeBlock extends Block {
private static final Boolean USE_ACCURATE_OUTLINES = Oritech.CONFIG.tightCableHitboxes();
protected VoxelShape[] boundingShapes;
public AbstractPipeBlock(Settings settings) {
super(settings);
this.boundingShapes = createShapes();
}
protected abstract VoxelShape getShape(BlockState state);
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
if (!USE_ACCURATE_OUTLINES)
return super.getOutlineShape(state, world, pos, context);
return getShape(state);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getShape(state);
}
protected abstract VoxelShape[] createShapes();
@Override
public abstract void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify);
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
var baseState = super.getPlacementState(ctx);
return addConnectionStates(baseState, ctx.getWorld(), ctx.getBlockPos(), true);
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess worldAccess, BlockPos pos, BlockPos neighborPos) {
var world = (World) worldAccess;
if (world.isClient) return state;
if (neighborState.isOf(Blocks.AIR))
// remove potential stale machine -> neighboring pipes mapping
getNetworkData(world).machinePipeNeighbors.remove(neighborPos);
return state;
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
if (!state.isOf(newState.getBlock()) && !(newState.getBlock() instanceof AbstractPipeBlock)) {
// block was removed/replaced instead of updated
onBlockRemoved(pos, state, world);
}
}
/**
* Updates all the neighboring pipes of the target position.
*
* @param world The target world
* @param pos The target position
* @param neighborToggled Whether the neighbor was toggled
*/
public abstract void updateNeighbors(World world, BlockPos pos, boolean neighborToggled);
@Override
public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) {
if (!player.isCreative() && !world.isClient) {
onBlockRemoved(pos, state, world);
}
return super.onBreak(world, pos, state, player);
}
/**
* Adds the connection states to the pipe block-state.
*
* @param state The current pipe block-state
* @param world The target world
* @param pos The target pipe position
* @param createConnection Whether to create a connection
* @return The updated block-state
*/
public abstract BlockState addConnectionStates(BlockState state, World world, BlockPos pos, boolean createConnection);
/**
* Adds the connection states to the pipe block-state.
* Attempts to create a connection ONLY in the specified direction.
* Useful for when only one connection needs to be created.
*
* @param state The current pipe block-state
* @param world The target world
* @param pos The target pipe position
* @param createDirection The direction to create a connection in
* @return The updated block-state
*/
public abstract BlockState addConnectionStates(BlockState state, World world, BlockPos pos, Direction createDirection);
/**
* Adds the straight property to the pipe block-state.
*
* @param state The current pipe block-state
* @return The updated block-state
*/
public abstract BlockState addStraightState(BlockState state);
/**
* Check if the pipe should connect in a specific direction.
*
* @param current The current pipe block-state
* @param direction The direction to check
* @param currentPos The current pipe position
* @param world The target world
* @param createConnection Whether to create a connection
* @return Boolean whether the pipe should connect
*/
public abstract boolean shouldConnect(BlockState current, Direction direction, BlockPos currentPos, World world, boolean createConnection);
/**
* Check if the pipe is connecting in a specific direction.
*
* @param current The target pipe block-state
* @param direction The direction to check
* @param createConnection Whether to create a connection
* @return Boolean whether the pipe is connecting
*/
public abstract boolean isConnectingInDirection(BlockState current, Direction direction, BlockPos currentPos, World world, boolean createConnection);
/**
* Check if the pipe node has a neighboring machine.
*
* @param state The target pipe block-state
* @param world The target world
* @param pos The target pipe position
* @return Boolean whether a machine is connected
*/
public boolean hasNeighboringMachine(BlockState state, World world, BlockPos pos, boolean createConnection) {
var lookup = apiValidationFunction();
return (isConnectingInDirection(state, Direction.NORTH, pos, world, createConnection) && hasMachineInDirection(Direction.NORTH, world, pos, lookup))
|| (isConnectingInDirection(state, Direction.EAST, pos, world, createConnection) && hasMachineInDirection(Direction.EAST, world, pos, lookup))
|| (isConnectingInDirection(state, Direction.SOUTH, pos, world, createConnection) && hasMachineInDirection(Direction.SOUTH, world, pos, lookup))
|| (isConnectingInDirection(state, Direction.WEST, pos, world, createConnection) && hasMachineInDirection(Direction.WEST, world, pos, lookup))
|| (isConnectingInDirection(state, Direction.UP, pos, world, createConnection) && hasMachineInDirection(Direction.UP, world, pos, lookup))
|| (isConnectingInDirection(state, Direction.DOWN, pos, world, createConnection) && hasMachineInDirection(Direction.DOWN, world, pos, lookup));
}
/**
* Check if a machine is connected in a specific direction.
*
* @param direction The direction to check
* @param world The target world
* @param ownPos The target pipe position
* @param lookup The lookup function {@link AbstractPipeBlock#apiValidationFunction()}
* @return Boolean whether a machine is connected
*/
public boolean hasMachineInDirection(Direction direction, World world, BlockPos ownPos, TriFunction<World, BlockPos, Direction, Boolean> lookup) {
var neighborPos = ownPos.add(direction.getVector());
var neighborState = world.getBlockState(neighborPos);
return !(neighborState.getBlock() instanceof GenericPipeBlock) && lookup.apply(world, neighborPos, direction.getOpposite());
}
/**
* Check if the target block is a valid connection target.
*
* @param target The target block
* @param world The target world
* @param direction The direction to check (IMPORTANT: This is the direction from the target to the current pipe)
* @param pos The target pipe position
* @return Boolean whether the target is a valid connection target
*/
public boolean isValidConnectionTarget(Block target, World world, Direction direction, BlockPos pos) {
var lookupFunction = apiValidationFunction();
return connectToOwnBlockType(target) || (lookupFunction.apply(world, pos, direction) && isCompatibleTarget(target));
}
/**
* Check if the target block is a valid interface target.
*
* @param target The target block
* @param world The target world
* @param direction The direction to check (IMPORTANT: This is the direction from the target to the current pipe)
* @param pos The target pipe position
* @return Boolean whether the target is a valid interface target
*/
public boolean isValidInterfaceTarget(Block target, World world, Direction direction, BlockPos pos) {
var lookupFunction = apiValidationFunction();
return (lookupFunction.apply(world, pos, direction) && isCompatibleTarget(target));
}
/**
* Check if the target block is compatible with the pipe block.
*
* @param block The target block
* @return Boolean whether the block is compatible
*/
public boolean isCompatibleTarget(Block block) {
return true;
}
/**
* Validation function which utilizes lookup API's to check if a block is a valid connection target.
*
* @return The validation function for the pipe block
*/
public abstract TriFunction<World, BlockPos, Direction, Boolean> apiValidationFunction();
public abstract BlockState getConnectionBlock();
public abstract BlockState getNormalBlock();
public abstract String getPipeTypeName();
public abstract boolean connectToOwnBlockType(Block block);
public abstract GenericPipeInterfaceEntity.PipeNetworkData getNetworkData(World world);
protected abstract void onBlockRemoved(BlockPos pos, BlockState oldState, World world);
}