401 lines
14 KiB
Java
401 lines
14 KiB
Java
package net.minecraft.world.level.block.piston;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.HolderGetter;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.core.registries.BuiltInRegistries;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.NbtUtils;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.MoverType;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
|
import net.minecraft.world.level.block.state.properties.PistonType;
|
|
import net.minecraft.world.level.material.PushReaction;
|
|
import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
|
|
public class PistonMovingBlockEntity extends BlockEntity {
|
|
private static final int TICKS_TO_EXTEND = 2;
|
|
private static final double PUSH_OFFSET = 0.01;
|
|
public static final double TICK_MOVEMENT = 0.51;
|
|
private BlockState movedState = Blocks.AIR.defaultBlockState();
|
|
private Direction direction;
|
|
/**
|
|
* Whether this piston is extending.
|
|
*/
|
|
private boolean extending;
|
|
private boolean isSourcePiston;
|
|
private static final ThreadLocal<Direction> NOCLIP = ThreadLocal.withInitial(() -> null);
|
|
private float progress;
|
|
/**
|
|
* The extension / retraction progress
|
|
*/
|
|
private float progressO;
|
|
private long lastTicked;
|
|
private int deathTicks;
|
|
|
|
public PistonMovingBlockEntity(BlockPos pos, BlockState blockState) {
|
|
super(BlockEntityType.PISTON, pos, blockState);
|
|
}
|
|
|
|
public PistonMovingBlockEntity(BlockPos pos, BlockState blockState, BlockState movedState, Direction direction, boolean extending, boolean isSourcePiston) {
|
|
this(pos, blockState);
|
|
this.movedState = movedState;
|
|
this.direction = direction;
|
|
this.extending = extending;
|
|
this.isSourcePiston = isSourcePiston;
|
|
}
|
|
|
|
@Override
|
|
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
|
|
return this.saveCustomOnly(registries);
|
|
}
|
|
|
|
/**
|
|
* @return whether this piston is extending
|
|
*/
|
|
public boolean isExtending() {
|
|
return this.extending;
|
|
}
|
|
|
|
public Direction getDirection() {
|
|
return this.direction;
|
|
}
|
|
|
|
public boolean isSourcePiston() {
|
|
return this.isSourcePiston;
|
|
}
|
|
|
|
/**
|
|
* @return interpolated progress value (between lastProgress and progress) given the partialTicks
|
|
*/
|
|
public float getProgress(float partialTicks) {
|
|
if (partialTicks > 1.0F) {
|
|
partialTicks = 1.0F;
|
|
}
|
|
|
|
return Mth.lerp(partialTicks, this.progressO, this.progress);
|
|
}
|
|
|
|
public float getXOff(float partialTicks) {
|
|
return this.direction.getStepX() * this.getExtendedProgress(this.getProgress(partialTicks));
|
|
}
|
|
|
|
public float getYOff(float partialTicks) {
|
|
return this.direction.getStepY() * this.getExtendedProgress(this.getProgress(partialTicks));
|
|
}
|
|
|
|
public float getZOff(float partialTicks) {
|
|
return this.direction.getStepZ() * this.getExtendedProgress(this.getProgress(partialTicks));
|
|
}
|
|
|
|
private float getExtendedProgress(float progress) {
|
|
return this.extending ? progress - 1.0F : 1.0F - progress;
|
|
}
|
|
|
|
private BlockState getCollisionRelatedBlockState() {
|
|
return !this.isExtending() && this.isSourcePiston() && this.movedState.getBlock() instanceof PistonBaseBlock
|
|
? Blocks.PISTON_HEAD
|
|
.defaultBlockState()
|
|
.setValue(PistonHeadBlock.SHORT, this.progress > 0.25F)
|
|
.setValue(PistonHeadBlock.TYPE, this.movedState.is(Blocks.STICKY_PISTON) ? PistonType.STICKY : PistonType.DEFAULT)
|
|
.setValue(PistonHeadBlock.FACING, (Direction)this.movedState.getValue(PistonBaseBlock.FACING))
|
|
: this.movedState;
|
|
}
|
|
|
|
private static void moveCollidedEntities(Level level, BlockPos pos, float partialTick, PistonMovingBlockEntity piston) {
|
|
Direction direction = piston.getMovementDirection();
|
|
double d = partialTick - piston.progress;
|
|
VoxelShape voxelShape = piston.getCollisionRelatedBlockState().getCollisionShape(level, pos);
|
|
if (!voxelShape.isEmpty()) {
|
|
AABB aABB = moveByPositionAndProgress(pos, voxelShape.bounds(), piston);
|
|
List<Entity> list = level.getEntities(null, PistonMath.getMovementArea(aABB, direction, d).minmax(aABB));
|
|
if (!list.isEmpty()) {
|
|
List<AABB> list2 = voxelShape.toAabbs();
|
|
boolean bl = piston.movedState.is(Blocks.SLIME_BLOCK);
|
|
Iterator var12 = list.iterator();
|
|
|
|
while (true) {
|
|
Entity entity;
|
|
while (true) {
|
|
if (!var12.hasNext()) {
|
|
return;
|
|
}
|
|
|
|
entity = (Entity)var12.next();
|
|
if (entity.getPistonPushReaction() != PushReaction.IGNORE) {
|
|
if (!bl) {
|
|
break;
|
|
}
|
|
|
|
if (!(entity instanceof ServerPlayer)) {
|
|
Vec3 vec3 = entity.getDeltaMovement();
|
|
double e = vec3.x;
|
|
double f = vec3.y;
|
|
double g = vec3.z;
|
|
switch (direction.getAxis()) {
|
|
case X:
|
|
e = direction.getStepX();
|
|
break;
|
|
case Y:
|
|
f = direction.getStepY();
|
|
break;
|
|
case Z:
|
|
g = direction.getStepZ();
|
|
}
|
|
|
|
entity.setDeltaMovement(e, f, g);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
double h = 0.0;
|
|
|
|
for (AABB aABB2 : list2) {
|
|
AABB aABB3 = PistonMath.getMovementArea(moveByPositionAndProgress(pos, aABB2, piston), direction, d);
|
|
AABB aABB4 = entity.getBoundingBox();
|
|
if (aABB3.intersects(aABB4)) {
|
|
h = Math.max(h, getMovement(aABB3, direction, aABB4));
|
|
if (h >= d) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(h <= 0.0)) {
|
|
h = Math.min(h, d) + 0.01;
|
|
moveEntityByPiston(direction, entity, h, direction);
|
|
if (!piston.extending && piston.isSourcePiston) {
|
|
fixEntityWithinPistonBase(pos, entity, direction, d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void moveEntityByPiston(Direction noClipDirection, Entity entity, double progress, Direction direction) {
|
|
NOCLIP.set(noClipDirection);
|
|
entity.move(MoverType.PISTON, new Vec3(progress * direction.getStepX(), progress * direction.getStepY(), progress * direction.getStepZ()));
|
|
entity.applyEffectsFromBlocks();
|
|
NOCLIP.set(null);
|
|
}
|
|
|
|
private static void moveStuckEntities(Level level, BlockPos pos, float partialTick, PistonMovingBlockEntity piston) {
|
|
if (piston.isStickyForEntities()) {
|
|
Direction direction = piston.getMovementDirection();
|
|
if (direction.getAxis().isHorizontal()) {
|
|
double d = piston.movedState.getCollisionShape(level, pos).max(Direction.Axis.Y);
|
|
AABB aABB = moveByPositionAndProgress(pos, new AABB(0.0, d, 0.0, 1.0, 1.5000010000000001, 1.0), piston);
|
|
double e = partialTick - piston.progress;
|
|
|
|
for (Entity entity : level.getEntities((Entity)null, aABB, entityx -> matchesStickyCritera(aABB, entityx, pos))) {
|
|
moveEntityByPiston(direction, entity, e, direction);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static boolean matchesStickyCritera(AABB box, Entity entity, BlockPos pos) {
|
|
return entity.getPistonPushReaction() == PushReaction.NORMAL
|
|
&& entity.onGround()
|
|
&& (entity.isSupportedBy(pos) || entity.getX() >= box.minX && entity.getX() <= box.maxX && entity.getZ() >= box.minZ && entity.getZ() <= box.maxZ);
|
|
}
|
|
|
|
private boolean isStickyForEntities() {
|
|
return this.movedState.is(Blocks.HONEY_BLOCK);
|
|
}
|
|
|
|
public Direction getMovementDirection() {
|
|
return this.extending ? this.direction : this.direction.getOpposite();
|
|
}
|
|
|
|
private static double getMovement(AABB headShape, Direction direction, AABB facing) {
|
|
switch (direction) {
|
|
case EAST:
|
|
return headShape.maxX - facing.minX;
|
|
case WEST:
|
|
return facing.maxX - headShape.minX;
|
|
case UP:
|
|
default:
|
|
return headShape.maxY - facing.minY;
|
|
case DOWN:
|
|
return facing.maxY - headShape.minY;
|
|
case SOUTH:
|
|
return headShape.maxZ - facing.minZ;
|
|
case NORTH:
|
|
return facing.maxZ - headShape.minZ;
|
|
}
|
|
}
|
|
|
|
private static AABB moveByPositionAndProgress(BlockPos pos, AABB aabb, PistonMovingBlockEntity pistonMovingBlockEntity) {
|
|
double d = pistonMovingBlockEntity.getExtendedProgress(pistonMovingBlockEntity.progress);
|
|
return aabb.move(
|
|
pos.getX() + d * pistonMovingBlockEntity.direction.getStepX(),
|
|
pos.getY() + d * pistonMovingBlockEntity.direction.getStepY(),
|
|
pos.getZ() + d * pistonMovingBlockEntity.direction.getStepZ()
|
|
);
|
|
}
|
|
|
|
private static void fixEntityWithinPistonBase(BlockPos pos, Entity entity, Direction dir, double progress) {
|
|
AABB aABB = entity.getBoundingBox();
|
|
AABB aABB2 = Shapes.block().bounds().move(pos);
|
|
if (aABB.intersects(aABB2)) {
|
|
Direction direction = dir.getOpposite();
|
|
double d = getMovement(aABB2, direction, aABB) + 0.01;
|
|
double e = getMovement(aABB2, direction, aABB.intersect(aABB2)) + 0.01;
|
|
if (Math.abs(d - e) < 0.01) {
|
|
d = Math.min(d, progress) + 0.01;
|
|
moveEntityByPiston(dir, entity, d, direction);
|
|
}
|
|
}
|
|
}
|
|
|
|
public BlockState getMovedState() {
|
|
return this.movedState;
|
|
}
|
|
|
|
/**
|
|
* Removes the piston's BlockEntity and stops any movement
|
|
*/
|
|
public void finalTick() {
|
|
if (this.level != null && (this.progressO < 1.0F || this.level.isClientSide)) {
|
|
this.progress = 1.0F;
|
|
this.progressO = this.progress;
|
|
this.level.removeBlockEntity(this.worldPosition);
|
|
this.setRemoved();
|
|
if (this.level.getBlockState(this.worldPosition).is(Blocks.MOVING_PISTON)) {
|
|
BlockState blockState;
|
|
if (this.isSourcePiston) {
|
|
blockState = Blocks.AIR.defaultBlockState();
|
|
} else {
|
|
blockState = Block.updateFromNeighbourShapes(this.movedState, this.level, this.worldPosition);
|
|
}
|
|
|
|
this.level.setBlock(this.worldPosition, blockState, 3);
|
|
this.level
|
|
.neighborChanged(this.worldPosition, blockState.getBlock(), ExperimentalRedstoneUtils.initialOrientation(this.level, this.getPushDirection(), null));
|
|
}
|
|
}
|
|
}
|
|
|
|
public Direction getPushDirection() {
|
|
return this.extending ? this.direction : this.direction.getOpposite();
|
|
}
|
|
|
|
public static void tick(Level level, BlockPos pos, BlockState state, PistonMovingBlockEntity blockEntity) {
|
|
blockEntity.lastTicked = level.getGameTime();
|
|
blockEntity.progressO = blockEntity.progress;
|
|
if (blockEntity.progressO >= 1.0F) {
|
|
if (level.isClientSide && blockEntity.deathTicks < 5) {
|
|
blockEntity.deathTicks++;
|
|
} else {
|
|
level.removeBlockEntity(pos);
|
|
blockEntity.setRemoved();
|
|
if (level.getBlockState(pos).is(Blocks.MOVING_PISTON)) {
|
|
BlockState blockState = Block.updateFromNeighbourShapes(blockEntity.movedState, level, pos);
|
|
if (blockState.isAir()) {
|
|
level.setBlock(pos, blockEntity.movedState, 84);
|
|
Block.updateOrDestroy(blockEntity.movedState, blockState, level, pos, 3);
|
|
} else {
|
|
if (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && (Boolean)blockState.getValue(BlockStateProperties.WATERLOGGED)) {
|
|
blockState = blockState.setValue(BlockStateProperties.WATERLOGGED, false);
|
|
}
|
|
|
|
level.setBlock(pos, blockState, 67);
|
|
level.neighborChanged(pos, blockState.getBlock(), ExperimentalRedstoneUtils.initialOrientation(level, blockEntity.getPushDirection(), null));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
float f = blockEntity.progress + 0.5F;
|
|
moveCollidedEntities(level, pos, f, blockEntity);
|
|
moveStuckEntities(level, pos, f, blockEntity);
|
|
blockEntity.progress = f;
|
|
if (blockEntity.progress >= 1.0F) {
|
|
blockEntity.progress = 1.0F;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
|
super.loadAdditional(tag, registries);
|
|
HolderGetter<Block> holderGetter = (HolderGetter<Block>)(this.level != null ? this.level.holderLookup(Registries.BLOCK) : BuiltInRegistries.BLOCK);
|
|
this.movedState = NbtUtils.readBlockState(holderGetter, tag.getCompound("blockState"));
|
|
this.direction = Direction.from3DDataValue(tag.getInt("facing"));
|
|
this.progress = tag.getFloat("progress");
|
|
this.progressO = this.progress;
|
|
this.extending = tag.getBoolean("extending");
|
|
this.isSourcePiston = tag.getBoolean("source");
|
|
}
|
|
|
|
@Override
|
|
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
|
super.saveAdditional(tag, registries);
|
|
tag.put("blockState", NbtUtils.writeBlockState(this.movedState));
|
|
tag.putInt("facing", this.direction.get3DDataValue());
|
|
tag.putFloat("progress", this.progressO);
|
|
tag.putBoolean("extending", this.extending);
|
|
tag.putBoolean("source", this.isSourcePiston);
|
|
}
|
|
|
|
public VoxelShape getCollisionShape(BlockGetter level, BlockPos pos) {
|
|
VoxelShape voxelShape;
|
|
if (!this.extending && this.isSourcePiston && this.movedState.getBlock() instanceof PistonBaseBlock) {
|
|
voxelShape = this.movedState.setValue(PistonBaseBlock.EXTENDED, true).getCollisionShape(level, pos);
|
|
} else {
|
|
voxelShape = Shapes.empty();
|
|
}
|
|
|
|
Direction direction = (Direction)NOCLIP.get();
|
|
if (this.progress < 1.0 && direction == this.getMovementDirection()) {
|
|
return voxelShape;
|
|
} else {
|
|
BlockState blockState;
|
|
if (this.isSourcePiston()) {
|
|
blockState = Blocks.PISTON_HEAD
|
|
.defaultBlockState()
|
|
.setValue(PistonHeadBlock.FACING, this.direction)
|
|
.setValue(PistonHeadBlock.SHORT, this.extending != 1.0F - this.progress < 0.25F);
|
|
} else {
|
|
blockState = this.movedState;
|
|
}
|
|
|
|
float f = this.getExtendedProgress(this.progress);
|
|
double d = this.direction.getStepX() * f;
|
|
double e = this.direction.getStepY() * f;
|
|
double g = this.direction.getStepZ() * f;
|
|
return Shapes.or(voxelShape, blockState.getCollisionShape(level, pos).move(d, e, g));
|
|
}
|
|
}
|
|
|
|
public long getLastTicked() {
|
|
return this.lastTicked;
|
|
}
|
|
|
|
@Override
|
|
public void setLevel(Level level) {
|
|
super.setLevel(level);
|
|
if (level.holderLookup(Registries.BLOCK).get(this.movedState.getBlock().builtInRegistryHolder().key()).isEmpty()) {
|
|
this.movedState = Blocks.AIR.defaultBlockState();
|
|
}
|
|
}
|
|
}
|