Improved Belts #2

- Entity Movement on Belts
- Velocity Boost on Exit (still needs improvements)
This commit is contained in:
simibubi 2019-08-12 19:52:06 +02:00
parent a0149425d5
commit 71f928ae9d
3 changed files with 360 additions and 5 deletions

View file

@ -13,11 +13,9 @@ import com.simibubi.create.modules.symmetry.client.SymmetryWandModel;
import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ModelResourceLocation; import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.client.renderer.model.ModelRotation;
import net.minecraft.client.renderer.tileentity.ItemStackTileEntityRenderer; import net.minecraft.client.renderer.tileentity.ItemStackTileEntityRenderer;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.Item.Properties; import net.minecraft.item.Item.Properties;
import net.minecraft.util.ResourceLocation;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Rarity; import net.minecraft.item.Rarity;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;

View file

@ -6,10 +6,13 @@ import java.util.List;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.block.IWithoutBlockItem; import com.simibubi.create.foundation.block.IWithoutBlockItem;
import com.simibubi.create.modules.kinetics.base.HorizontalKineticBlock; import com.simibubi.create.modules.kinetics.base.HorizontalKineticBlock;
import com.simibubi.create.modules.kinetics.relays.BeltTileEntity.TransportedEntityInfo;
import net.minecraft.block.Block; import net.minecraft.block.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.player.PlayerEntity;
import net.minecraft.state.EnumProperty; import net.minecraft.state.EnumProperty;
import net.minecraft.state.IProperty; import net.minecraft.state.IProperty;
import net.minecraft.state.StateContainer.Builder; import net.minecraft.state.StateContainer.Builder;
@ -20,7 +23,13 @@ import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.IStringSerializable; import net.minecraft.util.IStringSerializable;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
import net.minecraft.util.math.shapes.IBooleanFunction;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World; import net.minecraft.world.World;
public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockItem { public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockItem {
@ -28,11 +37,100 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
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 void onLanded(IBlockReader worldIn, Entity entityIn) {
super.onLanded(worldIn, entityIn);
if (entityIn instanceof PlayerEntity && entityIn.isSneaking())
return;
if (entityIn instanceof PlayerEntity && ((PlayerEntity) entityIn).moveVertical > 0)
return;
BeltTileEntity belt = null;
BlockPos entityPosition = entityIn.getPosition();
if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition)))
belt = (BeltTileEntity) worldIn.getTileEntity(entityPosition);
else if (AllBlocks.BELT.typeOf(worldIn.getBlockState(entityPosition.down())))
belt = (BeltTileEntity) worldIn.getTileEntity(entityPosition.down());
if (belt == null || !belt.hasSource())
return;
BeltTileEntity controller = (BeltTileEntity) worldIn.getTileEntity(belt.getController());
if (controller == null)
return;
if (controller.passengers.containsKey(entityIn))
controller.passengers.get(entityIn).refresh(belt.getPos(), belt.getBlockState());
else
controller.passengers.put(entityIn, new TransportedEntityInfo(belt.getPos(), belt.getBlockState()));
}
@Override
public float getSlipperiness(BlockState state, IWorldReader world, BlockPos pos, Entity entity) {
return super.getSlipperiness(state, world, pos, entity);
}
@Override
public void onEntityCollision(BlockState state, World worldIn, BlockPos pos, Entity entityIn) {
if (entityIn instanceof PlayerEntity && entityIn.isSneaking())
return;
if (entityIn instanceof PlayerEntity && ((PlayerEntity) entityIn).moveVertical > 0)
return;
BeltTileEntity belt = null;
belt = (BeltTileEntity) worldIn.getTileEntity(pos);
if (belt == null || !belt.hasSource())
return;
BeltTileEntity controller = (BeltTileEntity) worldIn.getTileEntity(belt.getController());
if (controller == null)
return;
if (controller.passengers.containsKey(entityIn)) {
TransportedEntityInfo transportedEntityInfo = controller.passengers.get(entityIn);
if (transportedEntityInfo.ticksSinceLastCollision != 0 || pos.equals(entityIn.getPosition()))
transportedEntityInfo.refresh(pos, state);
} else
controller.passengers.put(entityIn, new TransportedEntityInfo(pos, state));
}
@Override @Override
protected void fillStateContainer(Builder<Block, BlockState> builder) { protected void fillStateContainer(Builder<Block, BlockState> builder) {
builder.add(SLOPE, PART); builder.add(SLOPE, PART);
@ -44,6 +142,49 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
return true; return true;
} }
@Override
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
Direction facing = state.get(HORIZONTAL_FACING);
Axis axis = facing.getAxis();
Part part = state.get(PART);
Slope slope = state.get(SLOPE);
if (slope == Slope.HORIZONTAL)
return axis == Axis.Z ? FLAT_STRAIGHT_X : FLAT_STRAIGHT_Z;
if (slope == Slope.VERTICAL)
return axis == Axis.X ? VERTICAL_STRAIGHT_X : VERTICAL_STRAIGHT_Z;
if (part != Part.MIDDLE) {
if (part == Part.START)
slope = slope == Slope.UPWARD ? Slope.DOWNWARD : Slope.UPWARD;
else
facing = facing.getOpposite();
if (facing == Direction.NORTH)
return slope == Slope.UPWARD ? SLOPE_UPWARD_END_NORTH : SLOPE_DOWNWARD_END_NORTH;
if (facing == Direction.SOUTH)
return slope == Slope.UPWARD ? SLOPE_UPWARD_END_SOUTH : SLOPE_DOWNWARD_END_SOUTH;
if (facing == Direction.EAST)
return slope == Slope.UPWARD ? SLOPE_UPWARD_END_EAST : SLOPE_DOWNWARD_END_EAST;
if (facing == Direction.WEST)
return slope == Slope.UPWARD ? SLOPE_UPWARD_END_WEST : SLOPE_DOWNWARD_END_WEST;
}
if (slope == Slope.DOWNWARD)
facing = facing.getOpposite();
if (facing == Direction.NORTH)
return SLOPE_NORTH;
if (facing == Direction.SOUTH)
return SLOPE_SOUTH;
if (facing == Direction.EAST)
return SLOPE_EAST;
if (facing == Direction.WEST)
return SLOPE_WEST;
return FULL;
}
@Override @Override
public TileEntity createTileEntity(BlockState state, IBlockReader world) { public TileEntity createTileEntity(BlockState state, IBlockReader world) {
return new BeltTileEntity(); return new BeltTileEntity();
@ -142,6 +283,18 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
} }
} }
public static boolean isUpperEnd(BlockState state, float speed) {
Direction facing = state.get(HORIZONTAL_FACING);
if (state.get(SLOPE) == Slope.UPWARD && state.get(PART) == Part.END) {
return facing.getAxisDirection().getOffset() * Math.signum(speed) == (facing.getAxis() == Axis.X ? -1 : 1);
}
if (state.get(SLOPE) == Slope.DOWNWARD && state.get(PART) == Part.START) {
return facing.getAxisDirection().getOffset() * Math.signum(speed) == (facing.getAxis() == Axis.Z ? -1 : 1);
}
return false;
}
public static List<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<>();
@ -173,4 +326,33 @@ 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

@ -1,21 +1,64 @@
package com.simibubi.create.modules.kinetics.relays; package com.simibubi.create.modules.kinetics.relays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.modules.kinetics.base.KineticTileEntity; import com.simibubi.create.modules.kinetics.base.KineticTileEntity;
import com.simibubi.create.modules.kinetics.relays.BeltBlock.Part; import com.simibubi.create.modules.kinetics.relays.BeltBlock.Part;
import com.simibubi.create.modules.kinetics.relays.BeltBlock.Slope;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
public class BeltTileEntity extends KineticTileEntity { public class BeltTileEntity extends KineticTileEntity implements ITickableTileEntity {
protected BlockPos controller; protected BlockPos controller;
public Map<Entity, TransportedEntityInfo> passengers;
protected static class TransportedEntityInfo {
int ticksSinceLastCollision;
BlockPos lastCollidedPos;
BlockState lastCollidedState;
public TransportedEntityInfo(BlockPos collision, BlockState belt) {
refresh(collision, belt);
}
public void refresh(BlockPos collision, BlockState belt) {
ticksSinceLastCollision = 0;
lastCollidedPos = new BlockPos(collision).toImmutable();
lastCollidedState = belt;
}
public TransportedEntityInfo tick() {
ticksSinceLastCollision++;
return this;
}
}
public BeltTileEntity() { public BeltTileEntity() {
super(AllTileEntities.BELT.type); super(AllTileEntities.BELT.type);
controller = BlockPos.ZERO; controller = BlockPos.ZERO;
passengers = new HashMap<>();
} }
@Override @Override
@ -48,4 +91,136 @@ public class BeltTileEntity extends KineticTileEntity {
return getBlockState().get(BeltBlock.PART) == Part.END || getBlockState().get(BeltBlock.PART) == Part.START; return getBlockState().get(BeltBlock.PART) == Part.END || getBlockState().get(BeltBlock.PART) == Part.START;
} }
@Override
public void tick() {
if (!isController())
return;
passengers.forEach((entity, info) -> {
transportEntity(entity, info);
});
List<Entity> toRemove = new ArrayList<>();
passengers.forEach((entity, info) -> {
if (!canTransport(entity))
toRemove.add(entity);
if (info.ticksSinceLastCollision > 0) {
toRemove.add(entity);
}
info.tick();
});
toRemove.forEach(passengers::remove);
if (speed == 0)
return;
}
public void transportEntity(Entity entityIn, TransportedEntityInfo info) {
BlockPos pos = info.lastCollidedPos;
TileEntity te = world.getTileEntity(pos);
TileEntity tileEntityBelowPassenger = world.getTileEntity(entityIn.getPosition());
BlockState blockState = info.lastCollidedState;
boolean onEndingBelt = blockState.getBlock() instanceof BeltBlock && BeltBlock.isUpperEnd(blockState, speed);
Direction movementFacing = Direction.getFacingFromAxisDirection(
blockState.get(BlockStateProperties.HORIZONTAL_FACING).getAxis(),
speed < 0 ? AxisDirection.POSITIVE : AxisDirection.NEGATIVE);
boolean hasBeltAdjacent = onEndingBelt
&& AllBlocks.BELT.typeOf(world.getBlockState(pos.offset(movementFacing)));
boolean collidedWithBelt = te instanceof BeltTileEntity;
boolean betweenBelts = tileEntityBelowPassenger instanceof BeltTileEntity && tileEntityBelowPassenger != te;
// Don't fight other Belts
if (!collidedWithBelt || betweenBelts) {
return;
}
if (((KineticTileEntity) te).getSpeed() == 0)
return;
if (entityIn.posY - .25f < pos.getY())
return;
if (entityIn instanceof LivingEntity) {
((LivingEntity) entityIn).setIdleTime(20);
}
final Direction beltFacing = blockState.get(BlockStateProperties.HORIZONTAL_FACING);
final Slope slope = blockState.get(BeltBlock.SLOPE);
final Axis axis = beltFacing.getAxis();
float movementSpeed = ((KineticTileEntity) te).getSpeed() / 1600f;
final Direction movementDirection = Direction
.getFacingFromAxis(axis == Axis.X ? AxisDirection.NEGATIVE : AxisDirection.POSITIVE, axis);
Vec3i centeringDirection = Direction.getFacingFromAxis(AxisDirection.POSITIVE, axis == Axis.X ? Axis.Z : Axis.X)
.getDirectionVec();
Vec3d movement = new Vec3d(movementDirection.getDirectionVec()).scale(movementSpeed);
double diffCenter = axis == Axis.Z ? (pos.getX() + .5f - entityIn.posX) : (pos.getZ() + .5f - entityIn.posZ);
if (Math.abs(diffCenter) > 48 / 64f)
return;
Part part = blockState.get(BeltBlock.PART);
float top = 13 / 16f;
boolean onSlope = part == Part.MIDDLE
|| part == (slope == Slope.UPWARD ? Part.END : Part.START) && entityIn.posY - pos.getY() < top
|| part == (slope == Slope.UPWARD ? Part.START : Part.END) && entityIn.posY - pos.getY() > top;
boolean movingDown = onSlope && slope == (movementFacing == beltFacing ? Slope.DOWNWARD : Slope.UPWARD);
boolean movingUp = onSlope && slope == (movementFacing == beltFacing ? Slope.UPWARD : Slope.DOWNWARD);
if (beltFacing.getAxis() == Axis.Z) {
boolean b = movingDown;
movingDown = movingUp;
movingUp = b;
}
if (movingUp)
movement = movement.add(0, Math.abs(axis.getCoordinate(movement.x, movement.y, movement.z)), 0);
if (movingDown)
movement = movement.add(0, -Math.abs(axis.getCoordinate(movement.x, movement.y, movement.z)), 0);
Vec3d centering = new Vec3d(centeringDirection).scale(diffCenter * Math.min(Math.abs(movementSpeed), .1f) * 4);
movement = movement.add(centering);
if (info.ticksSinceLastCollision > 0 && !betweenBelts && onEndingBelt && !hasBeltAdjacent) {
entityIn.setPosition(entityIn.posX, entityIn.posY + movement.y, entityIn.posZ);
float verticalMultiplier = entityIn instanceof ItemEntity ? .25f : 1;
if (movementSpeed > .25f)
movement = movement.add(0, Math.abs(movementSpeed) * verticalMultiplier, 0);
entityIn.setMotion(movement);
return;
}
float step = entityIn.stepHeight;
entityIn.stepHeight = 1;
if (movingUp) {
float minVelocity = entityIn instanceof ItemEntity ? .09f : .13f;
float yMovement = (float) (Math.signum(movementSpeed) * Math.max(Math.abs(movement.y), minVelocity));
entityIn.move(MoverType.SELF, new Vec3d(0, yMovement, 0));
entityIn.move(MoverType.SELF, movement.mul(1, 0, 1));
} else if (movingDown) {
entityIn.move(MoverType.SELF, movement.mul(1, 0, 1));
entityIn.move(MoverType.SELF, movement.mul(0, 1, 0));
} else {
entityIn.move(MoverType.SELF, movement);
}
entityIn.stepHeight = step;
if (!betweenBelts && onEndingBelt && !hasBeltAdjacent) {
entityIn.setMotion(movement);
}
}
public boolean canTransport(Entity entity) {
if (!entity.isAlive())
return false;
if (entity instanceof PlayerEntity && ((PlayerEntity) entity).isSneaking())
return false;
return true;
}
} }