package nuclear.module.impl.combat;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.monster.MonsterEntity;
import net.minecraft.entity.passive.AnimalEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.potion.Effects;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.vector.Vector2f;
import net.minecraft.util.math.vector.Vector3d;
import nuclear.control.Manager;
import nuclear.control.events.Event;
import nuclear.control.events.impl.player.EventInput;
import nuclear.control.events.impl.player.EventMotion;
import nuclear.control.events.impl.player.EventUpdate;
import nuclear.module.TypeList;
import nuclear.module.api.Annotation;
import nuclear.module.api.Module;
import nuclear.module.settings.imp.BooleanSetting;
import nuclear.module.settings.imp.ModeSetting;
import nuclear.module.settings.imp.MultiBoxSetting;
import nuclear.module.settings.imp.SliderSetting;
import nuclear.utils.move.MoveUtil;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
@Annotation(
name = "AttackAura",
type = TypeList.Combat,
desc = "AttackAura + SmartCrit (SpookyTime Max-Damage 2026)"
)
public class AttackAura extends Module {
// ── Публичное состояние ──────────────────────────────────────────────────
public static volatile LivingEntity target;
public static volatile Vector2f rotate = new Vector2f(0.0F, 0.0F);
// ── Приватное состояние ──────────────────────────────────────────────────
private final Random rng = new Random();
private int groundAttackDelay = 0;
private boolean critHitThisCycle = false;
private int postAttackLock = 0;
private int targetLockTicks = 0;
private LivingEntity lockedTarget = null;
// Анти-телепорт
private double lastPosX = 0.0;
private double lastPosY = 0.0;
private double lastPosZ = 0.0;
private boolean positionInit = false;
// W-tap
private boolean wTapRelease = false;
private int wTapTimer = 0;
// Визуальные эффекты (только для визуала)
private float visualNoise = 0.0F;
private int visualNoiseTimer = 0;
private float visualJitterYaw = 0.0F;
private float visualJitterPitch = 0.0F;
private int visualJitterTimer = 0;
// Динамические пороги
private float currentCritFallDist = 0.12F;
private float currentCooldownThreshold = 0.98F;
private int extraAttackDelay = 0;
// ── Константы ────────────────────────────────────────────────────────────
private static final int GROUND_DELAY_BASE = 5;
private static final int GROUND_DELAY_EXTRA = 4;
private static final int W_TAP_TICKS = 2;
private static final int POST_ATTACK_LOCK_TICKS = 1;
private static final int TARGET_LOCK_TICKS = 8;
private static final double TP_DETECT_DISTANCE = 8.0;
private static final float GAUSSIAN_CLAMP = 2.0F;
// ── ВИДИМЫЕ настройки ─────────────────────────────────────────────────────
public final ModeSetting sortingMode = new ModeSetting(
"Сортировка", "Поле зрения",
"Дистанция", "Здоровье", "Поле зрения"
);
public final ModeSetting sprintMode = new ModeSetting(
"Спринт", "Легитный",
"Обычный", "Легитный"
);
public final ModeSetting attackMode = new ModeSetting(
"Режим атаки", "УмныеКриты",
"Обычный", "ТолькоКриты", "УмныеКриты"
);
public final SliderSetting attackDistance = new SliderSetting(
"Дистанция", 3.1F, 2.5F, 6.0F, 0.1F
);
public final SliderSetting rotationSpeed = new SliderSetting(
"Скорость ротации", 30.0F, 5.0F, 40.0F, 1.0F
);
public final MultiBoxSetting targetFilters = new MultiBoxSetting(
"Цели",
new BooleanSetting("Игроки", true),
new BooleanSetting("Мобы", true),
new BooleanSetting("Животные", false),
new BooleanSetting("Невидимки", true)
);
public final MultiBoxSetting featureSettings = new MultiBoxSetting(
"Настройки",
new BooleanSetting("Водная коррекция", false),
new BooleanSetting("Сквозь стены", false),
new BooleanSetting("Не бить при еде", true)
);
public AttackAura() {
addSettings(sortingMode, sprintMode, attackMode,
attackDistance, rotationSpeed,
targetFilters, featureSettings);
}
public static LivingEntity getTarget() {
return target;
}
// ── Основной обработчик ───────────────────────────────────────────────────
@Override
public boolean onEvent(Event event) {
if (!isClientReady()) return false;
if (event instanceof EventInput) {
handleInput((EventInput) event);
return false;
}
if (event instanceof EventMotion) {
handleMotion((EventMotion) event);
return false;
}
if (!(event instanceof EventUpdate)) return false;
if (detectTeleport()) {
critHitThisCycle = false;
postAttackLock = 0;
targetLockTicks = 0;
lockedTarget = null;
}
if (mc.player.fallDistance == 0.0F && mc.player.isOnGround()) {
critHitThisCycle = false;
}
if (postAttackLock > 0) postAttackLock--;
if (targetLockTicks > 0) targetLockTicks--;
updateVisualNoise();
tickWTap();
tickVisualJitter();
LivingEntity newTarget = selectTarget();
if (newTarget == null) {
resetState();
return false;
}
// [ФИКС HitSelect] Если цель сменилась — сбрасываем счётчики
if (lockedTarget != newTarget) {
lockedTarget = newTarget;
critHitThisCycle = false;
groundAttackDelay = 0;
}
target = newTarget;
applySnapRotation(target);
handleAttack(target);
return false;
}
private LivingEntity selectTarget() {
if (targetLockTicks > 0 && target != null && isStillValid(target)) {
return target;
}
LivingEntity fresh = findBestTarget();
if (fresh != null) {
targetLockTicks = TARGET_LOCK_TICKS;
}
return fresh;
}
private boolean isStillValid(LivingEntity entity) {
if (entity == null || mc.player == null) return false;
if (!entity.isAlive() || entity.getHealth() <= 0) return false;
double range = attackDistance.getValue().doubleValue() + 0.5;
return mc.player.getDistance(entity) <= range;
}
private boolean detectTeleport() {
if (mc.player == null) return false;
double x = mc.player.getPosX();
double y = mc.player.getPosY();
double z = mc.player.getPosZ();
if (!positionInit) {
lastPosX = x; lastPosY = y; lastPosZ = z;
positionInit = true;
return false;
}
double dx = x - lastPosX;
double dy = y - lastPosY;
double dz = z - lastPosZ;
double delta = Math.sqrt(dx * dx + dy * dy + dz * dz);
lastPosX = x; lastPosY = y; lastPosZ = z;
return delta > TP_DETECT_DISTANCE;
}
private void handleInput(EventInput input) {
if (target != null && rotate != null) {
MoveUtil.fixMovement(input, rotate.x);
}
if (wTapRelease) {
input.setForward(0);
}
}
private void handleMotion(EventMotion motion) {
if (target != null && rotate != null) {
motion.setYaw(rotate.x);
motion.setPitch(rotate.y);
syncVisualRotation(rotate.x + visualNoise + visualJitterYaw,
rotate.y + visualJitterPitch);
}
}
private void triggerWTap() {
wTapRelease = true;
wTapTimer = W_TAP_TICKS;
}
private void tickWTap() {
if (wTapRelease && --wTapTimer <= 0) {
wTapRelease = false;
}
}
private void updateVisualNoise() {
if (--visualNoiseTimer <= 0) {
visualNoise = (float) (clampedGaussian() * 0.05F);
visualNoiseTimer = 2 + rng.nextInt(3);
}
}
private void triggerVisualJitter() {
visualJitterYaw = (float) (clampedGaussian() * 0.3F);
visualJitterPitch = (float) (clampedGaussian() * 0.15F);
visualJitterTimer = 2;
}
private void tickVisualJitter() {
if (visualJitterTimer > 0) {
visualJitterTimer--;
visualJitterYaw *= 0.7F;
visualJitterPitch *= 0.7F;
} else {
visualJitterYaw = 0.0F;
visualJitterPitch = 0.0F;
}
}
private void randomizeThresholds() {
// [MAX DAMAGE] Снижен минимальный порог fallDistance для надёжности крита
currentCritFallDist = 0.10F + (rng.nextFloat() * 0.12F); // 0.10-0.22
// [MAX DAMAGE] Поднят cooldown threshold для ~100% урона
currentCooldownThreshold = 0.97F + (rng.nextFloat() * 0.03F); // 0.97-1.00
}
private void refreshExtraDelay() {
// [ФИКС] Было 1/3 (33%), теперь 1/6 (~16%) — реже режет ритм атак
extraAttackDelay = rng.nextInt(6) == 0 ? 1 : 0;
}
private boolean hasLineOfSight(LivingEntity entity) {
if (mc.world == null || mc.player == null) return false;
try {
Vector3d eye = mc.player.getEyePosition(1.0F);
AxisAlignedBB aabb = entity.getBoundingBox();
double cx = MathHelper.clamp(eye.x, aabb.minX, aabb.maxX);
double cy = MathHelper.clamp(eye.y, aabb.minY + 0.1, aabb.maxY - 0.1);
double cz = MathHelper.clamp(eye.z, aabb.minZ, aabb.maxZ);
Vector3d tgt = new Vector3d(cx, cy, cz);
RayTraceResult res = mc.world.rayTraceBlocks(
new RayTraceContext(eye, tgt,
RayTraceContext.BlockMode.COLLIDER,
RayTraceContext.FluidMode.NONE,
mc.player)
);
return res.getType() == RayTraceResult.Type.MISS;
} catch (Exception e) {
return true;
}
}
// [НОВОЕ] Проверка Blindness — крит не сработает на серверной стороне
private boolean hasBlindness() {
return mc.player != null && mc.player.isPotionActive(Effects.BLINDNESS);
}
// ── Логика атаки ─────────────────────────────────────────────────────────
private void handleAttack(LivingEntity entity) {
if (postAttackLock > 0) return;
if (mc.currentScreen != null) return;
if (mc.playerController == null) return;
if (mc.player.connection == null) return; // [ФИКС] safety
if (mc.player.isHandActive() && featureSettings.get("Не бить при еде")) return;
if (attackMode.is("УмныеКриты")) {
handleSmartCrit(entity);
} else if (attackMode.is("ТолькоКриты")) {
handleOnlyCrit(entity);
} else {
if (canAttackBase(entity)) {
if (getCooldown() >= currentCooldownThreshold) {
performAttack(entity);
}
}
}
}
// [ФИКС] Используем partialTicks=1.0 для более стабильной проверки полного кд
private float getCooldown() {
return mc.player.getCooledAttackStrength(1.0F);
}
private void handleOnlyCrit(LivingEntity entity) {
if (mc.player == null || mc.player.isOnGround() || critHitThisCycle) return;
if (!canAttackBase(entity)) return;
if (getCooldown() < currentCooldownThreshold) return;
if (hasBlindness()) return; // [НОВОЕ] слепота отменяет крит
double motionY = mc.player.getMotion().y;
if (motionY >= 0.0 || motionY < -3.0) return;
if (mc.player.fallDistance >= currentCritFallDist
&& !mc.player.isInWater() && !mc.player.isInLava()
&& !mc.player.isOnLadder() && mc.player.getRidingEntity() == null) {
performAttack(entity);
critHitThisCycle = true;
}
}
private void handleSmartCrit(LivingEntity entity) {
if (mc.player == null || !canAttackBase(entity)) return;
// [НОВОЕ] Если слепота — бьём только на земле, в воздухе крита не будет
if (hasBlindness()) {
if (mc.player.isOnGround()) attackOnGround(entity);
return;
}
if (mc.player.isOnGround()) {
attackOnGround(entity);
} else if (featureSettings.get("Водная коррекция") && (mc.player.isInWater() || mc.player.isInLava())) {
attackInFluid(entity);
} else {
attackInAir(entity);
}
}
private void attackOnGround(LivingEntity entity) {
if (getCooldown() < currentCooldownThreshold) return;
if (--groundAttackDelay > 0) return;
if (extraAttackDelay > 0) { extraAttackDelay--; return; }
performAttack(entity);
int delay = GROUND_DELAY_BASE + (int) Math.abs(clampedGaussian() * 1.5);
groundAttackDelay = Math.max(1, Math.min(delay, GROUND_DELAY_BASE + GROUND_DELAY_EXTRA));
refreshExtraDelay();
}
private void attackInFluid(LivingEntity entity) {
if (getCooldown() >= currentCooldownThreshold) {
performAttack(entity);
}
}
private void attackInAir(LivingEntity entity) {
if (critHitThisCycle) return;
if (getCooldown() < currentCooldownThreshold) return;
double motionY = mc.player.getMotion().y;
boolean falling = motionY < 0.0 && motionY > -3.0;
boolean fallOk = mc.player.fallDistance >= currentCritFallDist;
boolean stable = !mc.player.isOnLadder() && mc.player.getRidingEntity() == null
&& !mc.player.isInWater() && !mc.player.isInLava();
if (falling && fallOk && stable) {
performAttack(entity);
critHitThisCycle = true;
}
}
private void performAttack(LivingEntity entity) {
if (entity == null || !entity.isAlive() || entity.getHealth() <= 0) return;
if (mc.playerController == null || mc.player == null) return;
if (mc.player.connection == null) return;
triggerWTap();
// Ванильный порядок: attack → swing (как делает vanilla Minecraft.clickMouse())
mc.playerController.attackEntity(mc.player, entity);
mc.player.swingArm(Hand.MAIN_HAND);
postAttackLock = POST_ATTACK_LOCK_TICKS;
triggerVisualJitter();
randomizeThresholds();
}
private boolean canAttackBase(LivingEntity entity) {
if (entity == null || mc.player == null) return false;
if (!entity.isAlive() || entity.getHealth() <= 0.0F) return false;
// [НОВОЕ] Не атакуем того, на ком едем
if (entity == mc.player.getRidingEntity()) return false;
double maxDist = attackDistance.getValue().doubleValue();
if (mc.player.getDistance(entity) > maxDist) return false;
if (featureSettings.get("Не бить при еде") && mc.player.isHandActive()) return false;
if (!featureSettings.get("Сквозь стены") && !hasLineOfSight(entity)) return false;
return true;
}
// ── Ротация (чистая, точно в хитбокс) ─────────────────────────────────────
private void applySnapRotation(LivingEntity entity) {
if (mc.player == null || entity == null) return;
float curYaw = rotate != null ? rotate.x : mc.player.rotationYaw;
float curPitch = rotate != null ? rotate.y : mc.player.rotationPitch;
AxisAlignedBB aabb = entity.getBoundingBox();
Vector3d eye = mc.player.getEyePosition(1.0F);
double clX = MathHelper.clamp(eye.x, aabb.minX, aabb.maxX);
double clY = MathHelper.clamp(eye.y, aabb.minY + 0.1, aabb.maxY - 0.1);
double clZ = MathHelper.clamp(eye.z, aabb.minZ, aabb.maxZ);
double dx = clX - eye.x;
double dy = clY - eye.y;
double dz = clZ - eye.z;
double hDist = Math.sqrt(dx * dx + dz * dz);
float tYaw = (float) (Math.toDegrees(Math.atan2(dz, dx)) - 90.0);
float tPitch = MathHelper.clamp(
(float) (-Math.toDegrees(Math.atan2(dy, hDist))), -90.0F, 90.0F
);
float dYaw = MathHelper.wrapDegrees(tYaw - curYaw);
float dPitch = tPitch - curPitch;
float maxYawPerTick = 90.0F;
if (Math.abs(dYaw) > maxYawPerTick) {
dYaw = Math.signum(dYaw) * maxYawPerTick;
}
float snap = MathHelper.clamp(rotationSpeed.getValue().floatValue() / 40.0F, 0.3F, 1.0F);
float smoothFactor = MathHelper.clamp(
snap * (0.75F + (float) Math.abs(clampedGaussian()) * 0.15F),
0.4F, 1.0F
);
applyGCD(
curYaw + dYaw * smoothFactor,
curPitch + dPitch * smoothFactor
);
}
private void applyGCD(float yaw, float pitch) {
float f = (float) (mc.gameSettings.mouseSensitivity * 0.6 + 0.2);
float gcd = f * f * f * 1.2F;
float deltaYaw = yaw - mc.player.rotationYaw;
float deltaPitch = pitch - mc.player.rotationPitch;
float fixedYaw = mc.player.rotationYaw + Math.round(deltaYaw / gcd) * gcd;
float fixedPitch = mc.player.rotationPitch + Math.round(deltaPitch / gcd) * gcd;
rotate = new Vector2f(fixedYaw, MathHelper.clamp(fixedPitch, -90.0F, 90.0F));
}
private void syncVisualRotation(float yaw, float pitch) {
if (mc.player == null) return;
float vYaw = MathHelper.wrapDegrees(yaw);
float vPitch = MathHelper.clamp(pitch, -90.0F, 90.0F);
float bodyDelta = MathHelper.wrapDegrees(vYaw - mc.player.renderYawOffset);
float bodyStep = MathHelper.clamp(bodyDelta, -12.0F, 12.0F);
mc.player.rotationYawHead = vYaw;
mc.player.prevRotationYawHead = vYaw;
mc.player.rotationPitchHead = vPitch;
mc.player.prevRotationPitchHead = vPitch;
mc.player.renderYawOffset = mc.player.renderYawOffset + bodyStep;
mc.player.prevRenderYawOffset = mc.player.renderYawOffset;
}
// ── Поиск и сортировка целей ─────────────────────────────────────────────
private LivingEntity findBestTarget() {
if (mc.world == null || mc.player == null) return null;
double range = attackDistance.getValue().doubleValue();
List<LivingEntity> candidates = new ArrayList<>();
try {
for (Entity ent : mc.world.getAllEntities()) {
if (ent instanceof LivingEntity) {
LivingEntity living = (LivingEntity) ent;
if (isValidTarget(living, range)) {
candidates.add(living);
}
}
}
} catch (Exception e) {
return null;
}
if (candidates.isEmpty()) return null;
sortTargets(candidates);
return candidates.get(0);
}
private void sortTargets(List<LivingEntity> targets) {
if (targets.isEmpty() || mc.player == null) return;
if (sortingMode.is("Здоровье")) {
targets.sort(Comparator.comparingDouble(LivingEntity::getHealth));
} else if (sortingMode.is("Поле зрения")) {
targets.sort(Comparator.comparingDouble(this::getFovDiff));
} else {
targets.sort(Comparator.comparingDouble(e -> mc.player.getDistance(e)));
}
}
private boolean isValidTarget(LivingEntity living, double range) {
if (living == null || mc.player == null) return false;
if (living == mc.player) return false;
if (living == mc.player.getRidingEntity()) return false; // [НОВОЕ]
if (!living.isAlive() || living.getHealth() <= 0) return false;
if (mc.player.getDistance(living) > range) return false;
if (!targetFilters.get("Невидимки") && living.isInvisible()) return false;
if (!featureSettings.get("Сквозь стены") && !mc.player.canEntityBeSeen(living)) return false;
if (living instanceof PlayerEntity) {
return !isFriend(living) && targetFilters.get("Игроки");
}
if (living instanceof MonsterEntity) return targetFilters.get("Мобы");
if (living instanceof AnimalEntity) return targetFilters.get("Животные");
return targetFilters.get("Мобы");
}
private boolean isFriend(LivingEntity living) {
if (Manager.FRIEND_MANAGER == null || living.getName() == null) return false;
try {
return Manager.FRIEND_MANAGER.isFriend(living.getName().getUnformattedComponentText());
} catch (Exception e) {
return false;
}
}
private double getFovDiff(LivingEntity living) {
if (living == null || mc.player == null) return Double.MAX_VALUE;
float[] rot = calcRotationsTo(living);
float bYaw = rotate != null ? rotate.x : mc.player.rotationYaw;
float bPit = rotate != null ? rotate.y : mc.player.rotationPitch;
return Math.abs(MathHelper.wrapDegrees(rot[0] - bYaw))
+ Math.abs(rot[1] - bPit) * 0.35;
}
private float[] calcRotationsTo(LivingEntity living) {
if (living == null || mc.player == null) {
return new float[]{ 0F, 0F };
}
double dx = living.getPosX() - mc.player.getPosX();
double dz = living.getPosZ() - mc.player.getPosZ();
double dy = (living.getPosY() + living.getHeight() * 0.5) - mc.player.getPosYEye();
double dist = Math.sqrt(dx * dx + dz * dz);
float yaw = (float) (Math.toDegrees(Math.atan2(dz, dx)) - 90.0);
float pitch = MathHelper.clamp(
(float) (-Math.toDegrees(Math.atan2(dy, dist))), -90.0F, 90.0F
);
return new float[]{ yaw, pitch };
}
private double clampedGaussian() {
double g = rng.nextGaussian();
if (g > GAUSSIAN_CLAMP) g = GAUSSIAN_CLAMP;
if (g < -GAUSSIAN_CLAMP) g = -GAUSSIAN_CLAMP;
return g;
}
private boolean isClientReady() {
return mc != null && mc.player != null && mc.world != null
&& mc.player.isAlive();
}
private void resetState() {
target = null;
lockedTarget = null;
groundAttackDelay = 0;
critHitThisCycle = false;
wTapRelease = false;
wTapTimer = 0;
visualJitterTimer = 0;
visualJitterYaw = 0.0F;
visualJitterPitch = 0.0F;
extraAttackDelay = 0;
postAttackLock = 0;
targetLockTicks = 0;
if (mc.player != null) {
rotate = new Vector2f(mc.player.rotationYaw, mc.player.rotationPitch);
}
randomizeThresholds();
}
@Override
protected void onEnable() {
resetState();
visualNoiseTimer = 0;
visualNoise = 0.0F;
positionInit = false;
}
@Override
protected void onDisable() {
resetState();
positionInit = false;
}
}