Linken’s Sphere: эволюция, устройство, баги и решение
Введение: эволюция Linken’s Sphere
В ранних версиях DotA предмет
Linken's Sphere (далее — линка, линкен сфера) представлял собой
Amulet of Spell Shield (далее — амулет защиты), который блокировал стандартные заклинания, однако весьма скоро появилась проблема – как отличить дефолтные способности от кастомных.
Дефолтные способности
Это встроенные в движок Warcraft III умения, например:
Storm Bolt —
Mountain King
Slow —
Sorceress
Blizzard —
Archmage
Их механику можно изменить только через патчинг game.dll — задача не из простых.
Кастомные способности
Они создавались авторами DotA вручную через код карты.
Например, кастомная способность
Magic Missile у героя
Vengeful Spirit — это обычный
Storm Bolt (далее – молот). Иконка, снаряд, описание, анимации – все это можно настроить, но нельзя заставить молот оглушать и замедлять цель одновременно.
Но решение было найдено весьма быстро: использовать одну дефолтную способность как основу, а поверх нее добавить вторую.
Пример —
Void у
Balanar.
- Урон и микростан обеспечиваются молотом.
- Замедление же основано на Slow, которое применяет Dummy юнит (далее – dummy). Dummy – невидимый и неуязвимый юнит, который создается кодом, выполняет определенное действие и затем обычно удаляется.
Амулет защиты не сумеет заблокировать такое заклинание. Поскольку Dummy юнит не имеет анимации каста, а Slow не имеет снаряда, то замедление применится первым и собьет амулет защиты, и лишь потом долетит молот (имеет скорость 10000, без анимации снаряда).
Руна Linken’s Sphere и её устройство
Так как стандартными средствами игры никак не проверить активен ли
Амулет защиты или еще находится на перезарядке, в одной из версий IceFrog сделал кастомный аналог
линки.
Сам предмет – пустышка, который ничего не блокирует, однако при его подборе, герою вручается
руна Linken's Sphere (далее – руна).
Вы тоже видели этот старый баг, когда руна выпадает при вручении? https://www.youtube.com/watch?v=kii4c_I1YVU
Это происходило, если в момент передачи линки, герой отсутствовал на карте (
Astral Imprisonment,
Disruption,
Burrowstrike,
Supernova), или у него заблокирован инвентарь (
Meat Hook во время броска) то руна оставалась лежать на земле возле героя.
IceFrog частично решил проблему, реализовав простую логику: если герой не смог подобрать руну, она удаляется, и попытка вручения повторяется до успешного результата.
На самом деле
руна основана на стандартном предмете
Rune of Shielding, который наделяет героя щитом, блокирующим одно вражеское заклинание.
Принцип работы почти такой же как у амулета защиты, но главное отличие – это бафф в статусе, наличие которого можно проверить кодом.
IceFrog ввел функцию, название которой нам точно неизвестно из-за обфускации (защиты) кода, но предположим что она имела такую сигнатуру:
function IsUnitHasNegation takes unit u returns boolean
return GetUnitAbilityLevel(u, 'B0BI') > 0
or GetUnitAbilityLevel(u, 'BNss') > 0
or GetUnitAbilityLevel(u, 'B0EV') > 0
endfunction
Эта функция проверяет, есть ли бафф руны у юнита ('B0BI').
С тех пор кастомные способности проверяют наличие баффа руны. Если он есть, то дальнейшая часть кода с созданием Dummy, применением замедления или еще чего-либо не выполняется. Сам бафф сбивается, блокируя основную способность, на которой основан конкретный навык. Однако IceFrog пропустил пару десятков способностей, не добавив туда эту проверку, что привело к неполноценной реализации. В нашей карте все выявленные проблемные способности исправлены и теперь корректно взаимодействуют с линкой.
Баг с Dummy и AOE способностями
Но были и другие проблемы: некоторые способности не имеют прицела по юниту, а применяются по области.
Пример —
Shrapnel у
Dwarven Sniper которая основана на канале, то есть это просто способность-пустышка, для которой можно настроить тип прицела и цели. Поскольку основа данной способности ничего не делает, то весь ее эффект производится кодом карты. Замедление в DotA от IceFrog – это все тот же
Slow, которое применяет Dummy. Однако IceFrog допустил ошибку и сделал владельцем Dummy самого Dwarven Sniper, вместо героя, на которого Dummy применяет замедление.
Соответственно бафф
руны срабатывал и сбивался. К счастью, мы исправили эту ошибку.
Баг с работой Linken’s Sphere после продажи
Знакомый баг, не так ли? https://www.youtube.com/watch?v=ORKW3tgYXgE
Он связан с устройством самой
линки. Поскольку она сделана кодом карты, технически она ничего не отражает – отражает именно бафф руны, которая вручается герою. Красивая анимация перезарядки – есть не что иное, как фикция, сделанная большим количеством кода.
Немного технической информации
В скрипте карты существует цикл, который со старта игры каждые 0.33 сек. проходится по всем героям и проверяет, жив ли герой, присутствует ли он на карте, а также, проверяет состояние счетчика линки.
Если счетчик больше 0, герой жив, присутствует на карте, но на нем нет отражающего баффа, то вызывается дополнительная функция, которая проверяет весь инвентарь, ищет там линкен сферы (если их вдруг больше, чем одна), выбрасывает их на землю и удаляет. После создает фейковые линки в инвентаре Dummy с такими же статами. Эти линки не имеют прицела и их нельзя применять на союзников, а при нажатии они уходят в перезарядку. Далее симулируется нажатие, линки уходят в перезарядку, а после вручаются герою.
Все это происходит почти мгновенно, игрок не успевает ничего заметить и видит, как идет перезарядка. Попутно создается таймер, который ждет время перезарядки
линки, либо пока герой оживет, если он умер за это время – чтобы вручить руну и заменить предмет в инвентаре.
Но почему же бафф
руны оставался даже после продажи предмета?
Дело в том, что из-за ошибки в коде с выкладыванием и подбором предметов возникает рекурсия (повторение) в триггере, отслеживающем счетчики линки. С каждым срабатыванием рекурсии счетчик растет, а код не проверяет, есть ли линка в инвентаре героя, ведь достаточно чтобы счетчик был больше 0.
Решение проблемы через глобальную переменную Зачем были нужны такие костыли – непонятно. Но решение мы нашли.
Добавив глобальную boolean переменную ItemBool, а также проверку ее значений в триггере, который реагирует на подбор и выкладывание предметов, теперь:
- перед запуском перезарядки линки, мы изменяем значение переменной
- после запуска – возвращаем значение назад
- рекурсия не происходит
- счётчик всегда равен фактическому количеству линкен сфер у героя
Вряд ли кто-то захочет купить больше одной линки.
Заключение
На этом мы завершаем наш обзор этого чудесного предмета. Мы рассмотрели его устройство, проблемы, с которыми он кочевал сквозь годы, а также пример элегантного решения. Дочитать такое – всё равно что создать линку с нуля, и вы с этим справились 🙂
