Автор благодарит компанию SirTech за предоставленные исходники, а также наших и забугорных камрадов за информацию и почву для размышлений.
Отдельная благодарность Little Alien'у, чьи труды вдохновили на создание данной статьи
Не каждый из нас имеет навыки программирования, но детская тяга разобрать что-нибудь, чтобы понять "как это устроено" не оставляет нас до старости.
Этот труд создан именно для тех, кто не утратил любопытства и по-прежнему хочет знать, "а как это работает?" и получить дополнительную информацию для применения на деле.
В этой статье мы рассмотрим, каким образом пули наносят повреждения тем, в кого они попадают, что на это влияет, и каким образом броня защищает своего носителя.
Важно: Многие из описанных тут параметров стали доступны для непосредственного обозрения и изменения только в JA2 v.1.13, где они были вынесены в XML-файлы. Поскольку эта платформа сейчас наиболее популярная и объективно интересная, я буду ссылаться на нее. Тем не менее игроки в оригинальную JA 2 АВ найдут тут для себя немало полезной (или не очень) информации.
Повреждения, которые мы будем сейчас рассматривать рассчитываются функцией BulletImpact(). Эта функция не затрагивает повреждения от взрывчатки или рукопашного боя (с оружием или без), она вызывается каждый раз, когда в цель попадает пуля или метательный нож.
То что вы увидите не является кодом игры, это "Псевдо-код", который призван в доступной форме изложить то, что происходит внутри игры.
Попадание пули в цель
Для начала ознакомимся с некоторыми параметрами, участвующими в расчетах:
Potential_Damage(Потенциальный Урон) - значение этого параметра отражает повреждения, которые пуля МОЖЕТ нанести при попадании в цель. Изначально это значение для пули равно значению "Повреждения" для оружия, из которого она была выпущена. Во время полета пуля теряет часть своей силы, так что расстояние влияет на повреждения. Кроме того, пуля теряет силу при попадании в препятствия или другие цели на пути.
В процессе вычислений, Потенциальный Урон может как уменьшиться, так и увеличиться.
HitLocation - Пуля может попасть в голову, тело или ноги. Для каждой из этих локаций урон рассчитывается по-разному, особенно при критическом попадании.
HitBy - это специальный параметр, который отражает разницу между Шансом Попадания в цель и случайным числом (от 1 до 100). Если число больше Шанса Попадания, то пуля в цель не попадет. Если меньше - разница между ними будет записана в этот параметр и использована в дальнейших вычислениях.
Теперь мы можем перейти непосредственно к формуле. Она начинается с нескольких проверок.
Код:
Если используемое оружие - метательный нож, то
Использовать свойства Метательных Ножей
Программа начинает с проверки, произошло ли попадание метательным ножом, или чем либо иным. Если проверка успешна - программа берет данные для "Ножей". Это важно, потому что метательные ножи не имеют "боеприпасов", так что программе необходимо понять, что она должна взять особые параметры для своих расчетов. Для остального оружия, программа берет свойства боеприпасов, которые используются.
Код:
Если (Цель это ТАНК) и (Боеприпасы не АНТИТАНК), то
урон = 0!
Естественно, только боеприпасы со свойством Антитанк могут нанести урон танку. Программа проверяет, если ли это свойство у используемых боеприпасов. Если нет - повреждения считаются равными 0.
Код:
Potential_Damage = Potential_Damage + разборос в +/-25%
Это случайный модификатор, который лежит в пределах -25% и +25% от потенциальных повреждений. Это дает прекрасную вариативность повреждений, независимо от чего-либо еще.
Код:
Potential_Damage = Potential_Damage + (HitBy / 2)%
Здесь впервые фигурирует параметр "HitBy" описанный выше. Шанс получить высокое значение HitBy тем больше, чем больше Шанс Попадания.
То есть, если шанс попадания равен 99% и сгеренированное число равно 15, то разница равна 84. Это даст +42% увеличения к нашему Potential_Damage!
Поскольку этот модификатор случайный, то чем более точным будет выстрел, тем больше шанс нанести повышенный урон. Выстрел с вероятностью 10% не только вряд ли попадет в цель, но также имеет высокую вероятность потерять значительную часть возможного урона.
Код:
Если Potential_Damage < 1 то
Potential_Damage = 1
Пуля иногда имеет шанс нанести 0 или меньший урон, специально для этого в программе предусмотрена проверка, которая делает минимальные возможные повреждения равными 1.
Код:
Если тип_боеприпасов - Разрывные (High Explosive) то
Potential_Damage умножается на значение "BeforeArmourImpact" для используемых боеприпасов.
Разрывные боеприпасы взрываются перед тем, как они ударят цель. Это происходит с миниракетами, но может использоваться и с другими типами боеприпасов в v.1.13. Значение "BeforeArmorImpact" прописано в XML-файле отвечающем за параметры боеприпасов. Этот множитель значительно повышает Потенциальный Урон, что обычно достаточно для пробивания брони и нанесения значительного урона цели.
Код:
Если Цель это Танк то
Potential_Damage делится на 2
Танки получают только половину Потенциальных Повреждений пули, если она вообще может им повредить...
После этого момента формула переходит в новую стадию, рассчитывая, как влияет на повреждения столкновение пули с броней.
Во-первых, проверяется элемент брони, в который попала пуля, в зависимости от HitLocation. После этого считываются все данные о параметрах этой брони и ее состоянии.
Если броня имеет какие-либо аттачи, например керамические пластины, все становится несколько сложнее, и мы рассмотрим это ниже. Сейчас же мы рассмотрим столкновение с не модифицированной броней.
Программа считывает 3 параметра для брони:
1. Armor_Status - состояние брони между 100% и 1%
2. Armor_Protection - насколько защищает броня для данного состояния
3. Armor_Coverage - насколько тело цели прикрыто данным элементом брони.
Кроме того, вводится четвертая результирующая переменная, называющаяся Total_Protection в которую записываются значения эффективности брони после всех модификаторов.
Теперь мы вернемся к формуле.
Во-первых, программа генерирует случайное число для определения, не попала ли пуля в место, не прикрытое броней:
Код:
Берется случайное число от 1 до 100.
Если это число > Armor_Coverage то
Если это бронежилет и пуля не имеет свойства "ignore armor" то
Бронежилет обойден, но попадание не фатально. Total_Protection = 1/2 Потенциального Урона.
Иначе
Total_Protection = 0. Броня не помогла остановить пулю
Таким образом, броня с маленькой "зоной покрытия" имеет невысокие шансы остановить пулю. Кроме того, мы можем видеть, что если пуля попала в торс, и "обошла" броню, тем не менее, она теряет половину своей энергии. То есть пуля как бы попадает в точку, защищенную более слабой броней.
Далее программа проверяет, не попала ли пуля в уязвимую точку, основываясь на состоянии брони:
Код:
Берется случайное число от 1 до 100.
Если это число > Armor_Status то
Total_Protection уменьшается на их разницу
и кроме того:
Если Total_protection стало меньше 0 то
Броня не смогла остановить пулю
Это означает, что чем хуже состояние брони, тем легче пуле найти уязвимую точку. При попадании в такую точку, эффективность брони уменьшается (основываясь на разнице между Случайным числом и Armor_Status). Естественно, если выстрел был очень удачен, то при попадании в такую точку, броня не может его остановить!
Далее проверяется эффект Бронебойности пули:
Код:
Total_Protection умножается на Bullet's Impact_Multiplier, и делится на Bullet's Impact_Divisor
Значения, указанные выше берутся из параметров боеприпасов в XML-файле. Для обычной бронебойной пули, модификатор равен 0.75, что означает, что считается только 75% от оригинального значения armor_protection (или, другими словами, броня на 25% менее эффективна). Для HP пуль, все наоборот - броня ЛУЧШЕ защищает от них. Для обычных пуль (окрашенных серым), это значение равно 1 и не отражается на эффективности брони.
Сразу после этого, проверяется эффект "кислоты", влияющий на состояние брони. Кислотные боеприпасы, например слюна жуков, портят броню очень быстро. Для этого программа считывает значение "Degrade percentage", которое определяет, насколько эта броня подвержена повреждениям, по сравнению с другими.
Код:
Если боеприпасы кислотные, то
Состояние брони ухудшается на (3 * armor_protection * armor_degrade) / 100
Кроме того:
Total_Protection уменьшается вдвое
Это означает, что чем больше рейтинг износа брони,тем больше она пострадает от слюны. Удивительно, но чем крепче броня, тем больше повреждений она получит. Как вы видели выше, кислота также снижает способность брони остановить пулю на 50%! Намного круче, чем Бронебойные боеприпасы!
Пули могут также иметь свойство "Игнорировать броню (Ignore Armor)" которое рассчитывает далее:
Код:
Если боеприпасы Ignore Armor то
Если попадание в торс или ноги, то
Total_Protection = 0. Броня полностью неспособна остановить эту атаку
Сейчас, только метательные ножи и дротики имеют свойство игнорирования брони. И как вы могли видеть, они не могут игнорировать каски. Странно, но это так. Так же помните, что керамические платины и т.д. все еще могут остановить эту атаку - только бронежилеты и поножи игнорируются такими боеприпасами.
Наконец, как в случае с "кислотностью", состояние брони ухудшается от попадания пули:
Код:
Состояние ухудшается по формуле (armor_protection * armor_degrade) / 100
Это происходит так же, как и от "кислотных" боеприпасов, но при этом повреждения не умножаются на 3. Это ухудшение происходит при любом попадании любого типа боеприпасов.
Наконец, программа возвращает финальное значение Total_Protection, после всех проведенных вычислений. Как применяется это значение мы рассмотрим ниже. Сначала же обратим внимание на то, как работают аттачи к броне.
Если элемент брони, в который произошло попадание имел аттачи, то все происходит немного иначе, чем описано выше. Каждый аттач имеет шанс остановить пулю ДО ТОГО как она ударит в броню. Помните, что когда мы говорим "аттачи к броне" мы имеем ввиду Керамические пластины или Защиту ног, которые имеют свое значение защиты. Другие предметы, которые можно приаттачить к броне в v.1.13 - например солнечные очки к некоторым шлемам, не останавливают пули.
Программа проверяет аттачи один за другим. Это происходит, как описано выше, для каждого в отдельности. Результат ("Total_Protection") добавляется позже.
Есть одна вещь, которую нужно знать об аттачах брони. Если зоны прикрытия аттача оказалось достаточно, и пуля не смогла обойти его, то для ВСЕХ ПОСЛЕДУЮЩИЙ аттачей зона прикрытия считается 100%, так же как и для самой брони! Это означает, что если пуля попала в пластину, она уже не может обойти броню.
Теперь мы имеем значение Total_Protection для попадания в любой элемент брони, и можно переходить к расчету оставшегося после столкновения с броней урона, который нанесет пуля.
Сначала программа разрушает любой элемент брони, состояние которого стало меньше 10%.
Теперь мы отнимаем значение Total_Protection получившееся после обсчета брони от Потенциального Урона пули:
Код:
Potential_Damage уменьшается на значениеTotal_Protection
Именно теперь, если потенциальные повреждения остались больше нуля, они переносятся на тело цели и причиняют ей ущерб. Однако, программе необходимо убедиться, что эти повреждения не стали меньше определенного минимума:
Во-первых, мы проверим свойство пули "нулевой минимальный урон".
Код:
если Пуля имеет свойство "Zero_Minimum_Damage" то
Если Potential_Damage < 0 то
Potential_Damage = 0
Пули, которые имеют это свойство могут нанести нулевой урон, если они остановлены броней. HP и Glaser боеприпасы имеют это свойство - пуля настолько слаба, что броня способна полностью предотвратить нанесение урона.
Но это не так для пуль, не имеющих свойства "Zero_minimum_damage". Они работают немного иначе, и я не совсем понимаю, зачем это, тем не менее происходит вот что:
Код:
Если пуля Не имеет свойства "Zero_Minimum_Damage" то
Если Potential_Damage < ( Original_Bullet_Power + 5 / 10 ) то
Potential_Damage = ( Original_Bullet_Power + 5 / 10 )
Я не очень понимаю, почему сделано именно так. Видимо разработчики хотели ограничить возможное снижение урона неким процентом от изначального Потенциального Урона пули.
Вернемся к коду. Если у пули Потенциальный урон стал равен 0, здесь все заканчивается. Пуля не причинила повреждений и была остановлена броней. Если же у пули еще остался Потенциальный Урон,то происходит расчет того, какой же урон она реально нанесет цели.
Во-первых, проверяется, стреляли ли мы Дротиком:
Код:
Если боеприпас - Дротик, то
Если HitBy более 20, то
Цель засыпает
Затем, независимо от значения HitBy
Цели наносятся оставшиеся повреждения
Дротики не всегда выполняют свое предназначение - они требуют хорошего попадания с высокой точностью для получения эффекта. Мы снова сталкиваемся с важностью значения HitBy описаного в начале данной статьи. Здесь дротики наносят все свои повреждения, для них расчеты заканчиваются и они не участвуют в проверке критических попаданий и т.п.
Для остальных боеприпасов проверяется модификатор "AfterArmorImpact", который берется из XML-файла:
Код:
Potential Damage умножается на After_Armor_Impact_Multiplier, потом делится на After_Armor_Impact_Divisor
Боеприпасы HP и Glaser имеют высокие значения в этом параметре - Потенциальный Урон пули умножается на 2 у боеприпасов Glaser, нанося огромный урон. Конечно, для этого им необходимо пробить броню, что они делают весьма плохо, и чаще всего приходят к этой точке формулы с нулевым уроном, а 0 * 2 это все еще 0.
Далее урон модифицируется в зависимости от места попадания:
Код:
Если HitLocation = Торс, то
Critical_Potential_Damage = Potential_Damage
Potential_Damage неизменяется
Если HitLocation = Голова, то
Critical_Potential_Damage = Potential_Damage * 1.5
Potential_Damage = Critial_Potential_Damage
Если HitLocation = Ноги, то
Critical_Potential_Damage = Potential_Damage / 2
Potential_Damage = Potential_Damage / 4
Новое значение "Critical_Potential_Damage" используется позднее, при проверке на нанесение критический повреждений. Реальные повреждения пули при критическом попадании не увеличиваются, но могут дать дополнительные эффекты - потерю характеристик и т.п.
Попадание в голову наносит в 1.5 раза больше повреждений, это происходит, если прошло критическое попадание.
Попадание в ноги - 25% от обычных повреждений, но при расчете критических повреждений - всего вдвое ниже потенциальных.
Вы можете наглядно видеть, почему критические попадания в голову рулят, особенно позже, когда мы разберем как определяются критические попадания. Кроме того, вы можете видеть, что попадание в ноги наносит гораздо меньше повреждений. Конечно, не считая того, что это попадание может сбить цель наземь...
В этой точке программа переходит к проверке "Special Criticals". Эта проверка определяет, какие особые эффекты может получить цель от попадания. В случае с хэдшотом, это может быть взрыв головы. При попадании в тело, это может быть струя кровищи из груди. При попадании в ноги, цель может упасть на землю.
Реальная формула этих расчетов очень сложна и я не полностью понимаю ее. Я не буду пытаться объяснить это здесь, поскольку это были бы мои домыслы. Важно помнить только то, что для достижения каждого эффекта, необходимо нанести некоторые минимальные повреждения. Для взрыва головы, вам требется НЕ МЕНЬШЕ 55 очков повреждений, оставшихся после всех предыдущих расчетов. Для тела, это около 85, как минимум. Чтобы противник упал после попадания в ноги - 20.
Есть еще множество факторов в этих вычислениях, которые действуют в комплексе. Дистанция до цели, бодитайп, и многое другое, влияющее на возможность нанесения критического попадания. И снова, поскольку все это весьма непросто для понимания, я не стану описывать их здесь.
Оставшаяся часть формулы определяет повреждения, наносимые особо удачливыми критическими попаданиями. Помните, что в автобитве критов не бывает. Если происходит автобитва, то формула заканчивается здесь и применяются рассчитанные повреждения.
Во-первых, мы рассмотрим скрытую атаку метательным ножом. Эта атака (означающая, что противник не знал о вас) наиболее часто наносит критические повреждения.
Код:
Если использовался Метательный Нож, то
Если цель не видит вас, то
Если HitLocation Голова или Торс, то
генерируется случайное число от 1 до 100
если это число меньше чем (HitBy, + 10 за каждый уровень МЕТАНИЯ)
Potential_Damage = Здоровье цели + 10
Critical_Potential_Damage = Potential_Damage
Снова "hitby"? Это число говорит нам, насколько удачной была атака, в сравнении с нашим Шансом Попадания. То есть чем точнее была атака, тем больше вероятность убить цель с одного попадания!
Теперь мы можем перейти к тому, как ведет себя пуля в случает критического попадания. Это комплексная формула, начинается она проверкой:
Код:
Chance_for_Critical_Hit = (Critical_Potential_Damage / 2) + (AimingTime * 5)
Чем выше повреждения, которые мы наносим цели, тем выше шанс критического попадания. В дополнение, это шанс зависит от того, как мы прицелились.
Кроме того, поскольку попадание в голову увеличивает Critical_Potential_Damage (в 1.5 раза от реальных повреждений пули), поэтому хэдшоты наиболее удачны для нанесения критических повреждений.
Код:
Генерируется случайное число от 1 до значения Chance_For_Critical_Hit
Если это число больше 30 то
происходит Критическое попадание
Если изначально "Chance_For_Critical" меньше 30 то мы однозначно не увидим критического попадания. Чем оно выше, тем выше наши шансы.
Если критического попадания не произошло, расчеты прекращаются, возвращая значение Potential_Damage и игра продолжается.
если крит случился, то мы переходим к расчетам, какого типа повреждения и в каком количестве получит цель.
Код:
Генерируется случайное число от 1 до (Critical_Potential_Damage / 2)
Stat_Change = Случайное число
Здесь программа определяет, насколько могут быть уменьшены характеристики цели. Когда ваш персонаж теряет мудрость при попадании в голову, это расчитывается здесь. Вы можете видеть, что максимальное количество потерь в характеристиках определяется силой пули, но само значение будет случайным.
Далее в коде идет расчет потерь в характеристиках в зависимости от места попадания пули:
Если попадание в голову - Мудрость уменьшится на значение Stat_Change.
Если в торс - то Сила или Ловкость уменьшится на значение Stat_Change. Шанс 50/50 для каждой из этих характеристик.
Если попадание в ноги - происходит уменьшение Проворности на значение Stat_Change.
Вообщем на этом все. В конце формула возвращает значение Potential_Damage и уменьшает на него здоровье цели.