Attack Aura улучшения
Erstellt am: 18. Januar 2025
Erstellt am: 18. Januar 2025
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 с учётом всех пожеланий:
type
с небольшим рандомом и сглаживанием, чтобы выглядело максимально «легитно» с точки зрения наблюдений со стороны.Обратите внимание, что атаковать сквозь стены на реальном сервере в большинстве случаев не сработает без эксплойтов на сервере или клиенте. Сетевая сторона Minecraft чаще всего не позволит наносить удары по противнику, если тот «за стеной». Однако код ниже демонстрирует именно логику игнорирования луча (raytrace) и проверки блока на стороне клиента.
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 "Опции": оставляем там только действительно «общие» параметры, * а крит-атаки и коррекцию движения выносим в отдельные 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; } /** * При атаке противника с щитом – пытаемся «сломать» щит топором. */ 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 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; } /** * Условное «эффективное хп» с учётом брони (в примере). */ private double getEntityHealth(LivingEntity ent) { if (ent instanceof PlayerEntity player) { return (player.getHealth() + player.getAbsorptionAmount()) * (getEntityArmor(player) / 20.0); } return ent.getHealth() + ent.getAbsorptionAmount(); } }
BooleanSetting для критов (crits
)
options
(ModeListSetting). Теперь это отдельная галочка crits
, которая включает/выключает крит-атаку.BooleanSetting для коррекции движения (movementCorrection
)
options
, сделали отдельную галочку.ModeSetting
correctionType
, которая видна (setVisible
) только если movementCorrection
= true
.Игнорирование стен (ignoreWalls
)
true
, при атаке мы не используем MouseUtil.getMouseOver(...)
для проверки луча, а просто проверяем дистанцию до цели.Режим ротации Funtime
new ModeSetting("Тип", "Плавная", "Плавная", "Резкая", "Funtime")
.Таким образом, KillAura теперь соответствует всем указанным требованиям:
Важно: если вы дорабатываете код под себя, убедитесь, что используете подобные функции «атаки сквозь стены» и «криты» только в целях тестирования / на своих серверах, где это разрешено правилами. На публичных серверах почти наверняка сработают античиты, и подобные модификации могут привести к бану.