CreateMod/src/main/java/com/simibubi/create/content/contraptions/fluids/pipes/FluidPipeBlock.java
2023-03-08 14:32:50 +01:00

341 lines
13 KiB
Java

package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.Arrays;
import java.util.Optional;
import javax.annotation.Nullable;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour;
import com.simibubi.create.content.contraptions.relays.elementary.EncasableBlock;
import com.simibubi.create.content.contraptions.wrench.IWrenchableWithBracket;
import com.simibubi.create.foundation.advancement.AdvancementBehaviour;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.ticks.TickPriority;
public class FluidPipeBlock extends PipeBlock
implements SimpleWaterloggedBlock, IWrenchableWithBracket, ITE<FluidPipeTileEntity>, EncasableBlock {
private static final VoxelShape OCCLUSION_BOX = Block.box(4, 4, 4, 12, 12, 12);
public FluidPipeBlock(Properties properties) {
super(4 / 16f, properties);
this.registerDefaultState(super.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, false));
}
@Override
public InteractionResult onWrenched(BlockState state, UseOnContext context) {
if (tryRemoveBracket(context))
return InteractionResult.SUCCESS;
Level world = context.getLevel();
BlockPos pos = context.getClickedPos();
Direction clickedFace = context.getClickedFace();
Axis axis = getAxis(world, pos, state);
if (axis == null) {
Vec3 clickLocation = context.getClickLocation()
.subtract(pos.getX(), pos.getY(), pos.getZ());
double closest = Float.MAX_VALUE;
Direction argClosest = Direction.UP;
for (Direction direction : Iterate.directions) {
if (clickedFace.getAxis() == direction.getAxis())
continue;
Vec3 centerOf = Vec3.atCenterOf(direction.getNormal());
double distance = centerOf.distanceToSqr(clickLocation);
if (distance < closest) {
closest = distance;
argClosest = direction;
}
}
axis = argClosest.getAxis();
}
if (clickedFace.getAxis() == axis)
return InteractionResult.PASS;
if (!world.isClientSide) {
withTileEntityDo(world, pos, fpte -> fpte.getBehaviour(FluidTransportBehaviour.TYPE).interfaces.values()
.stream()
.filter(pc -> pc != null && pc.hasFlow())
.findAny()
.ifPresent($ -> AllAdvancements.GLASS_PIPE.awardTo(context.getPlayer())));
FluidTransportBehaviour.cacheFlows(world, pos);
world.setBlockAndUpdate(pos, AllBlocks.GLASS_FLUID_PIPE.getDefaultState()
.setValue(GlassFluidPipeBlock.AXIS, axis)
.setValue(BlockStateProperties.WATERLOGGED, state.getValue(BlockStateProperties.WATERLOGGED)));
FluidTransportBehaviour.loadFlows(world, pos);
}
return InteractionResult.SUCCESS;
}
@Override
public void setPlacedBy(Level pLevel, BlockPos pPos, BlockState pState, LivingEntity pPlacer, ItemStack pStack) {
super.setPlacedBy(pLevel, pPos, pState, pPlacer, pStack);
AdvancementBehaviour.setPlacedBy(pLevel, pPos, pPlacer);
}
@Override
public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand,
BlockHitResult ray) {
ItemStack heldItem = player.getItemInHand(hand);
InteractionResult result = tryEncase(state, world, pos, heldItem, player, hand, ray);
if (result.consumesAction())
return result;
return InteractionResult.PASS;
}
public BlockState getAxisState(Axis axis) {
BlockState defaultState = defaultBlockState();
for (Direction d : Iterate.directions)
defaultState = defaultState.setValue(PROPERTY_BY_DIRECTION.get(d), d.getAxis() == axis);
return defaultState;
}
@Nullable
private Axis getAxis(BlockGetter world, BlockPos pos, BlockState state) {
return FluidPropagator.getStraightPipeAxis(state);
}
@Override
public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) {
boolean blockTypeChanged = state.getBlock() != newState.getBlock();
if (blockTypeChanged && !world.isClientSide)
FluidPropagator.propagateChangedPipe(world, pos, state);
if (state != newState && !isMoving)
removeBracket(world, pos, true).ifPresent(stack -> Block.popResource(world, pos, stack));
if (state.hasBlockEntity() && (blockTypeChanged || !newState.hasBlockEntity()))
world.removeBlockEntity(pos);
}
@Override
public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean isMoving) {
if (world.isClientSide)
return;
if (state != oldState)
world.scheduleTick(pos, this, 1, TickPriority.HIGH);
}
@Override
public void neighborChanged(BlockState state, Level world, BlockPos pos, Block otherBlock, BlockPos neighborPos,
boolean isMoving) {
DebugPackets.sendNeighborsUpdatePacket(world, pos);
Direction d = FluidPropagator.validateNeighbourChange(state, world, pos, otherBlock, neighborPos, isMoving);
if (d == null)
return;
if (!isOpenAt(state, d))
return;
world.scheduleTick(pos, this, 1, TickPriority.HIGH);
}
@Override
public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource r) {
FluidPropagator.propagateChangedPipe(world, pos, state);
}
public static boolean isPipe(BlockState state) {
return state.getBlock() instanceof FluidPipeBlock;
}
public static boolean canConnectTo(BlockAndTintGetter world, BlockPos neighbourPos, BlockState neighbour,
Direction direction) {
if (FluidPropagator.hasFluidCapability(world, neighbourPos, direction.getOpposite()))
return true;
if (VanillaFluidTargets.shouldPipesConnectTo(neighbour))
return true;
FluidTransportBehaviour transport = TileEntityBehaviour.get(world, neighbourPos, FluidTransportBehaviour.TYPE);
BracketedTileEntityBehaviour bracket =
TileEntityBehaviour.get(world, neighbourPos, BracketedTileEntityBehaviour.TYPE);
if (isPipe(neighbour))
return bracket == null || !bracket.isBracketPresent()
|| FluidPropagator.getStraightPipeAxis(neighbour) == direction.getAxis();
if (transport == null)
return false;
return transport.canHaveFlowToward(neighbour, direction.getOpposite());
}
public static boolean shouldDrawRim(BlockAndTintGetter world, BlockPos pos, BlockState state, Direction direction) {
BlockPos offsetPos = pos.relative(direction);
BlockState facingState = world.getBlockState(offsetPos);
if (facingState.getBlock() instanceof EncasedPipeBlock)
return true;
if (!isPipe(facingState))
return true;
if (!canConnectTo(world, offsetPos, facingState, direction))
return true;
return false;
}
public static boolean isOpenAt(BlockState state, Direction direction) {
return state.getValue(PROPERTY_BY_DIRECTION.get(direction));
}
public static boolean isCornerOrEndPipe(BlockAndTintGetter world, BlockPos pos, BlockState state) {
return isPipe(state) && FluidPropagator.getStraightPipeAxis(state) == null
&& !shouldDrawCasing(world, pos, state);
}
public static boolean shouldDrawCasing(BlockAndTintGetter world, BlockPos pos, BlockState state) {
if (!isPipe(state))
return false;
for (Axis axis : Iterate.axes) {
int connections = 0;
for (Direction direction : Iterate.directions)
if (direction.getAxis() != axis && isOpenAt(state, direction))
connections++;
if (connections > 2)
return true;
}
return false;
}
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> builder) {
builder.add(NORTH, EAST, SOUTH, WEST, UP, DOWN, BlockStateProperties.WATERLOGGED);
super.createBlockStateDefinition(builder);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
FluidState FluidState = context.getLevel()
.getFluidState(context.getClickedPos());
return updateBlockState(defaultBlockState(), context.getNearestLookingDirection(), null, context.getLevel(),
context.getClickedPos()).setValue(BlockStateProperties.WATERLOGGED,
Boolean.valueOf(FluidState.getType() == Fluids.WATER));
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighbourState, LevelAccessor world,
BlockPos pos, BlockPos neighbourPos) {
if (state.getValue(BlockStateProperties.WATERLOGGED))
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
if (isOpenAt(state, direction) && neighbourState.hasProperty(BlockStateProperties.WATERLOGGED))
world.scheduleTick(pos, this, 1, TickPriority.HIGH);
return updateBlockState(state, direction, direction.getOpposite(), world, pos);
}
public BlockState updateBlockState(BlockState state, Direction preferredDirection, @Nullable Direction ignore,
BlockAndTintGetter world, BlockPos pos) {
BracketedTileEntityBehaviour bracket = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE);
if (bracket != null && bracket.isBracketPresent())
return state;
BlockState prevState = state;
int prevStateSides = (int) Arrays.stream(Iterate.directions)
.map(PROPERTY_BY_DIRECTION::get)
.filter(prevState::getValue)
.count();
// Update sides that are not ignored
for (Direction d : Iterate.directions)
if (d != ignore) {
boolean shouldConnect = canConnectTo(world, pos.relative(d), world.getBlockState(pos.relative(d)), d);
state = state.setValue(PROPERTY_BY_DIRECTION.get(d), shouldConnect);
}
// See if it has enough connections
Direction connectedDirection = null;
for (Direction d : Iterate.directions) {
if (isOpenAt(state, d)) {
if (connectedDirection != null)
return state;
connectedDirection = d;
}
}
// Add opposite end if only one connection
if (connectedDirection != null)
return state.setValue(PROPERTY_BY_DIRECTION.get(connectedDirection.getOpposite()), true);
// If we can't connect to anything and weren't connected before, do nothing
if (prevStateSides == 2)
return prevState;
// Use preferred
return state.setValue(PROPERTY_BY_DIRECTION.get(preferredDirection), true)
.setValue(PROPERTY_BY_DIRECTION.get(preferredDirection.getOpposite()), true);
}
@Override
public FluidState getFluidState(BlockState state) {
return state.getValue(BlockStateProperties.WATERLOGGED) ? Fluids.WATER.getSource(false)
: Fluids.EMPTY.defaultFluidState();
}
@Override
public Optional<ItemStack> removeBracket(BlockGetter world, BlockPos pos, boolean inOnReplacedContext) {
BracketedTileEntityBehaviour behaviour =
BracketedTileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE);
if (behaviour == null)
return Optional.empty();
BlockState bracket = behaviour.removeBracket(inOnReplacedContext);
if (bracket == null)
return Optional.empty();
return Optional.of(new ItemStack(bracket.getBlock()));
}
@Override
public boolean isPathfindable(BlockState state, BlockGetter reader, BlockPos pos, PathComputationType type) {
return false;
}
@Override
public Class<FluidPipeTileEntity> getTileEntityClass() {
return FluidPipeTileEntity.class;
}
@Override
public BlockEntityType<? extends FluidPipeTileEntity> getTileEntityType() {
return AllTileEntities.FLUID_PIPE.get();
}
@Override
public boolean supportsExternalFaceHiding(BlockState state) {
return false;
}
@Override
public VoxelShape getOcclusionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
return OCCLUSION_BOX;
}
}