commit 30b3ef72a4573c9573db968755940dcc25370097 Author: mmichlol Date: Sun May 31 20:12:28 2026 +0000 Upload files to "/" diff --git a/MixinPlayerInteractionManager.java b/MixinPlayerInteractionManager.java new file mode 100644 index 0000000..dccb635 --- /dev/null +++ b/MixinPlayerInteractionManager.java @@ -0,0 +1,33 @@ +package xyz.nodrop.farmingtools.mixin.client; + +import net.minecraft.block.Block; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import xyz.nodrop.farmingtools.client.hud.FarmingTrackerModule; + +@Mixin(ClientPlayerInteractionManager.class) +public class MixinPlayerInteractionManager { + + private Block pendingBrokenBlock = null; // tymczasowe przechowanie + + @Inject(method = "breakBlock", at = @At("HEAD")) + private void beforeBreakBlock(BlockPos pos, CallbackInfoReturnable cir) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.world != null) { + pendingBrokenBlock = client.world.getBlockState(pos).getBlock(); + } + } + + @Inject(method = "breakBlock", at = @At("RETURN")) + private void onBreakBlock(BlockPos pos, CallbackInfoReturnable cir) { + if (Boolean.TRUE.equals(cir.getReturnValue()) && pendingBrokenBlock != null) { + FarmingTrackerModule.INSTANCE.onBlockBroken(pendingBrokenBlock); + } + pendingBrokenBlock = null; + } +} \ No newline at end of file diff --git a/PathExecutor.java b/PathExecutor.java new file mode 100644 index 0000000..4957bd3 --- /dev/null +++ b/PathExecutor.java @@ -0,0 +1,328 @@ +package xyz.nodrop.farmingtools.client.pathfinding; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.RaycastContext; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.HitResult; + +import java.util.List; + +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 ───────────────────────────────────────────────────────────────── + 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 float cameraYaw = 0f; + private float cameraPitch = 0f; + private boolean cameraInitialized = false; + + private List path; + private int waypointIndex = 0; + private boolean active = false; + + // Read by MixinKeyboardInput each tick + private volatile PathInput currentInput = PathInput.NONE; + + // ── Public API ───────────────────────────────────────────────────────── + + public synchronized void start(List newPath) { + this.path = newPath; + this.waypointIndex = 0; + this.active = !newPath.isEmpty(); + this.currentInput = PathInput.NONE; + } + + public synchronized void stop(MinecraftClient client) { + active = false; + currentInput = PathInput.NONE; + yawVelocity = 0f; // ← dodaj + pitchVelocity = 0f; // ← dodaj + smoothLookPoint = null; // ← dodaj + 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; } + public synchronized BlockPos getGoal() { + return (path != null && !path.isEmpty()) ? path.get(path.size() - 1) : null; + } + + public synchronized List getPath() { return path; } + + public PathInput getCurrentInput() { + return active ? currentInput : null; + } + + 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().allowFlying = true; + } + + advanceWaypoints(client.player); + + if (waypointIndex >= path.size()) { + stop(client); + return; + } + + 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 + client.player.setSprinting(shouldSprint); + } + + private void rotateCamera(ClientPlayerEntity player, BlockPos target, MinecraftClient client) { + if (!cameraInitialized) { + cameraYaw = player.getYaw(); + cameraPitch = player.getPitch(); + cameraInitialized = true; + } + + BlockPos lookTarget = chooseLookTarget(player, client); + Vec3d rawLookPoint = Vec3d.ofCenter(lookTarget).add(0, 0.5, 0); + + if (smoothLookPoint == null) { + smoothLookPoint = rawLookPoint; + } else { + 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; + + 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)); + + 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; + + cameraYaw += yawVelocity; + cameraPitch += pitchVelocity; + cameraPitch = Math.max(-90f, Math.min(90f, cameraPitch)); + + nextYaw = cameraYaw; + nextPitch = 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; + } + + + private BlockPos chooseLookTarget(ClientPlayerEntity player, MinecraftClient client) { + Vec3d eyePos = player.getEyePos(); + + 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); + + if (eyePos.distanceTo(candidateVec) < MIN_LOOKAHEAD_DIST) continue; + + BlockHitResult hit = client.world.raycast(new RaycastContext( + eyePos, + candidateVec, + RaycastContext.ShapeType.COLLIDER, + RaycastContext.FluidHandling.NONE, + player + )); + + if (hit.getType() == HitResult.Type.MISS) { + return candidate; + } + } + + return path.get(waypointIndex); + } + + // ── Internal ─────────────────────────────────────────────────────────── + + private void advanceWaypoints(ClientPlayerEntity player) { + while (waypointIndex < path.size()) { + Vec3d center = Vec3d.ofCenter(path.get(waypointIndex)); + Vec3d pPos = player.getEntityPos(); + + double dist; + double reach; + + if (flying) { + dist = pPos.distanceTo(center); // pełny 3D + reach = 2.5; // większy reach w locie + } else { + dist = horizontalDist(pPos, center); // poziomy 2D + reach = Math.min(1.8, 0.55 + dist * 0.15); + } + + if (dist < 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); + + 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 rad = Math.toRadians(diff); + float forward = (float) Math.cos(rad); + float sideways = (float) -Math.sin(rad); + + if (Math.abs(forward) < 0.05f) forward = 0; + if (Math.abs(sideways) < 0.05f) sideways = 0; + + // ── Oblicz mnożnik prędkości ────────────────────────────────────────── + float speedMult = 1.0f; + + // 1. Hamowanie przed celem (ostatnie N bloków) + 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 / 6.0); + } + } 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); + + 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 + double distToTurn = flying + ? playerPos.distanceTo(targetPos) + : horizontalDist(playerPos, targetPos); + + if (distToTurn < 5.0) { + speedMult *= turnMult; + } + } + } + + // 3. Hamowanie przy zmianie wysokości w locie + float upward = 0f; + boolean jumping = false; + boolean sneaking = false; + + if (flying) { + 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; + } + } 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 sprint = !isLastWaypoint + && Math.abs(diff) < SPRINT_ANGLE + && forward > 0.5f + && speedMult > 0.7f; // nie sprintuj gdy hamujesz + + return new PathInput(forward, sideways, upward, jumping, sprint, sneaking); + } + + // ── Utilities ────────────────────────────────────────────────────────── + + private static double horizontalDist(Vec3d a, Vec3d b) { + double dx = a.x - b.x; + double dz = a.z - b.z; + return Math.sqrt(dx * dx + dz * dz); + } + + private static double angleDiff(double target, double current) { + double diff = target - current; + while (diff > 180) diff -= 360; + while (diff < -180) diff += 360; + return diff; + } + + public boolean isFlying() { + return flying; + } + + public void setFlying(boolean flying) { + this.flying = flying; + } +}