From Entity to Inventory

- Started work on belts no longer moving itementities around
This commit is contained in:
simibubi 2019-11-12 21:09:32 +01:00
parent c2814f8ad2
commit 25cac1fe8e
16 changed files with 1174 additions and 475 deletions

View File

@ -6,7 +6,7 @@ import com.simibubi.create.modules.IModule;
import com.simibubi.create.modules.contraptions.WrenchItem; import com.simibubi.create.modules.contraptions.WrenchItem;
import com.simibubi.create.modules.contraptions.WrenchItemRenderer; import com.simibubi.create.modules.contraptions.WrenchItemRenderer;
import com.simibubi.create.modules.contraptions.relays.VerticalGearboxItem; 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.ChromaticCompoundCubeItem;
import com.simibubi.create.modules.curiosities.deforester.DeforesterItem; import com.simibubi.create.modules.curiosities.deforester.DeforesterItem;
import com.simibubi.create.modules.curiosities.deforester.DeforesterItemRenderer; import com.simibubi.create.modules.curiosities.deforester.DeforesterItemRenderer;
@ -82,7 +82,7 @@ public enum AllItems {
BLUEPRINT(new SchematicItem(standardItemProperties())), BLUEPRINT(new SchematicItem(standardItemProperties())),
__CONTRAPTIONS__(), __CONTRAPTIONS__(),
BELT_CONNECTOR(new BeltItem(standardItemProperties())), BELT_CONNECTOR(new BeltConnectorItem(standardItemProperties())),
VERTICAL_GEARBOX(new VerticalGearboxItem(new Properties())), VERTICAL_GEARBOX(new VerticalGearboxItem(new Properties())),
FLOUR(ingredient()), FLOUR(ingredient()),
DOUGH(ingredient()), DOUGH(ingredient()),

View File

@ -10,7 +10,7 @@ import com.simibubi.create.foundation.utility.TooltipHelper;
import com.simibubi.create.modules.contraptions.KineticDebugger; import com.simibubi.create.modules.contraptions.KineticDebugger;
import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer;
import com.simibubi.create.modules.contraptions.receivers.TurntableHandler; 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.client.Minecraft;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@ -56,7 +56,7 @@ public class ClientEvents {
public static void onGameTick() { public static void onGameTick() {
CreateClient.gameTick(); CreateClient.gameTick();
BeltItemHandler.gameTick(); BeltConnectorItemHandler.gameTick();
} }
@SubscribeEvent @SubscribeEvent

View File

@ -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.base.KineticTileEntityRenderer;
import com.simibubi.create.modules.contraptions.receivers.constructs.ContraptionRenderer; import com.simibubi.create.modules.contraptions.receivers.constructs.ContraptionRenderer;
import com.simibubi.create.modules.contraptions.receivers.constructs.MechanicalBearingTileEntityRenderer; 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.client.resources.ReloadListener;
import net.minecraft.profiler.IProfiler; import net.minecraft.profiler.IProfiler;
@ -22,6 +23,7 @@ public class CachedBufferReloader extends ReloadListener<String> {
ContraptionRenderer.invalidateCache(); ContraptionRenderer.invalidateCache();
MechanicalBearingTileEntityRenderer.invalidateCache(); MechanicalBearingTileEntityRenderer.invalidateCache();
ColoredIndicatorRenderer.invalidateCache(); ColoredIndicatorRenderer.invalidateCache();
FastItemRenderer.invalidateCache();
} }

View File

@ -76,12 +76,6 @@ public class RotationPropagator {
return connected ? 1 : 0; 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 // Large Gear <-> Large Gear
if (isLargeToLargeGear(stateFrom, stateTo, diff)) { if (isLargeToLargeGear(stateFrom, stateTo, diff)) {
Axis sourceAxis = stateFrom.get(AXIS); Axis sourceAxis = stateFrom.get(AXIS);

View File

@ -34,7 +34,7 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber(value = Dist.CLIENT) @EventBusSubscriber(value = Dist.CLIENT)
public class KineticTileEntityRenderer extends TileEntityRendererFast<KineticTileEntity> { public class KineticTileEntityRenderer extends TileEntityRendererFast<KineticTileEntity> {
protected static Map<BlockState, BufferManipulator> cachedBuffers; public static Map<BlockState, BufferManipulator> cachedBuffers;
public static boolean rainbowMode = false; public static boolean rainbowMode = false;
public static class BlockModelSpinner extends BufferManipulator { public static class BlockModelSpinner extends BufferManipulator {

View File

@ -9,12 +9,13 @@ import com.simibubi.create.foundation.block.IWithTileEntity;
import com.simibubi.create.foundation.block.IWithoutBlockItem; import com.simibubi.create.foundation.block.IWithoutBlockItem;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock; 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.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.DyeColor; import net.minecraft.item.DyeColor;
import net.minecraft.item.ItemStack; 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.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult; 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.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.Tags; import net.minecraftforge.common.Tags;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.ItemStackHandler;
public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockItem, IWithTileEntity<BeltTileEntity> { public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockItem, IWithTileEntity<BeltTileEntity> {
public static final IProperty<Slope> SLOPE = EnumProperty.create("slope", Slope.class); public static final IProperty<Slope> SLOPE = EnumProperty.create("slope", Slope.class);
public static final IProperty<Part> PART = EnumProperty.create("part", Part.class); public static final IProperty<Part> 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() { public BeltBlock() {
super(Properties.from(Blocks.BROWN_WOOL)); super(Properties.from(Blocks.BROWN_WOOL));
setDefaultState(getDefaultState().with(SLOPE, Slope.HORIZONTAL).with(PART, Part.START)); 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 @Override
public ItemStack getPickBlock(BlockState state, RayTraceResult target, IBlockReader world, BlockPos pos, public ItemStack getPickBlock(BlockState state, RayTraceResult target, IBlockReader world, BlockPos pos,
PlayerEntity player) { PlayerEntity player) {
return new ItemStack(AllItems.BELT_CONNECTOR.item); return AllItems.BELT_CONNECTOR.asStack();
} }
@Override @Override
public void onLanded(IBlockReader worldIn, Entity entityIn) { public void onLanded(IBlockReader worldIn, Entity entityIn) {
super.onLanded(worldIn, 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 entityPosition = entityIn.getPosition();
BlockPos beltPos = null;
if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition))) if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition)))
belt = (BeltTileEntity) worldIn.getTileEntity(entityPosition); beltPos = entityPosition;
else if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition.down()))) else if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition.down())))
belt = (BeltTileEntity) worldIn.getTileEntity(entityPosition.down()); beltPos = entityPosition.down();
if (beltPos == null)
if (belt == null || !belt.hasSource()) return;
if (!(worldIn instanceof World))
return; return;
BeltTileEntity controller = (BeltTileEntity) worldIn.getTileEntity(belt.getController()); onEntityCollision(worldIn.getBlockState(beltPos), (World) worldIn, beltPos, entityIn);
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);
} }
@Override @Override
public void onEntityCollision(BlockState state, World worldIn, BlockPos pos, Entity entityIn) { 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; BeltTileEntity belt = null;
belt = (BeltTileEntity) worldIn.getTileEntity(pos); belt = (BeltTileEntity) worldIn.getTileEntity(pos);
if (belt == null || !belt.hasSource()) if (entityIn instanceof PlayerEntity && entityIn.isSneaking())
return; 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()); BeltTileEntity controller = (BeltTileEntity) worldIn.getTileEntity(belt.getController());
if (controller == null || controller.passengers == null)
if (controller == null)
return; return;
if (controller.passengers == null)
return;
if (controller.passengers.containsKey(entityIn)) { if (controller.passengers.containsKey(entityIn)) {
TransportedEntityInfo transportedEntityInfo = controller.passengers.get(entityIn); TransportedEntityInfo info = controller.passengers.get(entityIn);
if (transportedEntityInfo.ticksSinceLastCollision != 0 || pos.equals(entityIn.getPosition())) if (info.ticksSinceLastCollision != 0 || pos.equals(entityIn.getPosition()))
transportedEntityInfo.refresh(pos, state); info.refresh(pos, state);
} else } else
controller.passengers.put(entityIn, new TransportedEntityInfo(pos, state)); controller.passengers.put(entityIn, new TransportedEntityInfo(pos, state));
} }
@ -167,7 +137,12 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
return false; return false;
if (worldIn.isRemote) if (worldIn.isRemote)
return true; 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()) if (!player.isCreative())
heldItem.shrink(1); heldItem.shrink(1);
return true; return true;
@ -186,45 +161,7 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
@Override @Override
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) { public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
Direction facing = state.get(HORIZONTAL_FACING); return BeltShapes.getShape(state, worldIn, pos, context);
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 @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 { public enum Slope implements IStringSerializable {
HORIZONTAL, UPWARD, DOWNWARD, VERTICAL; 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<BlockPos> getBeltChain(World world, BlockPos controllerPos) { public static List<BlockPos> getBeltChain(World world, BlockPos controllerPos) {
List<BlockPos> positions = new LinkedList<>(); List<BlockPos> positions = new LinkedList<>();
@ -382,33 +294,4 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
return positions; 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;
}
} }

View File

@ -6,6 +6,7 @@ import java.util.List;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.CreateConfig; import com.simibubi.create.CreateConfig;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity; 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.Part;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope; 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.util.math.BlockPos;
import net.minecraft.world.World; 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); super(properties);
} }
@ -95,11 +96,22 @@ public class BeltItem extends Item {
List<BlockPos> beltsToCreate = getBeltChainBetween(start, end, slope, facing); List<BlockPos> beltsToCreate = getBeltChainBetween(start, end, slope, facing);
BlockState beltBlock = AllBlocks.BELT.get().getDefaultState(); BlockState beltBlock = AllBlocks.BELT.get().getDefaultState();
int index = 0;
for (BlockPos pos : beltsToCreate) { for (BlockPos pos : beltsToCreate) {
BeltBlock.Part part = pos.equals(start) ? Part.START : pos.equals(end) ? Part.END : Part.MIDDLE; 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) world.setBlockState(pos, beltBlock.with(BeltBlock.SLOPE, slope).with(BeltBlock.PART, part)
.with(BeltBlock.HORIZONTAL_FACING, facing), 3); .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; 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) { public static boolean canConnect(World world, BlockPos first, BlockPos second) {
if (!world.isAreaLoaded(first, 1)) if (!world.isAreaLoaded(first, 1))
return false; return false;
@ -190,11 +196,15 @@ public class BeltItem extends Item {
int limit = 1000; int limit = 1000;
for (BlockPos currentPos = first.add(step); !currentPos.equals(second) for (BlockPos currentPos = first.add(step); !currentPos.equals(second)
&& limit-- > 0; currentPos = currentPos.add(step)) { && 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 false;
} }
return true; return true;
} }
public static boolean validateAxis(World world, BlockPos pos) { public static boolean validateAxis(World world, BlockPos pos) {

View File

@ -23,7 +23,7 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
public class BeltItemHandler { public class BeltConnectorItemHandler {
private static Random r = new Random(); private static Random r = new Random();
@ -73,7 +73,7 @@ public class BeltItemHandler {
if (!selected.withinDistance(first, CreateConfig.parameters.maxBeltLength.get())) if (!selected.withinDistance(first, CreateConfig.parameters.maxBeltLength.get()))
return; 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 start = new Vec3d(first);
Vec3d end = new Vec3d(selected); Vec3d end = new Vec3d(selected);

View File

@ -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<TransportedItemStack> 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<TransportedItemStack> 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<TransportedItemStack> {
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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -1,5 +1,13 @@
package com.simibubi.create.modules.contraptions.relays.belt; 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.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -9,113 +17,167 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity; 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.AllBeltAttachments.Tracker;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part; 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.BeltBlock.Slope;
import com.simibubi.create.modules.contraptions.relays.belt.BeltMovementHandler.TransportedEntityInfo;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity; 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.item.DyeColor;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i; 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 { public class BeltTileEntity extends KineticTileEntity {
protected BlockPos controller;
public Map<Entity, TransportedEntityInfo> passengers; public Map<Entity, TransportedEntityInfo> passengers;
public AllBeltAttachments.Tracker attachmentTracker; public AllBeltAttachments.Tracker attachmentTracker;
private CompoundNBT trackerUpdateTag;
public int color; public int color;
public int beltLength;
public int index;
public boolean hasPulley;
protected static class TransportedEntityInfo { protected BlockPos controller;
int ticksSinceLastCollision; protected BeltInventory inventory;
BlockPos lastCollidedPos; protected LazyOptional<IItemHandler> itemHandler;
BlockState lastCollidedState;
public TransportedEntityInfo(BlockPos collision, BlockState belt) { private CompoundNBT trackerUpdateTag;
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() { public BeltTileEntity() {
super(AllTileEntities.BELT.type); super(AllTileEntities.BELT.type);
controller = BlockPos.ZERO; controller = BlockPos.ZERO;
attachmentTracker = new Tracker(this); attachmentTracker = new Tracker(this);
itemHandler = LazyOptional.empty();
color = -1; 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) if (getSpeed() == 0)
return false; return;
Direction direction = getBlockState().get(BlockStateProperties.HORIZONTAL_FACING); initializeItemHandler();
if (getBlockState().get(BeltBlock.SLOPE) == Slope.VERTICAL)
return false;
Part part = getBlockState().get(BeltBlock.PART); // Move Items
if (part == Part.MIDDLE) if (!isController())
return false; return;
getInventory().tick();
boolean movingPositively = (getSpeed() > 0 == (direction.getAxisDirection().getOffset() == 1)) // Move Entities
^ direction.getAxis() == Axis.X; if (passengers == null)
return part == Part.START ^ movingPositively; passengers = new HashMap<>();
List<Entity> 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 <T> LazyOptional<T> getCapability(Capability<T> 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 @Override
public CompoundNBT write(CompoundNBT compound) { public CompoundNBT write(CompoundNBT compound) {
attachmentTracker.write(compound);
compound.put("Controller", NBTUtil.writeBlockPos(controller)); compound.put("Controller", NBTUtil.writeBlockPos(controller));
compound.putInt("Color", color); 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); return super.write(compound);
} }
@Override @Override
public void read(CompoundNBT compound) { public void read(CompoundNBT compound) {
controller = NBTUtil.readBlockPos(compound.getCompound("Controller"));
trackerUpdateTag = compound; trackerUpdateTag = compound;
controller = NBTUtil.readBlockPos(compound.getCompound("Controller"));
color = compound.getInt("Color"); 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); super.read(compound);
} }
public void applyColor(DyeColor colorIn) { public void applyColor(DyeColor colorIn) {
int colorValue = colorIn.getMapColor().colorValue; int colorValue = colorIn.getMapColor().colorValue;
for (BlockPos blockPos : BeltBlock.getBeltChain(world, getController())) { for (BlockPos blockPos : BeltBlock.getBeltChain(world, getController())) {
BeltTileEntity tileEntity = (BeltTileEntity) world.getTileEntity(blockPos); BeltTileEntity belt = (BeltTileEntity) world.getTileEntity(blockPos);
if (tileEntity != null) { if (belt == null)
if (tileEntity.color == -1) { continue;
tileEntity.color = colorValue; belt.color = belt.color == -1 ? colorValue : ColorHelper.mixColors(belt.color, colorValue, .5f);
} else { belt.markDirty();
tileEntity.color = ColorHelper.mixColors(tileEntity.color, colorValue, .5f); belt.sendData();
}
tileEntity.sendData();
}
} }
} }
@ -131,178 +193,81 @@ public class BeltTileEntity extends KineticTileEntity {
return controller.equals(pos); return controller.equals(pos);
} }
public float getBeltMovementSpeed() {
return getSpeed() / 1600f;
}
public boolean hasPulley() { public boolean hasPulley() {
if (!AllBlocks.BELT.typeOf(getBlockState())) if (!AllBlocks.BELT.typeOf(getBlockState()))
return false; 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 protected boolean isLastBelt() {
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<Entity> 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);
});
if (getSpeed() == 0) 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) { public Vec3i getMovementDirection(boolean firstHalf) {
BlockPos pos = info.lastCollidedPos; return this.getMovementDirection(firstHalf, false);
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);
boolean collidedWithBelt = te instanceof BeltTileEntity;
boolean betweenBelts = tileEntityBelowPassenger instanceof BeltTileEntity && tileEntityBelowPassenger != te;
// Don't fight other Belts
if (!collidedWithBelt || betweenBelts) {
return;
} }
// Too slow public Vec3i getBeltChainDirection() {
boolean notHorizontal = getBlockState().get(BeltBlock.SLOPE) != Slope.HORIZONTAL; return this.getMovementDirection(true, true);
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; protected Vec3i getMovementDirection(boolean firstHalf, boolean ignoreHalves) {
if (getSpeed() == 0)
// Attachment pauses movement return BlockPos.ZERO;
for (BeltAttachmentState state : belt.attachmentTracker.attachments) {
if (state.attachment.handleEntity(belt, entityIn, state)) {
info.ticksSinceLastCollision--;
return;
}
}
final BlockState blockState = getBlockState();
final Direction beltFacing = blockState.get(BlockStateProperties.HORIZONTAL_FACING); final Direction beltFacing = blockState.get(BlockStateProperties.HORIZONTAL_FACING);
final Slope slope = blockState.get(BeltBlock.SLOPE); final Slope slope = blockState.get(BeltBlock.SLOPE);
final Part part = blockState.get(BeltBlock.PART);
final Axis axis = beltFacing.getAxis(); 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) Direction movementFacing = Direction.getFacingFromAxis(axis == Axis.X ? NEGATIVE : POSITIVE, axis);
.getDirectionVec(); boolean notHorizontal = blockState.get(BeltBlock.SLOPE) != HORIZONTAL;
Vec3d movement = new Vec3d(movementDirection.getDirectionVec()).scale(movementSpeed); 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); boolean slopeBeforeHalf = (part == END) == (beltFacing.getAxisDirection() == POSITIVE);
float maxDiffCenter = (entityIn instanceof ItemEntity) ? 32 / 64f : 48 / 64f; boolean onSlope = notHorizontal && (part == MIDDLE || slopeBeforeHalf == firstHalf || ignoreHalves);
if (Math.abs(diffCenter) > maxDiffCenter) boolean movingUp = onSlope && slope == (movementFacing == beltFacing ? UPWARD : DOWNWARD);
return;
Part part = blockState.get(BeltBlock.PART); if (!onSlope)
float top = 13 / 16f; return movement;
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); return new Vec3i(movement.getX(), movingUp ? 1 : -1, movement.getZ());
boolean movingUp = onSlope && slope == (movementFacing == beltFacing ? Slope.UPWARD : Slope.DOWNWARD);
if (beltFacing.getAxis() == Axis.Z) {
boolean b = movingDown;
movingDown = movingUp;
movingUp = b;
} }
if (movingUp) protected Direction getMovementFacing() {
movement = movement.add(0, Math.abs(axis.getCoordinate(movement.x, movement.y, movement.z)), 0); return Direction.getFacingFromAxisDirection(getBeltFacing().getAxis(),
if (movingDown) getBeltMovementSpeed() < 0 ? POSITIVE : NEGATIVE);
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) { protected Direction getBeltFacing() {
float minVelocity = entityIn instanceof ItemEntity ? .09f : .13f; return getBlockState().get(BlockStateProperties.HORIZONTAL_FACING);
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)) public BeltInventory getInventory() {
entityIn.stepHeight = step; if (inventory == null)
inventory = new BeltInventory(this);
boolean movedPastEndingSlope = onSlope && (AllBlocks.BELT.typeOf(world.getBlockState(entityIn.getPosition())) return inventory;
|| 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);
}
public boolean canTransport(Entity entity) {
if (!entity.isAlive())
return false;
if (entity instanceof PlayerEntity && ((PlayerEntity) entity).isSneaking())
return false;
return true;
} }
} }

View File

@ -1,114 +1,81 @@
package com.simibubi.create.modules.contraptions.relays.belt; 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.AllBlocks;
import com.simibubi.create.Create; import com.simibubi.create.foundation.utility.TessellatorHelper;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.BufferManipulator;
import com.simibubi.create.modules.contraptions.base.IRotate; import com.simibubi.create.modules.contraptions.base.IRotate;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity; import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer; 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.block.BlockState;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; 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.state.properties.BlockStateProperties;
import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis;
import net.minecraft.util.ResourceLocation; 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<BeltTileEntity> {
protected static class BeltModelAnimator extends BufferManipulator { @Override
protected static TextureAtlasSprite beltTextures; public void render(BeltTileEntity te, double x, double y, double z, float partialTicks, int destroyStage) {
protected static TextureAtlasSprite originalTexture; 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) { for (TransportedItemStack transported : te.getInventory().items) {
super(template); GlStateManager.pushMatrix();
if (beltTextures == null) Vec3i direction = te.getBeltChainDirection();
initSprites(); 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();
} }
private void initSprites() { GlStateManager.popMatrix();
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) { TessellatorHelper.prepareFastRender();
original.rewind(); TessellatorHelper.begin(DefaultVertexFormats.BLOCK);
mutable.rewind(); renderTileEntityFast(te, x, y, z, partialTicks, destroyStage, Tessellator.getInstance().getBuffer());
TessellatorHelper.draw();
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;
}
} }
@Override @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) { int destroyStage, BufferBuilder buffer) {
BeltTileEntity beltEntity = (BeltTileEntity) te;
if (beltEntity.hasPulley()) if (te.hasPulley()) {
super.renderTileEntityFast(te, x, y, z, partialTicks, destroyStage, buffer); final BlockState state = getRenderedBlockState(te);
KineticTileEntityRenderer.cacheIfMissing(state, getWorld(), BlockModelSpinner::new);
cacheIfMissing(beltEntity.getBlockState(), getWorld(), BeltModelAnimator::new); final BlockPos pos = te.getPos();
renderBeltFromCache(beltEntity, (float) x, (float) y, (float) z, buffer); 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);
}
KineticTileEntityRenderer.cacheIfMissing(te.getBlockState(), getWorld(), BeltModelAnimator::new);
renderBeltFromCache(te, (float) x, (float) y, (float) z, buffer);
} }
@Override
protected BlockState getRenderedBlockState(KineticTileEntity te) { protected BlockState getRenderedBlockState(KineticTileEntity te) {
return AllBlocks.BELT_PULLEY.get().getDefaultState().with(BlockStateProperties.AXIS, return AllBlocks.BELT_PULLEY.get().getDefaultState().with(BlockStateProperties.AXIS,
((IRotate) AllBlocks.BELT.get()).getRotationAxis(te.getBlockState())); ((IRotate) AllBlocks.BELT.get()).getRotationAxis(te.getBlockState()));
} }
public void renderBeltFromCache(BeltTileEntity te, float x, float y, float z, BufferBuilder buffer) { public void renderBeltFromCache(BeltTileEntity te, float x, float y, float z, BufferBuilder buffer) {
buffer.putBulkData( buffer.putBulkData(((BeltModelAnimator) KineticTileEntityRenderer.cachedBuffers.get(te.getBlockState()))
((BeltModelAnimator) cachedBuffers.get(te.getBlockState())).getTransformed(te, x, y, z, te.color)); .getTransformed(te, x, y, z, te.color));
} }
} }

View File

@ -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<Item, FastItemRenderer> 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();
}
}

View File

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "create:belt_support"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}