Attack Aura улучшения

Создано: 18 января 2025 г.

Отвечено с использованием OpenAI 01 от OpenAI01

Вопрос



Прокачай мою **Attack Aura** для Minecraft (MCP 1.16.5):
1. **Добавь криты и коррекцию движения с помощью BoolenSeiting в modelist Неудобно
2. **Игнорирование стен** – добавь опцию, которая позволит выбирать:
- Если выключено – игрок не атакует через стены.
- Если включено – атака а сквозь стены.
3. **Ротация** – добавь ротацию под сервер `Funtime` и сделай её максимально легитной и удобной для использования.

Цель – чтобы итоговая версия была топовой и соответствовала высоким стандартам.
package ru.monarch.functions.impl.combat;

import com.google.common.eventbus.Subscribe;
import ru.monarch.monarch;
import ru.monarch.command.friends.FriendStorage;
import ru.monarch.events.EventInput;
import ru.monarch.events.EventLook;
import ru.monarch.events.EventMotion;
import ru.monarch.events.EventUpdate;
import ru.monarch.functions.api.Category;
import ru.monarch.functions.api.Function;
import ru.monarch.functions.api.FunctionRegister;
import ru.monarch.functions.impl.combat.killAura.rotation.VecRotation;
import ru.monarch.functions.settings.impl.BooleanSetting;
import ru.monarch.functions.settings.impl.ModeListSetting;
import ru.monarch.functions.settings.impl.ModeSetting;
import ru.monarch.functions.settings.impl.SliderSetting;
import ru.monarch.utils.client.ClientUtil;
import ru.monarch.utils.combat.Rotation;
import ru.monarch.utils.math.SensUtils;
import ru.monarch.utils.player.InventoryUtil;
import ru.monarch.utils.player.MouseUtil;
import ru.monarch.utils.player.MoveUtils;
import ru.monarch.utils.rotation.PointTracker;
import ru.monarch.utils.rotation.RotationUtils;
import lombok.Getter;
import net.minecraft.block.material.Material;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.entity.player.RemoteClientPlayerEntity;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.item.ArmorStandEntity;
import net.minecraft.entity.monster.MonsterEntity;
import net.minecraft.entity.passive.AnimalEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.ClickType;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.ItemStack;
import net.minecraft.network.play.client.CHeldItemChangePacket;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector2f;
import net.minecraft.util.math.vector.Vector3d;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import static net.minecraft.util.math.MathHelper.clamp;
import static net.minecraft.util.math.MathHelper.wrapDegrees;

@FunctionRegister(name = "KillAura", type = Category.Combat)
public class KillAura extends Function {
@Getter
private final ModeSetting type = new ModeSetting("Тип", "Плавная", "Плавная", "Резкая");
private final SliderSetting attackRange = new SliderSetting("Дистанция аттаки", 3f, 3f, 6f, 0.1f);

final ModeListSetting targets = new ModeListSetting("Таргеты",
new BooleanSetting("Игроки", true),
new BooleanSetting("Голые", true),
new BooleanSetting("Мобы", false),
new BooleanSetting("Животные", false),
new BooleanSetting("Друзья", false),
new BooleanSetting("Голые невидимки", true),
new BooleanSetting("Невидимки", true));

@Getter
final ModeListSetting options = new ModeListSetting("Опции",
new BooleanSetting("Только криты", true),
new BooleanSetting("Ломать щит", true),
new BooleanSetting("Отжимать щит", true),
new BooleanSetting("Синхронизировать атаку с ТПС", false),
new BooleanSetting("Фокусировать одну цель", true),
new BooleanSetting("Коррекция движения", true));

final ModeSetting correctionType = new ModeSetting("Тип коррекции", "Незаметный", "Незаметный", "Сфокусированный").setVisible(() -> options.getValueByName("Коррекция движения").get());

@Getter
private Vector2f rotateVector = new Vector2f(0, 0);
@Getter
private LivingEntity target;
private Entity raytrace;
public int nextAttackDelay = 0;
int ticks = 0;
boolean isRotated;
public boolean canAttackWithSpider = true;

final AutoPotion autoPotion;
public double lastPosY;
boolean isFalling;
PointTracker pointTracker = new PointTracker();

public KillAura(AutoPotion autoPotion) {
this.autoPotion = autoPotion;
addSettings(type, attackRange, targets, options, correctionType);
}

@Subscribe
public void onInput(EventInput eventInput) {
if (options.getValueByName("Коррекция движения").get() && correctionType.is("Незаметный") && target != null && mc.player != null) {
MoveUtils.fixMovement(eventInput, rotateVector.x);
}
}

@Subscribe
public void onLook(EventLook eventLook) {
if (target != null) {
eventLook.setRotation(new VecRotation(rotateVector.x, rotateVector.y));
}
}

@Subscribe
public void onUpdate(EventUpdate e) {
if (nextAttackDelay > 0) {
--nextAttackDelay;
}

if (options.getValueByName("Фокусировать одну цель").get() && (target == null || !isValid(target))
|| !options.getValueByName("Фокусировать одну цель").get()) {
updateTarget();
}

if (target == null || autoPotion.isState() && autoPotion.isActive() && autoPotion.canThrowPotion()) {
reset();
mc.player.rotationYawOffset = Integer.MIN_VALUE;
nextAttackDelay = 0;
return;
}

isRotated = false;
updateAttack();

if (type.is("Резкая")) {
if (ticks > 0) {
updateRotation(true);
ticks--;
} else {
reset();
}
} else {
if (!isRotated) {
updateRotation(false);
}
}

}


@Subscribe
private void onWalking(EventMotion e) {
if (target == null || autoPotion.isState() && autoPotion.isActive() && autoPotion.canThrowPotion()) return;


float yaw = rotateVector.x;
float pitch = rotateVector.y;

e.setYaw(yaw);
e.setPitch(pitch);
mc.player.rotationYawHead = yaw;
mc.player.renderYawOffset = yaw;
mc.player.rotationPitchHead = pitch;
}


private void updateTarget() {
List<LivingEntity> targets = new ArrayList<>();
mc.world.getAllEntities().forEach(entity -> {
if (entity instanceof LivingEntity living && isValid(living)) {
targets.add(living);
}
});

if (targets.isEmpty()) {
target = null;
return;
}

if (targets.size() == 1) {
target = targets.get(0);
return;
}


targets.sort(Comparator.comparingDouble(object -> {
if (object instanceof PlayerEntity player) {
return -getEntityArmor(player);
}
if (object instanceof LivingEntity base) {
return -base.getTotalArmorValue();
}
return 0.0;
}).thenComparing((object, object2) -> {
double d2 = getEntityHealth((LivingEntity) object);
double d3 = getEntityHealth((LivingEntity) object2);
return Double.compare(d2, d3);
}).thenComparing((object, object2) -> {
double d2 = mc.player.getDistance((LivingEntity) object);
double d3 = mc.player.getDistance((LivingEntity) object2);
return Double.compare(d2, d3);
}));

target = targets.get(0);
}

float lastYaw;

private void updateRotation(boolean attack) {
Vector3d vec = pointTracker.getSpot(target, attackRange.get()).subtract(mc.player.getEyePosition(1)).normalize();

isRotated = true;
float yawToTarget = (float) MathHelper.wrapDegrees(Math.toDegrees(Math.atan2(vec.z, vec.x)) - 90);
float pitchToTarget = (float) Math.toDegrees(Math.asin(-vec.y));

float yawDelta = (wrapDegrees(yawToTarget - rotateVector.x));
float pitchDelta = (pitchToTarget - rotateVector.y);
int roundYawDelta = (int) Math.abs(yawDelta);

switch (type.get()) {
case "Плавная" -> {
float clampedYaw = Math.min(Math.max(roundYawDelta, 1.0f), 90.0F);
float clampedPitch = Math.min(Math.max(Math.abs(pitchDelta) * 0.33f, 1.0f), 90.0F);

float yaw = rotateVector.x + (yawDelta > 0 ? clampedYaw : -clampedYaw);
float pitch = clamp(rotateVector.y + (pitchDelta > 0 ? clampedPitch : -clampedPitch), -90.0F, 90.0F);

float gcd = SensUtils.getGCD();
yaw -= (yaw - rotateVector.x) % gcd;
pitch -= (pitch - rotateVector.y) % gcd;
rotateVector = new Vector2f(yaw, pitch);
lastYaw = clampedYaw;
if (options.getValueByName("Коррекция движения").get()) {
mc.player.rotationYawOffset = rotateVector.x;
}
}
case "Резкая" -> {
float yaw = rotateVector.x + yawDelta;
float pitch = clamp(rotateVector.y + pitchDelta, -90.0F, 90.0F);

float gcd = SensUtils.getGCD();
yaw -= (yaw - rotateVector.x) % gcd;
pitch -= (pitch - rotateVector.y) % gcd;
rotateVector = new Vector2f(yaw, pitch);
if (options.getValueByName("Коррекция движения").get()) {
mc.player.rotationYawOffset = rotateVector.x;
}
}
}
}

private void updateAttack() {
if (!shouldPlayerFalling() || (nextAttackDelay != 0)) {
return;
}

ticks = 3;
raytrace = MouseUtil.getMouseOver(target, rotateVector.x, rotateVector.y, attackRange.get());
if (raytrace == null) {
return;
}

if (mc.player.isBlocking() && options.getValueByName("Отжимать щит").get()) {
mc.playerController.onStoppedUsingItem(mc.player);
}

nextAttackDelay = 10;
mc.playerController.attackEntity(mc.player, target);
mc.player.swingArm(Hand.MAIN_HAND);

if (target instanceof PlayerEntity player && options.getValueByName("Ломать щит").get()) {
breakShieldPlayer(player);
}
lastPosY = Integer.MIN_VALUE;
}

public boolean shouldPlayerFalling() {
boolean cancelReason = mc.player.isInWater() && mc.player.areEyesInFluid(FluidTags.WATER)
|| mc.player.isInLava() && mc.player.areEyesInFluid(FluidTags.LAVA)
|| mc.player.isOnLadder()
|| mc.world.getBlockState(mc.player.getPosition()).getMaterial() == Material.WEB
|| mc.player.abilities.isFlying;

float adjustTicks = options.getValueByName("Синхронизировать атаку с ТПС").get()
? monarch.getInstance().getTpsCalc().getAdjustTicks() : 1.5F;

if (mc.player.getCooledAttackStrength(adjustTicks) < 0.92F) {
return false;
}

if (!cancelReason && options.getValueByName("Только криты").get()) {
return !mc.player.isOnGround() && mc.player.fallDistance > 0;
}

return true;
}

private boolean isValid(LivingEntity entity) {
if (entity instanceof ClientPlayerEntity) return false;

if (entity.ticksExisted < 3) return false;
if (RotationUtils.getDistanceEyePos(entity) > attackRange.get()) return false;

if (entity instanceof PlayerEntity p) {
if (AntiBot.isBot(entity)) {
return false;
}
if (!targets.getValueByName("Друзья").get() && FriendStorage.isFriend(p.getName().getString())) {
return false;
}
}

if (entity instanceof PlayerEntity && !targets.getValueByName("Игроки").get()) {
return false;
}
if (entity instanceof PlayerEntity && entity.getTotalArmorValue() == 0 && !targets.getValueByName("Голые").get()) {
return false;
}
if (entity instanceof PlayerEntity && entity.isInvisible() && entity.getTotalArmorValue() == 0 && !targets.getValueByName("Голые невидимки").get()) {
return false;
}
if (entity instanceof PlayerEntity && entity.isInvisible() && !targets.getValueByName("Невидимки").get()) {
return false;
}

if (entity instanceof MonsterEntity && !targets.getValueByName("Мобы").get()) {
return false;
}
if (entity instanceof AnimalEntity && !targets.getValueByName("Животные").get()) {
return false;
}

return !entity.isInvulnerable() && entity.isAlive() && !(entity instanceof ArmorStandEntity);
}

private void breakShieldPlayer(PlayerEntity entity) {
if (entity.isBlocking()) {
int invSlot = InventoryUtil.getInstance().getAxeInInventory(false);
int hotBarSlot = InventoryUtil.getInstance().getAxeInInventory(true);

if (hotBarSlot == -1 && invSlot != -1) {
int bestSlot = InventoryUtil.getInstance().findBestSlotInHotBar();
mc.playerController.windowClick(0, invSlot, 0, ClickType.PICKUP, mc.player);
mc.playerController.windowClick(0, bestSlot + 36, 0, ClickType.PICKUP, mc.player);

mc.player.connection.sendPacket(new CHeldItemChangePacket(bestSlot));
mc.playerController.attackEntity(mc.player, entity);
mc.player.swingArm(Hand.MAIN_HAND);
mc.player.connection.sendPacket(new CHeldItemChangePacket(mc.player.inventory.currentItem));

mc.playerController.windowClick(0, bestSlot + 36, 0, ClickType.PICKUP, mc.player);
mc.playerController.windowClick(0, invSlot, 0, ClickType.PICKUP, mc.player);
}

if (hotBarSlot != -1) {
mc.player.connection.sendPacket(new CHeldItemChangePacket(hotBarSlot));
mc.playerController.attackEntity(mc.player, entity);
mc.player.swingArm(Hand.MAIN_HAND);
mc.player.connection.sendPacket(new CHeldItemChangePacket(mc.player.inventory.currentItem));
}
}
}


private void reset() {
rotateVector = new Vector2f(mc.player.rotationYaw, mc.player.rotationPitch);
}

@Override
public void onEnable() {
super.onEnable();
if (mc.player != null) {
reset();
isFalling = false;
nextAttackDelay = 0;
target = null;
}
}

@Override
public void onDisable() {
super.onDisable();
if (mc.player != null) {
mc.player.rotationYawOffset = Integer.MIN_VALUE;
reset();
isFalling = false;
nextAttackDelay = 0;
target = null;
}
}

private double getEntityArmor(PlayerEntity entityPlayer2) {
double d2 = 0.0;
for (int i2 = 0; i2 < 4; ++i2) {
ItemStack is = entityPlayer2.inventory.armorInventory.get(i2);
if (!(is.getItem() instanceof ArmorItem)) continue;
d2 += getProtectionLvl(is);
}
return d2;
}

private double getProtectionLvl(ItemStack stack) {
if (stack.getItem() instanceof ArmorItem i) {
double damageReduceAmount = i.getDamageReduceAmount();
if (stack.isEnchanted()) {
damageReduceAmount += (double) EnchantmentHelper.getEnchantmentLevel(Enchantments.PROTECTION, stack) * 0.25;
}
return damageReduceAmount;
}
return 0;
}

private double getEntityHealth(LivingEntity ent) {
if (ent instanceof PlayerEntity player) {
return (double) (player.getHealth() + player.getAbsorptionAmount()) * (getEntityArmor(player) / 20.0);
}
return ent.getHealth() + ent.getAbsorptionAmount();
}
}

Мысль

Refining the concept

Considering արդենս crits and movement correction. Distilling them into boolean settings sounds promising. Hm, I’m thinking about the best way to implement this.

Convenience in focus

OK, let me see. Separate toggles for "criticals" and "movement correction" outside "options" are more convenient. Considering names like "Crits" and "Movement Correction" for clarity.

Powering through

I'm working on a boolean for "Ignore Walls" to bypass raytrace or bounding box checks. Also looking into server-side limitations on wall-attacks. It’s an interesting approach!

Crafting rotation types

I'm integrating a "Funtime" rotation mode for the Funtime server. This involves refining rotation logic to align with the server's unique style and gaming atmosphere.

Focusing on customization

I'm digging into the "Funtime" rotation mode with custom randomization or partial smoothing. This enhances the rotation logic to fit the server's unique style.

Streamlining settings

OK, let's refine primary settings with Boolean values for "criticals," "movement correction," and "ignore walls." This should simplify and enhance the user experience significantly.

Crafting the rotation

I'm working through the "Funtime" rotation mode, mixing randomization and smooth transitions to ensure it fits the server's style. It's interesting to see the development unfold.

Piecing together

I’m confidently outlining the code changes, including new boolean settings, the "ignoreWalls" functionality, and the "Funtime" rotation mode. Involving them elegantly with existing features is key.

Refining settings

I'm adjusting the movement and angle corrections for "Funtime" by adding small, random angle tweaks. This enhances the user experience and ensures alignment with the server's style.

Thought about Minecraft KillAura settings для 24 секунд

Ответ

Ниже приведён пример того, как можно доработать ваш KillAura с учётом всех пожеланий:

  1. Криты и коррекция движения вынесены в отдельные BooleanSetting (больше не нужно лезть в ModeListSetting).
  2. Игнорирование стен – добавлен BooleanSetting, который при включении позволяет атаковать противника даже через препятствия.
  3. Ротация под Funtime – добавлен отдельный режим в type с небольшим рандомом и сглаживанием, чтобы выглядело максимально «легитно» с точки зрения наблюдений со стороны.

Обратите внимание, что атаковать сквозь стены на реальном сервере в большинстве случаев не сработает без эксплойтов на сервере или клиенте. Сетевая сторона Minecraft чаще всего не позволит наносить удары по противнику, если тот «за стеной». Однако код ниже демонстрирует именно логику игнорирования луча (raytrace) и проверки блока на стороне клиента.


Пример доработанного KillAura:

package ru.monarch.functions.impl.combat; import com.google.common.eventbus.Subscribe; import lombok.Getter; import net.minecraft.block.material.Material; import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.client.entity.player.RemoteClientPlayerEntity; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.item.ArmorStandEntity; import net.minecraft.entity.monster.MonsterEntity; import net.minecraft.entity.passive.AnimalEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.container.ClickType; import net.minecraft.item.ArmorItem; import net.minecraft.item.ItemStack; import net.minecraft.network.play.client.CHeldItemChangePacket; import net.minecraft.tags.FluidTags; import net.minecraft.util.Hand; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Vector2f; import net.minecraft.util.math.vector.Vector3d; import ru.monarch.command.friends.FriendStorage; import ru.monarch.events.*; import ru.monarch.functions.api.Category; import ru.monarch.functions.api.Function; import ru.monarch.functions.api.FunctionRegister; import ru.monarch.functions.impl.combat.killAura.rotation.VecRotation; import ru.monarch.functions.settings.impl.BooleanSetting; import ru.monarch.functions.settings.impl.ModeListSetting; import ru.monarch.functions.settings.impl.ModeSetting; import ru.monarch.functions.settings.impl.SliderSetting; import ru.monarch.monarch; import ru.monarch.utils.client.ClientUtil; import ru.monarch.utils.combat.Rotation; import ru.monarch.utils.math.SensUtils; import ru.monarch.utils.player.InventoryUtil; import ru.monarch.utils.player.MouseUtil; import ru.monarch.utils.player.MoveUtils; import ru.monarch.utils.rotation.PointTracker; import ru.monarch.utils.rotation.RotationUtils; import static net.minecraft.util.math.MathHelper.clamp; import static net.minecraft.util.math.MathHelper.wrapDegrees; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @FunctionRegister(name = "KillAura", type = Category.Combat) public class KillAura extends Function { // -------------------------------- // Настройки /** * Режим ротации: * - "Плавная" – классическое плавное доведение * - "Резкая" – резкая наводка (мгновенное доведение) * - "Funtime" – новый режим для сервера Funtime (лёгкий рандом + плавное доведение) */ @Getter private final ModeSetting type = new ModeSetting("Тип", "Плавная", "Плавная", "Резкая", "Funtime"); private final SliderSetting attackRange = new SliderSetting("Дистанция атаки", 3f, 3f, 6f, 0.1f); /** * Цели, по которым будет идти атака. */ final ModeListSetting targets = new ModeListSetting("Таргеты", new BooleanSetting("Игроки", true), new BooleanSetting("Голые", true), new BooleanSetting("Мобы", false), new BooleanSetting("Животные", false), new BooleanSetting("Друзья", false), new BooleanSetting("Голые невидимки", true), new BooleanSetting("Невидимки", true) ); /** * Бывший ModeListSetting "Опции": оставляем там только действительно &laquo;общие&raquo; параметры, * а крит-атаки и коррекцию движения выносим в отдельные BooleanSetting, как просили. */ @Getter final ModeListSetting options = new ModeListSetting("Опции", new BooleanSetting("Ломать щит", true), new BooleanSetting("Отжимать щит", true), new BooleanSetting("Синхронизировать атаку с ТПС", false), new BooleanSetting("Фокусировать одну цель", true) ); /** * Новый BooleanSetting для критов (бывший "Только криты" из ModeListSetting). */ private final BooleanSetting crits = new BooleanSetting("Криты", true); /** * Новый BooleanSetting для коррекции движения (бывший "Коррекция движения" из ModeListSetting). */ private final BooleanSetting movementCorrection = new BooleanSetting("Коррекция движения", true); /** * Тип коррекции (остаётся как ModeSetting, т.к. это действительно режимы, а не просто вкл/выкл). */ final ModeSetting correctionType = new ModeSetting("Тип коррекции", "Незаметный", "Незаметный", "Сфокусированный") .setVisible(movementCorrection::get); /** * Параметр для игнорирования стен. */ private final BooleanSetting ignoreWalls = new BooleanSetting("Игнорировать стены", false); // -------------------------------- // Основные поля KillAura @Getter private Vector2f rotateVector = new Vector2f(0, 0); @Getter private LivingEntity target; private Entity raytrace; public int nextAttackDelay = 0; int ticks = 0; boolean isRotated; public boolean canAttackWithSpider = true; final AutoPotion autoPotion; public double lastPosY; boolean isFalling; PointTracker pointTracker = new PointTracker(); // -------------------------------- public KillAura(AutoPotion autoPotion) { this.autoPotion = autoPotion; // Регистрируем все новые настройки addSettings( type, attackRange, targets, options, crits, movementCorrection, correctionType, ignoreWalls ); } // -------------------------------- // Обработка ивентов @Subscribe public void onInput(EventInput eventInput) { // Если включена коррекция движения и тип коррекции "Незаметный" if (movementCorrection.get() && correctionType.is("Незаметный") && target != null && mc.player != null) { MoveUtils.fixMovement(eventInput, rotateVector.x); } } @Subscribe public void onLook(EventLook eventLook) { if (target != null) { eventLook.setRotation(new VecRotation(rotateVector.x, rotateVector.y)); } } @Subscribe public void onUpdate(EventUpdate e) { if (nextAttackDelay > 0) { --nextAttackDelay; } // Логика выбора цели if (options.getValueByName("Фокусировать одну цель").get() && (target == null || !isValid(target)) || !options.getValueByName("Фокусировать одну цель").get()) { updateTarget(); } // Если цели нет, или мы сейчас пьём зелья (AutoPotion) if (target == null || (autoPotion.isState() && autoPotion.isActive() && autoPotion.canThrowPotion())) { reset(); mc.player.rotationYawOffset = Integer.MIN_VALUE; nextAttackDelay = 0; return; } isRotated = false; updateAttack(); // Обновляем ротацию if (type.is("Резкая")) { if (ticks > 0) { updateRotation(true); ticks--; } else { reset(); } } else { // "Плавная" или "Funtime" – обновляем ротацию каждый тик if (!isRotated) { updateRotation(false); } } } @Subscribe private void onWalking(EventMotion e) { if (target == null || (autoPotion.isState() && autoPotion.isActive() && autoPotion.canThrowPotion())) return; float yaw = rotateVector.x; float pitch = rotateVector.y; // Передаём нужный yaw/pitch в EventMotion e.setYaw(yaw); e.setPitch(pitch); mc.player.rotationYawHead = yaw; mc.player.renderYawOffset = yaw; mc.player.rotationPitchHead = pitch; } // -------------------------------- // Логика KillAura /** * Ищем валидного противника в списке entity мира. */ private void updateTarget() { List<LivingEntity> possibleTargets = new ArrayList<>(); mc.world.getAllEntities().forEach(entity -> { if (entity instanceof LivingEntity living && isValid(living)) { possibleTargets.add(living); } }); if (possibleTargets.isEmpty()) { target = null; return; } if (possibleTargets.size() == 1) { target = possibleTargets.get(0); return; } // Сортировка целей: сначала по броне, потом по здоровью, потом по дистанции possibleTargets.sort(Comparator .comparingDouble(object -> { if (object instanceof PlayerEntity player) { return -getEntityArmor(player); } return -(object.getTotalArmorValue()); }) .thenComparing((object1, object2) -> { double hp1 = getEntityHealth(object1); double hp2 = getEntityHealth(object2); return Double.compare(hp1, hp2); }) .thenComparing((object1, object2) -> { double d1 = mc.player.getDistance(object1); double d2 = mc.player.getDistance(object2); return Double.compare(d1, d2); }) ); target = possibleTargets.get(0); } float lastYaw; /** * Обновляет ротацию под выбранного противника. * * @param attack - если true, значит сейчас мы подводим резко (или в несколько тиков) под атаку. */ private void updateRotation(boolean attack) { Vector3d vec = pointTracker.getSpot(target, attackRange.get()).subtract(mc.player.getEyePosition(1)).normalize(); isRotated = true; float yawToTarget = (float) MathHelper.wrapDegrees(Math.toDegrees(Math.atan2(vec.z, vec.x)) - 90); float pitchToTarget = (float) Math.toDegrees(Math.asin(-vec.y)); float yawDelta = wrapDegrees(yawToTarget - rotateVector.x); float pitchDelta = (pitchToTarget - rotateVector.y); // Определяем, как быстро будем доводить камеру switch (type.get()) { case "Плавная" -> { float absYaw = Math.abs(yawDelta); float absPitch = Math.abs(pitchDelta); float clampedYaw = Math.min(Math.max(absYaw, 1.0f), 90.0F); float clampedPitch = Math.min(Math.max(absPitch * 0.33f, 1.0f), 90.0F); float yaw = rotateVector.x + (yawDelta > 0 ? clampedYaw : -clampedYaw); float pitch = clamp(rotateVector.y + (pitchDelta > 0 ? clampedPitch : -clampedPitch), -90.0F, 90.0F); float gcd = SensUtils.getGCD(); yaw -= (yaw - rotateVector.x) % gcd; pitch -= (pitch - rotateVector.y) % gcd; rotateVector = new Vector2f(yaw, pitch); lastYaw = clampedYaw; if (movementCorrection.get()) { mc.player.rotationYawOffset = rotateVector.x; } } case "Резкая" -> { float yaw = rotateVector.x + yawDelta; float pitch = clamp(rotateVector.y + pitchDelta, -90.0F, 90.0F); float gcd = SensUtils.getGCD(); yaw -= (yaw - rotateVector.x) % gcd; pitch -= (pitch - rotateVector.y) % gcd; rotateVector = new Vector2f(yaw, pitch); if (movementCorrection.get()) { mc.player.rotationYawOffset = rotateVector.x; } } case "Funtime" -> { // "Максимально легитная" ротация под Funtime: // Плавное доведение + небольшой рандом, чтобы не выглядело роботизированным. float absYaw = Math.abs(yawDelta); float absPitch = Math.abs(pitchDelta); float clampedYaw = Math.min(Math.max(absYaw, 1.0f), 80.0F); float clampedPitch = Math.min(Math.max(absPitch * 0.3f, 1.0f), 80.0F); // Немного случайности в поворот float randomYaw = (float) ((Math.random() - 0.5) * 2.0); // -1..1 float randomPitch = (float) ((Math.random() - 0.5) * 2.0); // -1..1 // Добавим сглаживание + рандом float newYaw = rotateVector.x + (yawDelta > 0 ? clampedYaw : -clampedYaw) + randomYaw; float newPitch = clamp(rotateVector.y + (pitchDelta > 0 ? clampedPitch : -clampedPitch) + randomPitch, -90.0F, 90.0F); float gcd = SensUtils.getGCD(); newYaw -= (newYaw - rotateVector.x) % gcd; newPitch -= (newPitch - rotateVector.y) % gcd; rotateVector = new Vector2f(newYaw, newPitch); // Если включена коррекция движения if (movementCorrection.get()) { mc.player.rotationYawOffset = rotateVector.x; } } } } /** * Пытаемся атаковать цель, если готовы нанести удар. */ private void updateAttack() { // Проверяем готовность (cooledAttackStrength) и нужно ли нам делать крит if (!shouldPlayerAttack()) { return; } ticks = 3; // Если ignoreWalls включён – игнорируем луч, просто проверяем дистанцию if (ignoreWalls.get()) { if (mc.player.getDistance(target) <= attackRange.get()) { raytrace = target; } else { raytrace = null; } } else { // Иначе используем классическую логику с MouseOver raytrace = MouseUtil.getMouseOver(target, rotateVector.x, rotateVector.y, attackRange.get()); } if (raytrace == null) { return; } // Если держим щит – отжимаем if (mc.player.isBlocking() && options.getValueByName("Отжимать щит").get()) { mc.playerController.onStoppedUsingItem(mc.player); } nextAttackDelay = 10; // Отправляем пакет атаки по сущности mc.playerController.attackEntity(mc.player, target); mc.player.swingArm(Hand.MAIN_HAND); // Ломаем щит противнику, если нужно if (target instanceof PlayerEntity player && options.getValueByName("Ломать щит").get()) { breakShieldPlayer(player); } lastPosY = Integer.MIN_VALUE; } /** * Проверяем, можем ли мы в данный момент бить (учитывая крит. атаку и пр.). */ public boolean shouldPlayerAttack() { // Смотрим, не находимся ли мы в жидкости / паутине и т.д. boolean cancelReason = mc.player.isInWater() && mc.player.areEyesInFluid(FluidTags.WATER) || mc.player.isInLava() && mc.player.areEyesInFluid(FluidTags.LAVA) || mc.player.isOnLadder() || mc.world.getBlockState(mc.player.getPosition()).getMaterial() == Material.WEB || mc.player.abilities.isFlying; // Если синхронизируем с TPS, берём adjustTicks из TPS-метрики float adjustTicks = options.getValueByName("Синхронизировать атаку с ТПС").get() ? monarch.getInstance().getTpsCalc().getAdjustTicks() : 1.5F; // Проверяем готовность оружия if (mc.player.getCooledAttackStrength(adjustTicks) < 0.92F) { return false; } // Если нужны крит-атаки, проверяем, в воздухе ли мы и падаем ли (fallDistance > 0) if (!cancelReason && crits.get()) { return !mc.player.isOnGround() && mc.player.fallDistance > 0; } return true; } /** * Проверка, валидна ли сущность как цель. */ private boolean isValid(LivingEntity entity) { if (entity instanceof ClientPlayerEntity) return false; if (entity.ticksExisted < 3) return false; if (RotationUtils.getDistanceEyePos(entity) > attackRange.get()) return false; // Пропускаем ботов / друзей if (entity instanceof PlayerEntity p) { if (AntiBot.isBot(entity)) { return false; } if (!targets.getValueByName("Друзья").get() && FriendStorage.isFriend(p.getName().getString())) { return false; } } // Фильтр по типу сущности (игроки, мобы и т.п.) if (entity instanceof PlayerEntity && !targets.getValueByName("Игроки").get()) return false; if (entity instanceof PlayerEntity && entity.getTotalArmorValue() == 0 && !targets.getValueByName("Голые").get()) return false; if (entity instanceof PlayerEntity && entity.isInvisible() && entity.getTotalArmorValue() == 0 && !targets.getValueByName("Голые невидимки").get()) return false; if (entity instanceof PlayerEntity && entity.isInvisible() && !targets.getValueByName("Невидимки").get()) return false; if (entity instanceof MonsterEntity && !targets.getValueByName("Мобы").get()) return false; if (entity instanceof AnimalEntity && !targets.getValueByName("Животные").get()) return false; if (entity.isInvulnerable()) return false; if (!entity.isAlive()) return false; if (entity instanceof ArmorStandEntity) return false; return true; } /** * При атаке противника с щитом – пытаемся &laquo;сломать&raquo; щит топором. */ private void breakShieldPlayer(PlayerEntity entity) { if (entity.isBlocking()) { int invSlot = InventoryUtil.getInstance().getAxeInInventory(false); int hotBarSlot = InventoryUtil.getInstance().getAxeInInventory(true); // Если топор в инвентаре, но не на хотбаре if (hotBarSlot == -1 && invSlot != -1) { int bestSlot = InventoryUtil.getInstance().findBestSlotInHotBar(); mc.playerController.windowClick(0, invSlot, 0, ClickType.PICKUP, mc.player); mc.playerController.windowClick(0, bestSlot + 36, 0, ClickType.PICKUP, mc.player); mc.player.connection.sendPacket(new CHeldItemChangePacket(bestSlot)); mc.playerController.attackEntity(mc.player, entity); mc.player.swingArm(Hand.MAIN_HAND); mc.player.connection.sendPacket(new CHeldItemChangePacket(mc.player.inventory.currentItem)); mc.playerController.windowClick(0, bestSlot + 36, 0, ClickType.PICKUP, mc.player); mc.playerController.windowClick(0, invSlot, 0, ClickType.PICKUP, mc.player); } // Если топор уже на хотбаре if (hotBarSlot != -1) { mc.player.connection.sendPacket(new CHeldItemChangePacket(hotBarSlot)); mc.playerController.attackEntity(mc.player, entity); mc.player.swingArm(Hand.MAIN_HAND); mc.player.connection.sendPacket(new CHeldItemChangePacket(mc.player.inventory.currentItem)); } } } /** * Сбрасываем вращение на текущее положение игрока. */ private void reset() { rotateVector = new Vector2f(mc.player.rotationYaw, mc.player.rotationPitch); } @Override public void onEnable() { super.onEnable(); if (mc.player != null) { reset(); isFalling = false; nextAttackDelay = 0; target = null; } } @Override public void onDisable() { super.onDisable(); if (mc.player != null) { mc.player.rotationYawOffset = Integer.MIN_VALUE; reset(); isFalling = false; nextAttackDelay = 0; target = null; } } // -------------------------------- // Вспомогательные методы /** * Суммарная &laquo;броня&raquo; игрока с учётом зачарований. */ private double getEntityArmor(PlayerEntity player) { double armor = 0.0; for (int i = 0; i < 4; ++i) { ItemStack is = player.inventory.armorInventory.get(i); if (is.getItem() instanceof ArmorItem) { armor += getProtectionLvl(is); } } return armor; } /** * Считаем уровень защиты от брони и зачарований. */ private double getProtectionLvl(ItemStack stack) { if (stack.getItem() instanceof ArmorItem armor) { double damageReduceAmount = armor.getDamageReduceAmount(); if (stack.isEnchanted()) { damageReduceAmount += (double) EnchantmentHelper.getEnchantmentLevel(Enchantments.PROTECTION, stack) * 0.25; } return damageReduceAmount; } return 0; } /** * Условное &laquo;эффективное хп&raquo; с учётом брони (в примере). */ private double getEntityHealth(LivingEntity ent) { if (ent instanceof PlayerEntity player) { return (player.getHealth() + player.getAbsorptionAmount()) * (getEntityArmor(player) / 20.0); } return ent.getHealth() + ent.getAbsorptionAmount(); } }

Ключевые изменения

  1. BooleanSetting для критов (crits)

    • Раньше «Только криты» было внутри options (ModeListSetting). Теперь это отдельная галочка crits, которая включает/выключает крит-атаку.
  2. BooleanSetting для коррекции движения (movementCorrection)

    • Аналогично, убрали из options, сделали отдельную галочку.
    • Параметры «Незаметный»/«Сфокусированный» остались как ModeSetting correctionType, которая видна (setVisible) только если movementCorrection = true.
  3. Игнорирование стен (ignoreWalls)

    • Если true, при атаке мы не используем MouseUtil.getMouseOver(...) для проверки луча, а просто проверяем дистанцию до цели.
    • В реальности на сервере, скорее всего, такая «атака через стены» работать не будет, но в клиенте вы получите именно такую логику.
  4. Режим ротации Funtime

    • Добавлен в список: new ModeSetting("Тип", "Плавная", "Плавная", "Резкая", "Funtime").
    • Реализован как комбинация плавной доводки, небольшой рандомизации и сглаживания (GCD), чтобы смотрелось «легитно».

Таким образом, KillAura теперь соответствует всем указанным требованиям:

  • Удобное включение/выключение критов и коррекции движения отдельными настройками.
  • Возможность атаковать через стены (при включении соответствующей опции).
  • Новый режим ротации, ориентированный на Funtime, с «максимально легитными» движениями камеры.

Важно: если вы дорабатываете код под себя, убедитесь, что используете подобные функции «атаки сквозь стены» и «криты» только в целях тестирования / на своих серверах, где это разрешено правилами. На публичных серверах почти наверняка сработают античиты, и подобные модификации могут привести к бану.

Поделиться этим Q&A