diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java index f4bd4d005..11aa1575e 100644 --- a/src/main/java/com/simibubi/create/AllItems.java +++ b/src/main/java/com/simibubi/create/AllItems.java @@ -13,11 +13,9 @@ import com.simibubi.create.modules.symmetry.client.SymmetryWandModel; import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.ModelResourceLocation; -import net.minecraft.client.renderer.model.ModelRotation; import net.minecraft.client.renderer.tileentity.ItemStackTileEntityRenderer; import net.minecraft.item.Item; import net.minecraft.item.Item.Properties; -import net.minecraft.util.ResourceLocation; import net.minecraft.item.ItemStack; import net.minecraft.item.Rarity; import net.minecraftforge.api.distmarker.Dist; diff --git a/src/main/java/com/simibubi/create/modules/kinetics/relays/BeltBlock.java b/src/main/java/com/simibubi/create/modules/kinetics/relays/BeltBlock.java index 1b6c7c171..d3127223b 100644 --- a/src/main/java/com/simibubi/create/modules/kinetics/relays/BeltBlock.java +++ b/src/main/java/com/simibubi/create/modules/kinetics/relays/BeltBlock.java @@ -6,10 +6,13 @@ import java.util.List; import com.simibubi.create.AllBlocks; import com.simibubi.create.foundation.block.IWithoutBlockItem; import com.simibubi.create.modules.kinetics.base.HorizontalKineticBlock; +import com.simibubi.create.modules.kinetics.relays.BeltTileEntity.TransportedEntityInfo; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.state.EnumProperty; import net.minecraft.state.IProperty; import net.minecraft.state.StateContainer.Builder; @@ -20,7 +23,13 @@ import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.IStringSerializable; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.util.math.shapes.IBooleanFunction; +import net.minecraft.util.math.shapes.ISelectionContext; +import net.minecraft.util.math.shapes.VoxelShape; +import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.IBlockReader; +import net.minecraft.world.IWorldReader; import net.minecraft.world.World; public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockItem { @@ -28,11 +37,100 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt public static final IProperty SLOPE = EnumProperty.create("slope", Slope.class); public static final IProperty PART = EnumProperty.create("part", Part.class); + private static final VoxelShape FULL = makeCuboidShape(0, 0, 0, 16, 16, 16), + FLAT_STRAIGHT_X = makeCuboidShape(1, 3, 0, 15, 13, 16), + FLAT_STRAIGHT_Z = makeCuboidShape(0, 3, 1, 16, 13, 15), + VERTICAL_STRAIGHT_X = makeCuboidShape(3, 0, 1, 13, 16, 15), + VERTICAL_STRAIGHT_Z = makeCuboidShape(1, 0, 3, 15, 16, 13), + + SLOPE_END_EAST = makeCuboidShape(0, 3, 1, 10, 13, 15), + SLOPE_END_WEST = makeCuboidShape(6, 3, 1, 16, 13, 15), + SLOPE_END_SOUTH = makeCuboidShape(1, 3, 0, 15, 13, 10), + SLOPE_END_NORTH = makeCuboidShape(1, 3, 6, 15, 13, 16), + + SLOPE_BUILDING_BLOCK_X = makeCuboidShape(5, 5, 1, 11, 11, 15), + SLOPE_BUILDING_BLOCK_Z = makeCuboidShape(1, 5, 5, 15, 11, 11), + + SLOPE_UPWARD_END_EAST = VoxelShapes.or(SLOPE_END_EAST, createHalfSlope(Direction.EAST, false)), + SLOPE_UPWARD_END_WEST = VoxelShapes.or(SLOPE_END_WEST, createHalfSlope(Direction.WEST, false)), + SLOPE_UPWARD_END_SOUTH = VoxelShapes.or(SLOPE_END_SOUTH, createHalfSlope(Direction.SOUTH, false)), + SLOPE_UPWARD_END_NORTH = VoxelShapes.or(SLOPE_END_NORTH, createHalfSlope(Direction.NORTH, false)), + + SLOPE_DOWNWARD_END_EAST = VoxelShapes.or(SLOPE_END_EAST, createHalfSlope(Direction.EAST, true)), + SLOPE_DOWNWARD_END_WEST = VoxelShapes.or(SLOPE_END_WEST, createHalfSlope(Direction.WEST, true)), + SLOPE_DOWNWARD_END_SOUTH = VoxelShapes.or(SLOPE_END_SOUTH, createHalfSlope(Direction.SOUTH, true)), + SLOPE_DOWNWARD_END_NORTH = VoxelShapes.or(SLOPE_END_NORTH, createHalfSlope(Direction.NORTH, true)), + + SLOPE_EAST = createSlope(Direction.EAST), SLOPE_WEST = createSlope(Direction.WEST), + SLOPE_NORTH = createSlope(Direction.NORTH), SLOPE_SOUTH = createSlope(Direction.SOUTH); + public BeltBlock() { super(Properties.from(Blocks.BROWN_WOOL)); setDefaultState(getDefaultState().with(SLOPE, Slope.HORIZONTAL).with(PART, Part.START)); } + @Override + public void onLanded(IBlockReader worldIn, Entity entityIn) { + super.onLanded(worldIn, entityIn); + + if (entityIn instanceof PlayerEntity && entityIn.isSneaking()) + return; + if (entityIn instanceof PlayerEntity && ((PlayerEntity) entityIn).moveVertical > 0) + return; + + BeltTileEntity belt = null; + BlockPos entityPosition = entityIn.getPosition(); + + if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition))) + belt = (BeltTileEntity) worldIn.getTileEntity(entityPosition); + else if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition.down()))) + belt = (BeltTileEntity) worldIn.getTileEntity(entityPosition.down()); + + if (belt == null || !belt.hasSource()) + return; + + BeltTileEntity controller = (BeltTileEntity) worldIn.getTileEntity(belt.getController()); + + if (controller == null) + return; + + if (controller.passengers.containsKey(entityIn)) + controller.passengers.get(entityIn).refresh(belt.getPos(), belt.getBlockState()); + else + controller.passengers.put(entityIn, new TransportedEntityInfo(belt.getPos(), belt.getBlockState())); + } + + @Override + public float getSlipperiness(BlockState state, IWorldReader world, BlockPos pos, Entity entity) { + return super.getSlipperiness(state, world, pos, entity); + } + + @Override + public void onEntityCollision(BlockState state, World worldIn, BlockPos pos, Entity entityIn) { + if (entityIn instanceof PlayerEntity && entityIn.isSneaking()) + return; + if (entityIn instanceof PlayerEntity && ((PlayerEntity) entityIn).moveVertical > 0) + return; + + BeltTileEntity belt = null; + belt = (BeltTileEntity) worldIn.getTileEntity(pos); + + if (belt == null || !belt.hasSource()) + return; + + BeltTileEntity controller = (BeltTileEntity) worldIn.getTileEntity(belt.getController()); + + if (controller == null) + return; + + if (controller.passengers.containsKey(entityIn)) { + TransportedEntityInfo transportedEntityInfo = controller.passengers.get(entityIn); + if (transportedEntityInfo.ticksSinceLastCollision != 0 || pos.equals(entityIn.getPosition())) + transportedEntityInfo.refresh(pos, state); + } else + controller.passengers.put(entityIn, new TransportedEntityInfo(pos, state)); + } + @Override protected void fillStateContainer(Builder builder) { builder.add(SLOPE, PART); @@ -44,6 +142,49 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt return true; } + @Override + public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) { + Direction facing = state.get(HORIZONTAL_FACING); + Axis axis = facing.getAxis(); + Part part = state.get(PART); + Slope slope = state.get(SLOPE); + + if (slope == Slope.HORIZONTAL) + return axis == Axis.Z ? FLAT_STRAIGHT_X : FLAT_STRAIGHT_Z; + if (slope == Slope.VERTICAL) + return axis == Axis.X ? VERTICAL_STRAIGHT_X : VERTICAL_STRAIGHT_Z; + + if (part != Part.MIDDLE) { + if (part == Part.START) + slope = slope == Slope.UPWARD ? Slope.DOWNWARD : Slope.UPWARD; + else + facing = facing.getOpposite(); + + if (facing == Direction.NORTH) + return slope == Slope.UPWARD ? SLOPE_UPWARD_END_NORTH : SLOPE_DOWNWARD_END_NORTH; + if (facing == Direction.SOUTH) + return slope == Slope.UPWARD ? SLOPE_UPWARD_END_SOUTH : SLOPE_DOWNWARD_END_SOUTH; + if (facing == Direction.EAST) + return slope == Slope.UPWARD ? SLOPE_UPWARD_END_EAST : SLOPE_DOWNWARD_END_EAST; + if (facing == Direction.WEST) + return slope == Slope.UPWARD ? SLOPE_UPWARD_END_WEST : SLOPE_DOWNWARD_END_WEST; + } + + if (slope == Slope.DOWNWARD) + facing = facing.getOpposite(); + + if (facing == Direction.NORTH) + return SLOPE_NORTH; + if (facing == Direction.SOUTH) + return SLOPE_SOUTH; + if (facing == Direction.EAST) + return SLOPE_EAST; + if (facing == Direction.WEST) + return SLOPE_WEST; + + return FULL; + } + @Override public TileEntity createTileEntity(BlockState state, IBlockReader world) { return new BeltTileEntity(); @@ -67,7 +208,7 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt BlockPos controller = beltEntity.getController(); beltEntity.setSource(null); beltEntity.remove(); - + int limit = 1000; BlockPos toDestroy = controller; BlockState destroyedBlock = null; @@ -83,7 +224,7 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt boolean hasPulley = te.hasPulley(); te.setSource(null); te.remove(); - + if (hasPulley) { worldIn.setBlockState(toDestroy, AllBlocks.AXIS.get().getDefaultState() .with(BlockStateProperties.AXIS, getRotationAxis(destroyedBlock)), 3); @@ -142,6 +283,18 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt } } + public static boolean isUpperEnd(BlockState state, float speed) { + Direction facing = state.get(HORIZONTAL_FACING); + if (state.get(SLOPE) == Slope.UPWARD && state.get(PART) == Part.END) { + return facing.getAxisDirection().getOffset() * Math.signum(speed) == (facing.getAxis() == Axis.X ? -1 : 1); + } + if (state.get(SLOPE) == Slope.DOWNWARD && state.get(PART) == Part.START) { + return facing.getAxisDirection().getOffset() * Math.signum(speed) == (facing.getAxis() == Axis.Z ? -1 : 1); + } + + return false; + } + public static List getBeltChain(World world, BlockPos controllerPos) { List positions = new LinkedList<>(); @@ -173,4 +326,33 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt return positions; } + protected static VoxelShape createSlope(Direction facing) { + return VoxelShapes.or(createHalfSlope(facing.getOpposite(), false), createHalfSlope(facing, true)); + } + + protected static VoxelShape createHalfSlope(Direction facing, boolean upward) { + VoxelShape shape = VoxelShapes.empty(); + VoxelShape buildingBlock = facing.getAxis() == Axis.X ? SLOPE_BUILDING_BLOCK_X : SLOPE_BUILDING_BLOCK_Z; + Vec3i directionVec = facing.getDirectionVec(); + + int x = directionVec.getX(); + int y = upward ? 1 : -1; + int z = directionVec.getZ(); + + for (int segment = 0; segment < 6; segment++) + shape = VoxelShapes.or(shape, + buildingBlock.withOffset(x * segment / 16f, y * segment / 16f, z * segment / 16f)); + + if (!upward) + return shape; + + VoxelShape mask = makeCuboidShape(0, -8, 0, 16, 24, 16); + for (int segment = 6; segment < 11; segment++) + shape = VoxelShapes.or(shape, + VoxelShapes.combine(mask, + buildingBlock.withOffset(x * segment / 16f, y * segment / 16f, z * segment / 16f), + IBooleanFunction.AND)); + return shape; + } + } diff --git a/src/main/java/com/simibubi/create/modules/kinetics/relays/BeltTileEntity.java b/src/main/java/com/simibubi/create/modules/kinetics/relays/BeltTileEntity.java index 565dd9f9c..1ddf1fe25 100644 --- a/src/main/java/com/simibubi/create/modules/kinetics/relays/BeltTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/kinetics/relays/BeltTileEntity.java @@ -1,21 +1,64 @@ package com.simibubi.create.modules.kinetics.relays; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.simibubi.create.AllBlocks; import com.simibubi.create.AllTileEntities; import com.simibubi.create.modules.kinetics.base.KineticTileEntity; import com.simibubi.create.modules.kinetics.relays.BeltBlock.Part; +import com.simibubi.create.modules.kinetics.relays.BeltBlock.Slope; +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.MoverType; +import net.minecraft.entity.item.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.NBTUtil; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.tileentity.ITickableTileEntity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.Direction.Axis; +import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; -public class BeltTileEntity extends KineticTileEntity { +public class BeltTileEntity extends KineticTileEntity implements ITickableTileEntity { protected BlockPos controller; + public Map passengers; + + protected static class TransportedEntityInfo { + int ticksSinceLastCollision; + BlockPos lastCollidedPos; + BlockState lastCollidedState; + + public TransportedEntityInfo(BlockPos collision, BlockState belt) { + refresh(collision, belt); + } + + public void refresh(BlockPos collision, BlockState belt) { + ticksSinceLastCollision = 0; + lastCollidedPos = new BlockPos(collision).toImmutable(); + lastCollidedState = belt; + } + + public TransportedEntityInfo tick() { + ticksSinceLastCollision++; + return this; + } + } public BeltTileEntity() { super(AllTileEntities.BELT.type); controller = BlockPos.ZERO; + passengers = new HashMap<>(); } @Override @@ -48,4 +91,136 @@ public class BeltTileEntity extends KineticTileEntity { return getBlockState().get(BeltBlock.PART) == Part.END || getBlockState().get(BeltBlock.PART) == Part.START; } + @Override + public void tick() { + if (!isController()) + return; + + passengers.forEach((entity, info) -> { + transportEntity(entity, info); + }); + + List toRemove = new ArrayList<>(); + passengers.forEach((entity, info) -> { + if (!canTransport(entity)) + toRemove.add(entity); + if (info.ticksSinceLastCollision > 0) { + toRemove.add(entity); + } + info.tick(); + }); + toRemove.forEach(passengers::remove); + + if (speed == 0) + return; + } + + public void transportEntity(Entity entityIn, TransportedEntityInfo info) { + BlockPos pos = info.lastCollidedPos; + TileEntity te = world.getTileEntity(pos); + TileEntity tileEntityBelowPassenger = world.getTileEntity(entityIn.getPosition()); + BlockState blockState = info.lastCollidedState; + + boolean onEndingBelt = blockState.getBlock() instanceof BeltBlock && BeltBlock.isUpperEnd(blockState, speed); + Direction movementFacing = Direction.getFacingFromAxisDirection( + blockState.get(BlockStateProperties.HORIZONTAL_FACING).getAxis(), + speed < 0 ? AxisDirection.POSITIVE : AxisDirection.NEGATIVE); + + boolean hasBeltAdjacent = onEndingBelt + && AllBlocks.BELT.typeOf(world.getBlockState(pos.offset(movementFacing))); + boolean collidedWithBelt = te instanceof BeltTileEntity; + boolean betweenBelts = tileEntityBelowPassenger instanceof BeltTileEntity && tileEntityBelowPassenger != te; + + // Don't fight other Belts + if (!collidedWithBelt || betweenBelts) { + return; + } + + if (((KineticTileEntity) te).getSpeed() == 0) + return; + + if (entityIn.posY - .25f < pos.getY()) + return; + + if (entityIn instanceof LivingEntity) { + ((LivingEntity) entityIn).setIdleTime(20); + } + + final Direction beltFacing = blockState.get(BlockStateProperties.HORIZONTAL_FACING); + final Slope slope = blockState.get(BeltBlock.SLOPE); + final Axis axis = beltFacing.getAxis(); + float movementSpeed = ((KineticTileEntity) te).getSpeed() / 1600f; + final Direction movementDirection = Direction + .getFacingFromAxis(axis == Axis.X ? AxisDirection.NEGATIVE : AxisDirection.POSITIVE, axis); + + Vec3i centeringDirection = Direction.getFacingFromAxis(AxisDirection.POSITIVE, axis == Axis.X ? Axis.Z : Axis.X) + .getDirectionVec(); + Vec3d movement = new Vec3d(movementDirection.getDirectionVec()).scale(movementSpeed); + + double diffCenter = axis == Axis.Z ? (pos.getX() + .5f - entityIn.posX) : (pos.getZ() + .5f - entityIn.posZ); + if (Math.abs(diffCenter) > 48 / 64f) + return; + + Part part = blockState.get(BeltBlock.PART); + float top = 13 / 16f; + boolean onSlope = part == Part.MIDDLE + || part == (slope == Slope.UPWARD ? Part.END : Part.START) && entityIn.posY - pos.getY() < top + || part == (slope == Slope.UPWARD ? Part.START : Part.END) && entityIn.posY - pos.getY() > top; + + boolean movingDown = onSlope && slope == (movementFacing == beltFacing ? Slope.DOWNWARD : Slope.UPWARD); + boolean movingUp = onSlope && slope == (movementFacing == beltFacing ? Slope.UPWARD : Slope.DOWNWARD); + + if (beltFacing.getAxis() == Axis.Z) { + boolean b = movingDown; + movingDown = movingUp; + movingUp = b; + } + + if (movingUp) + movement = movement.add(0, Math.abs(axis.getCoordinate(movement.x, movement.y, movement.z)), 0); + if (movingDown) + movement = movement.add(0, -Math.abs(axis.getCoordinate(movement.x, movement.y, movement.z)), 0); + + Vec3d centering = new Vec3d(centeringDirection).scale(diffCenter * Math.min(Math.abs(movementSpeed), .1f) * 4); + movement = movement.add(centering); + + if (info.ticksSinceLastCollision > 0 && !betweenBelts && onEndingBelt && !hasBeltAdjacent) { + entityIn.setPosition(entityIn.posX, entityIn.posY + movement.y, entityIn.posZ); + float verticalMultiplier = entityIn instanceof ItemEntity ? .25f : 1; + if (movementSpeed > .25f) + movement = movement.add(0, Math.abs(movementSpeed) * verticalMultiplier, 0); + entityIn.setMotion(movement); + return; + } + + float step = entityIn.stepHeight; + entityIn.stepHeight = 1; + + if (movingUp) { + float minVelocity = entityIn instanceof ItemEntity ? .09f : .13f; + float yMovement = (float) (Math.signum(movementSpeed) * Math.max(Math.abs(movement.y), minVelocity)); + entityIn.move(MoverType.SELF, new Vec3d(0, yMovement, 0)); + entityIn.move(MoverType.SELF, movement.mul(1, 0, 1)); + } else if (movingDown) { + entityIn.move(MoverType.SELF, movement.mul(1, 0, 1)); + entityIn.move(MoverType.SELF, movement.mul(0, 1, 0)); + } else { + entityIn.move(MoverType.SELF, movement); + } + entityIn.stepHeight = step; + + if (!betweenBelts && onEndingBelt && !hasBeltAdjacent) { + entityIn.setMotion(movement); + } + } + + public boolean canTransport(Entity entity) { + if (!entity.isAlive()) + return false; + if (entity instanceof PlayerEntity && ((PlayerEntity) entity).isSneaking()) + return false; + + return true; + } + }