diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java index e8c7118e8..b5069679b 100644 --- a/src/main/java/com/simibubi/create/AllItems.java +++ b/src/main/java/com/simibubi/create/AllItems.java @@ -6,7 +6,7 @@ import com.simibubi.create.modules.IModule; import com.simibubi.create.modules.contraptions.WrenchItem; import com.simibubi.create.modules.contraptions.WrenchItemRenderer; import com.simibubi.create.modules.contraptions.relays.VerticalGearboxItem; -import com.simibubi.create.modules.contraptions.relays.belt.BeltItem; +import com.simibubi.create.modules.contraptions.relays.belt.BeltConnectorItem; import com.simibubi.create.modules.curiosities.ChromaticCompoundCubeItem; import com.simibubi.create.modules.curiosities.deforester.DeforesterItem; import com.simibubi.create.modules.curiosities.deforester.DeforesterItemRenderer; @@ -82,7 +82,7 @@ public enum AllItems { BLUEPRINT(new SchematicItem(standardItemProperties())), __CONTRAPTIONS__(), - BELT_CONNECTOR(new BeltItem(standardItemProperties())), + BELT_CONNECTOR(new BeltConnectorItem(standardItemProperties())), VERTICAL_GEARBOX(new VerticalGearboxItem(new Properties())), FLOUR(ingredient()), DOUGH(ingredient()), diff --git a/src/main/java/com/simibubi/create/ClientEvents.java b/src/main/java/com/simibubi/create/ClientEvents.java index d33ca1cff..f1ccba3d9 100644 --- a/src/main/java/com/simibubi/create/ClientEvents.java +++ b/src/main/java/com/simibubi/create/ClientEvents.java @@ -10,7 +10,7 @@ import com.simibubi.create.foundation.utility.TooltipHelper; import com.simibubi.create.modules.contraptions.KineticDebugger; import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.modules.contraptions.receivers.TurntableHandler; -import com.simibubi.create.modules.contraptions.relays.belt.BeltItemHandler; +import com.simibubi.create.modules.contraptions.relays.belt.BeltConnectorItemHandler; import net.minecraft.client.Minecraft; import net.minecraft.item.ItemStack; @@ -56,7 +56,7 @@ public class ClientEvents { public static void onGameTick() { CreateClient.gameTick(); - BeltItemHandler.gameTick(); + BeltConnectorItemHandler.gameTick(); } @SubscribeEvent diff --git a/src/main/java/com/simibubi/create/modules/contraptions/CachedBufferReloader.java b/src/main/java/com/simibubi/create/modules/contraptions/CachedBufferReloader.java index 862496802..899742dc0 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/CachedBufferReloader.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/CachedBufferReloader.java @@ -4,6 +4,7 @@ import com.simibubi.create.foundation.utility.ColoredIndicatorRenderer; import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.modules.contraptions.receivers.constructs.ContraptionRenderer; import com.simibubi.create.modules.contraptions.receivers.constructs.MechanicalBearingTileEntityRenderer; +import com.simibubi.create.modules.contraptions.relays.belt.FastItemRenderer; import net.minecraft.client.resources.ReloadListener; import net.minecraft.profiler.IProfiler; @@ -22,6 +23,7 @@ public class CachedBufferReloader extends ReloadListener { ContraptionRenderer.invalidateCache(); MechanicalBearingTileEntityRenderer.invalidateCache(); ColoredIndicatorRenderer.invalidateCache(); + FastItemRenderer.invalidateCache(); } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/RotationPropagator.java b/src/main/java/com/simibubi/create/modules/contraptions/RotationPropagator.java index d3a5b4c6d..f4f929ae7 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/RotationPropagator.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/RotationPropagator.java @@ -76,12 +76,6 @@ public class RotationPropagator { return connected ? 1 : 0; } - // Attached Fans - if (ENCASED_FAN.typeOf(stateFrom) && ENCASED_FAN.typeOf(stateTo)) { - if (stateFrom.get(AXIS) == stateTo.get(AXIS)) - return 1; - } - // Large Gear <-> Large Gear if (isLargeToLargeGear(stateFrom, stateTo, diff)) { Axis sourceAxis = stateFrom.get(AXIS); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/base/KineticTileEntityRenderer.java b/src/main/java/com/simibubi/create/modules/contraptions/base/KineticTileEntityRenderer.java index f678ae385..8013d74c0 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/base/KineticTileEntityRenderer.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/base/KineticTileEntityRenderer.java @@ -34,7 +34,7 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber; @EventBusSubscriber(value = Dist.CLIENT) public class KineticTileEntityRenderer extends TileEntityRendererFast { - protected static Map cachedBuffers; + public static Map cachedBuffers; public static boolean rainbowMode = false; public static class BlockModelSpinner extends BufferManipulator { diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltBlock.java index 799ab5847..e703e64bd 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltBlock.java @@ -9,12 +9,13 @@ import com.simibubi.create.foundation.block.IWithTileEntity; import com.simibubi.create.foundation.block.IWithoutBlockItem; import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock; -import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity.TransportedEntityInfo; +import com.simibubi.create.modules.contraptions.relays.belt.BeltMovementHandler.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.item.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.DyeColor; import net.minecraft.item.ItemStack; @@ -31,121 +32,90 @@ import net.minecraft.util.IStringSerializable; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.RayTraceResult; -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; import net.minecraftforge.common.Tags; +import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.items.ItemStackHandler; public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockItem, IWithTileEntity { 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 boolean hasShaftTowards(World world, BlockPos pos, BlockState state, Direction face) { + if (face.getAxis() != getRotationAxis(state)) + return false; + BeltTileEntity beltEntity = (BeltTileEntity) world.getTileEntity(pos); + return beltEntity != null && beltEntity.hasPulley(); + } + + @Override + public Axis getRotationAxis(BlockState state) { + return state.get(HORIZONTAL_FACING).rotateY().getAxis(); + } + @Override public ItemStack getPickBlock(BlockState state, RayTraceResult target, IBlockReader world, BlockPos pos, PlayerEntity player) { - return new ItemStack(AllItems.BELT_CONNECTOR.item); + return AllItems.BELT_CONNECTOR.asStack(); } @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(); + BlockPos beltPos = null; if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition))) - belt = (BeltTileEntity) worldIn.getTileEntity(entityPosition); + beltPos = entityPosition; else if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition.down()))) - belt = (BeltTileEntity) worldIn.getTileEntity(entityPosition.down()); - - if (belt == null || !belt.hasSource()) + beltPos = entityPosition.down(); + if (beltPos == null) + return; + if (!(worldIn instanceof World)) return; - BeltTileEntity controller = (BeltTileEntity) worldIn.getTileEntity(belt.getController()); - - if (controller == null) - return; - if (controller.passengers == 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); + onEntityCollision(worldIn.getBlockState(beltPos), (World) worldIn, beltPos, entityIn); } @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()) + if (entityIn instanceof PlayerEntity && entityIn.isSneaking()) return; + if (belt == null || belt.getSpeed() == 0) + return; + if (entityIn instanceof ItemEntity && entityIn.isAlive()) { + if (worldIn.isRemote) + return; + withTileEntityDo(worldIn, pos, te -> { + ItemEntity itemEntity = (ItemEntity) entityIn; + ItemStack remainder = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) + .orElseGet(() -> new ItemStackHandler(0)).insertItem(0, itemEntity.getItem().copy(), false); + if (remainder.isEmpty()) + itemEntity.remove(); + }); + return; + } BeltTileEntity controller = (BeltTileEntity) worldIn.getTileEntity(belt.getController()); - - if (controller == null) + if (controller == null || controller.passengers == null) return; - if (controller.passengers == 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); + TransportedEntityInfo info = controller.passengers.get(entityIn); + if (info.ticksSinceLastCollision != 0 || pos.equals(entityIn.getPosition())) + info.refresh(pos, state); } else controller.passengers.put(entityIn, new TransportedEntityInfo(pos, state)); } @@ -167,7 +137,12 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt return false; if (worldIn.isRemote) return true; - withTileEntityDo(worldIn, pos, te -> te.applyColor(DyeColor.getColor(heldItem))); + withTileEntityDo(worldIn, pos, te -> { + DyeColor dyeColor = DyeColor.getColor(heldItem); + if (dyeColor == null) + return; + te.applyColor(dyeColor); + }); if (!player.isCreative()) heldItem.shrink(1); return true; @@ -186,45 +161,7 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt @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; + return BeltShapes.getShape(state, worldIn, pos, context); } @Override @@ -308,19 +245,6 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt } - @Override - public boolean hasShaftTowards(World world, BlockPos pos, BlockState state, Direction face) { - if (face.getAxis() != getRotationAxis(state)) - return false; - BeltTileEntity beltEntity = (BeltTileEntity) world.getTileEntity(pos); - return beltEntity != null && beltEntity.hasPulley(); - } - - @Override - public Axis getRotationAxis(BlockState state) { - return state.get(HORIZONTAL_FACING).getAxis() == Axis.X ? Axis.Z : Axis.X; - } - public enum Slope implements IStringSerializable { HORIZONTAL, UPWARD, DOWNWARD, VERTICAL; @@ -339,18 +263,6 @@ 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<>(); @@ -382,33 +294,4 @@ 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/contraptions/relays/belt/BeltItem.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltConnectorItem.java similarity index 90% rename from src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltItem.java rename to src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltConnectorItem.java index 6760981ea..010db1740 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltItem.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltConnectorItem.java @@ -6,6 +6,7 @@ import java.util.List; import com.simibubi.create.AllBlocks; import com.simibubi.create.CreateConfig; import com.simibubi.create.modules.contraptions.base.KineticTileEntity; +import com.simibubi.create.modules.contraptions.relays.ShaftBlock; import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part; import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope; @@ -22,9 +23,9 @@ import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -public class BeltItem extends Item { +public class BeltConnectorItem extends Item { - public BeltItem(Properties properties) { + public BeltConnectorItem(Properties properties) { super(properties); } @@ -95,11 +96,22 @@ public class BeltItem extends Item { List beltsToCreate = getBeltChainBetween(start, end, slope, facing); BlockState beltBlock = AllBlocks.BELT.get().getDefaultState(); + int index = 0; for (BlockPos pos : beltsToCreate) { BeltBlock.Part part = pos.equals(start) ? Part.START : pos.equals(end) ? Part.END : Part.MIDDLE; + boolean pulley = AllBlocks.SHAFT.typeOf(world.getBlockState(pos)); world.setBlockState(pos, beltBlock.with(BeltBlock.SLOPE, slope).with(BeltBlock.PART, part) .with(BeltBlock.HORIZONTAL_FACING, facing), 3); - connectBelt(world, pos, start); + + BeltTileEntity te = (BeltTileEntity) world.getTileEntity(pos); + if (te != null) { + te.setController(start); + te.beltLength = beltsToCreate.size(); + te.index = index; + te.hasPulley = pulley; + } + + index++; } } @@ -151,12 +163,6 @@ public class BeltItem extends Item { return positions; } - private void connectBelt(World world, BlockPos pos, BlockPos target) { - BeltTileEntity te = (BeltTileEntity) world.getTileEntity(pos); - if (te != null) - te.setController(target); - } - public static boolean canConnect(World world, BlockPos first, BlockPos second) { if (!world.isAreaLoaded(first, 1)) return false; @@ -190,11 +196,15 @@ public class BeltItem extends Item { int limit = 1000; for (BlockPos currentPos = first.add(step); !currentPos.equals(second) && limit-- > 0; currentPos = currentPos.add(step)) { - if (!world.getBlockState(currentPos).getMaterial().isReplaceable()) + BlockState blockState = world.getBlockState(currentPos); + if (AllBlocks.SHAFT.typeOf(blockState) && blockState.get(ShaftBlock.AXIS) == axis) + continue; + if (!blockState.getMaterial().isReplaceable()) return false; } return true; + } public static boolean validateAxis(World world, BlockPos pos) { diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltItemHandler.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltConnectorItemHandler.java similarity index 96% rename from src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltItemHandler.java rename to src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltConnectorItemHandler.java index 39721de7e..b7210dd91 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltItemHandler.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltConnectorItemHandler.java @@ -23,7 +23,7 @@ import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -public class BeltItemHandler { +public class BeltConnectorItemHandler { private static Random r = new Random(); @@ -73,7 +73,7 @@ public class BeltItemHandler { if (!selected.withinDistance(first, CreateConfig.parameters.maxBeltLength.get())) return; - boolean canConnect = BeltItem.validateAxis(world, selected) && BeltItem.canConnect(world, first, selected); + boolean canConnect = BeltConnectorItem.validateAxis(world, selected) && BeltConnectorItem.canConnect(world, first, selected); Vec3d start = new Vec3d(first); Vec3d end = new Vec3d(selected); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltInventory.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltInventory.java new file mode 100644 index 000000000..fb75c4748 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltInventory.java @@ -0,0 +1,352 @@ +package com.simibubi.create.modules.contraptions.relays.belt; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import com.simibubi.create.AllBlocks; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.entity.item.ItemEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.items.IItemHandler; + +public class BeltInventory { + + final BeltTileEntity belt; + final List items; + boolean beltMovementPositive; + final float SEGMENT_WINDOW = .75f; + + public BeltInventory(BeltTileEntity te) { + this.belt = te; + items = new LinkedList<>(); + } + + public void tick() { + + // Reverse item collection if belt just reversed + if (beltMovementPositive != movingPositive()) { + beltMovementPositive = movingPositive(); + Collections.reverse(items); + belt.markDirty(); + belt.sendData(); + } + + // Assuming the first entry is furthest on the belt + TransportedItemStack stackInFront = null; + TransportedItemStack current = null; + Iterator iterator = items.iterator(); + float beltSpeed = belt.getBeltMovementSpeed(); + float spacing = 1; + + while (iterator.hasNext()) { + stackInFront = current; + current = iterator.next(); + + if (current.stack.isEmpty()) { + iterator.remove(); + current = null; + continue; + } + + float movement = beltSpeed; + + // Don't move if other items are waiting in front + float currentPos = current.beltPosition; + if (stackInFront != null) { + float diff = stackInFront.beltPosition - currentPos; + if (Math.abs(diff) <= spacing) + continue; + movement = beltMovementPositive ? Math.min(movement, diff - spacing) + : Math.max(movement, diff + spacing); + } + + float diffToEnd = beltMovementPositive ? belt.beltLength - currentPos : -currentPos; + float limitedMovement = beltMovementPositive ? Math.min(movement, diffToEnd) + : Math.max(movement, diffToEnd); + + int segmentBefore = (int) currentPos; + float min = segmentBefore + .5f - (SEGMENT_WINDOW / 2); + float max = segmentBefore + .5f + (SEGMENT_WINDOW / 2); + if (currentPos < min || currentPos > max) + segmentBefore = -1; + + current.beltPosition += limitedMovement; + + int segmentAfter = (int) currentPos; + min = segmentAfter + .5f - (SEGMENT_WINDOW / 2); + max = segmentAfter + .5f + (SEGMENT_WINDOW / 2); + if (currentPos < min || currentPos > max) + segmentAfter = -1; + + // Item changed segments + World world = belt.getWorld(); + if (segmentBefore != segmentAfter) { + for (int segment : new int[] { segmentBefore, segmentAfter }) { + if (segment == -1) + continue; + if (!world.isRemote) + world.updateComparatorOutputLevel(getPositionForOffset(segment), + belt.getBlockState().getBlock()); + } + } + + // End reached + if (limitedMovement != movement) { + if (world.isRemote) + continue; + + BlockPos nextPosition = getPositionForOffset(beltMovementPositive ? belt.beltLength : -1); + BlockState state = world.getBlockState(nextPosition); + Direction movementFacing = belt.getMovementFacing(); + + // next block is not a belt + if (!AllBlocks.BELT.typeOf(state)) { + if (!Block.hasSolidSide(state, world, nextPosition, movementFacing.getOpposite())) { + eject(current); + iterator.remove(); + current = null; + } + continue; + } + + // Next block is a belt + TileEntity te = world.getTileEntity(nextPosition); + if (te == null || !(te instanceof BeltTileEntity)) + continue; + BeltTileEntity nextBelt = (BeltTileEntity) te; + Direction nextMovementFacing = nextBelt.getMovementFacing(); + + // next belt goes the opposite way + if (nextMovementFacing == movementFacing.getOpposite()) + continue; + + // Inserting into other belt + BlockPos controller = nextBelt.getController(); + if (!world.isBlockPresent(controller)) + continue; + te = world.getTileEntity(controller); + if (te == null || !(te instanceof BeltTileEntity)) + continue; + BeltTileEntity nextBeltController = (BeltTileEntity) te; + BeltInventory nextInventory = nextBeltController.getInventory(); + + if (!nextInventory.canInsertAt(nextBelt.index)) + continue; + + current.beltPosition = nextBelt.index + .5f; + current.insertedAt = nextBelt.index; + nextInventory.insert(current); + iterator.remove(); + current = null; + belt.sendData(); + nextBeltController.sendData(); + } + + } + + } + + public static class TransportedItemStack implements Comparable { + public ItemStack stack; + public float beltPosition; + public float sideOffset; + public int insertedAt; + + public TransportedItemStack(ItemStack stack) { + this.stack = stack; + } + + @Override + public int compareTo(TransportedItemStack o) { + return beltPosition < o.beltPosition ? 1 : beltPosition > o.beltPosition ? -1 : 0; + } + + public CompoundNBT serializeNBT() { + CompoundNBT nbt = new CompoundNBT(); + nbt.put("Item", stack.serializeNBT()); + nbt.putFloat("Pos", beltPosition); + nbt.putFloat("Offset", sideOffset); + nbt.putInt("InSegment", insertedAt); + return nbt; + } + + public static TransportedItemStack read(CompoundNBT nbt) { + TransportedItemStack stack = new TransportedItemStack(ItemStack.read(nbt.getCompound("Item"))); + stack.beltPosition = nbt.getFloat("Pos"); + stack.sideOffset = nbt.getFloat("Offset"); + stack.insertedAt = nbt.getInt("InSegment"); + return stack; + } + + } + + public boolean canInsertAt(int segment) { + float min = segment + .5f - (SEGMENT_WINDOW / 2); + float max = segment + .5f + (SEGMENT_WINDOW / 2); + + for (TransportedItemStack stack : items) { + float currentPos = stack.beltPosition; + + // Searched past relevant stacks + if (beltMovementPositive ? currentPos < segment : currentPos - 1 > segment) + break; + + // Item inside extraction window + if (currentPos > min && currentPos < max) + return false; + + // Items on the belt get prioritized if the previous item was inserted on the + // same segment + if (stack.insertedAt == segment && currentPos <= segment + 1) + return false; + + } + return true; + } + + protected void insert(TransportedItemStack newStack) { + int index = 0; + if (items.isEmpty()) + items.add(newStack); + for (TransportedItemStack stack : items) { + if (stack.compareTo(newStack) > 0 == beltMovementPositive) + break; + index++; + } + items.add(index, newStack); + belt.markDirty(); + belt.sendData(); + } + + protected TransportedItemStack getStackAtOffset(int offset) { + float min = offset + .5f - (SEGMENT_WINDOW / 2); + float max = offset + .5f + (SEGMENT_WINDOW / 2); + for (TransportedItemStack stack : items) { + if (stack.beltPosition > max) + break; + if (stack.beltPosition > min) + return stack; + } + return null; + } + + public void read(CompoundNBT nbt) { + items.clear(); + nbt.getList("Items", NBT.TAG_COMPOUND) + .forEach(inbt -> items.add(TransportedItemStack.read((CompoundNBT) inbt))); + beltMovementPositive = nbt.getBoolean("PositiveOrder"); + } + + public CompoundNBT write() { + CompoundNBT nbt = new CompoundNBT(); + ListNBT itemsNBT = new ListNBT(); + items.forEach(stack -> itemsNBT.add(stack.serializeNBT())); + nbt.put("Items", itemsNBT); + nbt.putBoolean("PositiveOrder", beltMovementPositive); + return nbt; + } + + private void eject(TransportedItemStack stack) { + ItemStack ejected = stack.stack; + Vec3d outPos = getVectorForOffset(stack.beltPosition); + ItemEntity entity = new ItemEntity(belt.getWorld(), outPos.x, outPos.y, outPos.z, ejected); + entity.setMotion(new Vec3d(belt.getBeltChainDirection()).scale(Math.abs(belt.getBeltMovementSpeed()))); + entity.velocityChanged = true; + } + + private Vec3d getVectorForOffset(float offset) { + Vec3d vec = VecHelper.getCenterOf(belt.getPos()); + vec.add(new Vec3d(belt.getBeltChainDirection()).scale(offset)); + return vec; + } + + private BlockPos getPositionForOffset(int offset) { + BlockPos pos = belt.getPos(); + Vec3i vec = belt.getBeltChainDirection(); + return pos.add(offset * vec.getX(), offset * vec.getY(), offset * vec.getZ()); + } + + private boolean movingPositive() { + return belt.getBeltMovementSpeed() > 0; + } + + public class ItemHandlerSegment implements IItemHandler { + int offset; + + public ItemHandlerSegment(int offset) { + this.offset = offset; + } + + @Override + public int getSlots() { + return 1; + } + + @Override + public ItemStack getStackInSlot(int slot) { + TransportedItemStack stackAtOffset = getStackAtOffset(offset); + if (stackAtOffset == null) + return ItemStack.EMPTY; + return stackAtOffset.stack; + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { + if (canInsertAt(offset)) { + if (!simulate) { + TransportedItemStack newStack = new TransportedItemStack(stack); + newStack.insertedAt = offset; + newStack.beltPosition = offset + .5f; + insert(newStack); + } + return ItemStack.EMPTY; + } + return stack; + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) { + TransportedItemStack transported = getStackAtOffset(offset); + if (transported == null) + return ItemStack.EMPTY; + + amount = Math.min(amount, transported.stack.getCount()); + ItemStack extracted = simulate ? transported.stack.copy().split(amount) : transported.stack.split(amount); + if (!simulate) { + belt.markDirty(); + belt.sendData(); + } + return extracted; + } + + @Override + public int getSlotLimit(int slot) { + return Math.min(getStackInSlot(slot).getMaxStackSize(), 64); + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return true; + } + + } + + public IItemHandler createHandlerForSegment(int segment) { + return new ItemHandlerSegment(segment); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltModelAnimator.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltModelAnimator.java new file mode 100644 index 000000000..1ff634398 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltModelAnimator.java @@ -0,0 +1,83 @@ +package com.simibubi.create.modules.contraptions.relays.belt; + +import java.nio.ByteBuffer; + +import com.simibubi.create.Create; +import com.simibubi.create.foundation.utility.AnimationTickHolder; +import com.simibubi.create.foundation.utility.BufferManipulator; + +import net.minecraft.block.BlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.AtlasTexture; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.util.Direction; +import net.minecraft.util.ResourceLocation; + +class BeltModelAnimator extends BufferManipulator { + protected static TextureAtlasSprite beltTextures; + protected static TextureAtlasSprite originalTexture; + + public BeltModelAnimator(ByteBuffer template) { + super(template); + if (beltTextures == null) + initSprites(); + } + + private void initSprites() { + AtlasTexture textureMap = Minecraft.getInstance().getTextureMap(); + originalTexture = textureMap.getSprite(new ResourceLocation(Create.ID, "block/belt")); + beltTextures = textureMap.getSprite(new ResourceLocation(Create.ID, "block/belt_animated")); + } + + public ByteBuffer getTransformed(BeltTileEntity te, float x, float y, float z, int color) { + original.rewind(); + mutable.rewind(); + + float textureOffsetX = 0; + float textureOffsetY = 0; + + if (te.getSpeed() != 0) { + float time = AnimationTickHolder.getRenderTick(); + Direction direction = te.getBlockState().get(BlockStateProperties.HORIZONTAL_FACING); + if (direction == Direction.EAST || direction == Direction.NORTH) + time = -time; + int textureIndex = (int) ((te.getSpeed() * time / 8) % 16); + if (textureIndex < 0) + textureIndex += 16; + + textureOffsetX = beltTextures.getInterpolatedU((textureIndex % 4) * 4) - originalTexture.getMinU(); + textureOffsetY = beltTextures.getInterpolatedV((textureIndex / 4) * 4) - originalTexture.getMinV(); + } + + final BlockState blockState = te.getBlockState(); + int packedLightCoords = blockState.getPackedLightmapCoords(te.getWorld(), te.getPos()); + float texOffX = textureOffsetX; + float texOffY = textureOffsetY; + + boolean defaultColor = color == -1; + int b = defaultColor ? 128 : color & 0xFF; + int g = defaultColor ? 128 : (color >> 8) & 0xFF; + int r = defaultColor ? 128 : (color >> 16) & 0xFF; + + for (int vertex = 0; vertex < vertexCount(original); vertex++) { + putPos(mutable, vertex, getX(original, vertex) + x, getY(original, vertex) + y, + getZ(original, vertex) + z); + putLight(mutable, vertex, packedLightCoords); + + int bufferPosition = getBufferPosition(vertex); + mutable.putFloat(bufferPosition + 16, original.getFloat(bufferPosition + 16) + texOffX); + mutable.putFloat(bufferPosition + 20, original.getFloat(bufferPosition + 20) + texOffY); + + byte lumByte = getR(original, vertex); + float lum = (lumByte < 0 ? 255 + lumByte : lumByte) / 256f; + + int r2 = (int) (r * lum); + int g2 = (int) (g * lum); + int b2 = (int) (b * lum); + putColor(mutable, vertex, (byte) r2, (byte) g2, (byte) b2, (byte) 255); + } + + return mutable; + } +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltMovementHandler.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltMovementHandler.java new file mode 100644 index 000000000..2b994a15a --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltMovementHandler.java @@ -0,0 +1,184 @@ +package com.simibubi.create.modules.contraptions.relays.belt; + +import static net.minecraft.entity.MoverType.SELF; +import static net.minecraft.util.Direction.AxisDirection.NEGATIVE; +import static net.minecraft.util.Direction.AxisDirection.POSITIVE; + +import com.simibubi.create.AllBlocks; +import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.BeltAttachmentState; +import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part; +import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.Direction.Axis; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.World; + +public class BeltMovementHandler { + + public 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 static boolean canBeTransported(Entity entity) { + if (!entity.isAlive()) + return false; + if (entity instanceof PlayerEntity && ((PlayerEntity) entity).isSneaking()) + return false; + return true; + } + + public static void transportEntity(BeltTileEntity beltTe, Entity entityIn, TransportedEntityInfo info) { + BlockPos pos = info.lastCollidedPos; + World world = beltTe.getWorld(); + TileEntity te = world.getTileEntity(pos); + TileEntity tileEntityBelowPassenger = world.getTileEntity(entityIn.getPosition()); + BlockState blockState = info.lastCollidedState; + Direction movementFacing = Direction.getFacingFromAxisDirection( + blockState.get(BlockStateProperties.HORIZONTAL_FACING).getAxis(), + beltTe.getSpeed() < 0 ? POSITIVE : NEGATIVE); + + boolean collidedWithBelt = te instanceof BeltTileEntity; + boolean betweenBelts = tileEntityBelowPassenger instanceof BeltTileEntity && tileEntityBelowPassenger != te; + + // Don't fight other Belts + if (!collidedWithBelt || betweenBelts) { + return; + } + + // Too slow + boolean notHorizontal = beltTe.getBlockState().get(BeltBlock.SLOPE) != Slope.HORIZONTAL; + if (Math.abs(beltTe.getSpeed()) < (notHorizontal ? 32 : 1)) + return; + + // Not on top + if (entityIn.posY - .25f < pos.getY()) + return; + + // Lock entities in place + boolean isPlayer = entityIn instanceof PlayerEntity; + if (entityIn instanceof LivingEntity && !isPlayer) { + ((LivingEntity) entityIn).addPotionEffect(new EffectInstance(Effects.SLOWNESS, 1, 9, false, false)); + } + + BeltTileEntity belt = (BeltTileEntity) te; + + // Attachment pauses movement + for (BeltAttachmentState state : belt.attachmentTracker.attachments) { + if (state.attachment.handleEntity(belt, entityIn, state)) { + info.ticksSinceLastCollision--; + return; + } + } + + final Direction beltFacing = blockState.get(BlockStateProperties.HORIZONTAL_FACING); + final Slope slope = blockState.get(BeltBlock.SLOPE); + final Axis axis = beltFacing.getAxis(); + float movementSpeed = beltTe.getBeltMovementSpeed(); + final Direction movementDirection = Direction.getFacingFromAxis(axis == Axis.X ? NEGATIVE : POSITIVE, axis); + + Vec3i centeringDirection = Direction.getFacingFromAxis(POSITIVE, beltFacing.rotateY().getAxis()) + .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 = notHorizontal && (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); + + float step = entityIn.stepHeight; + if (!isPlayer) + entityIn.stepHeight = 1; + + // Entity Collisions + if (Math.abs(movementSpeed) < .5f) { + Vec3d checkDistance = movement.scale(2f).add(movement.normalize()); + AxisAlignedBB bb = entityIn.getBoundingBox(); + AxisAlignedBB checkBB = new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); + if (!world + .getEntitiesWithinAABBExcludingEntity(entityIn, checkBB.offset(checkDistance) + .grow(-Math.abs(checkDistance.x), -Math.abs(checkDistance.y), -Math.abs(checkDistance.z))) + .isEmpty()) { + entityIn.setMotion(0, 0, 0); + info.ticksSinceLastCollision--; + return; + } + } + + if (movingUp) { + float minVelocity = .13f; + float yMovement = (float) -(Math.max(Math.abs(movement.y), minVelocity)); + entityIn.move(SELF, new Vec3d(0, yMovement, 0)); + entityIn.move(SELF, movement.mul(1, 0, 1)); + } else if (movingDown) { + entityIn.move(SELF, movement.mul(1, 0, 1)); + entityIn.move(SELF, movement.mul(0, 1, 0)); + } else { + entityIn.move(SELF, movement); + } + + if (!isPlayer) + entityIn.stepHeight = step; + + boolean movedPastEndingSlope = onSlope && (AllBlocks.BELT.typeOf(world.getBlockState(entityIn.getPosition())) + || AllBlocks.BELT.typeOf(world.getBlockState(entityIn.getPosition().down()))); + + if (movedPastEndingSlope && !movingDown && Math.abs(movementSpeed) > 0) + entityIn.setPosition(entityIn.posX, entityIn.posY + movement.y, entityIn.posZ); + if (movedPastEndingSlope) { + entityIn.setMotion(movement); + entityIn.velocityChanged = true; + } + } + +} diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltShapes.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltShapes.java new file mode 100644 index 000000000..20a85962c --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltShapes.java @@ -0,0 +1,120 @@ +package com.simibubi.create.modules.contraptions.relays.belt; + +import static net.minecraft.block.Block.makeCuboidShape; + +import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part; +import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope; + +import net.minecraft.block.BlockState; +import net.minecraft.util.Direction; +import net.minecraft.util.Direction.Axis; +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; + +public class BeltShapes { + + 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 static VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) { + Direction facing = state.get(BeltBlock.HORIZONTAL_FACING); + Axis axis = facing.getAxis(); + Part part = state.get(BeltBlock.PART); + Slope slope = state.get(BeltBlock.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) { + boolean upward = slope == Slope.UPWARD; + if (part == Part.START) + slope = upward ? Slope.DOWNWARD : Slope.UPWARD; + else + facing = facing.getOpposite(); + + if (facing == Direction.NORTH) + return upward ? SLOPE_UPWARD_END_NORTH : SLOPE_DOWNWARD_END_NORTH; + if (facing == Direction.SOUTH) + return upward ? SLOPE_UPWARD_END_SOUTH : SLOPE_DOWNWARD_END_SOUTH; + if (facing == Direction.EAST) + return upward ? SLOPE_UPWARD_END_EAST : SLOPE_DOWNWARD_END_EAST; + if (facing == Direction.WEST) + return 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; + } + + 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/contraptions/relays/belt/BeltTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntity.java index af0e97f2c..a9bbac170 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntity.java @@ -1,5 +1,13 @@ package com.simibubi.create.modules.contraptions.relays.belt; +import static com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part.END; +import static com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part.MIDDLE; +import static com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope.DOWNWARD; +import static com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope.HORIZONTAL; +import static com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope.UPWARD; +import static net.minecraft.util.Direction.AxisDirection.NEGATIVE; +import static net.minecraft.util.Direction.AxisDirection.POSITIVE; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -9,113 +17,167 @@ import com.simibubi.create.AllBlocks; import com.simibubi.create.AllTileEntities; import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.modules.contraptions.base.KineticTileEntity; -import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.BeltAttachmentState; import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.Tracker; import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part; import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope; +import com.simibubi.create.modules.contraptions.relays.belt.BeltMovementHandler.TransportedEntityInfo; 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.item.DyeColor; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.NBTUtil; -import net.minecraft.potion.EffectInstance; -import net.minecraft.potion.Effects; import net.minecraft.state.properties.BlockStateProperties; 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.AxisAlignedBB; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.items.IItemHandler; public class BeltTileEntity extends KineticTileEntity { - protected BlockPos controller; public Map passengers; public AllBeltAttachments.Tracker attachmentTracker; - private CompoundNBT trackerUpdateTag; public int color; + public int beltLength; + public int index; + public boolean hasPulley; - protected static class TransportedEntityInfo { - int ticksSinceLastCollision; - BlockPos lastCollidedPos; - BlockState lastCollidedState; + protected BlockPos controller; + protected BeltInventory inventory; + protected LazyOptional itemHandler; - 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; - } - } + private CompoundNBT trackerUpdateTag; public BeltTileEntity() { super(AllTileEntities.BELT.type); controller = BlockPos.ZERO; attachmentTracker = new Tracker(this); + itemHandler = LazyOptional.empty(); color = -1; + beltLength = -1; + index = -1; } - protected boolean isLastBelt() { + @Override + public void tick() { + super.tick(); + + // Initialize Belt Attachments + if (world != null && trackerUpdateTag != null) { + attachmentTracker.readAndSearch(trackerUpdateTag, this); + trackerUpdateTag = null; + } if (getSpeed() == 0) - return false; + return; - Direction direction = getBlockState().get(BlockStateProperties.HORIZONTAL_FACING); - if (getBlockState().get(BeltBlock.SLOPE) == Slope.VERTICAL) - return false; + initializeItemHandler(); - Part part = getBlockState().get(BeltBlock.PART); - if (part == Part.MIDDLE) - return false; + // Move Items + if (!isController()) + return; + getInventory().tick(); - boolean movingPositively = (getSpeed() > 0 == (direction.getAxisDirection().getOffset() == 1)) - ^ direction.getAxis() == Axis.X; - return part == Part.START ^ movingPositively; + // Move Entities + if (passengers == null) + passengers = new HashMap<>(); + + List toRemove = new ArrayList<>(); + passengers.forEach((entity, info) -> { + boolean canBeTransported = BeltMovementHandler.canBeTransported(entity); + boolean leftTheBelt = info.ticksSinceLastCollision > ((getBlockState().get(BeltBlock.SLOPE) != HORIZONTAL) + ? 3 + : 1); + if (!canBeTransported || leftTheBelt) { + toRemove.add(entity); + return; + } + + info.tick(); + BeltMovementHandler.transportEntity(this, entity, info); + }); + toRemove.forEach(passengers::remove); + } + + @Override + public AxisAlignedBB getRenderBoundingBox() { + if (!isController()) + return super.getRenderBoundingBox(); + return super.getRenderBoundingBox().grow(beltLength); + } + + protected void initializeItemHandler() { + if (world.isRemote || itemHandler.isPresent()) + return; + if (!world.isBlockPresent(controller)) + return; + TileEntity te = world.getTileEntity(controller); + if (te == null || !(te instanceof BeltTileEntity)) + return; + IItemHandler handler = ((BeltTileEntity) te).getInventory().createHandlerForSegment(index); + itemHandler = LazyOptional.of(() -> handler); + } + + @Override + public boolean hasFastRenderer() { + return !isController(); + } + + @Override + public LazyOptional getCapability(Capability cap, Direction side) { + if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) + return itemHandler.cast(); + return super.getCapability(cap, side); + } + + @Override + public void remove() { + super.remove(); + itemHandler.invalidate(); } @Override public CompoundNBT write(CompoundNBT compound) { + attachmentTracker.write(compound); compound.put("Controller", NBTUtil.writeBlockPos(controller)); compound.putInt("Color", color); - attachmentTracker.write(compound); + compound.putInt("Length", beltLength); + compound.putInt("Index", index); + compound.putBoolean("Pulley", hasPulley); + + if (isController()) + compound.put("Inventory", getInventory().write()); return super.write(compound); } @Override public void read(CompoundNBT compound) { - controller = NBTUtil.readBlockPos(compound.getCompound("Controller")); trackerUpdateTag = compound; + controller = NBTUtil.readBlockPos(compound.getCompound("Controller")); color = compound.getInt("Color"); + beltLength = compound.getInt("Length"); + index = compound.getInt("Index"); + hasPulley = compound.getBoolean("Pulley"); + + if (isController()) + getInventory().read(compound.getCompound("Inventory")); super.read(compound); } public void applyColor(DyeColor colorIn) { int colorValue = colorIn.getMapColor().colorValue; for (BlockPos blockPos : BeltBlock.getBeltChain(world, getController())) { - BeltTileEntity tileEntity = (BeltTileEntity) world.getTileEntity(blockPos); - if (tileEntity != null) { - if (tileEntity.color == -1) { - tileEntity.color = colorValue; - } else { - tileEntity.color = ColorHelper.mixColors(tileEntity.color, colorValue, .5f); - } - tileEntity.sendData(); - } + BeltTileEntity belt = (BeltTileEntity) world.getTileEntity(blockPos); + if (belt == null) + continue; + belt.color = belt.color == -1 ? colorValue : ColorHelper.mixColors(belt.color, colorValue, .5f); + belt.markDirty(); + belt.sendData(); } } @@ -131,178 +193,81 @@ public class BeltTileEntity extends KineticTileEntity { return controller.equals(pos); } + public float getBeltMovementSpeed() { + return getSpeed() / 1600f; + } + public boolean hasPulley() { if (!AllBlocks.BELT.typeOf(getBlockState())) return false; - return getBlockState().get(BeltBlock.PART) == Part.END || getBlockState().get(BeltBlock.PART) == Part.START; + Part part = getBlockState().get(BeltBlock.PART); + return part == END || part == Part.START || hasPulley; } - @Override - public void tick() { - super.tick(); - - if (world != null && trackerUpdateTag != null) { - attachmentTracker.readAndSearch(trackerUpdateTag, this); - trackerUpdateTag = null; - } - if (!isController()) - return; - if (passengers == null) - passengers = new HashMap<>(); - - passengers.forEach((entity, info) -> { - transportEntity(entity, info); - }); - - List toRemove = new ArrayList<>(); - passengers.forEach((entity, info) -> { - if (!canTransport(entity)) - toRemove.add(entity); - if (info.ticksSinceLastCollision > ((getBlockState().get(BeltBlock.SLOPE) != Slope.HORIZONTAL) ? 3 : 1)) { - toRemove.add(entity); - } - info.tick(); - }); - toRemove.forEach(e -> { - if (e instanceof ItemEntity) - ((ItemEntity) e).setAgeToCreativeDespawnTime(); - passengers.remove(e); - }); - + protected boolean isLastBelt() { if (getSpeed() == 0) - return; + return false; + + Direction direction = getBeltFacing(); + if (getBlockState().get(BeltBlock.SLOPE) == Slope.VERTICAL) + return false; + + Part part = getBlockState().get(BeltBlock.PART); + if (part == MIDDLE) + return false; + + boolean movingPositively = (getSpeed() > 0 == (direction.getAxisDirection().getOffset() == 1)) + ^ direction.getAxis() == Axis.X; + return part == Part.START ^ movingPositively; } - 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; - Direction movementFacing = Direction.getFacingFromAxisDirection( - blockState.get(BlockStateProperties.HORIZONTAL_FACING).getAxis(), - getSpeed() < 0 ? AxisDirection.POSITIVE : AxisDirection.NEGATIVE); + public Vec3i getMovementDirection(boolean firstHalf) { + return this.getMovementDirection(firstHalf, false); + } - boolean collidedWithBelt = te instanceof BeltTileEntity; - boolean betweenBelts = tileEntityBelowPassenger instanceof BeltTileEntity && tileEntityBelowPassenger != te; + public Vec3i getBeltChainDirection() { + return this.getMovementDirection(true, true); + } - // Don't fight other Belts - if (!collidedWithBelt || betweenBelts) { - return; - } - - // Too slow - boolean notHorizontal = getBlockState().get(BeltBlock.SLOPE) != Slope.HORIZONTAL; - if (Math.abs(getSpeed()) < (notHorizontal ? 32 : 1)) - return; - - // Not on top - if (entityIn.posY - .25f < pos.getY()) - return; - - // Lock entities in place - if (entityIn instanceof LivingEntity && !(entityIn instanceof PlayerEntity)) { - ((LivingEntity) entityIn).addPotionEffect(new EffectInstance(Effects.SLOWNESS, 1, 9, false, false)); - } - - BeltTileEntity belt = (BeltTileEntity) te; - - // Attachment pauses movement - for (BeltAttachmentState state : belt.attachmentTracker.attachments) { - if (state.attachment.handleEntity(belt, entityIn, state)) { - info.ticksSinceLastCollision--; - return; - } - } + protected Vec3i getMovementDirection(boolean firstHalf, boolean ignoreHalves) { + if (getSpeed() == 0) + return BlockPos.ZERO; + final BlockState blockState = getBlockState(); final Direction beltFacing = blockState.get(BlockStateProperties.HORIZONTAL_FACING); final Slope slope = blockState.get(BeltBlock.SLOPE); + final Part part = blockState.get(BeltBlock.PART); 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); + Direction movementFacing = Direction.getFacingFromAxis(axis == Axis.X ? NEGATIVE : POSITIVE, axis); + boolean notHorizontal = blockState.get(BeltBlock.SLOPE) != HORIZONTAL; + if (getSpeed() < 0) + movementFacing = movementFacing.getOpposite(); + Vec3i movement = movementFacing.getDirectionVec(); - double diffCenter = axis == Axis.Z ? (pos.getX() + .5f - entityIn.posX) : (pos.getZ() + .5f - entityIn.posZ); - float maxDiffCenter = (entityIn instanceof ItemEntity) ? 32 / 64f : 48 / 64f; - if (Math.abs(diffCenter) > maxDiffCenter) - return; + boolean slopeBeforeHalf = (part == END) == (beltFacing.getAxisDirection() == POSITIVE); + boolean onSlope = notHorizontal && (part == MIDDLE || slopeBeforeHalf == firstHalf || ignoreHalves); + boolean movingUp = onSlope && slope == (movementFacing == beltFacing ? UPWARD : DOWNWARD); - Part part = blockState.get(BeltBlock.PART); - float top = 13 / 16f; - boolean onSlope = notHorizontal && (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); + if (!onSlope) + return movement; - 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); - - float step = entityIn.stepHeight; - if (!(entityIn instanceof PlayerEntity)) - entityIn.stepHeight = 1; - - // Entity Collisions - if (Math.abs(movementSpeed) < .5f) { - Vec3d checkDistance = movement.scale(2f).add(movement.normalize()); - AxisAlignedBB bb = entityIn.getBoundingBox(); - AxisAlignedBB checkBB = new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); - if (!world - .getEntitiesWithinAABBExcludingEntity(entityIn, checkBB.offset(checkDistance) - .grow(-Math.abs(checkDistance.x), -Math.abs(checkDistance.y), -Math.abs(checkDistance.z))) - .isEmpty()) { - entityIn.setMotion(0, 0, 0); - info.ticksSinceLastCollision--; - return; - } - } - - if (movingUp) { - float minVelocity = entityIn instanceof ItemEntity ? .09f : .13f; - float yMovement = (float) -(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); - } - - if (!(entityIn instanceof PlayerEntity)) - entityIn.stepHeight = step; - - boolean movedPastEndingSlope = onSlope && (AllBlocks.BELT.typeOf(world.getBlockState(entityIn.getPosition())) - || AllBlocks.BELT.typeOf(world.getBlockState(entityIn.getPosition().down()))); - - if (movedPastEndingSlope && !movingDown && Math.abs(movementSpeed) > 0) - entityIn.setPosition(entityIn.posX, entityIn.posY + movement.y, entityIn.posZ); - if (movedPastEndingSlope) - entityIn.setMotion(movement); + return new Vec3i(movement.getX(), movingUp ? 1 : -1, movement.getZ()); } - public boolean canTransport(Entity entity) { - if (!entity.isAlive()) - return false; - if (entity instanceof PlayerEntity && ((PlayerEntity) entity).isSneaking()) - return false; + protected Direction getMovementFacing() { + return Direction.getFacingFromAxisDirection(getBeltFacing().getAxis(), + getBeltMovementSpeed() < 0 ? POSITIVE : NEGATIVE); + } - return true; + protected Direction getBeltFacing() { + return getBlockState().get(BlockStateProperties.HORIZONTAL_FACING); + } + + public BeltInventory getInventory() { + if (inventory == null) + inventory = new BeltInventory(this); + return inventory; } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntityRenderer.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntityRenderer.java index 669af7319..ed5dc140d 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntityRenderer.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntityRenderer.java @@ -1,114 +1,81 @@ package com.simibubi.create.modules.contraptions.relays.belt; -import java.nio.ByteBuffer; - +import com.mojang.blaze3d.platform.GlStateManager; import com.simibubi.create.AllBlocks; -import com.simibubi.create.Create; -import com.simibubi.create.foundation.utility.AnimationTickHolder; -import com.simibubi.create.foundation.utility.BufferManipulator; +import com.simibubi.create.foundation.utility.TessellatorHelper; import com.simibubi.create.modules.contraptions.base.IRotate; import com.simibubi.create.modules.contraptions.base.KineticTileEntity; import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer; +import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer.BlockModelSpinner; +import com.simibubi.create.modules.contraptions.relays.belt.BeltInventory.TransportedItemStack; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.texture.AtlasTexture; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType; +import net.minecraft.client.renderer.tileentity.TileEntityRenderer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.state.properties.BlockStateProperties; -import net.minecraft.util.Direction; -import net.minecraft.util.ResourceLocation; +import net.minecraft.util.Direction.Axis; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; -public class BeltTileEntityRenderer extends KineticTileEntityRenderer { +@SuppressWarnings("deprecation") +public class BeltTileEntityRenderer extends TileEntityRenderer { - protected static class BeltModelAnimator extends BufferManipulator { - protected static TextureAtlasSprite beltTextures; - protected static TextureAtlasSprite originalTexture; + @Override + public void render(BeltTileEntity te, double x, double y, double z, float partialTicks, int destroyStage) { + super.render(te, x, y, z, partialTicks, destroyStage); + if (te.isController()) { + GlStateManager.pushMatrix(); + GlStateManager.translated(x + .5, y + 13 / 16f + .25, z + .5); - public BeltModelAnimator(ByteBuffer template) { - super(template); - if (beltTextures == null) - initSprites(); - } - - private void initSprites() { - AtlasTexture textureMap = Minecraft.getInstance().getTextureMap(); - originalTexture = textureMap.getSprite(new ResourceLocation(Create.ID, "block/belt")); - beltTextures = textureMap.getSprite(new ResourceLocation(Create.ID, "block/belt_animated")); - } - - public ByteBuffer getTransformed(BeltTileEntity te, float x, float y, float z, int color) { - original.rewind(); - mutable.rewind(); - - float textureOffsetX = 0; - float textureOffsetY = 0; - - if (te.getSpeed() != 0) { - float time = AnimationTickHolder.getRenderTick(); - Direction direction = te.getBlockState().get(BlockStateProperties.HORIZONTAL_FACING); - if (direction == Direction.EAST || direction == Direction.NORTH) - time = -time; - int textureIndex = (int) ((te.getSpeed() * time / 8) % 16); - if (textureIndex < 0) - textureIndex += 16; - - textureOffsetX = beltTextures.getInterpolatedU((textureIndex % 4) * 4) - originalTexture.getMinU(); - textureOffsetY = beltTextures.getInterpolatedV((textureIndex / 4) * 4) - originalTexture.getMinV(); + for (TransportedItemStack transported : te.getInventory().items) { + GlStateManager.pushMatrix(); + Vec3i direction = te.getBeltChainDirection(); + float offset = transported.beltPosition; + Vec3d offsetVec = new Vec3d(direction).scale(offset); + GlStateManager.translated(offsetVec.x, offsetVec.y, offsetVec.z); + Minecraft.getInstance().getItemRenderer().renderItem(transported.stack, TransformType.FIXED); + GlStateManager.popMatrix(); } - final BlockState blockState = te.getBlockState(); - int packedLightCoords = blockState.getPackedLightmapCoords(te.getWorld(), te.getPos()); - float texOffX = textureOffsetX; - float texOffY = textureOffsetY; - - boolean defaultColor = color == -1; - int b = defaultColor ? 128 : color & 0xFF; - int g = defaultColor ? 128 : (color >> 8) & 0xFF; - int r = defaultColor ? 128 : (color >> 16) & 0xFF; - - for (int vertex = 0; vertex < vertexCount(original); vertex++) { - putPos(mutable, vertex, getX(original, vertex) + x, getY(original, vertex) + y, - getZ(original, vertex) + z); - putLight(mutable, vertex, packedLightCoords); - - int bufferPosition = getBufferPosition(vertex); - mutable.putFloat(bufferPosition + 16, original.getFloat(bufferPosition + 16) + texOffX); - mutable.putFloat(bufferPosition + 20, original.getFloat(bufferPosition + 20) + texOffY); - - byte lumByte = getR(original, vertex); - float lum = (lumByte < 0 ? 255 + lumByte : lumByte) / 256f; - - int r2 = (int) (r * lum); - int g2 = (int) (g * lum); - int b2 = (int) (b * lum); - putColor(mutable, vertex, (byte) r2, (byte) g2, (byte) b2, (byte) 255); - } - - return mutable; + GlStateManager.popMatrix(); } + + TessellatorHelper.prepareFastRender(); + TessellatorHelper.begin(DefaultVertexFormats.BLOCK); + renderTileEntityFast(te, x, y, z, partialTicks, destroyStage, Tessellator.getInstance().getBuffer()); + TessellatorHelper.draw(); } @Override - public void renderTileEntityFast(KineticTileEntity te, double x, double y, double z, float partialTicks, + public void renderTileEntityFast(BeltTileEntity te, double x, double y, double z, float partialTicks, int destroyStage, BufferBuilder buffer) { - BeltTileEntity beltEntity = (BeltTileEntity) te; - if (beltEntity.hasPulley()) - super.renderTileEntityFast(te, x, y, z, partialTicks, destroyStage, buffer); + if (te.hasPulley()) { + final BlockState state = getRenderedBlockState(te); + KineticTileEntityRenderer.cacheIfMissing(state, getWorld(), BlockModelSpinner::new); + final BlockPos pos = te.getPos(); + Axis axis = ((IRotate) te.getBlockState().getBlock()).getRotationAxis(te.getBlockState()); + float angle = KineticTileEntityRenderer.getAngleForTe(te, pos, axis); + KineticTileEntityRenderer.renderFromCache(buffer, state, getWorld(), (float) x, (float) y, (float) z, pos, + axis, angle); + } - cacheIfMissing(beltEntity.getBlockState(), getWorld(), BeltModelAnimator::new); - renderBeltFromCache(beltEntity, (float) x, (float) y, (float) z, buffer); + KineticTileEntityRenderer.cacheIfMissing(te.getBlockState(), getWorld(), BeltModelAnimator::new); + renderBeltFromCache(te, (float) x, (float) y, (float) z, buffer); } - @Override protected BlockState getRenderedBlockState(KineticTileEntity te) { return AllBlocks.BELT_PULLEY.get().getDefaultState().with(BlockStateProperties.AXIS, ((IRotate) AllBlocks.BELT.get()).getRotationAxis(te.getBlockState())); } public void renderBeltFromCache(BeltTileEntity te, float x, float y, float z, BufferBuilder buffer) { - buffer.putBulkData( - ((BeltModelAnimator) cachedBuffers.get(te.getBlockState())).getTransformed(te, x, y, z, te.color)); + buffer.putBulkData(((BeltModelAnimator) KineticTileEntityRenderer.cachedBuffers.get(te.getBlockState())) + .getTransformed(te, x, y, z, te.color)); } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/FastItemRenderer.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/FastItemRenderer.java new file mode 100644 index 000000000..9a594a7fb --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/FastItemRenderer.java @@ -0,0 +1,120 @@ +package com.simibubi.create.modules.contraptions.relays.belt; + +import java.nio.ByteBuffer; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import org.lwjgl.opengl.GL11; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.simibubi.create.foundation.utility.BufferManipulator; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Direction; +import net.minecraft.util.Direction.Axis; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; +import net.minecraftforge.client.model.data.EmptyModelData; + +public class FastItemRenderer extends BufferManipulator { + + public FastItemRenderer(ByteBuffer original) { + super(original); + } + + public ByteBuffer getTranslatedAndRotated(World world, float x, float y, float z, float yaw, float pitch) { + original.rewind(); + mutable.rewind(); + + float cosYaw = MathHelper.cos(yaw); + float sinYaw = MathHelper.sin(yaw); + float cosPitch = MathHelper.cos(pitch); + float sinPitch = MathHelper.sin(pitch); + + for (int vertex = 0; vertex < vertexCount(original); vertex++) { + float xL = getX(original, vertex); // - (float) rotationOffset.x; + float yL = getY(original, vertex); // - (float) rotationOffset.y; + float zL = getZ(original, vertex); // - (float) rotationOffset.z; + + float xL2 = rotateX(xL, yL, zL, sinPitch, cosPitch, Axis.X); + float yL2 = rotateY(xL, yL, zL, sinPitch, cosPitch, Axis.X); + float zL2 = rotateZ(xL, yL, zL, sinPitch, cosPitch, Axis.X); + // + xL = rotateX(xL2, yL2, zL2, sinYaw, cosYaw, Axis.Y); + yL = rotateY(xL2, yL2, zL2, sinYaw, cosYaw, Axis.Y); + zL = rotateZ(xL2, yL2, zL2, sinYaw, cosYaw, Axis.Y); + + float xPos = xL + x; // + (float) (offset.x + rotationOffset.x); + float yPos = yL + y; // + (float) (offset.y + rotationOffset.y); + float zPos = zL + z; // + (float) (offset.z + rotationOffset.z); + putPos(mutable, vertex, xPos, yPos, zPos); + BlockPos pos = new BlockPos(xPos + .5f, yPos + .5f, zPos + .5f); + putLight(mutable, vertex, world.getCombinedLight(pos, 15)); + } + + return mutable; + } + + protected static Cache cachedItems; + + public static void renderItem(BufferBuilder buffer, World world, ItemStack stack, float x, float y, float z, + float yaw, float pitch) { + if (stack.isEmpty()) + return; + + ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer(); + IBakedModel model = itemRenderer.getModelWithOverrides(stack); + + if (model.isBuiltInRenderer()) { + renderItemIntoBuffer(stack, itemRenderer, model, 0, buffer); + return; + } + + cacheIfMissing(stack); + FastItemRenderer renderer = cachedItems.getIfPresent(stack.getItem()); + if (renderer == null) + return; + buffer.putBulkData(renderer.getTranslatedAndRotated(world, x, y +1, z, yaw, pitch)); + } + + protected static void cacheIfMissing(ItemStack stack) { + if (cachedItems == null) + cachedItems = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.SECONDS).build(); + if (cachedItems.getIfPresent(stack.getItem()) != null) + return; + + ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer(); + IBakedModel model = itemRenderer.getModelWithOverrides(stack); + + int color = 0; + BufferBuilder bufferbuilder = new BufferBuilder(0); + bufferbuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); + renderItemIntoBuffer(stack, itemRenderer, model, color, bufferbuilder); + bufferbuilder.finishDrawing(); + cachedItems.put(stack.getItem(), new FastItemRenderer(bufferbuilder.getByteBuffer())); + } + + protected static void renderItemIntoBuffer(ItemStack stack, ItemRenderer itemRenderer, IBakedModel model, int color, + BufferBuilder bufferbuilder) { + Random random = new Random(42L); + for (Direction direction : Direction.values()) + itemRenderer.renderQuads(bufferbuilder, model.getQuads(null, direction, random, EmptyModelData.INSTANCE), + color, stack); + itemRenderer.renderQuads(bufferbuilder, model.getQuads(null, null, random, EmptyModelData.INSTANCE), color, + stack); + } + + public static void invalidateCache() { + if (cachedItems != null) + cachedItems.invalidateAll(); + } + +} diff --git a/src/main/resources/data/create/loot_tables/blocks/belt_support.json b/src/main/resources/data/create/loot_tables/blocks/belt_support.json new file mode 100644 index 000000000..6ff088bdd --- /dev/null +++ b/src/main/resources/data/create/loot_tables/blocks/belt_support.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "create:belt_support" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file