528 lines
18 KiB
Java
528 lines
18 KiB
Java
package net.minecraft.world.level.pathfinder;
|
|
|
|
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction;
|
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
|
import it.unimi.dsi.fastutil.objects.Object2BooleanFunction;
|
|
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
|
|
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
|
|
import java.util.EnumSet;
|
|
import java.util.Set;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.Direction.Axis;
|
|
import net.minecraft.core.Direction.Plane;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.tags.FluidTags;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.level.PathNavigationRegion;
|
|
import net.minecraft.world.level.block.BaseRailBlock;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.DoorBlock;
|
|
import net.minecraft.world.level.block.FenceGateBlock;
|
|
import net.minecraft.world.level.block.LeavesBlock;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.level.material.Fluids;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class WalkNodeEvaluator extends NodeEvaluator {
|
|
public static final double SPACE_BETWEEN_WALL_POSTS = 0.5;
|
|
private static final double DEFAULT_MOB_JUMP_HEIGHT = 1.125;
|
|
private final Long2ObjectMap<PathType> pathTypesByPosCacheByMob = new Long2ObjectOpenHashMap<>();
|
|
private final Object2BooleanMap<AABB> collisionCache = new Object2BooleanOpenHashMap<>();
|
|
private final Node[] reusableNeighbors = new Node[Plane.HORIZONTAL.length()];
|
|
|
|
@Override
|
|
public void prepare(PathNavigationRegion level, Mob mob) {
|
|
super.prepare(level, mob);
|
|
mob.onPathfindingStart();
|
|
}
|
|
|
|
@Override
|
|
public void done() {
|
|
this.mob.onPathfindingDone();
|
|
this.pathTypesByPosCacheByMob.clear();
|
|
this.collisionCache.clear();
|
|
super.done();
|
|
}
|
|
|
|
@Override
|
|
public Node getStart() {
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
int i = this.mob.getBlockY();
|
|
BlockState blockState = this.currentContext.getBlockState(mutableBlockPos.set(this.mob.getX(), (double)i, this.mob.getZ()));
|
|
if (!this.mob.canStandOnFluid(blockState.getFluidState())) {
|
|
if (this.canFloat() && this.mob.isInWater()) {
|
|
while (true) {
|
|
if (!blockState.is(Blocks.WATER) && blockState.getFluidState() != Fluids.WATER.getSource(false)) {
|
|
i--;
|
|
break;
|
|
}
|
|
|
|
blockState = this.currentContext.getBlockState(mutableBlockPos.set(this.mob.getX(), (double)(++i), this.mob.getZ()));
|
|
}
|
|
} else if (this.mob.onGround()) {
|
|
i = Mth.floor(this.mob.getY() + 0.5);
|
|
} else {
|
|
mutableBlockPos.set(this.mob.getX(), this.mob.getY() + 1.0, this.mob.getZ());
|
|
|
|
while (mutableBlockPos.getY() > this.currentContext.level().getMinY()) {
|
|
i = mutableBlockPos.getY();
|
|
mutableBlockPos.setY(mutableBlockPos.getY() - 1);
|
|
BlockState blockState2 = this.currentContext.getBlockState(mutableBlockPos);
|
|
if (!blockState2.isAir() && !blockState2.isPathfindable(PathComputationType.LAND)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
while (this.mob.canStandOnFluid(blockState.getFluidState())) {
|
|
blockState = this.currentContext.getBlockState(mutableBlockPos.set(this.mob.getX(), (double)(++i), this.mob.getZ()));
|
|
}
|
|
|
|
i--;
|
|
}
|
|
|
|
BlockPos blockPos = this.mob.blockPosition();
|
|
if (!this.canStartAt(mutableBlockPos.set(blockPos.getX(), i, blockPos.getZ()))) {
|
|
AABB aABB = this.mob.getBoundingBox();
|
|
if (this.canStartAt(mutableBlockPos.set(aABB.minX, (double)i, aABB.minZ))
|
|
|| this.canStartAt(mutableBlockPos.set(aABB.minX, (double)i, aABB.maxZ))
|
|
|| this.canStartAt(mutableBlockPos.set(aABB.maxX, (double)i, aABB.minZ))
|
|
|| this.canStartAt(mutableBlockPos.set(aABB.maxX, (double)i, aABB.maxZ))) {
|
|
return this.getStartNode(mutableBlockPos);
|
|
}
|
|
}
|
|
|
|
return this.getStartNode(new BlockPos(blockPos.getX(), i, blockPos.getZ()));
|
|
}
|
|
|
|
protected Node getStartNode(BlockPos pos) {
|
|
Node node = this.getNode(pos);
|
|
node.type = this.getCachedPathType(node.x, node.y, node.z);
|
|
node.costMalus = this.mob.getPathfindingMalus(node.type);
|
|
return node;
|
|
}
|
|
|
|
protected boolean canStartAt(BlockPos pos) {
|
|
PathType pathType = this.getCachedPathType(pos.getX(), pos.getY(), pos.getZ());
|
|
return pathType != PathType.OPEN && this.mob.getPathfindingMalus(pathType) >= 0.0F;
|
|
}
|
|
|
|
@Override
|
|
public Target getTarget(double x, double y, double z) {
|
|
return this.getTargetNodeAt(x, y, z);
|
|
}
|
|
|
|
@Override
|
|
public int getNeighbors(Node[] outputArray, Node node) {
|
|
int i = 0;
|
|
int j = 0;
|
|
PathType pathType = this.getCachedPathType(node.x, node.y + 1, node.z);
|
|
PathType pathType2 = this.getCachedPathType(node.x, node.y, node.z);
|
|
if (this.mob.getPathfindingMalus(pathType) >= 0.0F && pathType2 != PathType.STICKY_HONEY) {
|
|
j = Mth.floor(Math.max(1.0F, this.mob.maxUpStep()));
|
|
}
|
|
|
|
double d = this.getFloorLevel(new BlockPos(node.x, node.y, node.z));
|
|
|
|
for (Direction direction : Plane.HORIZONTAL) {
|
|
Node node2 = this.findAcceptedNode(node.x + direction.getStepX(), node.y, node.z + direction.getStepZ(), j, d, direction, pathType2);
|
|
this.reusableNeighbors[direction.get2DDataValue()] = node2;
|
|
if (this.isNeighborValid(node2, node)) {
|
|
outputArray[i++] = node2;
|
|
}
|
|
}
|
|
|
|
for (Direction directionx : Plane.HORIZONTAL) {
|
|
Direction direction2 = directionx.getClockWise();
|
|
if (this.isDiagonalValid(node, this.reusableNeighbors[directionx.get2DDataValue()], this.reusableNeighbors[direction2.get2DDataValue()])) {
|
|
Node node3 = this.findAcceptedNode(
|
|
node.x + directionx.getStepX() + direction2.getStepX(), node.y, node.z + directionx.getStepZ() + direction2.getStepZ(), j, d, directionx, pathType2
|
|
);
|
|
if (this.isDiagonalValid(node3)) {
|
|
outputArray[i++] = node3;
|
|
}
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
protected boolean isNeighborValid(@Nullable Node neighbor, Node node) {
|
|
return neighbor != null && !neighbor.closed && (neighbor.costMalus >= 0.0F || node.costMalus < 0.0F);
|
|
}
|
|
|
|
protected boolean isDiagonalValid(Node root, @Nullable Node xNode, @Nullable Node zNode) {
|
|
if (zNode == null || xNode == null || zNode.y > root.y || xNode.y > root.y) {
|
|
return false;
|
|
} else if (xNode.type != PathType.WALKABLE_DOOR && zNode.type != PathType.WALKABLE_DOOR) {
|
|
boolean bl = zNode.type == PathType.FENCE && xNode.type == PathType.FENCE && this.mob.getBbWidth() < 0.5;
|
|
return (zNode.y < root.y || zNode.costMalus >= 0.0F || bl) && (xNode.y < root.y || xNode.costMalus >= 0.0F || bl);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected boolean isDiagonalValid(@Nullable Node node) {
|
|
if (node == null || node.closed) {
|
|
return false;
|
|
} else {
|
|
return node.type == PathType.WALKABLE_DOOR ? false : node.costMalus >= 0.0F;
|
|
}
|
|
}
|
|
|
|
private static boolean doesBlockHavePartialCollision(PathType pathType) {
|
|
return pathType == PathType.FENCE || pathType == PathType.DOOR_WOOD_CLOSED || pathType == PathType.DOOR_IRON_CLOSED;
|
|
}
|
|
|
|
private boolean canReachWithoutCollision(Node node) {
|
|
AABB aABB = this.mob.getBoundingBox();
|
|
Vec3 vec3 = new Vec3(
|
|
node.x - this.mob.getX() + aABB.getXsize() / 2.0, node.y - this.mob.getY() + aABB.getYsize() / 2.0, node.z - this.mob.getZ() + aABB.getZsize() / 2.0
|
|
);
|
|
int i = Mth.ceil(vec3.length() / aABB.getSize());
|
|
vec3 = vec3.scale(1.0F / i);
|
|
|
|
for (int j = 1; j <= i; j++) {
|
|
aABB = aABB.move(vec3);
|
|
if (this.hasCollisions(aABB)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected double getFloorLevel(BlockPos pos) {
|
|
BlockGetter blockGetter = this.currentContext.level();
|
|
return (this.canFloat() || this.isAmphibious()) && blockGetter.getFluidState(pos).is(FluidTags.WATER) ? pos.getY() + 0.5 : getFloorLevel(blockGetter, pos);
|
|
}
|
|
|
|
public static double getFloorLevel(BlockGetter level, BlockPos pos) {
|
|
BlockPos blockPos = pos.below();
|
|
VoxelShape voxelShape = level.getBlockState(blockPos).getCollisionShape(level, blockPos);
|
|
return blockPos.getY() + (voxelShape.isEmpty() ? 0.0 : voxelShape.max(Axis.Y));
|
|
}
|
|
|
|
protected boolean isAmphibious() {
|
|
return false;
|
|
}
|
|
|
|
@Nullable
|
|
protected Node findAcceptedNode(int x, int y, int z, int verticalDeltaLimit, double nodeFloorLevel, Direction direction, PathType pathType) {
|
|
Node node = null;
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
double d = this.getFloorLevel(mutableBlockPos.set(x, y, z));
|
|
if (d - nodeFloorLevel > this.getMobJumpHeight()) {
|
|
return null;
|
|
} else {
|
|
PathType pathType2 = this.getCachedPathType(x, y, z);
|
|
float f = this.mob.getPathfindingMalus(pathType2);
|
|
if (f >= 0.0F) {
|
|
node = this.getNodeAndUpdateCostToMax(x, y, z, pathType2, f);
|
|
}
|
|
|
|
if (doesBlockHavePartialCollision(pathType) && node != null && node.costMalus >= 0.0F && !this.canReachWithoutCollision(node)) {
|
|
node = null;
|
|
}
|
|
|
|
if (pathType2 != PathType.WALKABLE && (!this.isAmphibious() || pathType2 != PathType.WATER)) {
|
|
if ((node == null || node.costMalus < 0.0F)
|
|
&& verticalDeltaLimit > 0
|
|
&& (pathType2 != PathType.FENCE || this.canWalkOverFences())
|
|
&& pathType2 != PathType.UNPASSABLE_RAIL
|
|
&& pathType2 != PathType.TRAPDOOR
|
|
&& pathType2 != PathType.POWDER_SNOW) {
|
|
node = this.tryJumpOn(x, y, z, verticalDeltaLimit, nodeFloorLevel, direction, pathType, mutableBlockPos);
|
|
} else if (!this.isAmphibious() && pathType2 == PathType.WATER && !this.canFloat()) {
|
|
node = this.tryFindFirstNonWaterBelow(x, y, z, node);
|
|
} else if (pathType2 == PathType.OPEN) {
|
|
node = this.tryFindFirstGroundNodeBelow(x, y, z);
|
|
} else if (doesBlockHavePartialCollision(pathType2) && node == null) {
|
|
node = this.getClosedNode(x, y, z, pathType2);
|
|
}
|
|
|
|
return node;
|
|
} else {
|
|
return node;
|
|
}
|
|
}
|
|
}
|
|
|
|
private double getMobJumpHeight() {
|
|
return Math.max(1.125, this.mob.maxUpStep());
|
|
}
|
|
|
|
private Node getNodeAndUpdateCostToMax(int x, int y, int z, PathType pathType, float malus) {
|
|
Node node = this.getNode(x, y, z);
|
|
node.type = pathType;
|
|
node.costMalus = Math.max(node.costMalus, malus);
|
|
return node;
|
|
}
|
|
|
|
private Node getBlockedNode(int x, int y, int z) {
|
|
Node node = this.getNode(x, y, z);
|
|
node.type = PathType.BLOCKED;
|
|
node.costMalus = -1.0F;
|
|
return node;
|
|
}
|
|
|
|
private Node getClosedNode(int x, int y, int z, PathType pathType) {
|
|
Node node = this.getNode(x, y, z);
|
|
node.closed = true;
|
|
node.type = pathType;
|
|
node.costMalus = pathType.getMalus();
|
|
return node;
|
|
}
|
|
|
|
@Nullable
|
|
private Node tryJumpOn(
|
|
int x, int y, int z, int verticalDeltaLimit, double nodeFloorLevel, Direction direction, PathType pathType, BlockPos.MutableBlockPos pos
|
|
) {
|
|
Node node = this.findAcceptedNode(x, y + 1, z, verticalDeltaLimit - 1, nodeFloorLevel, direction, pathType);
|
|
if (node == null) {
|
|
return null;
|
|
} else if (this.mob.getBbWidth() >= 1.0F) {
|
|
return node;
|
|
} else if (node.type != PathType.OPEN && node.type != PathType.WALKABLE) {
|
|
return node;
|
|
} else {
|
|
double d = x - direction.getStepX() + 0.5;
|
|
double e = z - direction.getStepZ() + 0.5;
|
|
double f = this.mob.getBbWidth() / 2.0;
|
|
AABB aABB = new AABB(
|
|
d - f,
|
|
this.getFloorLevel(pos.set(d, (double)(y + 1), e)) + 0.001,
|
|
e - f,
|
|
d + f,
|
|
this.mob.getBbHeight() + this.getFloorLevel(pos.set((double)node.x, (double)node.y, (double)node.z)) - 0.002,
|
|
e + f
|
|
);
|
|
return this.hasCollisions(aABB) ? null : node;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private Node tryFindFirstNonWaterBelow(int x, int y, int z, @Nullable Node node) {
|
|
y--;
|
|
|
|
while (y > this.mob.level().getMinY()) {
|
|
PathType pathType = this.getCachedPathType(x, y, z);
|
|
if (pathType != PathType.WATER) {
|
|
return node;
|
|
}
|
|
|
|
node = this.getNodeAndUpdateCostToMax(x, y, z, pathType, this.mob.getPathfindingMalus(pathType));
|
|
y--;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
private Node tryFindFirstGroundNodeBelow(int x, int y, int z) {
|
|
for (int i = y - 1; i >= this.mob.level().getMinY(); i--) {
|
|
if (y - i > this.mob.getMaxFallDistance()) {
|
|
return this.getBlockedNode(x, i, z);
|
|
}
|
|
|
|
PathType pathType = this.getCachedPathType(x, i, z);
|
|
float f = this.mob.getPathfindingMalus(pathType);
|
|
if (pathType != PathType.OPEN) {
|
|
if (f >= 0.0F) {
|
|
return this.getNodeAndUpdateCostToMax(x, i, z, pathType, f);
|
|
}
|
|
|
|
return this.getBlockedNode(x, i, z);
|
|
}
|
|
}
|
|
|
|
return this.getBlockedNode(x, y, z);
|
|
}
|
|
|
|
private boolean hasCollisions(AABB boundingBox) {
|
|
return this.collisionCache
|
|
.computeIfAbsent(boundingBox, (Object2BooleanFunction<? super AABB>)(object -> !this.currentContext.level().noCollision(this.mob, boundingBox)));
|
|
}
|
|
|
|
protected PathType getCachedPathType(int x, int y, int z) {
|
|
return this.pathTypesByPosCacheByMob
|
|
.computeIfAbsent(BlockPos.asLong(x, y, z), (Long2ObjectFunction<? extends PathType>)(l -> this.getPathTypeOfMob(this.currentContext, x, y, z, this.mob)));
|
|
}
|
|
|
|
@Override
|
|
public PathType getPathTypeOfMob(PathfindingContext context, int x, int y, int z, Mob mob) {
|
|
Set<PathType> set = this.getPathTypeWithinMobBB(context, x, y, z);
|
|
if (set.contains(PathType.FENCE)) {
|
|
return PathType.FENCE;
|
|
} else if (set.contains(PathType.UNPASSABLE_RAIL)) {
|
|
return PathType.UNPASSABLE_RAIL;
|
|
} else {
|
|
PathType pathType = PathType.BLOCKED;
|
|
|
|
for (PathType pathType2 : set) {
|
|
if (mob.getPathfindingMalus(pathType2) < 0.0F) {
|
|
return pathType2;
|
|
}
|
|
|
|
if (mob.getPathfindingMalus(pathType2) >= mob.getPathfindingMalus(pathType)) {
|
|
pathType = pathType2;
|
|
}
|
|
}
|
|
|
|
return this.entityWidth <= 1
|
|
&& pathType != PathType.OPEN
|
|
&& mob.getPathfindingMalus(pathType) == 0.0F
|
|
&& this.getPathType(context, x, y, z) == PathType.OPEN
|
|
? PathType.OPEN
|
|
: pathType;
|
|
}
|
|
}
|
|
|
|
public Set<PathType> getPathTypeWithinMobBB(PathfindingContext context, int x, int y, int z) {
|
|
EnumSet<PathType> enumSet = EnumSet.noneOf(PathType.class);
|
|
|
|
for (int i = 0; i < this.entityWidth; i++) {
|
|
for (int j = 0; j < this.entityHeight; j++) {
|
|
for (int k = 0; k < this.entityDepth; k++) {
|
|
int l = i + x;
|
|
int m = j + y;
|
|
int n = k + z;
|
|
PathType pathType = this.getPathType(context, l, m, n);
|
|
BlockPos blockPos = this.mob.blockPosition();
|
|
boolean bl = this.canPassDoors();
|
|
if (pathType == PathType.DOOR_WOOD_CLOSED && this.canOpenDoors() && bl) {
|
|
pathType = PathType.WALKABLE_DOOR;
|
|
}
|
|
|
|
if (pathType == PathType.DOOR_OPEN && !bl) {
|
|
pathType = PathType.BLOCKED;
|
|
}
|
|
|
|
if (pathType == PathType.RAIL
|
|
&& this.getPathType(context, blockPos.getX(), blockPos.getY(), blockPos.getZ()) != PathType.RAIL
|
|
&& this.getPathType(context, blockPos.getX(), blockPos.getY() - 1, blockPos.getZ()) != PathType.RAIL) {
|
|
pathType = PathType.UNPASSABLE_RAIL;
|
|
}
|
|
|
|
enumSet.add(pathType);
|
|
}
|
|
}
|
|
}
|
|
|
|
return enumSet;
|
|
}
|
|
|
|
@Override
|
|
public PathType getPathType(PathfindingContext context, int x, int y, int z) {
|
|
return getPathTypeStatic(context, new BlockPos.MutableBlockPos(x, y, z));
|
|
}
|
|
|
|
public static PathType getPathTypeStatic(Mob mob, BlockPos pos) {
|
|
return getPathTypeStatic(new PathfindingContext(mob.level(), mob), pos.mutable());
|
|
}
|
|
|
|
public static PathType getPathTypeStatic(PathfindingContext context, BlockPos.MutableBlockPos pos) {
|
|
int i = pos.getX();
|
|
int j = pos.getY();
|
|
int k = pos.getZ();
|
|
PathType pathType = context.getPathTypeFromState(i, j, k);
|
|
if (pathType == PathType.OPEN && j >= context.level().getMinY() + 1) {
|
|
return switch (context.getPathTypeFromState(i, j - 1, k)) {
|
|
case OPEN, WATER, LAVA, WALKABLE -> PathType.OPEN;
|
|
case DAMAGE_FIRE -> PathType.DAMAGE_FIRE;
|
|
case DAMAGE_OTHER -> PathType.DAMAGE_OTHER;
|
|
case STICKY_HONEY -> PathType.STICKY_HONEY;
|
|
case POWDER_SNOW -> PathType.DANGER_POWDER_SNOW;
|
|
case DAMAGE_CAUTIOUS -> PathType.DAMAGE_CAUTIOUS;
|
|
case TRAPDOOR -> PathType.DANGER_TRAPDOOR;
|
|
default -> checkNeighbourBlocks(context, i, j, k, PathType.WALKABLE);
|
|
};
|
|
} else {
|
|
return pathType;
|
|
}
|
|
}
|
|
|
|
public static PathType checkNeighbourBlocks(PathfindingContext context, int x, int y, int z, PathType pathType) {
|
|
for (int i = -1; i <= 1; i++) {
|
|
for (int j = -1; j <= 1; j++) {
|
|
for (int k = -1; k <= 1; k++) {
|
|
if (i != 0 || k != 0) {
|
|
PathType pathType2 = context.getPathTypeFromState(x + i, y + j, z + k);
|
|
if (pathType2 == PathType.DAMAGE_OTHER) {
|
|
return PathType.DANGER_OTHER;
|
|
}
|
|
|
|
if (pathType2 == PathType.DAMAGE_FIRE || pathType2 == PathType.LAVA) {
|
|
return PathType.DANGER_FIRE;
|
|
}
|
|
|
|
if (pathType2 == PathType.WATER) {
|
|
return PathType.WATER_BORDER;
|
|
}
|
|
|
|
if (pathType2 == PathType.DAMAGE_CAUTIOUS) {
|
|
return PathType.DAMAGE_CAUTIOUS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return pathType;
|
|
}
|
|
|
|
protected static PathType getPathTypeFromState(BlockGetter level, BlockPos pos) {
|
|
BlockState blockState = level.getBlockState(pos);
|
|
Block block = blockState.getBlock();
|
|
if (blockState.isAir()) {
|
|
return PathType.OPEN;
|
|
} else if (blockState.is(BlockTags.TRAPDOORS) || blockState.is(Blocks.LILY_PAD) || blockState.is(Blocks.BIG_DRIPLEAF)) {
|
|
return PathType.TRAPDOOR;
|
|
} else if (blockState.is(Blocks.POWDER_SNOW)) {
|
|
return PathType.POWDER_SNOW;
|
|
} else if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH)) {
|
|
return PathType.DAMAGE_OTHER;
|
|
} else if (blockState.is(Blocks.HONEY_BLOCK)) {
|
|
return PathType.STICKY_HONEY;
|
|
} else if (blockState.is(Blocks.COCOA)) {
|
|
return PathType.COCOA;
|
|
} else if (!blockState.is(Blocks.WITHER_ROSE) && !blockState.is(Blocks.POINTED_DRIPSTONE)) {
|
|
FluidState fluidState = blockState.getFluidState();
|
|
if (fluidState.is(FluidTags.LAVA)) {
|
|
return PathType.LAVA;
|
|
} else if (isBurningBlock(blockState)) {
|
|
return PathType.DAMAGE_FIRE;
|
|
} else if (block instanceof DoorBlock doorBlock) {
|
|
if ((Boolean)blockState.getValue(DoorBlock.OPEN)) {
|
|
return PathType.DOOR_OPEN;
|
|
} else {
|
|
return doorBlock.type().canOpenByHand() ? PathType.DOOR_WOOD_CLOSED : PathType.DOOR_IRON_CLOSED;
|
|
}
|
|
} else if (block instanceof BaseRailBlock) {
|
|
return PathType.RAIL;
|
|
} else if (block instanceof LeavesBlock) {
|
|
return PathType.LEAVES;
|
|
} else if (!blockState.is(BlockTags.FENCES)
|
|
&& !blockState.is(BlockTags.WALLS)
|
|
&& (!(block instanceof FenceGateBlock) || (Boolean)blockState.getValue(FenceGateBlock.OPEN))) {
|
|
if (!blockState.isPathfindable(PathComputationType.LAND)) {
|
|
return PathType.BLOCKED;
|
|
} else {
|
|
return fluidState.is(FluidTags.WATER) ? PathType.WATER : PathType.OPEN;
|
|
}
|
|
} else {
|
|
return PathType.FENCE;
|
|
}
|
|
} else {
|
|
return PathType.DAMAGE_CAUTIOUS;
|
|
}
|
|
}
|
|
}
|