Загрузка данных


package fun.wonderful.client.modules.impl.combat.components.rotations;

import fun.wonderful.api.QClient;
import fun.wonderful.api.storages.implement.FreeLookStorage;
import fun.wonderful.api.storages.implement.RotationStorage;
import fun.wonderful.api.utils.combat.IdealHitUtils;
import fun.wonderful.api.utils.combat.RayTraceUtil;
import fun.wonderful.api.utils.rotate.MultipointUtils;
import fun.wonderful.api.utils.rotate.Rotation;
import fun.wonderful.api.utils.rotate.RotationUtils;
import fun.wonderful.client.modules.impl.combat.Aura;
import fun.wonderful.client.modules.impl.combat.components.RotationsSystem;
import fun.wonderful.client.modules.impl.combat.components.interpolation.BestPoint;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;

public class FuntimeRotation extends RotationsSystem implements QClient {

    private final Aura aura;

    private LivingEntity trackedTarget;
    private Vec2f attackAimRotation = Vec2f.ZERO;
    private Vec3d aimPoint;
    private Vec3d aimPointTarget;

    private float currentYaw;
    private float currentPitch;
    private float lastSentYaw;
    private float lastSentPitch;
    private float driftYaw;
    private float driftPitch;
    private float driftYawTarget;
    private float driftPitchTarget;
    private float noisePhase;

    private int aimPointTicks;
    private int snapTicks;
    private int driftTicks;
    private boolean attackSnapQueued;

    public FuntimeRotation(Aura aura) {
        this.aura = aura;
        reset();
    }

    public void reset() {
        trackedTarget = null;
        aimPoint = null;
        aimPointTarget = null;
        aimPointTicks = 0;
        snapTicks = 0;
        driftTicks = 0;
        attackSnapQueued = false;
        driftYaw = 0.0F;
        driftPitch = 0.0F;
        driftYawTarget = 0.0F;
        driftPitchTarget = 0.0F;
        noisePhase = (float) (Math.random() * Math.PI * 2.0D);

        if (mc.player != null) {
            currentYaw = mc.player.getYaw();
            currentPitch = mc.player.getPitch();
        } else {
            currentYaw = 0.0F;
            currentPitch = 0.0F;
        }

        lastSentYaw = currentYaw;
        lastSentPitch = currentPitch;
        attackAimRotation = new Vec2f(currentYaw, currentPitch);
    }

    @Override
    public void updateRotations(LivingEntity target) {
        if (mc.player == null || target == null) {
            return;
        }

        if (trackedTarget != target) {
            trackedTarget = target;
            currentYaw = mc.player.getYaw();
            currentPitch = mc.player.getPitch();
            lastSentYaw = currentYaw;
            lastSentPitch = currentPitch;
            aimPoint = null;
            aimPointTarget = null;
            aimPointTicks = 0;
            snapTicks = 0;
            resetDrift();
        }

        float attackReadiness = mc.player.getAttackCooldownProgress(1.5F);
        float distance = (float) mc.player.getEyePos().distanceTo(target.getBoundingBox().getCenter());
        float distanceFactor = getDistanceFactor(distance);
        boolean snapToHitbox = attackSnapQueued && shouldSnapToHitbox(attackReadiness, distanceFactor);
        if (snapToHitbox) {
            snapTicks = Math.max(snapTicks, 3);
        } else if (snapTicks > 0) {
            snapTicks--;
        }

        boolean activeSnap = snapToHitbox || snapTicks > 0;
        if (!activeSnap) {
            holdRealHeadShake();
            return;
        }

        Vec3d point = updateMultipoint(target, true, distanceFactor);
        Vec3d predicted = getPredictedPoint(target, point);
        Vec2f targetRot = RotationUtils.getRotations(predicted);
        attackAimRotation = targetRot;

        updateDrift(snapToHitbox, distanceFactor);

        float desiredYaw = targetRot.x + driftYaw;
        float desiredPitch = MathHelper.clamp(targetRot.y + driftPitch, -89.0F, 89.0F);

        float yawDelta = MathHelper.wrapDegrees(desiredYaw - currentYaw);
        float pitchDelta = desiredPitch - currentPitch;
        boolean hardSnap = true;

        float yawStep = hardSnap
                ? 360.0F
                : MathHelper.lerp(distanceFactor, 10.5F, 17.0F);
        float pitchStep = hardSnap
                ? 360.0F
                : MathHelper.lerp(distanceFactor, 5.5F, 8.5F);

        currentYaw += MathHelper.clamp(yawDelta, -yawStep, yawStep);
        currentPitch = MathHelper.clamp(currentPitch + MathHelper.clamp(pitchDelta, -pitchStep, pitchStep), -89.0F, 89.0F);

        float follow = hardSnap ? 1.0F : 0.7F;
        float outYaw = smoothLerp(lastSentYaw, currentYaw, follow);
        float outPitch = MathHelper.clamp(MathHelper.lerp(follow, lastSentPitch, currentPitch), -89.0F, 89.0F);

        float[] noise = getMicroNoise(snapToHitbox, distanceFactor);
        outYaw += noise[0];
        outPitch = MathHelper.clamp(outPitch + noise[1], -89.0F, 89.0F);

        lastSentYaw = outYaw;
        lastSentPitch = outPitch;

        Vec2f currentRot = new Vec2f(mc.player.getYaw(), mc.player.getPitch());
        Vec2f outputRot = new Vec2f(outYaw, outPitch);
        aura.setRotationSnapshots(currentRot, outputRot);
        rotate = outputRot;

        RotationStorage.update(
                new Rotation(outYaw, outPitch),
                360.0F,
                hardSnap ? 360.0F : 75.0F,
                45.0F,
                45.0F,
                0,
                1,
                false
        );

        syncBodyToSnap(outYaw);
    }

    public void queueAttackSnap() {
        attackSnapQueued = true;
        snapTicks = Math.max(snapTicks, 3);
        aimPointTicks = 0;
    }

    public boolean isAttackSnapActive() {
        return attackSnapQueued || snapTicks > 0;
    }

    public void clearAttackSnap() {
        attackSnapQueued = false;
        snapTicks = 0;
    }

    private void holdRealHeadShake() {
        float realYaw = FreeLookStorage.getFreeYaw();
        float realPitch = FreeLookStorage.getFreePitch();

        if (driftTicks > 0) {
            driftTicks--;
        } else {
            driftYawTarget = randomRange(-1.8F, 1.8F);
            driftPitchTarget = randomRange(-0.45F, 0.45F);
            driftTicks = 3 + (int) (Math.random() * 5.0D);
        }

        noisePhase += 0.18F;
        driftYaw = MathHelper.lerp(0.34F, driftYaw, driftYawTarget);
        driftPitch = MathHelper.lerp(0.28F, driftPitch, driftPitchTarget);

        float shakeYaw = (float) Math.sin(noisePhase * 1.4F) * 0.65F
                + (float) Math.cos(noisePhase * 0.73F) * 0.35F
                + driftYaw;
        float shakePitch = (float) Math.sin(noisePhase * 1.17F + 0.45F) * 0.18F
                + driftPitch;

        float outYaw = realYaw + shakeYaw;
        float outPitch = MathHelper.clamp(realPitch + shakePitch, -89.0F, 89.0F);
        currentYaw = outYaw;
        currentPitch = outPitch;
        lastSentYaw = outYaw;
        lastSentPitch = outPitch;

        Vec2f realRot = new Vec2f(realYaw, realPitch);
        Vec2f outputRot = new Vec2f(outYaw, outPitch);
        attackAimRotation = outputRot;
        aura.setRotationSnapshots(realRot, outputRot);
        rotate = outputRot;

        RotationStorage.update(
                new Rotation(outYaw, outPitch),
                12.0F,
                6.0F,
                18.0F,
                10.0F,
                1,
                1,
                false
        );
    }

    private void syncBodyToSnap(float yaw) {
        if (mc.player == null) {
            return;
        }

        float bodyYaw = approachYaw(mc.player.bodyYaw, yaw, 75.0F);
        float headYaw = approachYaw(mc.player.headYaw, yaw, 95.0F);
        mc.player.prevBodyYaw = mc.player.bodyYaw;
        mc.player.prevHeadYaw = mc.player.headYaw;
        mc.player.bodyYaw = bodyYaw;
        mc.player.headYaw = headYaw;
    }

    public void smoothReturnToRealHead() {
        if (mc.player == null) {
            reset();
            return;
        }

        float targetYaw = FreeLookStorage.getFreeYaw();
        float targetPitch = FreeLookStorage.getFreePitch();
        float yawDelta = Math.abs(MathHelper.wrapDegrees(targetYaw - mc.player.getYaw()));
        float pitchDelta = Math.abs(targetPitch - mc.player.getPitch());
        float yawSpeed = MathHelper.clamp(yawDelta * 0.22F, 7.0F, 24.0F);
        float pitchSpeed = MathHelper.clamp(pitchDelta * 0.26F, 5.0F, 16.0F);

        if (RotationStorage.instance != null) {
            RotationStorage.instance.stopRotation();
        }

        RotationStorage.update(
                new Rotation(targetYaw, targetPitch),
                yawSpeed,
                pitchSpeed,
                yawSpeed,
                pitchSpeed,
                0,
                1,
                false
        );
        reset();
    }

    public boolean isAimReadyForAttack(EntityHitResult result) {
        if (mc.player == null || trackedTarget == null || attackAimRotation == null) {
            return false;
        }

        float yawDiff = Math.abs(MathHelper.wrapDegrees(attackAimRotation.x - mc.player.getYaw()));
        float pitchDiff = Math.abs(attackAimRotation.y - mc.player.getPitch());
        double reach = Math.min(6.5D, mc.player.getEyePos().distanceTo(trackedTarget.getBoundingBox().getCenter()) + 1.2D);
        boolean onTarget = (result != null && result.getEntity() == trackedTarget)
                || RayTraceUtil.rayTraceSingleEntity(mc.player.getYaw(), mc.player.getPitch(), reach, trackedTarget);

        return yawDiff <= 3.2F && pitchDiff <= 2.6F && onTarget;
    }

    private Vec3d updateMultipoint(LivingEntity target, boolean snapToHitbox, float distanceFactor) {
        if (aimPoint == null || aimPointTarget == null || aimPointTicks <= 0 || snapToHitbox) {
            aimPointTarget = pickMultipoint(target, snapToHitbox, distanceFactor);
            aimPoint = aimPoint == null || snapToHitbox ? aimPointTarget : aimPoint;
            aimPointTicks = snapToHitbox ? 2 : 5 + (int) (Math.random() * 5.0D);
        } else {
            aimPointTicks--;
        }

        float follow = snapToHitbox ? 0.9F : MathHelper.lerp(distanceFactor, 0.34F, 0.48F);
        aimPoint = new Vec3d(
                MathHelper.lerp(follow, aimPoint.x, aimPointTarget.x),
                MathHelper.lerp(follow, aimPoint.y, aimPointTarget.y),
                MathHelper.lerp(follow, aimPoint.z, aimPointTarget.z)
        );
        return aimPoint;
    }

    private Vec3d pickMultipoint(LivingEntity target, boolean snapToHitbox, float distanceFactor) {
        double reach = mc.player.getEyePos().distanceTo(target.getBoundingBox().getCenter()) + 1.5D;
        Vec3d point;

        if (snapToHitbox) {
            point = lowerBodyPoint(target, distanceFactor, 0.0D);
        } else {
            int mode = (int) (Math.random() * 5.0D);
            if (mode == 0) {
                point = BestPoint.getMultipoint(target, reach);
            } else if (mode == 1) {
                point = lowerBodyPoint(target, distanceFactor, -0.16D);
            } else if (mode == 2) {
                point = lowerBodyPoint(target, distanceFactor, 0.16D);
            } else if (mode == 3) {
                point = lowerBodyPoint(target, distanceFactor, Math.random() < 0.5D ? -0.08D : 0.08D);
            } else {
                point = stableBodyPoint(target);
            }
        }

        if (point == null) {
            point = stableBodyPoint(target);
        }

        Vec3d visible = BestPoint.getNearestVisiblePoint(target, point, reach);
        if (visible != null) {
            point = visible;
        }

        return clampInsideBody(target, point, snapToHitbox, distanceFactor);
    }

    private Vec3d clampInsideBody(LivingEntity target, Vec3d point, boolean snapToHitbox, float distanceFactor) {
        Box box = target.getBoundingBox();
        double inset = snapToHitbox ? 0.025D : MathHelper.lerp(distanceFactor, 0.045D, 0.075D);
        double minX = box.minX + inset;
        double maxX = box.maxX - inset;
        double minZ = box.minZ + inset;
        double maxZ = box.maxZ - inset;
        double minY = box.minY + box.getLengthY() * (snapToHitbox ? 0.28D : 0.24D);
        double maxY = box.minY + box.getLengthY() * (snapToHitbox ? 0.54D : 0.58D);

        return new Vec3d(
                MathHelper.clamp(point.x, minX, maxX),
                MathHelper.clamp(point.y, minY, maxY),
                MathHelper.clamp(point.z, minZ, maxZ)
        );
    }

    private Vec3d stableBodyPoint(LivingEntity target) {
        Box box = target.getBoundingBox();
        return new Vec3d(
                box.getCenter().x,
                box.minY + box.getLengthY() * 0.42D,
                box.getCenter().z
        );
    }

    private Vec3d lowerBodyPoint(LivingEntity target, float distanceFactor, double sideOffset) {
        Box box = target.getBoundingBox();
        double halfX = box.getLengthX() * MathHelper.lerp(distanceFactor, 0.18D, 0.28D);
        double halfZ = box.getLengthZ() * MathHelper.lerp(distanceFactor, 0.18D, 0.28D);
        double xOffset = MathHelper.clamp(sideOffset, -0.22D, 0.22D) * box.getLengthX();
        double zOffset = randomRange(-0.08F, 0.08F) * box.getLengthZ();

        return new Vec3d(
                box.getCenter().x + MathHelper.clamp(xOffset, -halfX, halfX),
                box.minY + box.getLengthY() * randomRange(0.31F, 0.5F),
                box.getCenter().z + MathHelper.clamp(zOffset, -halfZ, halfZ)
        );
    }

    private void updateDrift(boolean snapToHitbox, float distanceFactor) {
        if (snapToHitbox) {
            driftYawTarget = 0.0F;
            driftPitchTarget = 0.0F;
            driftYaw = MathHelper.lerp(0.48F, driftYaw, driftYawTarget);
            driftPitch = MathHelper.lerp(0.48F, driftPitch, driftPitchTarget);
            return;
        }

        if (driftTicks > 0) {
            driftTicks--;
        } else {
            driftYawTarget = randomRange(
                    -MathHelper.lerp(distanceFactor, 2.4F, 4.8F),
                    MathHelper.lerp(distanceFactor, 2.4F, 4.8F)
            );
            driftPitchTarget = randomRange(
                    -MathHelper.lerp(distanceFactor, 0.55F, 1.15F),
                    MathHelper.lerp(distanceFactor, 0.55F, 1.15F)
            );
            driftTicks = 5 + (int) (Math.random() * 8.0D);
        }

        driftYaw = MathHelper.lerp(0.18F, driftYaw, driftYawTarget);
        driftPitch = MathHelper.lerp(0.16F, driftPitch, driftPitchTarget);
    }

    private void resetDrift() {
        driftYaw = 0.0F;
        driftPitch = 0.0F;
        driftYawTarget = 0.0F;
        driftPitchTarget = 0.0F;
        driftTicks = 0;
    }

    private float[] getMicroNoise(boolean snapToHitbox, float distanceFactor) {
        noisePhase += snapToHitbox ? 0.065F : 0.04F;
        float yawScale = snapToHitbox
                ? MathHelper.lerp(distanceFactor, 0.035F, 0.07F)
                : MathHelper.lerp(distanceFactor, 0.08F, 0.16F);
        float pitchScale = snapToHitbox
                ? MathHelper.lerp(distanceFactor, 0.018F, 0.04F)
                : MathHelper.lerp(distanceFactor, 0.035F, 0.075F);

        float yawNoise = (float) (Math.sin(noisePhase) + Math.cos(noisePhase * 1.7F)) * yawScale;
        float pitchNoise = (float) Math.sin(noisePhase * 1.31F + 0.4F) * pitchScale;
        return new float[]{yawNoise, pitchNoise};
    }

    private boolean shouldSnapToHitbox(float attackReadiness, float distanceFactor) {
        float threshold = Math.max(0.78F, IdealHitUtils.getAICooldown() - MathHelper.lerp(distanceFactor, 0.035F, 0.16F));
        boolean readyByCooldown = attackReadiness >= threshold;
        boolean fallingForCrit = !mc.player.isOnGround()
                && mc.player.getVelocity().y < 0.0D
                && mc.player.fallDistance > 0.0F;
        return readyByCooldown || fallingForCrit;
    }

    private float smoothLerp(float from, float to, float alpha) {
        float delta = MathHelper.wrapDegrees(to - from);
        return from + delta * MathHelper.clamp(alpha, 0.0F, 1.0F);
    }

    private float approachYaw(float from, float to, float maxStep) {
        float delta = MathHelper.wrapDegrees(to - from);
        return from + MathHelper.clamp(delta, -maxStep, maxStep);
    }

    private float randomRange(float min, float max) {
        return min + (float) Math.random() * (max - min);
    }

    private float getDistanceFactor(float distance) {
        return MathHelper.clamp((distance - 1.6F) / 3.8F, 0.0F, 1.0F);
    }
}