Skip to content

Commit

Permalink
Add new navigator generator & follower
Browse files Browse the repository at this point in the history
  • Loading branch information
iam4722202468 committed Jun 8, 2024
1 parent 4a6641e commit d1624c2
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,6 @@ public synchronized void tick() {
Point currentTarget = path.getCurrent();
Point nextTarget = path.getNext();

// If we're at the end of the path, navigate directly to the entity
if (nextTarget == null) {
path.setState(PPath.PathState.INVALID);
return;
}

// Repath
if (currentTarget == null || path.getCurrentType() == PNode.NodeType.REPATH || path.getCurrentType() == null) {
if (computingPath != null && computingPath.getState() == PPath.PathState.CALCULATING) return;
Expand All @@ -176,6 +170,11 @@ public synchronized void tick() {
return;
}

if (nextTarget == null) {
path.setState(PPath.PathState.INVALID);
return;
}

boolean nextIsRepath = nextTarget.sameBlock(Pos.ZERO);
nodeFollower.moveTowards(currentTarget, nodeFollower.movementSpeed(), nextIsRepath ? currentTarget : nextTarget);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public FlyingNodeFollower(@NotNull Entity entity) {
* @param direction the targeted position
* @param speed define how far the entity will move
*/
public @NotNull PhysicsResult moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) {
public void moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) {
final Pos position = entity.getPosition();
final double dx = direction.x() - position.x();
final double dy = direction.y() - position.y();
Expand Down Expand Up @@ -56,8 +56,6 @@ public FlyingNodeFollower(@NotNull Entity entity) {

final var physicsResult = CollisionUtils.handlePhysics(entity, new Vec(speedX, speedY, speedZ));
this.entity.refreshPosition(Pos.fromPoint(physicsResult.newPosition()).withView(yaw, pitch));

return physicsResult;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public GroundNodeFollower(@NotNull Entity entity) {
* @param direction the targeted position
* @param speed define how far the entity will move
*/
public @NotNull PhysicsResult moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) {
public void moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) {
final Pos position = entity.getPosition();
final double dx = direction.x() - position.x();
final double dy = direction.y() - position.y();
Expand All @@ -51,8 +51,6 @@ public GroundNodeFollower(@NotNull Entity entity) {

final var physicsResult = CollisionUtils.handlePhysics(entity, new Vec(speedX, 0, speedZ));
this.entity.refreshPosition(Pos.fromPoint(physicsResult.newPosition()).withView(yaw, pitch));

return physicsResult;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package net.minestom.server.entity.pathfinding.followers;

import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.utils.position.PositionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class NoPhysicsNodeFollower implements NodeFollower {
private final Entity entity;

public NoPhysicsNodeFollower(@NotNull Entity entity) {
this.entity = entity;
}

/**
* Used to move the entity toward {@code direction} in the X and Z axis
* Gravity is still applied but the entity will not attempt to jump
* Also update the yaw/pitch of the entity to look along 'direction'
*
* @param direction the targeted position
* @param speed define how far the entity will move
*/
public void moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) {
final Pos position = entity.getPosition();
final double dx = direction.x() - position.x();
final double dy = direction.y() - position.y();
final double dz = direction.z() - position.z();

if (dy > 0 && entity.isOnGround()) jump(4f);

final double dxLook = lookAt.x() - position.x();
final double dyLook = lookAt.y() - position.y();
final double dzLook = lookAt.z() - position.z();

// the purpose of these few lines is to slow down entities when they reach their destination
final double distSquared = dx * dx + dy * dy + dz * dz;
if (speed > distSquared) {
speed = distSquared;
}

final double radians = Math.atan2(dz, dx);
final double speedX = Math.cos(radians) * speed;
final double speedZ = Math.sin(radians) * speed;
final float yaw = PositionUtils.getLookYaw(dxLook, dzLook);
final float pitch = PositionUtils.getLookPitch(dxLook, dyLook, dzLook);

var newPosition = position.add(speedX, 0, speedZ);
this.entity.refreshPosition(newPosition.withView(yaw, pitch));
}

@Override
public void jump(@Nullable Point point, @Nullable Point target) {
if (entity.isOnGround()) {
jump(4f);
}
}

@Override
public boolean isAtPoint(@NotNull Point point) {
return entity.getPosition().sameBlock(point);
}

public void jump(float height) {
this.entity.setVelocity(new Vec(0, height * 2.5f, 0));
}

@Override
public double movementSpeed() {
if (entity instanceof LivingEntity living) {
return living.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).getBaseValue();
}

return 0.1f;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ public interface NodeFollower {
* @param target the point to move towards
* @param speed the speed to move at
* @param lookAt the point to look at
* @return the result of the movement
*/
@NotNull PhysicsResult moveTowards(@NotNull Point target, double speed, @NotNull Point lookAt);
void moveTowards(@NotNull Point target, double speed, @NotNull Point lookAt);

/**
* Jump
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public WaterNodeFollower(@NotNull Entity entity) {
* @param direction the targeted position
* @param speed define how far the entity will move
*/
public @NotNull PhysicsResult moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) {
public void moveTowards(@NotNull Point direction, double speed, @NotNull Point lookAt) {
final Pos position = entity.getPosition();
final double dx = direction.x() - position.x();
final double dy = direction.y() - position.y();
Expand Down Expand Up @@ -63,8 +63,6 @@ public WaterNodeFollower(@NotNull Entity entity) {

final var physicsResult = CollisionUtils.handlePhysics(entity, new Vec(speedX, speedY, speedZ));
this.entity.refreshPosition(Pos.fromPoint(physicsResult.newPosition()).withView(yaw, pitch));

return physicsResult;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package net.minestom.server.entity.pathfinding.generators;

import net.minestom.server.collision.BoundingBox;
import net.minestom.server.collision.CollisionUtils;
import net.minestom.server.collision.PhysicsResult;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.pathfinding.PNode;
import net.minestom.server.instance.Instance;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.OptionalDouble;
import java.util.Set;

public class PreciseGroundNodeGenerator implements NodeGenerator {
private PNode tempNode = null;
private final static int MAX_FALL_DISTANCE = 5;

@Override
public @NotNull Collection<? extends PNode> getWalkable(@NotNull Instance instance, @NotNull Set<PNode> visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox) {
Collection<PNode> nearby = new ArrayList<>();
tempNode = new PNode(0, 0, 0, 0, 0, current);

int stepSize = (int) Math.max(Math.floor(boundingBox.width() / 2), 1);
if (stepSize < 1) stepSize = 1;

for (int x = -stepSize; x <= stepSize; ++x) {
for (int z = -stepSize; z <= stepSize; ++z) {
if (x == 0 && z == 0) continue;
double cost = Math.sqrt(x * x + z * z) * 0.98;

double floorPointX = current.blockX() + 0.5 + x;
double floorPointY = current.y();
double floorPointZ = current.blockZ() + 0.5 + z;

var optionalFloorPointY = gravitySnap(instance, floorPointX, floorPointY, floorPointZ, boundingBox, MAX_FALL_DISTANCE);
if (optionalFloorPointY.isEmpty()) continue;
floorPointY = optionalFloorPointY.getAsDouble();

var floorPoint = new Vec(floorPointX, floorPointY, floorPointZ);
var nodeWalk = createWalk(instance, floorPoint, boundingBox, cost, current, goal, visited);

if (nodeWalk != null && !visited.contains(nodeWalk)) nearby.add(nodeWalk);

for (int i = 1; i <= 1; ++i) {
Point jumpPoint = new Vec(current.blockX() + 0.5 + x, current.y() + i, current.blockZ() + 0.5 + z);
OptionalDouble jumpPointY = gravitySnap(instance, jumpPoint.x(), jumpPoint.y(), jumpPoint.z(), boundingBox, MAX_FALL_DISTANCE);
if (jumpPointY.isEmpty()) continue;
jumpPoint = jumpPoint.withY(jumpPointY.getAsDouble());

if (!floorPoint.sameBlock(jumpPoint)) {
var nodeJump = createJump(instance, jumpPoint, boundingBox, cost + 0.8, current, goal, visited);
if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump);
}
}
}
}

return nearby;
}

@Override
public boolean hasGravitySnap() {
return true;
}

private PNode createWalk(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set<PNode> closed) {
var snapped = gravitySnap(instance, point.x(), point.y(), point.z(), boundingBox, MAX_FALL_DISTANCE);

if (snapped.isPresent()) {
var snappedPoint = new Vec(point.x(), snapped.getAsDouble(), point.z());

var n = newNode(start, cost, snappedPoint, goal);
if (closed.contains(n)) {
return null;
}

if (Math.abs(snappedPoint.y() - start.y()) > Vec.EPSILON && snappedPoint.y() < start.y()) {
if (start.y() - snappedPoint.y() > MAX_FALL_DISTANCE) {
return null;
}
if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), snappedPoint.withY(start.y()), boundingBox)) {
return null;
}
n.setType(PNode.NodeType.FALL);
} else {
if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), snappedPoint, boundingBox)) {
return null;
}
}

return n;
} else {
return null;
}
}

private PNode createJump(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set<PNode> closed) {
if (Math.abs(point.y() - start.y()) < Vec.EPSILON) return null;
if (point.y() - start.y() > 2) return null;
if (point.blockX() != start.blockX() && point.blockZ() != start.blockZ()) return null;

var n = newNode(start, cost, point, goal);
if (closed.contains(n)) return null;

if (pointInvalid(instance, point, boundingBox)) return null;
if (pointInvalid(instance, new Vec(start.x(), start.y() + 1, start.z()), boundingBox)) return null;

n.setType(PNode.NodeType.JUMP);
return n;
}

private PNode newNode(PNode current, double cost, Point point, Point goal) {
tempNode.setG(current.g() + cost);
tempNode.setH(heuristic(point, goal));
tempNode.setPoint(point.x(), point.y(), point.z());

var newNode = tempNode;
tempNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.WALK, current);

return newNode;
}

@Override
public @NotNull OptionalDouble gravitySnap(@NotNull Instance instance, double pointOrgX, double pointOrgY, double pointOrgZ, @NotNull BoundingBox boundingBox, double maxFall) {
double pointX = (int) Math.floor(pointOrgX) + 0.5;
double pointZ = (int) Math.floor(pointOrgZ) + 0.5;
var res= CollisionUtils.handlePhysics(instance, boundingBox, new Pos(pointX, pointOrgY, pointZ), new Vec(0, -MAX_FALL_DISTANCE, 0), null, true);
return OptionalDouble.of(res.newPosition().y());
}

@Override
public boolean canMoveTowards(@NotNull Instance instance, @NotNull Point startOrg, @NotNull Point endOrg, @NotNull BoundingBox boundingBox) {
var end = endOrg.add(0, Vec.EPSILON, 0);
var start = startOrg.add(0, Vec.EPSILON, 0);

Point diff = end.sub(start);
PhysicsResult res = CollisionUtils.handlePhysics(instance, instance.getChunkAt(start), boundingBox, Pos.fromPoint(start), Vec.fromPoint(diff), null, false);
return !res.collisionZ() && !res.collisionY() && !res.collisionX();
}
}

0 comments on commit d1624c2

Please sign in to comment.