Ротация это просто куда смотрит игрок: пара чисел, yaw (поворот вбок) и pitch (наклон вверх-вниз).
Aura это боевой модуль клиента. Он сам ищет цель, держит дистанцию и бьёт. Единственное, что он спрашивает у режима ротации, это куда смотреть. Скрипт может добавить свой режим, и тот появится обычным пунктом в настройке Rotation модуля Aura, рядом со штатными.
Режим это объект с именем и методом rotate(ctx). Метод зовётся каждый тик, пока Aura включена и цель есть, и возвращает пару углов:
class SnapRot:
name = "Snap" # так режим назовётся в списке Aura
def rotate(self, ctx):
yaw, pitch = ctx.default() # углы, которые Aura взяла бы сама
return (yaw, pitch) # вернули, клиент их применит
aura.add(SnapRot(), select=True) # select=True сразу включает этот режимИз rotate можно вернуть кортеж (yaw, pitch), словарь с настройками или None, тогда в этом тике наводки не будет вообще. Словарь даёт управлять скоростью и коррекцией прямо из режима:
| Ключ словаря | Что задаёт |
|---|---|
yaw / pitch | Углы наводки. Обязательные |
correction | silent, none, direct, strict, smooth, change_look, targeted |
priority | low, normal, target, use_item, override, max |
yaw_speed / pitch_speed | Максимальный доворот за один тик, в градусах (по умолчанию 180) |
return_speed | Как быстро камера вернётся назад, когда наводка кончилась |
raw_gcd | True значит не подгонять углы под шаг мыши (по умолчанию подгоняет) |
Сделаем что-то полезное: доворачиваемся к цели не рывком, а шагами, так наводка выглядит по-человечески. Шаг вынесем в слайдер, а для слайдера нужен модуль:
cfg = Module("SmoothRot", "Combat")
cfg.setDesc("Настройки моего режима наводки для Aura")
step = Slider(cfg, "Step").min(2).max(60).step(1).set(14).suffix("° / тик")
speed = Slider(cfg, "Speed").min(20).max(180).step(5).set(80).suffix("° / тик")
class SmoothRot:
name = "Smooth (script)"
def rotate(self, ctx):
if ctx.target is None:
return None
yaw, pitch = ctx.default() # куда надо смотреть
cur_yaw, cur_pitch = rotations.player() # где камера сейчас
# доворот не больше step градусов за тик плюс подгонка под шаг мыши
yaw, pitch = rotations.gcd_step(cur_yaw, cur_pitch, yaw, pitch, float(step.get()))
return {
"yaw": yaw,
"pitch": pitch,
"correction": "silent",
"priority": "target",
"yaw_speed": float(speed.get()),
"pitch_speed": float(speed.get()),
}
aura.add(SmoothRot(), select=True)Единственный аргумент rotate это контекст. В нём всё, что Aura знает о текущей драке, плюс помощники, чтобы не считать углы вручную.
| Поле / метод | Что это |
|---|---|
ctx.target | Цель Aura (сущность) или None |
ctx.attack_distance | Дистанция атаки из настроек Aura |
ctx.walls | Разрешено ли бить сквозь стены |
ctx.ray_trace | Включена ли проверка, что луч до цели не упирается в блок |
ctx.move_correction | Режим коррекции движения, выбранный в Aura |
ctx.handler | Внутренний обработчик ротаций. Методы на нём звать нельзя, всё нужное есть в ctx и rotations |
ctx.rotations | Тот же объект, что и глобальный rotations |
ctx.default() | Углы (yaw, pitch), которые Aura взяла бы по умолчанию |
ctx.to(entity) | Углы до сущности. Без аргумента до ctx.target |
ctx.apply(yaw, pitch, **kw) | Применить углы вручную (обычно хватает просто вернуть их из rotate) |
Из этих кусочков собираются хитрые режимы. Например, ленивый: если цель дальше дистанции атаки, не доворачиваемся вовсе, чтобы не палиться лишними движениями.
class LazyRot:
name = "Lazy"
def rotate(self, ctx):
target = ctx.target
if target is None:
return None
me = world.self()
if float(me.distanceTo(target)) > float(ctx.attack_distance):
return None # далеко, не крутим камеру зря
return ctx.to(target) # углы на ближайшую точку цели
def can_attack(self):
return world.ingame() and mc.player.isOnGround() # бьём только с земли
def target_null(self):
client.overlay("цель потеряна")
aura.add(LazyRot())Кроме rotate у режима бывают необязательные методы: attack() своя логика удара, can_attack() разрешить ли Aura бить прямо сейчас, target_null() цель пропала, и update() зовётся каждый тик, есть цель или нет.
Класс писать не обязательно. Простой режим это две строчки с декоратором:
@aura.rotation("Simple", select=True)
def rotate(ctx):
return ctx.to(ctx.target)А если нужны все обработчики, но не хочется класса, есть форма из обычных функций:
def rotate(ctx):
return ctx.default()
def can_attack():
return world.ingame() and not mc.player.isUsingItem()
aura.add_rotation("Functional", rotate=rotate, can_attack=can_attack, select=True)| Вызов | Что делает |
|---|---|
aura.add(mode, select=False) | Зарегистрировать режим-объект |
aura.add_rotation(name, rotate=, attack=, can_attack=, target_null=, update=, select=) | То же самое, но из функций |
aura.rotation(name, select=) | Декоратор над функцией rotate |
aura.select(name) | Выбрать режим: свой или штатный |
aura.remove(name) | Убрать свой режим |
aura.has(name) | Есть ли такой режим |
aura.current() | Имя выбранного сейчас режима |
aura.target() | Текущая цель Aura (то же, что world.target()) |
aura.enabled | Включена ли Aura. Свойство, можно и присваивать |
aura.attack_distance | Дистанция атаки из настроек |
aura.module | Сам модуль Aura |
Наводка нужна не только Aura: автоудочка, авто-лук, посмотреть на блок перед постановкой. Для этого есть rotations, тот же обработчик ротаций клиента, только доступный напрямую.
| Метод | Что делает |
|---|---|
rotations.to(entity) | Углы (yaw, pitch) до ближайшей точки сущности |
rotations.to_point(x, y, z) | Углы до точки в мире |
rotations.default(target) | Углы, как их взяла бы Aura, до цели |
rotations.player() | Куда камера смотрит прямо сейчас |
rotations.current() | Углы, которые сейчас держит обработчик ротаций |
rotations.idling() | True, если камеру сейчас никто не крутит: ни Aura, ни другой модуль, ни скрипт |
rotations.gcd(fy, fp, ty, tp) | Подогнать углы под шаг мыши |
rotations.gcd_step(fy, fp, ty, tp, step) | То же плюс ограничить доворот шагом в градусах |
rotations.vanilla_step() | Размер шага мыши при твоей текущей чувствительности |
rotations.apply(yaw, pitch, ...) | Применить наводку |
Главный тут apply. Его зовут каждый тик, пока наводка нужна: обработчик сам размажет поворот по тикам, подгонит под шаг мыши и вернёт камеру назад, когда вызовы прекратятся.
mod = Module("AimHelper", "Combat")
mod.setDesc("Тихо наводится на ближайшего игрока в радиусе")
reach = Slider(mod, "Distance").min(2).max(8).step(0.5).set(4.5).suffix(" м")
speed = Slider(mod, "Speed").min(10).max(180).step(5).set(45).suffix("° / тик")
@events.tick
def on_tick(event):
if not mod.isEnabled() or not world.ingame():
return
target = world.nearest_player()
if target is None:
return
if float(world.self().distanceTo(target)) > float(reach.get()):
return
yaw, pitch = rotations.to(target) # куда смотреть
rotations.apply(
yaw, pitch,
correction="silent", # сервер видит поворот, камера на экране нет
priority="normal",
yaw_speed=float(speed.get()),
pitch_speed=float(speed.get()),
return_speed=90.0,
vanilla_gcd=True, # подгонять под шаг мыши: движения выглядят живыми
)| Параметр apply | Значения | Зачем |
|---|---|---|
correction | silent (по умолчанию), none, direct, strict, smooth, change_look, targeted | Как связать поворот с движением: silent значит сервер видит поворот, а камера на экране не дёргается; direct крутит саму камеру |
priority | low, normal (по умолчанию), target, use_item, override, max | Кто победит, если наводку одновременно просят несколько модулей |
yaw_speed / pitch_speed | градусы за тик, по умолчанию 180 | Ограничение скорости доворота |
return_speed | градусы за тик, по умолчанию 180 | Скорость возврата камеры после наводки |
vanilla_gcd | True / False | Подгонять углы под шаг мыши (по умолчанию True) |
raw_gcd | True / False | Обратный тумблер: True отдаёт углы как есть, без подгонки |
Боевые модули клиента выбирают, кого бить, не сами: у них общий фильтр и общая сортировка. Объект target даёт то же самое скрипту, поэтому свой аим или ESP выбирает цель ровно так же, как Aura.
target.current() # кого сейчас бьёт Aura
target.living() # она же, если это живое существо
# своя выборка: только игроки в 6 блоках, ближайший первым
victim = target.best(players=True, range=6, sort="distance")
# все подходящие, в том же порядке
for entity in target.all(players=True, mobs=True, range=16):
client.msg(str(entity.getNameForScoreboard()))| Настройка | Что значит |
|---|---|
players / mobs / animals | Кого вообще брать в расчёт |
invisibles | Считать ли невидимых |
naked | Считать ли игроков без брони |
friends | Бить ли друзей из списка клиента |
users | Бить ли других игроков с этим клиентом |
armor_stands | Считать ли стойки для брони |
exclude_teammates | Пропускать ли свою команду по цвету ника |
range | Дальность в блоках |
sort | distance, health, fov, bad_armor, good_armor |
sort решает, кто окажется первым: ближайший, самый раненый, тот кто ближе к прицелу, в худшей или лучшей броне.
mod = Module("TargetESP", "Visuals")
@events.render_3d
def draw(event):
if not mod.isEnabled() or not world.ingame():
return
victim = target.best(players=True, invisibles=True, range=8, sort="fov")
if victim is not None:
render3d.box(event, victim, theme.accent())Отдельно есть список приоритетных ников: клиент бьёт их первыми, а команда .target правит тот же список.
target.add("Nick")
target.is_target("Nick") # True
target.list() # ["Nick"]
target.remove("Nick")
target.clear()