Upload files to "/"

This commit is contained in:
2026-06-01 12:19:01 +00:00
parent 20971bf328
commit 8b7fcf3a78
3 changed files with 362 additions and 154 deletions

View File

@@ -8,79 +8,151 @@ import net.minecraft.world.RaycastContext;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
public class PathExecutor {
private static final double SPRINT_ANGLE = 35.0; // max yaw diff to allow sprint
// ── Pola ──────────────────────────────────────────────────────────────────
private float yawVelocity = 0f;
private float pitchVelocity = 0f;
private Vec3d smoothLookPoint = null;
private boolean flying = false;
// ══════════════════════════════════════════════════════════════════════════
// STAŁE
// ══════════════════════════════════════════════════════════════════════════
// ── Stałe ─────────────────────────────────────────────────────────────────
private static final float SPRING_STIFFNESS = 0.18f;
private static final float SPRING_DAMPING = 0.72f;
private static final float MAX_VELOCITY = 18.0f;
private static final float PITCH_LOOK_AHEAD = 6.0f;
private static final float LOOK_POINT_LERP = 0.08f;
private volatile float prevYaw = 0f;
private volatile float prevPitch = 0f;
private volatile float nextYaw = 0f;
private volatile float nextPitch = 0f;
private static final double SPRINT_ANGLE = 35.0; // max kąt yaw do sprintu
private static final float SPRING_STIFFNESS = 0.18f;
private static final float SPRING_DAMPING = 0.72f;
private static final float MAX_VELOCITY = 18.0f;
private static final float PITCH_LOOK_AHEAD = 6.0f;
private static final float LOOK_POINT_LERP = 0.08f;
private float cameraYaw = 0f;
private float cameraPitch = 0f;
// Lookahead
private static final int HOLD_TICKS = 10;
private static final float NEAR_THRESHOLD = 3.5f;
// Stuck detection
private static final int STUCK_TICKS = 26; // ~1.3s przy 20 TPS
private static final float STUCK_MIN_DIST = 0.3f;
// ══════════════════════════════════════════════════════════════════════════
// INTERPOLACJA KAMERY (atomic frame dla render threadu)
// ══════════════════════════════════════════════════════════════════════════
private record YawPitchFrame(float prevYaw, float prevPitch,
float nextYaw, float nextPitch) {}
private volatile YawPitchFrame frame = new YawPitchFrame(0, 0, 0, 0);
// ══════════════════════════════════════════════════════════════════════════
// STAN KAMERY
// ══════════════════════════════════════════════════════════════════════════
private float cameraYaw = 0f;
private float cameraPitch = 0f;
private boolean cameraInitialized = false;
private float currentMoveYaw = 0f; // yaw do aktualnego waypointu (nie lookahead)
private float yawVelocity = 0f;
private float pitchVelocity = 0f;
private Vec3d smoothLookPoint = null;
// Lookahead state
private int lookTargetIndex = 0;
private int lookHoldTicks = 0;
// ══════════════════════════════════════════════════════════════════════════
// STAN PATHFINDINGU
// ══════════════════════════════════════════════════════════════════════════
private List<BlockPos> path;
private int waypointIndex = 0;
private boolean active = false;
private int waypointIndex = 0;
private boolean active = false;
private boolean flying = false;
// Read by MixinKeyboardInput each tick
private volatile PathInput currentInput = PathInput.NONE;
// ── Public API ─────────────────────────────────────────────────────────
// Waypoint offsety (randomizacja pozycji w bloku)
private final Random rng = new Random();
private final Map<BlockPos, Vec3d> waypointOffsets = new HashMap<>();
// ══════════════════════════════════════════════════════════════════════════
// STUCK DETECTION
// ══════════════════════════════════════════════════════════════════════════
private Vec3d lastStuckCheckPos = null;
private int stuckTickCounter = 0;
private final Set<BlockPos> blacklistedNodes = new HashSet<>();
// ══════════════════════════════════════════════════════════════════════════
// PUBLIC API
// ══════════════════════════════════════════════════════════════════════════
public synchronized void start(List<BlockPos> newPath) {
this.path = newPath;
this.path = newPath;
this.waypointIndex = 0;
this.active = !newPath.isEmpty();
this.currentInput = PathInput.NONE;
this.active = !newPath.isEmpty();
this.currentInput = PathInput.NONE;
}
/** Zatrzymaj pathfinding — blacklista zostaje (potrzebna przy retry po stucku). */
public synchronized void stop(MinecraftClient client) {
active = false;
currentInput = PathInput.NONE;
yawVelocity = 0f; // ← dodaj
pitchVelocity = 0f; // ← dodaj
smoothLookPoint = null; // ← dodaj
active = false;
currentInput = PathInput.NONE;
yawVelocity = 0f;
pitchVelocity = 0f;
smoothLookPoint = null;
cameraInitialized = false;
currentMoveYaw = 0f;
lookTargetIndex = 0;
lookHoldTicks = 0;
waypointIndex = 0;
waypointOffsets.clear();
lastStuckCheckPos = null;
stuckTickCounter = 0;
if (client.player != null) client.player.setSprinting(false);
}
public synchronized boolean isActive() { return active; }
public synchronized int getWaypointIndex(){ return waypointIndex; }
public synchronized int getPathLength() { return path != null ? path.size() : 0; }
/** Pełny reset — wywołuj tylko przy ręcznym zatrzymaniu przez gracza. */
public synchronized void reset(MinecraftClient client) {
stop(client);
blacklistedNodes.clear();
}
public synchronized void clearBlacklist() {
blacklistedNodes.clear();
}
public Set<BlockPos> getBlacklist() { return blacklistedNodes; }
public synchronized boolean isActive() { return active; }
public synchronized int getWaypointIndex() { return waypointIndex; }
public synchronized int getPathLength() { return path != null ? path.size() : 0; }
public synchronized List<BlockPos> getPath() { return path; }
public synchronized BlockPos getGoal() {
return (path != null && !path.isEmpty()) ? path.get(path.size() - 1) : null;
}
public synchronized List<BlockPos> getPath() { return path; }
public PathInput getCurrentInput() {
return active ? currentInput : null;
}
public boolean isFlying() { return flying; }
public void setFlying(boolean f) { this.flying = f; }
// ══════════════════════════════════════════════════════════════════════════
// TICK GŁÓWNY
// ══════════════════════════════════════════════════════════════════════════
public synchronized void tick(MinecraftClient client) {
if (!active || path == null || client.player == null) {
currentInput = PathInput.NONE;
return;
}
// ── Włącz/wyłącz latanie ──────────────────────────────────────────────
if (flying) {
client.player.getAbilities().flying = true;
client.player.getAbilities().flying = true;
client.player.getAbilities().allowFlying = true;
}
@@ -91,16 +163,22 @@ public class PathExecutor {
return;
}
checkStuck(client.player, client);
BlockPos target = path.get(waypointIndex);
rotateCamera(client.player, target, client);
currentInput = computeInput(client.player, target);
boolean shouldSprint = currentInput.sprint()
&& !currentInput.jumping()
&& (flying || client.player.isOnGround()); // ← flying nie wymaga gruntu
&& (flying || client.player.isOnGround());
client.player.setSprinting(shouldSprint);
}
// ══════════════════════════════════════════════════════════════════════════
// KAMERA — rotacja i interpolacja
// ══════════════════════════════════════════════════════════════════════════
private void rotateCamera(ClientPlayerEntity player, BlockPos target, MinecraftClient client) {
if (!cameraInitialized) {
cameraYaw = player.getYaw();
@@ -108,6 +186,7 @@ public class PathExecutor {
cameraInitialized = true;
}
// ── Look target (lookahead) ───────────────────────────────────────────
BlockPos lookTarget = chooseLookTarget(player, client);
Vec3d rawLookPoint = Vec3d.ofCenter(lookTarget).add(0, 0.5, 0);
@@ -117,109 +196,202 @@ public class PathExecutor {
smoothLookPoint = smoothLookPoint.lerp(rawLookPoint, LOOK_POINT_LERP);
}
Vec3d playerPos = new Vec3d(player.getX(), player.getEyeY(), player.getZ());
double dx = smoothLookPoint.x - playerPos.x;
double dz = smoothLookPoint.z - playerPos.z;
double dy = smoothLookPoint.y - playerPos.y;
// ── Oblicz target yaw/pitch z smoothLookPoint ─────────────────────────
Vec3d playerEye = new Vec3d(player.getX(), player.getEyeY(), player.getZ());
double dx = smoothLookPoint.x - playerEye.x;
double dz = smoothLookPoint.z - playerEye.z;
double dy = smoothLookPoint.y - playerEye.y;
float targetYaw = (float) Math.toDegrees(Math.atan2(-dx, dz));
float targetPitch = (float) -Math.toDegrees(
Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)));
targetPitch = Math.max(-90f, Math.min(90f, targetPitch - PITCH_LOOK_AHEAD));
float yawError = (float) angleDiff(targetYaw, cameraYaw);
float yawAccel = SPRING_STIFFNESS * yawError - SPRING_DAMPING * yawVelocity;
yawVelocity += yawAccel;
yawVelocity = Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, yawVelocity));
// ── Spring damper — yaw ───────────────────────────────────────────────
float yawError = (float) angleDiff(targetYaw, cameraYaw);
float yawAccel = SPRING_STIFFNESS * yawError - SPRING_DAMPING * yawVelocity;
yawVelocity += yawAccel;
yawVelocity = Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, yawVelocity));
float pitchError = targetPitch - cameraPitch;
float pitchAccel = SPRING_STIFFNESS * pitchError - SPRING_DAMPING * pitchVelocity;
pitchVelocity += pitchAccel;
pitchVelocity = Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, pitchVelocity));
// ── Spring damper — pitch ─────────────────────────────────────────────
float pitchError = targetPitch - cameraPitch;
float pitchAccel = SPRING_STIFFNESS * pitchError - SPRING_DAMPING * pitchVelocity;
pitchVelocity += pitchAccel;
pitchVelocity = Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, pitchVelocity));
prevYaw = cameraYaw;
prevPitch = cameraPitch;
float oldYaw = cameraYaw;
float oldPitch = cameraPitch;
cameraYaw += yawVelocity;
cameraPitch += pitchVelocity;
cameraPitch = Math.max(-90f, Math.min(90f, cameraPitch));
nextYaw = cameraYaw;
nextPitch = cameraPitch;
// Atomowy zapis dla render threadu
frame = new YawPitchFrame(oldYaw, oldPitch, cameraYaw, cameraPitch);
player.setYaw(cameraYaw);
player.setPitch(cameraPitch);
}
public float getInterpolatedYaw(float tickDelta) {
float diff = (float) angleDiff(nextYaw, prevYaw);
return prevYaw + diff * tickDelta;
}
public float getInterpolatedPitch(float tickDelta) {
return prevPitch + (nextPitch - prevPitch) * tickDelta;
// ── moveYaw — osobny kąt dla nóg (do aktualnego waypointu, nie lookahead) ──
Vec3d playerFeet = player.getEntityPos();
Vec3d wpTarget = getWaypointTarget(path.get(waypointIndex));
double mdx = wpTarget.x - playerFeet.x;
double mdz = wpTarget.z - playerFeet.z;
float rawMoveYaw = (float) Math.toDegrees(Math.atan2(-mdx, mdz));
float moveDiff = (float) angleDiff(rawMoveYaw, currentMoveYaw);
currentMoveYaw += moveDiff * 0.3f; // lerp 30% — wygładza drobne wahania
}
public float getInterpolatedYaw(float tickDelta) {
YawPitchFrame f = frame;
float diff = (float) angleDiff(f.nextYaw(), f.prevYaw());
return f.prevYaw() + diff * tickDelta;
}
public float getInterpolatedPitch(float tickDelta) {
YawPitchFrame f = frame;
return f.prevPitch() + (f.nextPitch() - f.prevPitch()) * tickDelta;
}
// ══════════════════════════════════════════════════════════════════════════
// LOOKAHEAD — wybór punktu do patrzenia
// ══════════════════════════════════════════════════════════════════════════
private BlockPos chooseLookTarget(ClientPlayerEntity player, MinecraftClient client) {
Vec3d eyePos = player.getEyePos();
Vec3d eyePos = player.getEyePos();
Vec3d playerPos = player.getEntityPos();
int MAX_LOOKAHEAD = 4;
int bestIdx = waypointIndex;
int LOOKAHEAD_STEPS = 3;
float MIN_LOOKAHEAD_DIST = 4.0f;
for (int i = LOOKAHEAD_STEPS; i >= 1; i--) {
int idx = Math.min(waypointIndex + i, path.size() - 1);
BlockPos candidate = path.get(idx);
Vec3d candidateVec = Vec3d.ofCenter(candidate).add(0, 0.5, 0);
for (int i = 1; i <= MAX_LOOKAHEAD; i++) {
int idx = waypointIndex + i;
if (idx >= path.size()) break;
if (eyePos.distanceTo(candidateVec) < MIN_LOOKAHEAD_DIST) continue;
BlockPos candidate = path.get(idx);
Vec3d candidateVec = Vec3d.ofCenter(candidate).add(0, 0.5, 0);
double distToCandidate = eyePos.distanceTo(candidateVec);
// ── Sprawdź czy między aktualnym a kandydatem jest zmiana wysokości ──
// Jeśli którykolwiek waypoint po drodze ma inną Y — zatrzymaj lookahead
boolean heightChangeOnPath = false;
for (int j = waypointIndex; j < idx; j++) {
int yA = path.get(j).getY();
int yB = path.get(j + 1).getY();
if (Math.abs(yA - yB) >= 1) {
heightChangeOnPath = true;
break;
}
}
// Jeśli na drodze do kandydata jest zmiana wysokości —
// patrz tylko na pierwszy waypoint tej zmiany, nie dalej
if (heightChangeOnPath) {
// Znajdź pierwszy waypoint ze zmianą Y i patrz na niego
for (int j = waypointIndex; j < idx; j++) {
if (Math.abs(path.get(j).getY() - path.get(j + 1).getY()) >= 1) {
bestIdx = j + 1; // patrz na wyższy/niższy waypoint
break;
}
}
break; // nie idź dalej w lookahead
}
// Brak zmiany wysokości — normalna logika
if (distToCandidate < 2.5) continue;
double angleToCandidate = angleToTarget(player, candidateVec);
if (Math.abs(angleToCandidate) > 70.0) break;
BlockHitResult hit = client.world.raycast(new RaycastContext(
eyePos,
candidateVec,
RaycastContext.ShapeType.COLLIDER,
eyePos, candidateVec,
RaycastContext.ShapeType.OUTLINE,
RaycastContext.FluidHandling.NONE,
player
));
if (hit.getType() == HitResult.Type.MISS) {
return candidate;
bestIdx = idx;
} else {
break;
}
}
return path.get(waypointIndex);
return path.get(bestIdx);
}
// ── Internal ───────────────────────────────────────────────────────────
/** Kąt między aktualnym kierunkiem ruchu gracza a punktem docelowym. */
private double angleToTarget(ClientPlayerEntity player, Vec3d target) {
Vec3d pos = player.getEntityPos();
double dx = target.x - pos.x;
double dz = target.z - pos.z;
double targetYaw = Math.toDegrees(Math.atan2(-dx, dz));
return angleDiff(targetYaw, currentMoveYaw);
}
// ══════════════════════════════════════════════════════════════════════════
// RUCH — waypoints i input
// ══════════════════════════════════════════════════════════════════════════
private void advanceWaypoints(ClientPlayerEntity player) {
while (waypointIndex < path.size()) {
Vec3d center = Vec3d.ofCenter(path.get(waypointIndex));
Vec3d pPos = player.getEntityPos();
double dist;
double reach;
BlockPos wpBlock = path.get(waypointIndex);
Vec3d center = getWaypointTarget(wpBlock);
Vec3d pPos = player.getEntityPos();
if (flying) {
dist = pPos.distanceTo(center); // pełny 3D
reach = 2.5; // większy reach w locie
double dist = pPos.distanceTo(center);
if (dist < 2.5) waypointIndex++;
else break;
} else {
dist = horizontalDist(pPos, center); // poziomy 2D
reach = Math.min(1.8, 0.55 + dist * 0.15);
}
double horizDist = horizontalDist(pPos, center);
double vertDiff = pPos.y - center.y; // dodatnie = gracz wyżej niż cel
if (dist < reach) waypointIndex++;
else break;
// ── Cel jest WYŻEJ niż gracz (skok w górę) ───────────────────────
// Nie zaliczaj dopóki gracz faktycznie nie wejdzie na ten poziom
if (wpBlock.getY() > Math.floor(pPos.y) + 0.1) {
// Gracz musi być na tym samym Y i poziomo blisko
if (horizDist < 1.0 && Math.abs(vertDiff) < 0.6) {
waypointIndex++;
} else {
break;
}
continue;
}
// ── Cel jest NIŻEJ niż gracz (schodzenie/spadanie) ───────────────
if (vertDiff > 0.8 && horizDist < 1.2) {
waypointIndex++;
continue;
}
// ── Ten sam poziom — normalne zaliczanie ─────────────────────────
double reach = Math.min(1.5, 0.5 + horizDist * 0.1);
if (horizDist < reach) waypointIndex++;
else break;
}
}
}
private PathInput computeInput(ClientPlayerEntity player, BlockPos target) {
Vec3d playerPos = new Vec3d(player.getX(), player.getY(), player.getZ());
Vec3d targetPos = Vec3d.ofCenter(target);
Vec3d targetPos = getWaypointTarget(target);
// Jeśli cel jest niżej i gracz jest poziomo nad nim — patrz na następny
// waypoint żeby nie wchodzić w ścianę przy schodzeniu
double horizDist = horizontalDist(playerPos, targetPos);
double vertDiff = playerPos.y - targetPos.y;
if (vertDiff > 0.8 && horizDist < 1.5 && waypointIndex + 1 < path.size()) {
targetPos = getWaypointTarget(path.get(waypointIndex + 1));
}
double dx = targetPos.x - playerPos.x;
double dz = targetPos.z - playerPos.z;
double targetYaw = Math.toDegrees(Math.atan2(-dx, dz));
double diff = angleDiff(targetYaw, player.getYaw());
double moveYaw = Math.toDegrees(Math.atan2(-dx, dz));
// Użyj currentMoveYaw (kąt do waypointu) zamiast player.getYaw()
// (który jest ustawiony przez lookahead kamery)
double diff = angleDiff(moveYaw, currentMoveYaw);
double rad = Math.toRadians(diff);
float forward = (float) Math.cos(rad);
float sideways = (float) -Math.sin(rad);
@@ -227,51 +399,28 @@ public class PathExecutor {
if (Math.abs(forward) < 0.05f) forward = 0;
if (Math.abs(sideways) < 0.05f) sideways = 0;
// ── Oblicz mnożnik prędkości ──────────────────────────────────────────
// ── Hamowanie przed zakrętem ──────────────────────────────────────────
float speedMult = 1.0f;
// 1. Hamowanie przed celem (ostatnie N bloków)z
boolean isLastWaypoint = (waypointIndex == path.size() - 1);
// if (flying) {
//double distToTarget = playerPos.distanceTo(targetPos);
// if (isLastWaypoint) {
// Hamuj płynnie na ostatnich 6 blokach
//speedMult *= (float) Math.min(1.0, distToTarget / 0.1);
// }
//} else {
//double distToTarget = horizontalDist(playerPos, targetPos);
//if (isLastWaypoint) {
//speedMult *= (float) Math.min(1.0, distToTarget / 4.0);
//}
//}
// 2. Hamowanie przed zakrętem — patrz na kąt do NASTĘPNEGO waypointu
if (waypointIndex + 1 < path.size()) {
BlockPos nextTarget = path.get(waypointIndex + 1);
Vec3d nextPos = Vec3d.ofCenter(nextTarget);
BlockPos nextTarget = path.get(waypointIndex + 1);
Vec3d nextPos = Vec3d.ofCenter(nextTarget);
double ndx = nextPos.x - targetPos.x;
double ndz = nextPos.z - targetPos.z;
double nextYaw = Math.toDegrees(Math.atan2(-ndx, ndz));
double turnAngle = Math.abs(angleDiff(nextYaw, moveYaw));
double ndx = nextPos.x - targetPos.x;
double ndz = nextPos.z - targetPos.z;
double nextYaw = Math.toDegrees(Math.atan2(-ndx, ndz));
double turnAngle = Math.abs(angleDiff(nextYaw, targetYaw));
// Im większy zakręt tym mocniej hamuj (powyżej 30° zaczyna hamować)
if (turnAngle > 30.0) {
float turnMult = (float) Math.max(0.3, 1.0 - (turnAngle - 30.0) / 120.0);
// Hamuj tylko gdy jesteś blisko zakrętu
float turnMult = (float) Math.max(0.3, 1.0 - (turnAngle - 30.0) / 120.0);
double distToTurn = flying
? playerPos.distanceTo(targetPos)
: horizontalDist(playerPos, targetPos);
if (distToTurn < 5.0) {
speedMult *= turnMult;
}
if (distToTurn < 5.0) speedMult *= turnMult;
}
}
// 3. Hamowanie przy zmianie wysokości w locie
float upward = 0f;
// ── Lot — sterowanie pionowe i hamowanie przy dużej zmianie Y ────────
float upward = 0f;
boolean jumping = false;
boolean sneaking = false;
@@ -279,31 +428,85 @@ public class PathExecutor {
double dy = targetPos.y - playerPos.y;
if (dy > 0.4) upward = 1.0f;
else if (dy < -0.4) upward = -1.0f;
// Jeśli duża zmiana wysokości — zwolnij ruch poziomy
if (Math.abs(dy) > 3.0) {
speedMult *= 0.5f;
}
if (Math.abs(dy) > 3.0) speedMult *= 0.5f;
} else {
jumping = target.getY() > Math.floor(playerPos.y) + 0.5;
}
// Clamp speedMult żeby nie był ujemny
speedMult = Math.max(0.0f, Math.min(1.0f, speedMult));
// Zastosuj mnożnik do forward/sideways
forward *= speedMult;
sideways *= speedMult;
boolean isLastWaypoint = (waypointIndex == path.size() - 1);
boolean sprint = !isLastWaypoint
&& Math.abs(diff) < SPRINT_ANGLE
&& forward > 0.5f
&& speedMult > 0.7f; // nie sprintuj gdy hamujesz
&& speedMult > 0.7f;
return new PathInput(forward, sideways, upward, jumping, sprint, sneaking);
}
// ── Utilities ──────────────────────────────────────────────────────────
// ══════════════════════════════════════════════════════════════════════════
// STUCK DETECTION
// ══════════════════════════════════════════════════════════════════════════
private void checkStuck(ClientPlayerEntity player, MinecraftClient client) {
Vec3d pos = player.getEntityPos();
if (lastStuckCheckPos == null) {
lastStuckCheckPos = pos;
return;
}
double moved = horizontalDist(pos, lastStuckCheckPos);
if (moved < STUCK_MIN_DIST) {
stuckTickCounter++;
if (stuckTickCounter >= STUCK_TICKS) {
stuckTickCounter = 0;
lastStuckCheckPos = pos;
handleStuck(client);
}
} else {
stuckTickCounter = 0;
lastStuckCheckPos = pos;
}
}
private void handleStuck(MinecraftClient client) {
if (path == null || client.player == null) return;
BlockPos goal = path.get(path.size() - 1);
BlockPos stuckAt = client.player.getBlockPos();
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
BlockPos candidate = stuckAt.add(dx, 0, dz);
if (candidate.isWithinDistance(goal, 3.0)) continue;
blacklistedNodes.add(candidate);
blacklistedNodes.add(candidate.up());
blacklistedNodes.add(candidate.down());
}
}
stop(client);
PathfindingController.INSTANCE.goTo(
goal.getX(), goal.getY(), goal.getZ(),
flying, null
);
}
// ══════════════════════════════════════════════════════════════════════════
// POMOCNICZE
// ══════════════════════════════════════════════════════════════════════════
/** Losowy offset w bloku — zapobiega chodzeniu przez idealny środek. */
private Vec3d getWaypointTarget(BlockPos pos) {
return waypointOffsets.computeIfAbsent(pos, p -> {
double ox = (rng.nextDouble() - 0.5) * 0.6;
double oz = (rng.nextDouble() - 0.5) * 0.6;
return new Vec3d(p.getX() + 0.5 + ox, p.getY(), p.getZ() + 0.5 + oz);
});
}
private static double horizontalDist(Vec3d a, Vec3d b) {
double dx = a.x - b.x;
@@ -317,12 +520,4 @@ public class PathExecutor {
while (diff < -180) diff += 360;
return diff;
}
public boolean isFlying() {
return flying;
}
public void setFlying(boolean flying) {
this.flying = flying;
}
}
}