На хабре как-то писали о создании ботов к WoW, а вот статей о написании аддонов я не нашел и решил описать этот процесс на примере одной поделки, которую я писал когда-то давно, когда я был еще студентом и моего времени хватало на игру в World of Warcraft и создание аддонов к нему. С деньгами тогда было туго и играл я не на официальном сервере, а на одном из бесплатных, имеющем кучу недоделок, багов, фич и аномалий.
После того, как за случайное использование одной из них я был забанен, появилась идея создать аддон, указывающий в игре на все отличия данного шарда от официального сервера, с предупреждениями о наказаниях за их использование.
Я выложил аддон на Google Code и форум серевера и он стал достаточно популярным (как для пиратского сервера). Кому интересно, как делаются аддоны — велкам под кат.
Итак, начнём с определения. Аддон в WoW — это набор некоторых файлов, которые позволяют пользователю изменить интерфейс клиентской части игры, но никак не влияют на серверную игровую механику. Аддоны были в WoW изначально, что выгодно отличает эту ММОРПГ от некоторых других, кричащих о своём превосходстве, но не имеющих возможности создания официальных аддонов и по сей день. Аддонов есть тысячи, если не десятки тысяч. Почему так много? А потому, что стилей игры и игровых задач ровно столько же. И конечно же, разработчики молодцы, что с самого начала дали способ каждому создать что-то своё, а не навязывать единый интерфейс, требуя считать его удобным.
Главным источником документации при создании аддона у нас будет портал WoWWiki. Пару ссылок по теме:
Полезной информации там еще очень много, но до неё можно добраться по ссылкам с этих четырёх страниц в пару кликов.
Никаких специальных инструментов для создания аддонов не нужно. Все они представляют собой текстовые файлы, так что берите свой любимый текстовый редактор — и вперёд! Если он имеет подсветку синтаксиса языка Lua (именно на нём создаются аддоны) — вообще прекрасно. Этим требованиям отвечает, например, Notepad++.
А еще, энтузиасты создали на базе движка Free Visual Studio Shell специальную open-source IDE, которая называется AddOn Studio.
Штука прикольная и берет на себя ряд функций, таких как:
+ пару других прикольных вещей. В общем, можно пользоваться. Ну и еще можно просмотреть эту страницу, может быть найдете плагин к своей любимой IDE или просто что-то знакомое.
1. TOC-файл (table of conteset). Это оглавление нашего аддона. Объясняет оболочке WoW кто мы такие, как называемся, кто автор, из чего состоим и пару других вещей. Вот, например, toc-файл моего аддона:
2. XML-файлы, описывающие изменения в интерфейсе, которые мы хотим внести (фреймы, кнопки, привязку событий в интерфейсе к определенным функциям в коде). Вот xml-файл моего аддона:
Что тут написано? Мы объявляем привязку этого файла интерфейса к файлу кода WnetFeaturer.lua, далее декларируем два фрейма — один чисто для возможности повесить на события загрузки плагина своё событие, второй будет использоваться в игре для показа собственных тултипов (подсказок) к определенным объектам игрового мира.
3. Lua-файлы. Это код на языке Lua. Надо признать, что Lua в WoW несколько урезанный (детали можно почитать в WoWWiki), но для большинства задач его хватает. Вот один из файлов кода аддона (самый короткий).
Этот код выполняет следующий задачи:
В проекте есть еще пару файлов кода, но они, в большей своей части, являются просто базой невалидных объектов и подписей к ним. Интереса не вызывают.
Полные исходники можно посмотреть тут: http://code.google.com/p/wnet-featurer/downloads/list
А вот тема на форуме, где я более детально описывал фичи аддона.
Буду рад, если кому-нибудь это поможет. Спрашивайте, если что-то непонятно.
После того, как за случайное использование одной из них я был забанен, появилась идея создать аддон, указывающий в игре на все отличия данного шарда от официального сервера, с предупреждениями о наказаниях за их использование.
Я выложил аддон на Google Code и форум серевера и он стал достаточно популярным (как для пиратского сервера). Кому интересно, как делаются аддоны — велкам под кат.
Введение
Итак, начнём с определения. Аддон в WoW — это набор некоторых файлов, которые позволяют пользователю изменить интерфейс клиентской части игры, но никак не влияют на серверную игровую механику. Аддоны были в WoW изначально, что выгодно отличает эту ММОРПГ от некоторых других, кричащих о своём превосходстве, но не имеющих возможности создания официальных аддонов и по сей день. Аддонов есть тысячи, если не десятки тысяч. Почему так много? А потому, что стилей игры и игровых задач ровно столько же. И конечно же, разработчики молодцы, что с самого начала дали способ каждому создать что-то своё, а не навязывать единый интерфейс, требуя считать его удобным.
Главным источником документации при создании аддона у нас будет портал WoWWiki. Пару ссылок по теме:
Полезной информации там еще очень много, но до неё можно добраться по ссылкам с этих четырёх страниц в пару кликов.
Инструментарий
Никаких специальных инструментов для создания аддонов не нужно. Все они представляют собой текстовые файлы, так что берите свой любимый текстовый редактор — и вперёд! Если он имеет подсветку синтаксиса языка Lua (именно на нём создаются аддоны) — вообще прекрасно. Этим требованиям отвечает, например, Notepad++.
А еще, энтузиасты создали на базе движка Free Visual Studio Shell специальную open-source IDE, которая называется AddOn Studio.
Штука прикольная и берет на себя ряд функций, таких как:
- Дизайнер интерфейса
- Просмотре интерфейса в виде XML-дерева
- Браузер ресурсов WoW
- Автодополнение по WoW API
- Автогенерацию некоторых файлов
+ пару других прикольных вещей. В общем, можно пользоваться. Ну и еще можно просмотреть эту страницу, может быть найдете плагин к своей любимой IDE или просто что-то знакомое.
Пару концептуальных моментов
- Есть такая штука, как политика Blizzard, требующая от аддона бесплатности, соответствия EULA, отсутствия вреда для других пользователей и пару других скучных, но идейно верных вещей. Так что о идеях написания ботов, хаков, читов и прочего мусора лучше забыть.
- Хотите Вы того или нет, Ваш аддон будет opensource, потому что пишется на LUA+XML и распространяется в исходных кодах.
Из чего состоит аддон
1. TOC-файл (table of conteset). Это оглавление нашего аддона. Объясняет оболочке WoW кто мы такие, как называемся, кто автор, из чего состоим и пару других вещей. Вот, например, toc-файл моего аддона:
## Interface: 30000
## Title: Wnet Featurer 1.3
## Notes: Wnet Server Features List
## RequiredDeps:
## OptionalDeps:
## LoadWith: Blizzard_TalentUI
## SavedVariables:
Data.lua
WnetWarnings.lua
WnetFeaturer.xml
2. XML-файлы, описывающие изменения в интерфейсе, которые мы хотим внести (фреймы, кнопки, привязку событий в интерфейсе к определенным функциям в коде). Вот xml-файл моего аддона:
<Ui xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://www.blizzard.com/wow/ui/" xmlns="http://www.blizzard.com/wow/ui/">
<Script file="WnetFeaturer.lua" />
<Frame name="WnetFeaturerFrame">
<Scripts>
<OnLoad>
WnetFeaturer_OnLoad(self);
</OnLoad>
<OnEvent>
WnetFeaturer_OnEvent(self, event, ...);
</OnEvent>
</Scripts>
</Frame>
<Frame name="GameTooltip" parent="GameTooltip">
<Scripts>
<OnUpdate>
WnetChecker_GameTooltip_OnUpdate();
</OnUpdate>
</Scripts>
</Frame>
</Ui>
Что тут написано? Мы объявляем привязку этого файла интерфейса к файлу кода WnetFeaturer.lua, далее декларируем два фрейма — один чисто для возможности повесить на события загрузки плагина своё событие, второй будет использоваться в игре для показа собственных тултипов (подсказок) к определенным объектам игрового мира.
3. Lua-файлы. Это код на языке Lua. Надо признать, что Lua в WoW несколько урезанный (детали можно почитать в WoWWiki), но для большинства задач его хватает. Вот один из файлов кода аддона (самый короткий).
--сохраняем процедуру отрисовки текста квестов (мы ее заменим на свою)
local before_wnet_featurer_old_QuestFrameDetailPanel_OnShow = QuestFrameDetailPanel_OnShow;
function WnetFeaturer_Log( text )
SELECTED_CHAT_FRAME:AddMessage( text );
end
function WnetFeaturer_OnLoad(self)
if ( not IsAddOnLoaded( "Blizzard_TalentUI" ) ) then
_, reason = LoadAddOn( "Blizzard_TalentUI" );
end
if ( reason ) then
return;
end
-- Hook the Talent Frame's Update function
hooksecurefunc( "TalentFrame_Update", Planner_TalentFrame_AfterUpdate );
-- Register for Loading Variables
self:RegisterEvent( "PLAYER_ENTERING_WORLD" );
self:RegisterEvent( "PLAYER_LEAVING_WORLD" );
-- Locale/Gender-neutral class names FTW
wnet_featurer_classes = { [1] = "DEATHKNIGHT",
[2] = "DRUID",
[3] = "HUNTER",
[4] = "MAGE",
[5] = "PALADIN",
[6] = "PRIEST",
[7] = "ROGUE",
[8] = "SHAMAN",
[9] = "WARLOCK",
[10] = "WARRIOR", };
end
function WnetFeaturer_OnEvent( self, event, ... )
if ( event == "PLAYER_ENTERING_WORLD" ) then
tp_name = UnitName( "player" ).." of "..GetRealmName();
_, class = UnitClass( "player" );
for k, v in ipairs( wnet_featurer_classes ) do
if ( v == class ) then
tp_class = k;
end
end
elseif ( tp_name ) then
if ( event == "PLAYER_LEAVING_WORLD" ) then
tp_name = nil;
tp_class = nil;
end
end
end
function Planner_TalentFrame_AfterUpdate( frame )
local page = PanelTemplates_GetSelectedTab( PlayerTalentFrame );
local numTalents = GetNumTalents( page );
local inspect = PlayerTalentFrame.inspect;
local pet = PlayerTalentFrame.pet;
local _, class = UnitClass( "player" );
if ( pet or tp_standby or not tp_name or not tp_class or frame ~= PlayerTalentFrame or inspect ) then
return;
end
for id = 1, numTalents, 1 do
local name, texture, row, col, rank, maxRank = GetTalentInfo( page, id );
local color = nil;
if( wnet_talents[tp_class][page][name] ) then
color = wnet_talents[tp_class][page][name];
else
color = NORMAL_COLOR;
end
if ( color ) then
getglobal( "PlayerTalentFrameTalent"..id.."Slot" ):SetVertexColor( color.r, color.g, color.b );
getglobal( "PlayerTalentFrameTalent"..id.."RankBorder" ):Show();
getglobal( "PlayerTalentFrameTalent"..id.."Rank" ):SetText( rank.."/"..maxRank.." " );
getglobal( "PlayerTalentFrameTalent"..id.."Rank" ):Show();
else
getglobal( "PlayerTalentFrameTalent"..id.."RankBorder" ):Hide();
getglobal( "PlayerTalentFrameTalent"..id.."Rank" ):Hide();
end
end
end
function checkIsItemValid( text )
if ( wnet_items[text] ) then
return 1;
else
return nil;
end
end
function checkIsNPCValid( text )
if ( wnet_npc[text] ) then
return 1;
else
return nil;
end
end
WnetChecker_GameTooltip_OnUpdate=function()
local errorString = nil;
for i=1, GameTooltip:NumLines(), 1 do
local currentTooltipStr = getglobal("GameTooltipTextLeft"..i):GetText();
--проверка на баговых NPC и вещи
if ( i == 1 ) then --имя NPC или вещи находится всегда в первой строке тултипа. Искать во всех незачем.
if( checkIsNPCValid( currentTooltipStr ) ) then
errorString = "Wnet: этот NPC может работать неверно!";
elseif( checkIsItemValid( currentTooltipStr ) ) then
errorString = "Wnet: эта вещь может работать неверно!";
end
end
if strfind( currentTooltipStr, "Unique(.)Equipped" ) then
errorString = "Wnet: Одевать 2 таких вещи нельзя!";
elseif strfind( currentTooltipStr, "Target Dummy" ) then
errorString = "Wnet: Использовать 2 таких вещи нельзя!";
elseif( strfind( currentTooltipStr, "Chance on hit(.) Stuns target for" ) or strfind( currentTooltipStr, "Chance on hit(.) Оглушает цель на" ) ) then
errorString = "Wnet: Использовать эту вещь нельзя!";
elseif( strfind( currentTooltipStr, "Chance on hit(.) Knocks target silly for" ) ) then
errorString = "Wnet: Использовать эту вещь нельзя!";
elseif( strfind( currentTooltipStr, "Blackblade of Shahram" ) ) then
errorString = "Wnet: Использовать эту вещь нельзя!";
elseif( strfind( currentTooltipStr, "Seduction" ) ) then
errorString = "Wnet: этот спелл запрещен!";
elseif( strfind( currentTooltipStr, "Ursius" ) or strfind( currentTooltipStr, "Avian Darkhawk" ) or strfind( currentTooltipStr, "Avian Ripper" ) or strfind( currentTooltipStr, "Windroc Matriarch" ) ) then
errorString = "Wnet: петом брать нельзя!";
elseif( strfind( currentTooltipStr, "Wnet:") ) then
errorString = nil;
end
end
if errorString then
GameTooltip:AddLine( errorString, 1, 0, 0 );
local i;
local s=10;
for i=1, GameTooltip:NumLines(), 1 do
s=s+getglobal("GameTooltipTextLeft"..i):GetHeight()+2;
end
GameTooltip:SetHeight(s+10);
GameTooltip:SetWidth( max( GameTooltip:GetWidth(), 300 ) );
end
end
function checkIsQuestNotValid( questTitle )
if ( wnet_quests[questTitle] ) then
return 1;
else
return nil;
end
end
QuestFrameDetailPanel_OnShow=function()
before_wnet_featurer_old_QuestFrameDetailPanel_OnShow();
if( checkIsQuestNotValid( GetTitleText() ) ) then
QuestDescription:SetText("|c00ff0000 Wnet: Этот квест может не работать!|r|n"..GetQuestText());
end
end
Этот код выполняет следующий задачи:
- WnetFeaturer_Log — функция вывода в чат игроку всяких логов
- WnetFeaturer_OnLoad — вызывается при загрузке аддона (помните, в хмл-файле выше мы привязывали вызов этой функции к событию загрузки аддона). Проверяет необходимые зависимости, регистрирует пару хуков, подписывается на пару событий (например, событие входа игрока в игровой мир).
- WnetFeaturer_OnEvent — вызывается при возникновении некоторых событий, на которые мы подписались ранее. Внутри — обработчики каждого события.
- Planner_TalentFrame_AfterUpdate — самая сложная функция в этом файле. Нужна для отрисовки в дереве талантов персонажа зеленых, желтых и красных квадратиков поверх работающих, частично работающих и полностью сломанных талантов.
- checkIsItemValid, checkIsNPCValid, checkIsQuestNotValid — проверка того, является ли вещь или NPC валидно работающими на данном сервере. Эти функции вызываются из других мест кода, которые потом возле невалидных вещей рисуют вот такие картинки:
- WnetChecker_GameTooltip_OnUpdate — это хук на функцию показа тултипа. Здесь мы анализируем текст тултипа и, если в первой строке находим упоминание некоторого объекта, который работает неверно, мы изменяем текст тултипа.
В проекте есть еще пару файлов кода, но они, в большей своей части, являются просто базой невалидных объектов и подписей к ним. Интереса не вызывают.
Полные исходники можно посмотреть тут: http://code.google.com/p/wnet-featurer/downloads/list
А вот тема на форуме, где я более детально описывал фичи аддона.
Буду рад, если кому-нибудь это поможет. Спрашивайте, если что-то непонятно.