Неведение - благо, или работает - не трогай

После того, как в 6.88 мы добавили отображение регенерации, некоторые игроки заметили, что реген хп иногда увеличивается. В некоторых случаях реген увеличивался до космических масштабов — около 50 хп в секунду при наличии всего пары колец регенерации. Интересно, что первыми этот баг нашли игроки LOD — классические дотеры на такие мелочи внимания не обращают, им бы быстрее  барьку и  сларка пикнуть. Итак, откуда баг взялся?

Специалист по регенерации недоумевает

Простое наблюдение показало, что везде, где наблюдались баги с регеном, был  сларк. Если точнее, то его пассивка, ворующая статы у вражеских героев. Стоило лишь умереть хотя бы под эффектом одного стака, и при возрождении реген оказывался увеличенным. Механика оказалась крайне простой:
  • когда герой умирает, мы возвращаем ему все отобранные параметры;
  • при изменении количества силы, в плюс или в минус, герой получал бонус регенерации;
  • бонус равен сумме хп регена от предметов.

Интересно, но ведь так всегда и было? Как только герой умирал, его параметры возвращались в норму. Получается, мы десять лет играем с этим багом, а заметить его смогли только сейчас, когда регенерацию, наконец, можно увидеть на панели? Ан нет.

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

В доте2 правила подсобрались в кучу, и все реинкарнации получили аналогичные свойста — при убийстве героя с  аегисом с него должны пропасть все дебафы. поэтому я и заменил EVENT_UNIT_DEATH на более емкое событие WidgetDeath (умирает любой игровой объект). С точки зрения движка это выглядит так:
  • срабатывает WidgetDeath;
  • если у юнита есть реинкарнация, дальнейшее выполнение прерывается здесь;
  • срабатывает UNIT_DEATH.

Казалось бы, разница минимальна, но вот тут близзарды и подловили меня, подарив баг с регенерацией.

Техническая часть
Когда мы манипулируем параметрами живого юнита, движок тут же пересчитывает связанные показатели. Если речь о силе, то сразу пересчитывается регенерация, чтобы правильно хилить юнита. Следует помнить, что движок не хранит многие данные в явном виде. Например, характеристики героя представлены целыми числами, без десятичной части, хотя тот же прирост за уровень явно имеет точку и хвостик. Как это сделано? Очень даже легко!

  • Каждый раз, когда герой получает уровень, производятся расчеты:
  • умножается бонус параметра за уровень на уровень героя минус 1 (пусть будет 2.3 за уровень и 2-й уровень, тогда бонус будет 2.3);
  • результат округляется вниз до целого (2);
  • проверяется, сколько было выдано силы за уровень до этого (отдельное поле в структуре, изначально 0, разумеется);
  • разницу (2-0=2) добавляют к параметру;
  • сохраняется количество выданных бонусов (2).

Простой и незатейливый алгоритм позволил не менять методику хранения, и при этом разрешил использовать дробные бонусы за уровень.

Регенерация также не хранится в дробном виде, только конечная сумма всех модификаторов. Когда юнит получает бонус регена, специальная функция полностью пересчитывает значение. Судя по всему, при перерасчете все бонусы способностей отбрасываются, чтобы не повлиять на определение регенерации сугубо от силы плюс родной базы. Но такой подход работает только при использовании UNIT_DEATH. При попытке вручить атрибут на пару виртуальных секунд раньше, в момент, когда хп юнита уже упало до нуля и сработало WidgetDeath, игра оказывается не в силах избавиться от лишних абилок. Почему? Не знаю. Но о том, что в движке активно практикуется периодическое отключение-подключение абилок при различных подсчетах, известно давно, на примере самого быстрого убийства рошана.

В итоге юнит остается с родной регенерацией ПЛЮС реген от абилок, любого рода. И игра засчитывает это состояние как исходное, смело плюсуя к нему реген от атрибутов. А затем… Затем игра инициирует добавление «снятых» абилок обратно, попутно заново активируя те, которые влияют на регенерацию. Ведь она не в курсе, что снять их не вышло, и в обычном режиме пошла включать их по второму кругу. В результате юнит приобретает бонус регенерации в размере суммы всех регенераций, что на нём есть. Эта погрешность дальше будет считаться основной регенерацией и никогда не исчезнет. Ну или не основной, но в доте нет способностей на увеличение базовой регенерации в процентах, так что не знаю наверняка. Всё вышесказанное касается и интеллекта — регенерация маны тоже может сойти с ума при таких махинациях.

Итог и костыль
Итак, баг появился из-за исправления другой проблемы, когда негативные эффекты продолжали действовать после  реинкарнации. У фрога этой проблемы не было, т.к. он в принципе не заморачивался такими мелочами как диспелы — как получится, так и будет диспеллиться, это же старый варкрафт, чего мудрить. И, сука, прокатывало же. А решение бага? Самое банальное — ничего не восстанавливать мертвецам, а лишь строго после респауна. Полет нормальный.

2 комментария

avatar
верните меня в 2007, чтобы я ничего этого не знал
  • Loki
  • 0
avatar
Да да, машину времени дай плз.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.