Что такое edge jump в читах
Описание функций читов CS: GO
Сегодня я бы хотел поделится с вами своими знаниями о читах и их функциях на всеми нами любимую игру Counter-Strike: Global Offensive или же попросту CS:GO. Начнём с того, какие виды читов бывают.
Прежде всего читы делятся на 2 типа, это:
Ключевое их отличие это метод взаимодействия с игрой.
Как можно уже понять из названия, Internal софты зачастую являются .dll файлами и их нужно вводить в игру специальными программами – “Инжекторами”. Они могут считывать и записывать данные из/в память игры и таким образом взаимодействуют с ней. На данный момент большинство софтов являются именно Internal софтами, и имеют довольно обширный функционал.
External и Internal софты также делятся ещё на несколько типов по своему основному предназначению в игре и типу игроков, которые будут с ним играть:
Это Legit софты, Rage, и так называемые “Универсальные” софты, в которых комбинируется сразу много функций и есть как Rage, так и Legit.
Теперь более детально рассмотрим Rage и Legit софты, функции, которые к ним относятся и их предназначения.
Legit. При переводе с английского уже можно понять, за что отвечают такие читы, переводится как “Легальный”. Legit предназначен в первую очередь для того, что бы играть с читами и получать преимущество незаметно для патруля и других игроков соответсвенно, что бы не быть забаненым естесвенно. Актуален по большей части для игры на официальных серверах Valve с Прайм-Аккаунтом. Идеально подходит для калибровки званий и играх на основных читерских аккаунтах. В Legit стиле игры также можно условно выделить Full Legit (Для игры на лигах, по типу Faceit, или если за тобой наблюдают и нельзя палится с читами) и то, что сильно отходит от Легита – Semi-Rage. О нём мы поговорим немножечко позже.
Из основных функций, которые есть во всех читах можно отметить такие:
FOV – радиус работы Аима. Говоря проще, чем больше ФОВ – тем за дальшее расстояние от прицела до противника будет происходить доводка. Зачастую в Легите используют FOV около 10 (Может варьироваться в зависимости от чита)
Вторая основная функция, которая работает совместно с FOV – это Smooth. Smooth это скорость доводки прицела до противника. В разных читах Smooth работает по разному. В некоторых читах, чем больше Smooth – тем быстрее скорость доводки до противника, а в некоторых наоборот, чем больше, то тем плавнее. Ещё существует такое довольно распространённое выражение как “Smooth 0” Под ним подразумевается мгновенная доводка до противника при нажатии кнопки выстрела. Зачастую используется на AWP и SSG-08.
RCS – Recoil Control System – Система контролирования отдачи при стрельбе “зажимом” из штурмовых винтовок по типу АК-47. Зачастую выставляется в процентах от 0% до 100%. Чем больше процентов – тем сильнее будет уводить прицел вниз и тем кучнее будут лететь пули в противника. Работает только при зажиме на противников. Не контролирует отдачу при стрельбе в стены или ещё куда либо. Для этого есть RCS Standalone – при его вкючении отдача контролируется всегда.
Hitbox Selection тоже одна из немаловажных функций в легите. Тут всё просто, какую часть тела вы выберете, туда и будет доводить прицел. Зачастую на пистолетах ставят голову, а например на AWP – тело. Также существует очень полезная и удобная фича – Nearest – “Ближайший”. при её включении доводка будет происходить до той части тела, к которой ближе всего находится прицел.
Target Switch Delay – задержка после убийства одного противника до наводки на следующего. Измеряется в миллисекундах (ms). Особенно полезно, если вы стреляете в толпу людей. При убийстве одного у вас перекинет прицел на следующего, а с этой функцией – сначала пройдёт время и только тогда перекинет на следующего. Значительно уменьшает вероятность спалится. Зачастую ставят от 200 ms и вплоть до 600 ms.
Теперь же разберёмся, что такое Rage и какие в нём есть функции.
Рейдж предназначен для игры против других жёстких читеров и на специальных серверах, где играют только читеры. Это называется HvH – Hack Versus Hack. А по простому – Читеры Против Читеров.
Основные функции в рейдже, которые есть во всех читах:
Не менее важная функция в Рейдже – это AutoWall – прострел через стены. Она работает со значениями HitChance и MinDamage, которые я уже описал выше. Её желательно включать, так у вас появляется возможность простреливать некоторые тонкие объекты на карте и преимущество над противником, если вы прострелите его раньше!
AutoWall также есть и в легите, но использовать его для легита я не советую, так как высока вероятность спалится! Чуть подробнее я об этом распишу, когда мы будем затрагивать Semi-Rage.
BodyAim – автоматическая стрельба в тело, если читу сложно попасть по голове противника. Для него может задаваться отдельный MinDamage.
AutoStop – Чит автоматически приостанавливается, когда может выстрелить для уменьшения разброса и более высокого шанса попадания.
AimStep/Speed Limit – функция, предназначенная для игры по рейджу в казуальных режимах, что бы не кикала VAC ошибка. Если играете в обычном, перелётных снайперах, бой насмерть – включайте её. Она ограничивает скорость смены углов, на позволительную VAC на этих серверах и таким образом вас не кикает VAC Error. Также нужно отключать Анти-Аимы.
SlowWalk – новая функция в читах, при активации которой, зажимая клавишу, вы начианете медленно ходить и по вам сложнее попасть. Так же благодаря снижении скорости передвижения Slow Walk увеличивает точность стрельбы. Это один из вариантов работы рассинхронизации – Desync, о котором вы можете прочитать чуть ниже.
И тут мы плавно подошли к одной из важнейших составляющих рейджа – Анти-Аимы!
Что такое edge jump в читах
Когда я вставляю в статью фрагменты кода, я стараюсь показать только то, что относится в рассматриваемому вопросу. Поэтому в статьях про физику Bhop и CountJump мы для упрощения проигнорировали функцию PM_CategorizePosition, однако в этой статье она сыграет очень важную роль. Для начала уточним, где именно она вызывается:
Как видим, PM_CategorizePosition делает трейс на 2 юнита вниз и проверяет, не задел ли он что-нибудь. Если пересечений нет, то все компоненты tr.plane.normal равны нулю, и мы оказываемся в воздухе. Если же трейс задел какой-то объект, то в tr.plane.normal запишется нормаль к поверхности этого объекта. Пусть x – это угол между нормалью и вертикальной осью, тогда условие tr.plane.normal[2] 45.573°. То есть если наклон поверхности составляет больше 45.573°, то мы имеем дело со слайдом, что с точки зрения переменной pmove->onground равносильно нахождению в воздухе.
Далее, если задетая поверхность не является слайдом, PM_CategorizePosition телепортирует нас на неё (если конечно мы уже не стоим на ней). Благодаря этому при спуске с обычной скоростью по не слишком крутому склону мы не оказываемся постоянно в воздухе, а спокойно идём по земле. Чуть позже мы вернёмся к этой безобидной на первый взгляд функции.
В статье про физику стрейфов мы рассматривали в функциях PM_AirMove и PM_WalkMove изменение скорости, при этом обделив вниманием изменение координат. Настало время исправить это.
1) В конце PM_AirMove сразу после получения новой скорости вызывается функция PM_FlyMove. Она не только получает новые координаты при движении в воздухе, но ещё и может обработать столкновения с поверхностями, в том числе одновременно с несколькими. Однако в этой статье нам будет интересно взаимодействие только с одной поверхностью, так что вид функции можно значительно упростить:
С учётом текущей скорости мы получаем точку end, в которой хотели бы оказаться, и делаем трейс из текущего положения до end. В случае свободного полёта на этом всё и заканчивается, end просто становится нашим новым положением. Если же трейс задел какой-то объект, то мы перемещаемся до точки пересечения с поверхностью объекта, получаем скорость после столкновения в функции PM_ClipVelocity и повторяем всё то же самое для оставшейся части пути, только на этот раз трейс будет производиться уже из новой точки с учётом нового вектора скорости. Плюс при расчёте новой точки end мы должны учесть, что часть пути уже пройдена, а значит время time_left, оставшееся до конца фрейма, стало меньше.
Всего PM_FlyMove может выполнить до 4 таких итераций, то есть в течение фрейма она способна обработать до 4 последовательных столкновений с различными поверхностями. Остаётся только понять, как PM_ClipVelocity меняет вектор скорости при столкновении:
Обозначим V – исходный вектор скорости in, Vnew – итоговый вектор скорости out, N – нормаль к поверхности normal. Тогда формулы из PM_ClipVelocity сводятся к следующему виду:
То есть фактически мы избавляемся от той части скорости, которая перпендикулярна поверхности. Если составляющая, параллельная поверхности, отсутствовала или была слишком маленькой, то это будет означать, что при столкновении мы просто остановимся.
2) при движении по земле в PM_WalkMove мы также получаем новые координаты:
Как и в PM_FlyMove, здесь есть получение точки dest, в которую мы хотели бы попасть. Отличие разве что в том, что мы игнорируем вертикальную скорость, то есть если трейс до dest ничего не задел, то мы переместимся только в горизонтальной плоскости. Этого нам для статьи хватит, поэтому дальнейший код я не привожу, однако для полноты картины вкратце расскажу о том, что там происходит.
Итак, мы находимся на земле и пытаемся двигаться в направлении горизонтальной скорости, но во что-то упираемся. Здесь возможны две принципиально разных ситуации: когда мы поднимаемся по наклонной плоскости и когда перед нами ступенька. В первом случае достаточно вызвать ту самую PM_FlyMove, а вот во втором всё происходит более хитро – игрока поднимает на высоту, равную значению квара sv_stepsize (по умолчанию 18 юнитов), вызывается PM_FlyMove, а затем игрока опускает на то же значение sv_stepsize обратно. Конечно, поднимание и опускание производятся аккуратно, с предварительным трейсом. Заранее неизвестно, какой из случаев нам попадётся, поэтому разработчики решили эту проблему следующим образом – делается прогноз положения для обеих ситуаций, а затем из получившихся точек выбирается та, которая находится дальше от текущего положения. Благодаря такому подходу мы можем не только подниматься по склонам, но и без дополнительных действий забираться на ступеньки высотой до 18 юнитов, о чём мы уже говорили в статье про физику CountJump.
Теперь мы готовы разобраться в том, как происходит приземление. Причём нас интересует не только тот фрейм, в который мы из воздуха попали на землю, а ещё и следующий сразу за ним. На рисунках мы будем изображать их отдельно, чтобы было видно, какой из фреймов ответственен за процессы, происходящие на их стыке. Условно разделим приземление на 4 типа:
Тип 1. мы падаем на горизонтальную поверхность, и после очередного вызова PM_FlyMove в PM_AirMove оказывается, что расстояние от модельки до земли меньше 2 юнитов, а значит после PM_AirMove вызовется PM_CategorizePosition, которая телепортирует нас на землю. В следующем фрейме за изменение координат будет отвечать уже PM_WalkMove. Она переместит нас по земле вдоль горизонтальной составляющей скорости (вертикальная просто обнулится).
Тип 2. мы падаем на горизонтальную поверхность, и очередной вызов PM_FlyMove обрабатывает столкновение с поверхностью земли, проецируя скорость на горизонтальное направление. Далее вызывается PM_CategorizePosition, которая с помощью переменной pmove->onground подтверждает, что мы оказались на земле, и поэтому в следующем фрейме вызовется PM_WalkMove. Итоговая скорость получится такой же, как и в случае первого типа.
Тип 3. мы падаем на наклонную поверхность, не являющуюся слайдом, при этом нас, прямо как в первом типе, телепортирует на поверхность, а вызов PM_WalkMove в следующем фрейме обнуляет вертикальную скорость. Если горизонтальная составляющая при этом была ненулевая, то после PM_WalkMove мы оказываемся в воздухе. Если же падение происходило строго вертикально, то после столкновения мы бы полностью остановились!
Тип 4. мы падаем на наклонную поверхность, не являющуюся слайдом, и в какой-то момент, как и во втором типе, PM_FlyMove обрабатывает столкновение, оставляя от скорости только ту часть, которая параллельна поверхности. Далее PM_WalkMove обнулит вертикальную составляющую новой скорости, и нас отбросит от поверхности.
Заметьте, что здесь скорость ведёт себя совершенно иначе, чем в случае третьего типа. Если бы мы падали вертикально со скоростью V на плоскость с наклоном в 45°, то после столкновения скорость равнялась бы V * cos(45°) * cos(45°) = V / 2, плюс PM_Friction забрала бы свои 4%. К примеру, при максимальной скорости падения 2000 юнитов/с (задаётся кваром sv_maxvelocity) мы отскочили бы со скоростью 960 юнитов/с. Столкновение такого рода можно назвать термином bounce.
1. EdgeBug. Приземление происходит на край блока.
Чем больше горизонтальная скорость, тем больше шансов, что в конце фрейма мы не останемся на земле. При этом приземление здесь именно второго типа, с изменением направления движения за счёт вызова PM_FlyMove.
2. JumpBug. Как мы выяснили, функция PM_CategorizePosition телепортирует нас на землю, если расстояние от ног до земли меньше 2 юнитов или, что то же, если центр стоячей модельки оказывается на высоте 36-38 юнитов от земли. Идея JumpBug заключается в том, что в конце первого фрейма мы оказываемся в этом зазоре, но в сидячем положении, так что телепорта не происходит, а в следующем фрейме мы одновременно встаём и прыгаем. При этом внутри PM_UnDuck происходит вызов PM_CategorizePosition, то есть во втором фрейме нас всё-таки телепортирует вниз, однако затем функция PM_Jump вновь помещает нас в воздух. Если бы PM_Jump вызывалась в коде не после, а до PM_UnDuck, то никакого JumpBug’а у нас бы уже не получилось.
Таким образом, здесь от нас требуется во-первых выполнить падение первого типа, а во-вторых одновременно встать и прыгнуть в определённом фрейме. Прыжок можно осуществить как с помощью нажатия кнопки, так и скроллом. Вставание обычно выполняют, отпуская кнопку приседания, которую заранее зажали в полёте. Тем не менее, как мы помним из статьи про физику CountJump, вызов PM_UnDuck происходит также спустя один фрейм после scroll duck’а. Так что в теории можно сделать JumpBug с помощью скролла: в первый фрейм сделать scroll duck, а в следующий прыгнуть, прокрутив скролл в другую сторону.
Горизонтальная скорость до прыжка роли не играет. Нужно лишь понимать, что раз вызывается PM_Jump, то достаточно большая скорость будет урезана функцией PM_PreventMegaBunnyJumping, о чём мы подробно говорили в статье про физику bhop.
3. DuckBug. В случае падения третьего типа мы также могли бы сделать JumpBug, но поскольку за счёт отскока от наклонной поверхности мы и так оказываемся в воздухе, то прыжок в данном случае можно и не делать. Получается, что нужно лишь в нужный момент встать, однако при падении с заданной высоты этот момент может изменяться в зависимости от того, в какую точку наклонной плоскости мы приземляемся. В связи с этим для DuckBug обычно используют прокручивание скролла (например, как в демке kz_6fd_volcano_kzz1lla_0103.42 [kz-rush.ru] ), хотя наряду со scroll duck’ом здесь сработает и обычное отпускание кнопки приседания.
4. SlideBug. Как мы видели ранее, с точки зрения переменной pmove->onground нет разницы между пребыванием в воздухе и на слайде, так что EdgeBug можно немного модифицировать, заменив падение на край блока приземлением в основание слайда. Например, SlideBug можно применить на той же kz_cg_wigblock (скачать демо [kz-rush.ru] ).
Скорее всего это не единственные возможные варианты, так что при желании можно придумать какую-нибудь свою технику.
Падения типов 1 и 3 принципиально отличаются от типов 2 и 4 тем, что в один из фреймов вертикальная координата игрока в стоячем положении находится на расстоянии 36-38 юнитов от земли. Если известна высота падения, то при стабильном значении FPS мы можем точно сказать, выполнится ли это условие, а значит и возможно ли выполнить одну из описанных выше техник. Для этого мы могли бы последовательно получить значения высот, используя формулы для нахождения координат из PM_FlyMove и изменения скорости из PM_AddCorrectGravity. Однако проще будет получить универсальную формулу, которая сразу давала бы ответ.
Пусть в процессе падения мы имеем стабильные 100 FPS, а начальная вертикальная скорость равняется нулю. Тогда длительность фрейма pmove->frametime = 0.01 секунды, а скорость каждый фрейм уменьшается на 8 юнитов/с. Значит, ускорение a = 8.0 / 0.01 = 800 юнитов/с^2. Если бы скорость падения изменялась непрерывно, то высота падения была бы связана с вертикальной скоростью перед приземлением как H = V^2 / (2 * a). На самом же деле скорость зависит от числа фреймов: V = 8 * N. Отсюда получаем N = 4 * sqrt(2 * H * a). Так как N должно быть целым, то берём ближайшее целое число, меньшее N, которое обозначим как [N]. За [N] фреймов мы преодолеем расстояние h = (8 * [N])^2 / (2 * a). Остаётся только найти разницу H – h и проверить, меньше ли она 2 юнитов.
Пусть мы хотим сделать JumpBug, упав с определённой высоты, но расчёт показал нам, что это невозможно. В таком случае мы можем попробовать изменить высоту, предварительно сделав какой-нибудь прыжок. Например, прыжок с места прибавит к высоте 45 юнитов, dd даст дополнительные 18 юнитов, а прыжок из сидячего положения 45 – 18 = 27 юнитов. Так как dd не влияет на fuser2, то cj и dcj также прибавят 45 юнитов, а duckbhop после dd 27 юнитов. Можно воспользоваться и обычным bhop’ом, но тогда высота будет зависеть от FOG и fuser2, о чём мы уже говорили в статье о физике bhop. Причём после первого bhop’а высота будет около 34.5-34.8 юнита, а после второго и далее 33.1-33.4. Для stand-up bhop’а получим соответственно 35.5-35.8 юнита после первого bhop’а и 34.5-34.8 после остальных. При duckbhop’е, если мы начинаем из стоячего положения, то перед первым bhop’ом мы проводим в воздухе столько же времени, как при stand-up’е, поэтому числа будут на 18 юнитов меньше, то есть 17.5-17.8 юнита. Между последующими bhop’ами мы находимся в воздухе столько же фреймов, как при обычном bhop’е, поэтому получаем 15.1-15.4 юнита. Хорошим примером варьирования высоты служит уже упоминавшаяся демка kz_cg_wigbl0ck_spr1n_0211.50 [kz-rush.ru] : первый JumpBug сделан после bhop’а, второй после обычного прыжка, третий после dd.
Производить расчёт для каждой из возможных высот довольно утомительно, поэтому позже в специальной статье я расскажу о плагине, который будет подсказывать, возможен ли JumpBug в определённом месте. А пока что давайте прикинем, насколько часто встречаются высоты, с которых можно сделать JumpBug.
Пока скорость падения меньше 200 юнитов/с, то за один фрейм мы пролетаем меньше 2 юнитов, следовательно, мы можем сделать JumpBug в любой из фреймов вплоть до достижения этой скорости. А достигнем мы её, пролетев H = 200^2 / (2 * 800) = 25 юнитов. На больших скоростях искомое соотношение P можно определить, поделив 2 юнита на расстояние, пролетаемое за один фрейм. Например, на скорости 400 юнитов/с, достигаемой на высоте H = 400^2 / (2 * 800) = 100 юнитов, получим P = 2 / 4 * 100% = 50%. А при максимальной скорости 2000 юнитов/с, достигаемой на высоте H = 2000^2 / (2 * 800) = 2500 юнитов, имеем P = 2 / 20 * 100% = 10%. То есть начиная с 2500 юнитов на одну высоту, с которой возможен JumpBug, будет приходиться 9 высот, с которых он невозможен (но зато с них можно сделать EdgeBug). В промежутке 25 frametime, используемая при получении новых координат, вычисляется в PM_PlayerMove прямо перед PM_ReduceTimers:
По этой же причине во вступлении к данной серии статей отдельное внимание было уделено квару fps_max. Если превысить легальное значение на 0.5, то реальное FPS будет отличаться не так сильно, однако engine FPS станет равным 111, что в свою очередь повлияет не только на столкновение с поверхностями, но и на то, как набирается скорость в функциях PM_Accelerate и PM_AirAccelerate (ведь там тоже используется pmove->frametime).
В следующей части PM_FlyMove также сыграет важную роль.