183 lines
5.4 KiB
Java
183 lines
5.4 KiB
Java
package net.minecraft.world.entity.ai.control;
|
|
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.pathfinder.NodeEvaluator;
|
|
import net.minecraft.world.level.pathfinder.PathType;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
|
|
public class MoveControl implements Control {
|
|
public static final float MIN_SPEED = 5.0E-4F;
|
|
public static final float MIN_SPEED_SQR = 2.5000003E-7F;
|
|
protected static final int MAX_TURN = 90;
|
|
protected final Mob mob;
|
|
protected double wantedX;
|
|
protected double wantedY;
|
|
protected double wantedZ;
|
|
protected double speedModifier;
|
|
protected float strafeForwards;
|
|
protected float strafeRight;
|
|
protected MoveControl.Operation operation = MoveControl.Operation.WAIT;
|
|
|
|
public MoveControl(Mob mob) {
|
|
this.mob = mob;
|
|
}
|
|
|
|
/**
|
|
* @return If the mob is currently trying to go somewhere
|
|
*/
|
|
public boolean hasWanted() {
|
|
return this.operation == MoveControl.Operation.MOVE_TO;
|
|
}
|
|
|
|
public double getSpeedModifier() {
|
|
return this.speedModifier;
|
|
}
|
|
|
|
/**
|
|
* Sets the speed and location to move to
|
|
*/
|
|
public void setWantedPosition(double x, double y, double z, double speed) {
|
|
this.wantedX = x;
|
|
this.wantedY = y;
|
|
this.wantedZ = z;
|
|
this.speedModifier = speed;
|
|
if (this.operation != MoveControl.Operation.JUMPING) {
|
|
this.operation = MoveControl.Operation.MOVE_TO;
|
|
}
|
|
}
|
|
|
|
public void strafe(float forward, float strafe) {
|
|
this.operation = MoveControl.Operation.STRAFE;
|
|
this.strafeForwards = forward;
|
|
this.strafeRight = strafe;
|
|
this.speedModifier = 0.25;
|
|
}
|
|
|
|
public void tick() {
|
|
if (this.operation == MoveControl.Operation.STRAFE) {
|
|
float f = (float)this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED);
|
|
float g = (float)this.speedModifier * f;
|
|
float h = this.strafeForwards;
|
|
float i = this.strafeRight;
|
|
float j = Mth.sqrt(h * h + i * i);
|
|
if (j < 1.0F) {
|
|
j = 1.0F;
|
|
}
|
|
|
|
j = g / j;
|
|
h *= j;
|
|
i *= j;
|
|
float k = Mth.sin(this.mob.getYRot() * (float) (Math.PI / 180.0));
|
|
float l = Mth.cos(this.mob.getYRot() * (float) (Math.PI / 180.0));
|
|
float m = h * l - i * k;
|
|
float n = i * l + h * k;
|
|
if (!this.isWalkable(m, n)) {
|
|
this.strafeForwards = 1.0F;
|
|
this.strafeRight = 0.0F;
|
|
}
|
|
|
|
this.mob.setSpeed(g);
|
|
this.mob.setZza(this.strafeForwards);
|
|
this.mob.setXxa(this.strafeRight);
|
|
this.operation = MoveControl.Operation.WAIT;
|
|
} else if (this.operation == MoveControl.Operation.MOVE_TO) {
|
|
this.operation = MoveControl.Operation.WAIT;
|
|
double d = this.wantedX - this.mob.getX();
|
|
double e = this.wantedZ - this.mob.getZ();
|
|
double o = this.wantedY - this.mob.getY();
|
|
double p = d * d + o * o + e * e;
|
|
if (p < 2.5000003E-7F) {
|
|
this.mob.setZza(0.0F);
|
|
return;
|
|
}
|
|
|
|
float n = (float)(Mth.atan2(e, d) * 180.0F / (float)Math.PI) - 90.0F;
|
|
this.mob.setYRot(this.rotlerp(this.mob.getYRot(), n, 90.0F));
|
|
this.mob.setSpeed((float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
|
|
BlockPos blockPos = this.mob.blockPosition();
|
|
BlockState blockState = this.mob.level().getBlockState(blockPos);
|
|
VoxelShape voxelShape = blockState.getCollisionShape(this.mob.level(), blockPos);
|
|
if (o > this.mob.maxUpStep() && d * d + e * e < Math.max(1.0F, this.mob.getBbWidth())
|
|
|| !voxelShape.isEmpty()
|
|
&& this.mob.getY() < voxelShape.max(Direction.Axis.Y) + blockPos.getY()
|
|
&& !blockState.is(BlockTags.DOORS)
|
|
&& !blockState.is(BlockTags.FENCES)) {
|
|
this.mob.getJumpControl().jump();
|
|
this.operation = MoveControl.Operation.JUMPING;
|
|
}
|
|
} else if (this.operation == MoveControl.Operation.JUMPING) {
|
|
this.mob.setSpeed((float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
|
|
if (this.mob.onGround()) {
|
|
this.operation = MoveControl.Operation.WAIT;
|
|
}
|
|
} else {
|
|
this.mob.setZza(0.0F);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return true if the mob can walk successfully to a given X and Z
|
|
*/
|
|
private boolean isWalkable(float relativeX, float relativeZ) {
|
|
PathNavigation pathNavigation = this.mob.getNavigation();
|
|
if (pathNavigation != null) {
|
|
NodeEvaluator nodeEvaluator = pathNavigation.getNodeEvaluator();
|
|
if (nodeEvaluator != null
|
|
&& nodeEvaluator.getPathType(this.mob, BlockPos.containing(this.mob.getX() + relativeX, this.mob.getBlockY(), this.mob.getZ() + relativeZ))
|
|
!= PathType.WALKABLE) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Attempt to rotate the first angle to become the second angle, but only allow overall direction change to at max be third parameter
|
|
*/
|
|
protected float rotlerp(float sourceAngle, float targetAngle, float maximumChange) {
|
|
float f = Mth.wrapDegrees(targetAngle - sourceAngle);
|
|
if (f > maximumChange) {
|
|
f = maximumChange;
|
|
}
|
|
|
|
if (f < -maximumChange) {
|
|
f = -maximumChange;
|
|
}
|
|
|
|
float g = sourceAngle + f;
|
|
if (g < 0.0F) {
|
|
g += 360.0F;
|
|
} else if (g > 360.0F) {
|
|
g -= 360.0F;
|
|
}
|
|
|
|
return g;
|
|
}
|
|
|
|
public double getWantedX() {
|
|
return this.wantedX;
|
|
}
|
|
|
|
public double getWantedY() {
|
|
return this.wantedY;
|
|
}
|
|
|
|
public double getWantedZ() {
|
|
return this.wantedZ;
|
|
}
|
|
|
|
protected static enum Operation {
|
|
WAIT,
|
|
MOVE_TO,
|
|
STRAFE,
|
|
JUMPING;
|
|
}
|
|
}
|