CreateMod/src/main/java/com/simibubi/create/content/logistics/block/funnel/BeltFunnelBlock.java
simibubi af86a7366a Mildly Experienced
- Players can now sneak while using exp nuggets to only consume one item at a time
- Fixed brass funnels losing their filter when changing from or to a belt/depot funnel
- Minecart contraption items can no longer be placed in container items like toolboxes or shulkers (configurable)
- Implement #4436, #4419
2023-03-08 13:33:05 +01:00

215 lines
8.3 KiB
Java

package com.simibubi.create.content.logistics.block.funnel;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.relays.belt.BeltBlock;
import com.simibubi.create.content.contraptions.relays.belt.BeltSlope;
import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.VoxelShaper;
import com.tterrag.registrate.util.entry.BlockEntry;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.item.ItemEntity;
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.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
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.EnumProperty;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.EntityCollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class BeltFunnelBlock extends AbstractHorizontalFunnelBlock implements ISpecialBlockItemRequirement {
private BlockEntry<? extends FunnelBlock> parent;
public static final EnumProperty<Shape> SHAPE = EnumProperty.create("shape", Shape.class);
public enum Shape implements StringRepresentable {
RETRACTED(AllShapes.BELT_FUNNEL_RETRACTED),
EXTENDED(AllShapes.BELT_FUNNEL_EXTENDED),
PUSHING(AllShapes.BELT_FUNNEL_PERPENDICULAR),
PULLING(AllShapes.BELT_FUNNEL_PERPENDICULAR);
VoxelShaper shaper;
private Shape(VoxelShaper shaper) {
this.shaper = shaper;
}
@Override
public String getSerializedName() {
return Lang.asId(name());
}
}
public BeltFunnelBlock(BlockEntry<? extends FunnelBlock> parent, Properties p_i48377_1_) {
super(p_i48377_1_);
this.parent = parent;
registerDefaultState(defaultBlockState().setValue(SHAPE, Shape.RETRACTED));
}
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> p_206840_1_) {
super.createBlockStateDefinition(p_206840_1_.add(SHAPE));
}
public boolean isOfSameType(FunnelBlock otherFunnel) {
return parent.get() == otherFunnel;
}
@Override
public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) {
if (newState.getBlock() instanceof FunnelBlock fb && isOfSameType(fb))
return;
super.onRemove(state, world, pos, newState, isMoving);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter p_220053_2_, BlockPos p_220053_3_,
CollisionContext p_220053_4_) {
return state.getValue(SHAPE).shaper.get(state.getValue(HORIZONTAL_FACING));
}
@Override
public VoxelShape getCollisionShape(BlockState p_220071_1_, BlockGetter p_220071_2_, BlockPos p_220071_3_,
CollisionContext p_220071_4_) {
if (p_220071_4_ instanceof EntityCollisionContext
&& ((EntityCollisionContext) p_220071_4_).getEntity() instanceof ItemEntity
&& (p_220071_1_.getValue(SHAPE) == Shape.PULLING || p_220071_1_.getValue(SHAPE) == Shape.PUSHING))
return AllShapes.FUNNEL_COLLISION.get(getFacing(p_220071_1_));
return getShape(p_220071_1_, p_220071_2_, p_220071_3_, p_220071_4_);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
BlockState stateForPlacement = super.getStateForPlacement(ctx);
BlockPos pos = ctx.getClickedPos();
Level world = ctx.getLevel();
Direction facing = ctx.getClickedFace()
.getAxis()
.isHorizontal() ? ctx.getClickedFace() : ctx.getHorizontalDirection();
BlockState state = stateForPlacement.setValue(HORIZONTAL_FACING, facing);
boolean sneaking = ctx.getPlayer() != null && ctx.getPlayer()
.isShiftKeyDown();
return state.setValue(SHAPE, getShapeForPosition(world, pos, facing, !sneaking));
}
public static Shape getShapeForPosition(BlockGetter world, BlockPos pos, Direction facing, boolean extracting) {
BlockPos posBelow = pos.below();
BlockState stateBelow = world.getBlockState(posBelow);
Shape perpendicularState = extracting ? Shape.PUSHING : Shape.PULLING;
if (!AllBlocks.BELT.has(stateBelow))
return perpendicularState;
Direction movementFacing = stateBelow.getValue(BeltBlock.HORIZONTAL_FACING);
return movementFacing.getAxis() != facing.getAxis() ? perpendicularState : Shape.RETRACTED;
}
@Override
public ItemStack getCloneItemStack(BlockState state, HitResult target, BlockGetter world, BlockPos pos,
Player player) {
return parent.asStack();
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighbour, LevelAccessor world,
BlockPos pos, BlockPos p_196271_6_) {
if (!isOnValidBelt(state, world, pos)) {
BlockState parentState = parent.getDefaultState();
if (state.getOptionalValue(POWERED)
.orElse(false))
parentState = parentState.setValue(POWERED, true);
if (state.getValue(SHAPE) == Shape.PUSHING)
parentState = parentState.setValue(FunnelBlock.EXTRACTING, true);
return parentState.setValue(FunnelBlock.FACING, state.getValue(HORIZONTAL_FACING));
}
Shape updatedShape =
getShapeForPosition(world, pos, state.getValue(HORIZONTAL_FACING), state.getValue(SHAPE) == Shape.PUSHING);
Shape currentShape = state.getValue(SHAPE);
if (updatedShape == currentShape)
return state;
// Don't revert wrenched states
if (updatedShape == Shape.PUSHING && currentShape == Shape.PULLING)
return state;
if (updatedShape == Shape.RETRACTED && currentShape == Shape.EXTENDED)
return state;
return state.setValue(SHAPE, updatedShape);
}
public static boolean isOnValidBelt(BlockState state, LevelReader world, BlockPos pos) {
BlockState stateBelow = world.getBlockState(pos.below());
if ((stateBelow.getBlock() instanceof BeltBlock))
return BeltBlock.canTransportObjects(stateBelow);
DirectBeltInputBehaviour directBeltInputBehaviour =
TileEntityBehaviour.get(world, pos.below(), DirectBeltInputBehaviour.TYPE);
if (directBeltInputBehaviour == null)
return false;
return directBeltInputBehaviour.canSupportBeltFunnels();
}
@Override
public InteractionResult onWrenched(BlockState state, UseOnContext context) {
Level world = context.getLevel();
if (world.isClientSide)
return InteractionResult.SUCCESS;
Shape shape = state.getValue(SHAPE);
Shape newShape = shape;
if (shape == Shape.PULLING)
newShape = Shape.PUSHING;
else if (shape == Shape.PUSHING)
newShape = Shape.PULLING;
else if (shape == Shape.EXTENDED)
newShape = Shape.RETRACTED;
else if (shape == Shape.RETRACTED) {
BlockState belt = world.getBlockState(context.getClickedPos()
.below());
if (belt.getBlock() instanceof BeltBlock && belt.getValue(BeltBlock.SLOPE) != BeltSlope.HORIZONTAL)
newShape = Shape.RETRACTED;
else
newShape = Shape.EXTENDED;
}
if (newShape == shape)
return InteractionResult.SUCCESS;
world.setBlockAndUpdate(context.getClickedPos(), state.setValue(SHAPE, newShape));
if (newShape == Shape.EXTENDED) {
Direction facing = state.getValue(HORIZONTAL_FACING);
BlockState opposite = world.getBlockState(context.getClickedPos()
.relative(facing));
if (opposite.getBlock() instanceof BeltFunnelBlock && opposite.getValue(SHAPE) == Shape.EXTENDED
&& opposite.getValue(HORIZONTAL_FACING) == facing.getOpposite())
AllAdvancements.FUNNEL_KISS.awardTo(context.getPlayer());
}
return InteractionResult.SUCCESS;
}
@Override
public ItemRequirement getRequiredItems(BlockState state, BlockEntity te) {
return ItemRequirement.of(parent.getDefaultState(), te);
}
}