Я вернулся и, как водится, сразу приступаю к гайдописанию =)
Этот гайд будет посвящен, как уже было заявлено, кликабельным викаурам. Данный вопрос не освящен в интернете от слова вообще, поэтому гайд является единственным в своем роде. Но не стоит думать, что это узкая тема. Если вникнуть в написанное здесь, можно весьма широко приоткрыть для себя дверь в мир написания аддонов. В общем, поехали.
Предыстория вопроса такова. В легионе я поставил себе аддон, создающий интерфейс в стиле дьябло. Со сферками и статуями. Как водится, при переходе к БФА, все аддоны поломались, и этот в том числе. Но он был глючный и так, а надеятся на своевременное обновление не приходилось, и я решил попробовать переписать его своими силами.
Выбор в первую очередь пал на известный всем аддон Weakauras2. Здесь, как ни странно, стоит уточнить, чем на самом деле является этот аддон. Рядовой пользователь просто тырит непонятные строчки с wago, на таких мы сразу поставим крест, и этот гайд вряд ли для вас. Продвинутый [все еще] пользователь использует стандартный инструментарий для создания значков, текстовых полей, анимированных текстур прогресса для отслеживания состояния игрового мира. И наконец те, кого стоит уже назвать "разработчиками" в состоянии дополнить эти шаблоны скриптами на lua с использованием WoW API, что позволяет выходить далеко за рамки базовых инструментов как в плане функциональности, так и в плане дизайна. На самом деле возможности разработки за счет интерфейса скриптовых функций так велики, что можно смело назвать Weakauras ФРЕЙМВОРКОМ для разработки аддонов: как фреймворк аддон инкапсулирует механизмы создания фреймов для аур, их распределения по экрану, загрузку ресурсов и проч, по сути избавляя нас от писанины.
Разберем с этой точки зрения то, что было сделано для эмуляции интерфейса в стиле дьябло:
1) Вставка статического арта. Для этого нам более чем достаточно стандартного, хоть и неплохо закопанного триггера. Картинка загружается с диска просто по пути в папке вов. Но это тривиально.
2) Сферки как текстуры прогресса и текст для них. это достаточно стандартные вещи.
3) Затем я решил, что я хочу повторить в своем аддоне значки отслеживания боя, отдыха и пвп. Для этого достаточно опять же триггера Статус > Условие
4) Более интересным оказался значок лидера и значок номера группы. Здесь стандартных триггеров уже нет, и потребовалось написать собственный код ака кастомный триггер
Для проверки на лидера достаточно выставить триггер Самостоятельно > Статус, затем можно проверку каждый кадр, но я оптимизировал событиями GROUP_ROSTER_UPDATE, PARTY_LEADER_CHANGED, которых более чем достаточно для отслеживания любых изменений в группе. Далее собственно следует код "Свой триггер", он же условие, которое будет возвращать, показывать или не показывать ауру, по этим событиям.
function(...) return UnitIsGroupLeader("player") endПроверка простая, и больше ничего от нас на самом деле не требуется.
Для проверки номера группы (ну уже, право, надоело спрашивать в войсе "А В КАКОЙ Я ГРУППЕ О_О") нужно сделать дополниельный шаг.
Во-первых, проверим, что мы вообще в группе, иначе зачем нам вообще ее номер. Пишем кастомный триггер:
function() -- Следует заметить, что здесь используется чуть другая форма записи, несколько более безопасная в рамках не строготипизированного языка lua return IsInRaid() and true endДальше напишем кастомный отображаемый текст, у текстового поля для этого надо вписать %c, и сразу появится поле для скрипта:
function () -- Проверим всех членов группы for i=1,GetNumGroupMembers() do -- У них узнаем имя n и номер группы g local n,_,g=GetRaidRosterInfo(i) if n==UnitName("player") then -- Если имя совпадает с именем игрока, немедленно возвращаем номер группы и завершаем выполнение return g end end -- Не нашли, не пишем ничего. Нужно скорее для очистки совести. ибо триггер с этим справился заранее. return "" end
В принципе на этом можно было бы и остановиться: интерфейс в каком-то виде мы уже запилили... Вот только пример этот мягко подводил читателя к тому, как мы от стандартных триггеров перешли к написанию кода на том же языке, что и аддоны. Не получается ли, что мы на самом деле написали свой маленький аддон? По факту да, так и работают фреймворки, скрывающие от нас кучу рутины.
И тут Остапа понесло. Раз мы уже запилили целый фрейм игрока, зачем нам стандартный? СКРЫТЬ!
Берем наш фрейм сферки хп, просто потому что он нам очень нравится и во вкладке Действия > При инициализации пишем:
-- Hiding blizzard frame PlayerFrame:Hide()Как к этому решению прийти... Спросить у гугла, как "WoW API lua hide player frame", и мы найдем ровно ту функцию Hide, которая применена выше. Остается запустить ее при загрузке интерфейса. Есть сайт, который позволяет СОЗДАТЬ, чтоб его, АДДОН, который вызовет на своей загрузке наш код. А оно нам надо? Воспользуемся нашей викаурой для запуска кода на загрузке. Это уже нетривиальное использование: по сути в этот момент мы полностью ушли от аур и статусов, в общем, от назначения аддона к его функциям как фреймворка.
Ладно, это мы сделали. Но теперь мы не можем (если мы не в группе) сделать две важные вещи: не можем вызвать контекстное меню игрока без выделения и не можем выделить себя мышью. Естественно, макросы с маусовером нам тоже делать некуда. И вот здесь мы приходим неожиданно к задаче о кликабельных викаурах.
С этой задачей сталкивались многие, кто копался в этом аддоне: викауры не реагируют на клики мышью. Да, вообще. В интернетах максимум что можно найти - это как Вася99 на своем ютьюб канальчике серьезным тоном вещает, как подложить панельки Bartender под викауру. издевательство. Нас такое не устраивает.
А теперь подумаем. Аддон говорит, нельзя. При этом мы можем писать в нем скрипты. В этих скриптах мы можем достучаться до объектов фреймов наших викаур, то есть знаем о них все. Но при этом другие аддоны реализуют свои фреймы игрока аналогично стандартному, а значит это возможно. Так и что нам, черт возьми, тогда мешает сделать то, что мы хотим? ... Ничего. Но мы опять же не обязаны писать свой аддон с нуля: нас устраивает то, что есть в викаурас.
Итак, гуглим и выясняем, что чтобы обработать клик, нам нужен некий кликабельный объект, которым фрейм викауры не является. Ну так в чем проблема? создадим его там же, где мы скрыли фрейм игрока, на загрузке:
-- Creating button local captor = CreateFrame("Button", "PlayerOrbButton", aura_env.region, "SecureUnitButtonTemplate") -- Setting position point, relativeTo, relativePoint, xOfs, yOfs = aura_env.region:GetPoint() captor:SetPoint(point, mainframe, relativePoint, xOfs, yOfs) captor:SetWidth(aura_env.region:GetWidth()) captor:SetHeight(aura_env.region:GetHeight())Во-первых мы создали объект типа Кнопка. Естественно, кнопка кликабельна. Мы дали ей название "PlayerOrbButton", чтобы можно было потом к ней стуачаться, затем родительский объект, к которому она будет привязана... Последний пораметр нам пока не нужен.
Затем из aura_env.rect, который и является фреймом викауры, копируем его положение и размеры.
Итак, теперь мы имеем кнопку, которая получает из системы сообщения onmousedown, и теперь мы можем задать скрипт, который будет выполняться по этому сообщению:
Player dropdown for right click captor:SetScript("onmousedown", function(self, button) if button == "RightButton" then -- Результат короткого гугления: функция контекста игрока ToggleDropDownMenu(1, nil, PlayerFrameDropDown, "PlayerOrbButton", 0, 0) end end)Ура. Перезагрузим интерфейс /reload, и у нас по клику правой кнопкой мыши вылезает контекстное меню. Получается, мы только что решили задачу, над которой уже по меньшей мере 6 лет бьются Васи99? На самом деле пока еще нет. У нас осталось еще две задачи, маусовер и выделение, и тут мы получаем веселую загвоздку.
Логичным образом дополним наш код выделением игрока:
Player dropdown for right click captor:SetScript("onmousedown", function(self, button) if button == "RightButton" then ToggleDropDownMenu(1, nil, PlayerFrameDropDown, "PlayerOrbButton", 0, 0) elseif button == "LeftButton" then TargetUnit("player") end end)Мы ожидаем, что теперь по ЛКМ у нас будет выдеяться игрок... А мы получаем ошибку: модификация якобы выполнила действие, доступное только интерфейсу близзард. Многие видели такие ошибки у аддонов, но в чем же дело у нас?
Дело в том, что в вов есть три интерпретатора lua. Но сначала взглянем на знакомые всем макросы. Допустим, когда-то давно в макросах можно было задавать /castsequence с задержками времени, творить большую магию... но потом это понерфили, чтобы игроки не злоупотребляли. При этом в макросах же мы встречаем первый интерпретатор lua - /script, где может быть записан луа-код. Вот только если разрешить там делать все, что угодно, то зачем было нерфить макросы? Поэтому /script может использовать только ПУБЛИЧНЫЕ функции интерфейса, функции выделения цели и использования скиллов такими не являются, и доступны они в макросах только через оболочки /use и /target, которые подчиняются упомянутым выше жестким ограничениям.
А теперь еще два интерпретатора: аддоны и интерфейс близзард. Для интерфейса близзард есть приватные функции, которые может использовать только он, но их СОВСЕМ мало, остальные же функции являются ЗАЩИЩЕННЫМИ, и нам нужно найти способ их вызывать.
Выясняется, что для этого объект, исполняющий защищенный код должен быть объектом из следующего списка:
https://wowwiki.wiki...SecureTemplates.
Логично, что среди них нас интересует SecureUnitButtonTemplate - кнопка юнита. Начинаем разбираться.
Во-первых, выясняется как его создать, но этим мы уже воспользовались выше 4ым параметром.
Во-вторых, такому фрейму можно задать юнита, которого он представляет, а это именно то, что нам надо для маусовера.
Теперь мы на самом деле можем вызывать функции в лоб, но мы воспользуемся специально формой записи для этого типа:
SecureUnitButton_onload(captor, "player", function() end)Все. Теперь наш фрейм поддерживает еще и выделение игрока с маусовером. Задача выполнена. Еще раз приведем полностью код нашего микроаддона:
-- Hiding blizzard frame PlayerFrame:Hide() -- Creating button local captor = CreateFrame("Button", "PlayerOrbButton", aura_env.region, "SecureUnitButtonTemplate") -- Setting position point, relativeTo, relativePoint, xOfs, yOfs = aura_env.region:GetPoint() captor:SetPoint(point, relativeTo, relativePoint, xOfs, yOfs) captor:SetWidth(aura_env.region:GetWidth()) captor:SetHeight(aura_env.region:GetHeight()) SecureUnitButton_onload(captor, "player", function() end) captor:SetScript("onmousedown", function(self, button) if button == "RightButton" then ToggleDropDownMenu(1, nil, PlayerFrameDropDown, "PlayerOrbButton", 0, 0) end end)Как видно, ничего сложного тут нет. А что самое хорошее, на этом примере можно выстроить вообще все что угодно с минимальным использованием гугла.
Остается последний вопрос, который я пока не затронул: а как нам использовать из такого самопала скиллы?
Ответ: нам требуется SecureActionButtonTemplate, который работает благодаря ровно тем же принципам, что и SecureUnitButtonTemplate.
До достижения результата достаточно прочитать инструкцию по ссылке выше, но я приведу пример викауры со скиллом, где по нажатию применяется эльфийский волшебный поток.
-- Creating button local captor = CreateFrame("Button", nil, aura_env.region, "SecureActionButtonTemplate") -- Setting position point, relativeTo, relativePoint, xOfs, yOfs = aura_env.region:GetPoint() captor:SetPoint(point, relativeTo, relativePoint, xOfs, yOfs) captor:SetWidth(aura_env.region:GetWidth()) captor:SetHeight(aura_env.region:GetHeight()) --Отладочная текстура для проверки, где получается кнопка --captor:SetNormalTexture("Interface\SPELLBOOK\UI-Spellbook-SpellBackground") captor:SetAttribute("type", "spell"); captor:SetAttribute("spell", "Волшебный поток");
Также для примера приведу строку для импорта этой викауры. Следует заметить, что в ней я использую простейший триггер и даже не делаю в ней иконку. Это связано лишь с тем, что в контексте уже существующих у меня викаур эта является заглушкой, выполняющей только функции клика, в то время как реальные триггеры для визуализации вынесены отдельно. Мне так было нужно
Все, на основании написанного читатель сможет создавать свои собственные кликабельные панели скиллов. Да и вообще, с использованием приведенных трюков возможности разработчика викаур становятся практически безграничными.