Files
Mohammad-Ali Minaie b86dedad2f base mod created
2018-10-08 09:07:47 -04:00

426 lines
14 KiB
Java

package net.minecraft.pathfinding;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.ChunkCache;
import net.minecraft.world.World;
public abstract class PathNavigate
{
protected EntityLiving entity;
protected World world;
/** The PathEntity being followed. */
@Nullable
protected Path currentPath;
protected double speed;
/** The number of blocks (extra) +/- in each axis that get pulled out as cache for the pathfinder's search space */
private final IAttributeInstance pathSearchRange;
/** Time, in number of ticks, following the current path */
protected int totalTicks;
/** The time when the last position check was done (to detect successful movement) */
private int ticksAtLastPos;
/** Coordinates of the entity's position last time a check was done (part of monitoring getting 'stuck') */
private Vec3d lastPosCheck = Vec3d.ZERO;
private Vec3d timeoutCachedNode = Vec3d.ZERO;
private long timeoutTimer;
private long lastTimeoutCheck;
private double timeoutLimit;
protected float maxDistanceToWaypoint = 0.5F;
protected boolean tryUpdatePath;
private long lastTimeUpdated;
protected NodeProcessor nodeProcessor;
private BlockPos targetPos;
private final PathFinder pathFinder;
public PathNavigate(EntityLiving entityIn, World worldIn)
{
this.entity = entityIn;
this.world = worldIn;
this.pathSearchRange = entityIn.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE);
this.pathFinder = this.getPathFinder();
}
protected abstract PathFinder getPathFinder();
/**
* Sets the speed
*/
public void setSpeed(double speedIn)
{
this.speed = speedIn;
}
/**
* Gets the maximum distance that the path finding will search in.
*/
public float getPathSearchRange()
{
return (float)this.pathSearchRange.getAttributeValue();
}
/**
* Returns true if path can be changed by {@link net.minecraft.pathfinding.PathNavigate#onUpdateNavigation()
* onUpdateNavigation()}
*/
public boolean canUpdatePathOnTimeout()
{
return this.tryUpdatePath;
}
public void updatePath()
{
if (this.world.getTotalWorldTime() - this.lastTimeUpdated > 20L)
{
if (this.targetPos != null)
{
this.currentPath = null;
this.currentPath = this.getPathToPos(this.targetPos);
this.lastTimeUpdated = this.world.getTotalWorldTime();
this.tryUpdatePath = false;
}
}
else
{
this.tryUpdatePath = true;
}
}
/**
* Returns the path to the given coordinates. Args : x, y, z
*/
@Nullable
public final Path getPathToXYZ(double x, double y, double z)
{
return this.getPathToPos(new BlockPos(x, y, z));
}
/**
* Returns path to given BlockPos
*/
@Nullable
public Path getPathToPos(BlockPos pos)
{
if (!this.canNavigate())
{
return null;
}
else if (this.currentPath != null && !this.currentPath.isFinished() && pos.equals(this.targetPos))
{
return this.currentPath;
}
else
{
this.targetPos = pos;
float f = this.getPathSearchRange();
this.world.profiler.startSection("pathfind");
BlockPos blockpos = new BlockPos(this.entity);
int i = (int)(f + 8.0F);
ChunkCache chunkcache = new ChunkCache(this.world, blockpos.add(-i, -i, -i), blockpos.add(i, i, i), 0);
Path path = this.pathFinder.findPath(chunkcache, this.entity, this.targetPos, f);
this.world.profiler.endSection();
return path;
}
}
/**
* Returns the path to the given EntityLiving. Args : entity
*/
@Nullable
public Path getPathToEntityLiving(Entity entityIn)
{
if (!this.canNavigate())
{
return null;
}
else
{
BlockPos blockpos = new BlockPos(entityIn);
if (this.currentPath != null && !this.currentPath.isFinished() && blockpos.equals(this.targetPos))
{
return this.currentPath;
}
else
{
this.targetPos = blockpos;
float f = this.getPathSearchRange();
this.world.profiler.startSection("pathfind");
BlockPos blockpos1 = (new BlockPos(this.entity)).up();
int i = (int)(f + 16.0F);
ChunkCache chunkcache = new ChunkCache(this.world, blockpos1.add(-i, -i, -i), blockpos1.add(i, i, i), 0);
Path path = this.pathFinder.findPath(chunkcache, this.entity, entityIn, f);
this.world.profiler.endSection();
return path;
}
}
}
/**
* Try to find and set a path to XYZ. Returns true if successful. Args : x, y, z, speed
*/
public boolean tryMoveToXYZ(double x, double y, double z, double speedIn)
{
return this.setPath(this.getPathToXYZ(x, y, z), speedIn);
}
/**
* Try to find and set a path to EntityLiving. Returns true if successful. Args : entity, speed
*/
public boolean tryMoveToEntityLiving(Entity entityIn, double speedIn)
{
Path path = this.getPathToEntityLiving(entityIn);
return path != null && this.setPath(path, speedIn);
}
/**
* Sets a new path. If it's diferent from the old path. Checks to adjust path for sun avoiding, and stores start
* coords. Args : path, speed
*/
public boolean setPath(@Nullable Path pathentityIn, double speedIn)
{
if (pathentityIn == null)
{
this.currentPath = null;
return false;
}
else
{
if (!pathentityIn.isSamePath(this.currentPath))
{
this.currentPath = pathentityIn;
}
this.removeSunnyPath();
if (this.currentPath.getCurrentPathLength() <= 0)
{
return false;
}
else
{
this.speed = speedIn;
Vec3d vec3d = this.getEntityPosition();
this.ticksAtLastPos = this.totalTicks;
this.lastPosCheck = vec3d;
return true;
}
}
}
/**
* gets the actively used PathEntity
*/
@Nullable
public Path getPath()
{
return this.currentPath;
}
public void onUpdateNavigation()
{
++this.totalTicks;
if (this.tryUpdatePath)
{
this.updatePath();
}
if (!this.noPath())
{
if (this.canNavigate())
{
this.pathFollow();
}
else if (this.currentPath != null && this.currentPath.getCurrentPathIndex() < this.currentPath.getCurrentPathLength())
{
Vec3d vec3d = this.getEntityPosition();
Vec3d vec3d1 = this.currentPath.getVectorFromIndex(this.entity, this.currentPath.getCurrentPathIndex());
if (vec3d.y > vec3d1.y && !this.entity.onGround && MathHelper.floor(vec3d.x) == MathHelper.floor(vec3d1.x) && MathHelper.floor(vec3d.z) == MathHelper.floor(vec3d1.z))
{
this.currentPath.setCurrentPathIndex(this.currentPath.getCurrentPathIndex() + 1);
}
}
this.debugPathFinding();
if (!this.noPath())
{
Vec3d vec3d2 = this.currentPath.getPosition(this.entity);
BlockPos blockpos = (new BlockPos(vec3d2)).down();
AxisAlignedBB axisalignedbb = this.world.getBlockState(blockpos).getBoundingBox(this.world, blockpos);
vec3d2 = vec3d2.subtract(0.0D, 1.0D - axisalignedbb.maxY, 0.0D);
this.entity.getMoveHelper().setMoveTo(vec3d2.x, vec3d2.y, vec3d2.z, this.speed);
}
}
}
protected void debugPathFinding()
{
}
protected void pathFollow()
{
Vec3d vec3d = this.getEntityPosition();
int i = this.currentPath.getCurrentPathLength();
for (int j = this.currentPath.getCurrentPathIndex(); j < this.currentPath.getCurrentPathLength(); ++j)
{
if ((double)this.currentPath.getPathPointFromIndex(j).y != Math.floor(vec3d.y))
{
i = j;
break;
}
}
this.maxDistanceToWaypoint = this.entity.width > 0.75F ? this.entity.width / 2.0F : 0.75F - this.entity.width / 2.0F;
Vec3d vec3d1 = this.currentPath.getCurrentPos();
if (MathHelper.abs((float)(this.entity.posX - (vec3d1.x + 0.5D))) < this.maxDistanceToWaypoint && MathHelper.abs((float)(this.entity.posZ - (vec3d1.z + 0.5D))) < this.maxDistanceToWaypoint && Math.abs(this.entity.posY - vec3d1.y) < 1.0D)
{
this.currentPath.setCurrentPathIndex(this.currentPath.getCurrentPathIndex() + 1);
}
int k = MathHelper.ceil(this.entity.width);
int l = MathHelper.ceil(this.entity.height);
int i1 = k;
for (int j1 = i - 1; j1 >= this.currentPath.getCurrentPathIndex(); --j1)
{
if (this.isDirectPathBetweenPoints(vec3d, this.currentPath.getVectorFromIndex(this.entity, j1), k, l, i1))
{
this.currentPath.setCurrentPathIndex(j1);
break;
}
}
this.checkForStuck(vec3d);
}
/**
* Checks if entity haven't been moved when last checked and if so, clears current {@link
* net.minecraft.pathfinding.PathEntity}
*/
protected void checkForStuck(Vec3d positionVec3)
{
if (this.totalTicks - this.ticksAtLastPos > 100)
{
if (positionVec3.squareDistanceTo(this.lastPosCheck) < 2.25D)
{
this.clearPath();
}
this.ticksAtLastPos = this.totalTicks;
this.lastPosCheck = positionVec3;
}
if (this.currentPath != null && !this.currentPath.isFinished())
{
Vec3d vec3d = this.currentPath.getCurrentPos();
if (vec3d.equals(this.timeoutCachedNode))
{
this.timeoutTimer += System.currentTimeMillis() - this.lastTimeoutCheck;
}
else
{
this.timeoutCachedNode = vec3d;
double d0 = positionVec3.distanceTo(this.timeoutCachedNode);
this.timeoutLimit = this.entity.getAIMoveSpeed() > 0.0F ? d0 / (double)this.entity.getAIMoveSpeed() * 1000.0D : 0.0D;
}
if (this.timeoutLimit > 0.0D && (double)this.timeoutTimer > this.timeoutLimit * 3.0D)
{
this.timeoutCachedNode = Vec3d.ZERO;
this.timeoutTimer = 0L;
this.timeoutLimit = 0.0D;
this.clearPath();
}
this.lastTimeoutCheck = System.currentTimeMillis();
}
}
/**
* If null path or reached the end
*/
public boolean noPath()
{
return this.currentPath == null || this.currentPath.isFinished();
}
/**
* sets active PathEntity to null
*/
public void clearPath()
{
this.currentPath = null;
}
protected abstract Vec3d getEntityPosition();
/**
* If on ground or swimming and can swim
*/
protected abstract boolean canNavigate();
/**
* Returns true if the entity is in water or lava, false otherwise
*/
protected boolean isInLiquid()
{
return this.entity.isInWater() || this.entity.isInLava();
}
/**
* Trims path data from the end to the first sun covered block
*/
protected void removeSunnyPath()
{
if (this.currentPath != null)
{
for (int i = 0; i < this.currentPath.getCurrentPathLength(); ++i)
{
PathPoint pathpoint = this.currentPath.getPathPointFromIndex(i);
PathPoint pathpoint1 = i + 1 < this.currentPath.getCurrentPathLength() ? this.currentPath.getPathPointFromIndex(i + 1) : null;
IBlockState iblockstate = this.world.getBlockState(new BlockPos(pathpoint.x, pathpoint.y, pathpoint.z));
Block block = iblockstate.getBlock();
if (block == Blocks.CAULDRON)
{
this.currentPath.setPoint(i, pathpoint.cloneMove(pathpoint.x, pathpoint.y + 1, pathpoint.z));
if (pathpoint1 != null && pathpoint.y >= pathpoint1.y)
{
this.currentPath.setPoint(i + 1, pathpoint1.cloneMove(pathpoint1.x, pathpoint.y + 1, pathpoint1.z));
}
}
}
}
}
/**
* Checks if the specified entity can safely walk to the specified location.
*/
protected abstract boolean isDirectPathBetweenPoints(Vec3d posVec31, Vec3d posVec32, int sizeX, int sizeY, int sizeZ);
public boolean canEntityStandOnPos(BlockPos pos)
{
return this.world.getBlockState(pos.down()).isFullBlock();
}
public NodeProcessor getNodeProcessor()
{
return this.nodeProcessor;
}
}