Belt funnel & World funnel implementation

- Funnels on belts can now extract and insert items according to their setting and filter
- Funnels can now push/pull items to/from a depot
- Fixed creative crates not marking their inventory as removed when destroyed
- Funnels can now pick up items colliding with their front face
- When holding a funnel the game will now always prefer placement over block interaction
- Fixed items not able to be held in place by tunnels
This commit is contained in:
simibubi 2020-06-28 13:31:11 +02:00
parent f7ad748a09
commit ce1c5d8697
13 changed files with 314 additions and 29 deletions

View file

@ -69,6 +69,10 @@ public class AllShapes {
.add(1, 6, 1, 15, 10, 15)
.add(0, 10, 0, 16, 16, 16)
.forDirectional(UP),
REALITY_FUNNEL_COLLISION = shape(3, -2, 3, 13, 2, 13).add(2, 2, 2, 14, 6, 14)
.add(1, 6, 1, 15, 10, 15)
.add(0, 10, 0, 16, 13, 16)
.forDirectional(UP),
CHUTE_FUNNEL = shape(3, -2, 3, 13, 2, 13).add(2, 2, 2, 14, 6, 14)
.add(1, 5, 1, 15, 16, 15)
.add(0, 8, 0, 16, 14, 16)

View file

@ -338,11 +338,12 @@ public class BeltTileEntity extends KineticTileEntity {
return inventory;
}
private void applyToAllItems(Function<TransportedItemStack, List<TransportedItemStack>> processFunction) {
private void applyToAllItems(float maxDistanceFromCenter,
Function<TransportedItemStack, List<TransportedItemStack>> processFunction) {
BeltTileEntity controller = getControllerTE();
if (controller != null)
controller.getInventory()
.applyToEachWithin(index + .5f, .51f, processFunction);
.applyToEachWithin(index + .5f, maxDistanceFromCenter, processFunction);
}
private Vec3d getWorldPositionOf(TransportedItemStack transported) {

View file

@ -0,0 +1,63 @@
package com.simibubi.create.content.contraptions.relays.belt.transport;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.relays.belt.BeltHelper;
import com.simibubi.create.content.logistics.block.realityFunnel.BeltFunnelBlock;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.inventory.InsertingBehaviour;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
public class BeltFunnelInteractionHandler {
public static boolean checkForFunnels(BeltInventory beltInventory, TransportedItemStack currentItem,
float nextOffset) {
boolean beltMovementPositive = beltInventory.beltMovementPositive;
int firstUpcomingSegment = (int) (currentItem.beltPosition + (beltMovementPositive ? .5f : -.5f));
int step = beltMovementPositive ? 1 : -1;
firstUpcomingSegment = MathHelper.clamp(firstUpcomingSegment, 0, beltInventory.belt.beltLength - 1);
for (int segment = firstUpcomingSegment; beltMovementPositive ? segment + .5f <= nextOffset
: segment + .5f >= nextOffset; segment += step) {
BlockPos funnelPos = BeltHelper.getPositionForOffset(beltInventory.belt, segment)
.up();
World world = beltInventory.belt.getWorld();
BlockState funnelState = world.getBlockState(funnelPos);
if (!AllBlocks.BELT_FUNNEL.has(funnelState))
continue;
if (funnelState.get(BeltFunnelBlock.HORIZONTAL_FACING) != beltInventory.belt.getMovementFacing()
.getOpposite())
continue;
currentItem.beltPosition = segment + .5f;
if (funnelState.get(BeltFunnelBlock.PUSHING))
return true;
if (funnelState.get(BeltFunnelBlock.POWERED))
return true;
InsertingBehaviour behaviour = TileEntityBehaviour.get(world, funnelPos, InsertingBehaviour.TYPE);
FilteringBehaviour filtering = TileEntityBehaviour.get(world, funnelPos, FilteringBehaviour.TYPE);
if (behaviour == null || world.isRemote)
return true;
if (filtering != null && !filtering.test(currentItem.stack))
return true;
ItemStack before = currentItem.stack.copy();
ItemStack remainder = behaviour.insert(before, false);
if (before.equals(remainder, false))
return true;
currentItem.stack = remainder;
beltInventory.belt.sendData();
return true;
}
return false;
}
}

View file

@ -138,7 +138,12 @@ public class BeltInventory {
}
// Belt Tunnels
BeltTunnelInteractionHandler.flapTunnelsAndCheckIfStuck(this, currentItem, nextOffset);
if (BeltTunnelInteractionHandler.flapTunnelsAndCheckIfStuck(this, currentItem, nextOffset))
continue;
// Belt Funnels
if (BeltFunnelInteractionHandler.checkForFunnels(this, currentItem, nextOffset))
continue;
// Apply Movement
currentItem.beltPosition += limitedMovement;
@ -154,10 +159,8 @@ public class BeltInventory {
int lastOffset = beltMovementPositive ? belt.beltLength - 1 : 0;
BlockPos nextPosition = BeltHelper.getPositionForOffset(belt, beltMovementPositive ? belt.beltLength : -1);
if (ending == Ending.FUNNEL) {
// TODO
if (ending == Ending.FUNNEL)
continue;
}
if (ending == Ending.INSERT) {
DirectBeltInputBehaviour inputBehaviour =
@ -267,7 +270,7 @@ public class BeltInventory {
}
private enum Ending {
UNRESOLVED(0), EJECT(0), INSERT(.25f), FUNNEL(.35f), BLOCKED(.45f);
UNRESOLVED(0), EJECT(0), INSERT(.25f), FUNNEL(.5f), BLOCKED(.45f);
private float margin;

View file

@ -28,8 +28,10 @@ public class BeltTunnelInteractionHandler {
if (!beltInventory.beltMovementPositive && nextOffset == 0)
upcomingSegment = -1;
if (currentSegment != upcomingSegment) {
if (stuckAtTunnel(beltInventory, upcomingSegment, current.stack, movementFacing))
if (stuckAtTunnel(beltInventory, upcomingSegment, current.stack, movementFacing)) {
current.beltPosition = currentSegment + (beltInventory.beltMovementPositive ? .99f : -.01f);
return true;
}
if (!beltInventory.belt.getWorld().isRemote) {
flapTunnel(beltInventory, currentSegment, movementFacing, false);
flapTunnel(beltInventory, upcomingSegment, movementFacing.getOpposite(), true);

View file

@ -148,9 +148,12 @@ public class DepotTileEntity extends SmartTileEntity {
return empty;
}
private void applyToAllItems(Function<TransportedItemStack, List<TransportedItemStack>> processFunction) {
private void applyToAllItems(float maxDistanceFromCentre,
Function<TransportedItemStack, List<TransportedItemStack>> processFunction) {
if (heldItem == null)
return;
if (.5f - heldItem.beltPosition > maxDistanceFromCentre)
return;
boolean dirty = false;
List<TransportedItemStack> toBeAdded = new ArrayList<>();
@ -190,7 +193,8 @@ public class DepotTileEntity extends SmartTileEntity {
public boolean isOutputEmpty() {
for (int i = 0; i < processingOutputBuffer.getSlots(); i++)
if (!processingOutputBuffer.getStackInSlot(i).isEmpty())
if (!processingOutputBuffer.getStackInSlot(i)
.isEmpty())
return false;
return true;
}

View file

@ -57,6 +57,12 @@ public class CreativeCrateTileEntity extends CrateTileEntity {
otherCrate.filter.setFilter(filter);
}
@Override
public void remove() {
if (itemHandler != null)
itemHandler.invalidate();
}
private CreativeCrateTileEntity getOtherCrate() {
if (!AllBlocks.CREATIVE_CRATE.has(getBlockState()))
return null;
@ -73,7 +79,8 @@ public class CreativeCrateTileEntity extends CrateTileEntity {
if (otherCrate == null)
return;
filter.withCallback($ -> {});
filter.withCallback($ -> {
});
filter.setFilter(otherCrate.filter.getFilter());
filter.withCallback(this::filterChanged);
}
@ -90,7 +97,8 @@ public class CreativeCrateTileEntity extends CrateTileEntity {
@Override
protected void rotate(BlockState state, MatrixStack ms) {
MatrixStacker.of(ms).rotateX(90);
MatrixStacker.of(ms)
.rotateX(90);
}
@Override

View file

@ -1,8 +1,10 @@
package com.simibubi.create.content.logistics.block.realityFunnel;
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.BeltBlock.Slope;
import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelBlock;
import com.simibubi.create.content.logistics.block.depot.DepotBlock;
import com.simibubi.create.foundation.utility.Lang;
@ -10,9 +12,11 @@ import com.simibubi.create.foundation.utility.VoxelShaper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.EnumProperty;
import net.minecraft.state.IProperty;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.IStringSerializable;
import net.minecraft.util.math.BlockPos;
@ -22,6 +26,7 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.ILightReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
public class BeltFunnelBlock extends HorizontalInteractionFunnelBlock {
@ -47,6 +52,26 @@ public class BeltFunnelBlock extends HorizontalInteractionFunnelBlock {
setDefaultState(getDefaultState().with(SHAPE, Shape.RETRACTED));
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext ctx) {
BlockState state = super.getStateForPlacement(ctx);
World world = ctx.getWorld();
BlockPos posBelow = ctx.getPos()
.down();
BlockState stateBelow = world.getBlockState(posBelow);
if (!AllBlocks.BELT.has(stateBelow))
return state;
TileEntity teBelow = world.getTileEntity(posBelow);
if (teBelow == null || !(teBelow instanceof BeltTileEntity))
return state;
BeltTileEntity beltTileEntity = (BeltTileEntity) teBelow;
if (beltTileEntity.getSpeed() == 0)
return state;
Direction movementFacing = beltTileEntity.getMovementFacing();
Direction funnelFacing = ctx.getFace();
return state.with(PUSHING, movementFacing == funnelFacing);
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> p_206840_1_) {
super.fillStateContainer(p_206840_1_.add(SHAPE));

View file

@ -10,13 +10,25 @@ import net.minecraft.util.Direction;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.Event.Result;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class FunnelItem extends BlockItem {
public FunnelItem(Block p_i48527_1_, Properties p_i48527_2_) {
super(p_i48527_1_, p_i48527_2_);
}
@SubscribeEvent
public static void funnelItemAlwaysPlacesWhenUsed(PlayerInteractEvent.RightClickBlock event) {
if (event.getItemStack()
.getItem() instanceof FunnelItem)
event.setUseBlock(Result.DENY);
}
@Override
protected BlockState getStateForPlacement(BlockItemUseContext ctx) {
BlockState state = super.getStateForPlacement(ctx);

View file

@ -6,17 +6,27 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.logistics.block.chute.ChuteBlock;
import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.block.ProperDirectionalBlock;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.inventory.InsertingBehaviour;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
@ -24,7 +34,7 @@ import net.minecraft.world.IWorld;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
public class RealityFunnelBlock extends ProperDirectionalBlock {
public class RealityFunnelBlock extends ProperDirectionalBlock implements ITE<RealityFunnelTileEntity> {
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
@ -48,6 +58,39 @@ public class RealityFunnelBlock extends ProperDirectionalBlock {
.isBlockPowered(context.getPos()));
}
@Override
public void onEntityCollision(BlockState state, World worldIn, BlockPos pos, Entity entityIn) {
if (worldIn.isRemote)
return;
if (!(entityIn instanceof ItemEntity))
return;
if (state.get(POWERED))
return;
ItemEntity itemEntity = (ItemEntity) entityIn;
Direction direction = state.get(FACING);
Vec3d diff = entityIn.getPositionVec()
.subtract(VecHelper.getCenterOf(pos));
double projectedDiff = direction.getAxis()
.getCoordinate(diff.x, diff.y, diff.z);
if (projectedDiff < 0 == (direction.getAxisDirection() == AxisDirection.POSITIVE))
return;
FilteringBehaviour filter = TileEntityBehaviour.get(worldIn, pos, FilteringBehaviour.TYPE);
InsertingBehaviour inserter = TileEntityBehaviour.get(worldIn, pos, InsertingBehaviour.TYPE);
if (inserter == null)
return;
ItemStack toInsert = itemEntity.getItem();
if (filter != null && !filter.test(toInsert))
return;
ItemStack remainder = inserter.insert(toInsert, false);
if (remainder.isEmpty())
itemEntity.remove();
if (remainder.getCount() < toInsert.getCount())
itemEntity.setItem(remainder);
}
@Override
public boolean hasTileEntity(BlockState state) {
return true;
@ -64,11 +107,17 @@ public class RealityFunnelBlock extends ProperDirectionalBlock {
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader p_220053_2_, BlockPos p_220053_3_,
ISelectionContext p_220053_4_) {
public VoxelShape getShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext context) {
return AllShapes.REALITY_FUNNEL.get(state.get(FACING));
}
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext context) {
if (context.getEntity() instanceof ItemEntity)
return AllShapes.REALITY_FUNNEL_COLLISION.get(state.get(FACING));
return getShape(state, world, pos, context);
}
@Override
public BlockState updatePostPlacement(BlockState state, Direction direction, BlockState p_196271_3_, IWorld world,
BlockPos pos, BlockPos p_196271_6_) {
@ -131,8 +180,7 @@ public class RealityFunnelBlock extends ProperDirectionalBlock {
@Override
public void onReplaced(BlockState p_196243_1_, World p_196243_2_, BlockPos p_196243_3_, BlockState p_196243_4_,
boolean p_196243_5_) {
if (p_196243_1_.hasTileEntity()
&& (p_196243_1_.getBlock() != p_196243_4_.getBlock() && !isFunnel(p_196243_4_)
if (p_196243_1_.hasTileEntity() && (p_196243_1_.getBlock() != p_196243_4_.getBlock() && !isFunnel(p_196243_4_)
|| !p_196243_4_.hasTileEntity())) {
p_196243_2_.removeTileEntity(p_196243_3_);
}
@ -144,4 +192,9 @@ public class RealityFunnelBlock extends ProperDirectionalBlock {
|| state.getBlock() instanceof HorizontalInteractionFunnelBlock;
}
@Override
public Class<RealityFunnelTileEntity> getTileEntityClass() {
return RealityFunnelTileEntity.class;
}
}

View file

@ -1,31 +1,130 @@
package com.simibubi.create.content.logistics.block.realityFunnel;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.relays.belt.transport.TransportedItemStack;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.inventory.ExtractingBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.inventory.InsertingBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.inventory.InventoryManagementBehaviour.Attachments;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
public class RealityFunnelTileEntity extends SmartTileEntity {
private FilteringBehaviour filtering;
private InsertingBehaviour inserting;
private ExtractingBehaviour extracting;
static enum Mode {
INVALID, PAUSED, COLLECT, BELT, CHUTE_SIDE, CHUTE_END
}
public RealityFunnelTileEntity(TileEntityType<?> tileEntityTypeIn) {
super(tileEntityTypeIn);
}
public Mode determineCurrentMode() {
BlockState state = getBlockState();
if (!RealityFunnelBlock.isFunnel(state))
return Mode.INVALID;
if (state.get(BlockStateProperties.POWERED))
return Mode.PAUSED;
if (AllBlocks.BELT_FUNNEL.has(state))
return Mode.BELT;
if (AllBlocks.CHUTE_FUNNEL.has(state))
return Mode.CHUTE_SIDE;
Direction facing = RealityFunnelBlock.getFunnelFacing(state);
BlockState input = world.getBlockState(pos.offset(facing));
if (AllBlocks.CHUTE.has(input))
return Mode.CHUTE_END;
return Mode.COLLECT;
}
@Override
public void tick() {
super.tick();
Mode mode = determineCurrentMode();
if (mode == Mode.BELT)
tickAsBeltFunnel();
}
public void tickAsBeltFunnel() {
BlockState blockState = getBlockState();
Direction facing = blockState.get(BeltFunnelBlock.HORIZONTAL_FACING);
if (world.isRemote)
return;
if (!blockState.get(BeltFunnelBlock.PUSHING)) {
// Belts handle insertion from their side
if (AllBlocks.BELT.has(world.getBlockState(pos.down())))
return;
TransportedItemStackHandlerBehaviour handler =
TileEntityBehaviour.get(world, pos.down(), TransportedItemStackHandlerBehaviour.TYPE);
if (handler == null)
return;
handler.handleCenteredProcessingOnAllItems(1 / 32f, this::collectFromHandler);
return;
}
DirectBeltInputBehaviour inputBehaviour =
TileEntityBehaviour.get(world, pos.down(), DirectBeltInputBehaviour.TYPE);
if (inputBehaviour == null)
return;
if (!inputBehaviour.canInsertFromSide(facing))
return;
extracting.setCallback(stack -> inputBehaviour.handleInsertion(stack, facing, false));
extracting.withAdditionalFilter(stack -> inputBehaviour.handleInsertion(stack, facing, true)
.isEmpty());
extracting.extract();
}
private List<TransportedItemStack> collectFromHandler(TransportedItemStack stack) {
ItemStack toInsert = stack.stack.copy();
if (!filtering.test(toInsert))
return null;
ItemStack remainder = inserting.insert(toInsert, false);
if (remainder.equals(stack.stack, false))
return null;
List<TransportedItemStack> list = new ArrayList<>();
if (remainder.isEmpty())
return list;
TransportedItemStack changed = stack.copy();
changed.stack = remainder;
list.add(changed);
return list;
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
Supplier<List<Pair<BlockPos, Direction>>> direction =
Attachments.toward(() -> RealityFunnelBlock.getFunnelFacing(getBlockState())
.getOpposite());
inserting = new InsertingBehaviour(this, direction);
extracting = new ExtractingBehaviour(this, direction);
filtering = new FilteringBehaviour(this, new FunnelFilterSlotPositioning());
behaviours.add(filtering);
inserting =
new InsertingBehaviour(this, Attachments.toward(() -> RealityFunnelBlock.getFunnelFacing(getBlockState())));
behaviours.add(inserting);
behaviours.add(extracting);
}
@Override

View file

@ -29,7 +29,12 @@ public class TransportedItemStackHandlerBehaviour extends TileEntityBehaviour {
}
public void handleProcessingOnAllItems(Function<TransportedItemStack, List<TransportedItemStack>> processFunction) {
this.processingCallback.applyToAllItems(processFunction);
handleCenteredProcessingOnAllItems(.51f, processFunction);
}
public void handleCenteredProcessingOnAllItems(float maxDistanceFromCenter,
Function<TransportedItemStack, List<TransportedItemStack>> processFunction) {
this.processingCallback.applyToAllItems(maxDistanceFromCenter, processFunction);
}
public Vec3d getWorldPositionOf(TransportedItemStack transported) {
@ -43,7 +48,8 @@ public class TransportedItemStackHandlerBehaviour extends TileEntityBehaviour {
@FunctionalInterface
public interface ProcessingCallback {
public void applyToAllItems(Function<TransportedItemStack, List<TransportedItemStack>> processFunction);
public void applyToAllItems(float maxDistanceFromCenter,
Function<TransportedItemStack, List<TransportedItemStack>> processFunction);
}
@FunctionalInterface

View file

@ -27,6 +27,11 @@ public class ExtractingBehaviour extends InventoryManagementBehaviour {
private Predicate<ItemStack> customFilter;
private Consumer<ItemStack> callback;
public ExtractingBehaviour(SmartTileEntity te, Supplier<List<Pair<BlockPos, Direction>>> attachments) {
this(te, attachments, item -> {
});
}
public ExtractingBehaviour(SmartTileEntity te, Supplier<List<Pair<BlockPos, Direction>>> attachments,
Consumer<ItemStack> onExtract) {
super(te, attachments);