В прошлой статье мы изучили базовые состявляющие триггерных "муи"-заклинаний - понятие хэндл, использование хэша и таймеры. В сегодняшней статье мы изучим создание заклинание с периодическим эффектом. На роль "жертвы" возьмем приблизительный аналог "горелки" Акса из ДотЫ. Как обычно готовим каркас триггера:
function Trig_Burning_Actions takes nothing returns nothing endfunction
function InitTrig_Burning takes nothing returns nothing set gg_trg_Burning = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(gg_trg_Burning, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gg_trg_Burning, Condition(function Trig_Burning_Condition)) call TriggerAddAction(gg_trg_Burning, function Trig_Burning_Actions) endfunction[/code]
Теперь продумаем последовательность действий, происходящих при использовании способности:
Мы кастуем на врага способность
Враг начинает гореть
Появляется эффект горения
Цель получает урон
Способность перестает действовать после N-секунд
Приступаем к написанию кода. Дальше будет полный спелл с последующим пояснением всего: [code=jass]function Trig_Burning_Condition takes nothing returns boolean return GetSpellAbilityId() == 'A000' endfunction
function Trig_Burning_Timer takes nothing returns nothing local timer t = GetExpiredTimer() local integer hid = GetHandleId(t) //Грузим сохраненные переменные local unit caster = LoadUnitHandle(udg_hash, hid, 0) local unit target = LoadUnitHandle(udg_hash, hid, 1) local effect burning = LoadEffectHandle(udg_hash, hid, 2) local real time = LoadReal(udg_hash, hid, 3) if time <= 7. then //Если время меньше семи, то //мы наносим цели урон call UnitDamageTarget(caster, target, 20., true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS) if GetWidgetLife(target) > .405 then //Если у цели есть еще здоровье, то set time = time + 1. //Увеличиваем время на 1 секунду call SaveReal(udg_hash, hid, 3, time) //и сохраняем его в хэш else //Если цель мертва, то мы call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!) call DestroyTimer(t) //Уничтожаем таймер call DestroyEffect(burning) //Уничтожаем эффект call FlushChildHashtable(udg_hash, hid) //Чистим хэш endif else //Если время больше 7, то все делаем аналогично: call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!) call DestroyTimer(t) //Уничтожаем таймер call DestroyEffect(burning) //Уничтожаем эффект call FlushChildHashtable(udg_hash, hid) //Чистим хэш endif set t = null set caster = null set target = null set burning = null endfunction
function Trig_Burning_Actions takes nothing returns nothing local timer t = CreateTimer() local integer hid = GetHandleId(t) local unit caster = GetTriggerUnit() local unit target = GetSpellTargetUnit() local effect burning = AddSpecialEffectTarget("Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", target, "origin") //Эффект горения //Сохраняем все переменные в хэш// call SaveAgentHandle(udg_hash, hid, 0, caster) call SaveAgentHandle(udg_hash, hid, 1, target) call SaveAgentHandle(udg_hash, hid, 2, burning) call SaveReal(udg_hash, hid, 3, 0.) //Время //==============================================// call TimerStart(t, 1., true, function Trig_Burning_Timer) //Запускаем периодический таймер set t = null set caster = null set target = null set burning = null endfunction
function InitTrig_Burning takes nothing returns nothing set udg_hash = InitHashtable() set gg_trg_Burning = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(gg_trg_Burning, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gg_trg_Burning, Condition(function Trig_Burning_Condition)) call TriggerAddAction(gg_trg_Burning, function Trig_Burning_Actions) //Здесь я просто отключу туман войны call FogEnable(false) call FogMaskEnable(false) //================================== endfunction[/code]
Я пометил комментариями все важные участки кода, которые и заставляют работать наш спелл. Обращу внимание на то, что периодические таймеры обязательно надо ставить на паузу перед уничтожением. Большинство спеллов на таймере представляют собой некоторое количество (зачастую очень большое) конструкций проверки условий (if then else) и действий, которые будут происходить после выполнения (или невыполнения) условия(-й). Так как я думаю, что из-за комментариев в коде бъяснять уже нечего, давайте отвлечемся от практики и окунемся в теорию. Мы с Вами займемся изучением некоторых кодерских фич, которые здорово облегчат и, зачастую, убыстрят ваш код. :)
Финт №1
Допустим, у нас есть функция, возвращающая какой-то тип (например, число): [code=jass]function abc takes nothing returns integer return 666 endfunction[/code] До сих пор если бы Вам сказали занести это число в хэш, вы бы сделали так: [code=jass]local integer def = abc() call SaveInteger(udg_hash, 0, 0, def)[/code] Так можно, но кодеры со стажем обходят этап выделения памяти под переменную, которая будет просто сохранена в хэш. Ну а зачем? Делаем так: [code=jass]call SaveInteger(udg_hash, 0, 0, abc())[/code] Так как функция возвращает значение, то она может вернуть его в аргумент функции, то есть мы можем не выделять память под локальную переменную, а просто сохранить в хэш возвращенное значение Так же вы можете сохранять в хэш составные операции, напрмер сложение строк, арифметические операции с числами (с точкой и без), например: [code=jass]call SaveInteger(udg_hash, 0, 0, abc() + 1)[/code] сохранит в хэш число 667.
Финт №2
Возьмем все тот же спелл из статьи. У нас есть время, к которому мы прибавляем секунду, пока оно не станет больше либо равно 7. Так как в начале спелла время у нас равно 0 секунд, то мы можем не сохранять значение в хэш. Почему? При загрузке несуществующего значения из ячейки хэш-таблицы, мы получаем null. У числа он эквивалентен 0, у реальной - 0.000, у логической - false, а у типов-наследников handle или widget - ничему, то есть null. Согласно этому, в действиях мы можем не сохранять значение 0. в хэш, а уж тем более не выделять под него область памяти (см. Финт 1).
А теперь давайте перепишем наш код, использую полученные знания: [code=jass]function Trig_Burning_Condition takes nothing returns boolean return GetSpellAbilityId() == 'A000' endfunction
function Trig_Burning_Timer takes nothing returns nothing local timer t = GetExpiredTimer() local integer hid = GetHandleId(t) //Грузим сохраненные переменные local unit caster = LoadUnitHandle(udg_hash, hid, 0) local unit target = LoadUnitHandle(udg_hash, hid, 1) local real time = LoadReal(udg_hash, hid, 3) //Мы не сохранили значение, поэтому получаем null, то есть 0.000 if time <= 7. then //Если время меньше семи, то //мы наносим цели урон call UnitDamageTarget(caster, target, 20., true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS) if GetWidgetLife(target) > .405 then //Если у цели есть еще здоровье, то call SaveReal(udg_hash, hid, 3, time + 1.) //Сохраняем время, увеличенное на 1 секунду в хэш else //Если цель мертва, то мы call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!) call DestroyTimer(t) //Уничтожаем таймер call DestroyEffect(LoadEffectHandle(udg_hash, hid, 2)) //Функция уничтожения берет эффект и вызывается один раз. Зачем переменная? call FlushChildHashtable(udg_hash, hid) //Чистим хэш endif else //Если время больше 7, то все делаем аналогично: call PauseTimer(t) //Ставим таймер на паузу (!!ОБЯЗАТЕЛЬНО ДЛЯ ПЕРИОДИЧ. ТАЙМЕРОВ!!) call DestroyTimer(t) //Уничтожаем таймер call DestroyEffect(LoadEffectHandle(udg_hash, hid, 2)) //Функция уничтожения берет эффект и вызывается один раз. Зачем переменная? call FlushChildHashtable(udg_hash, hid) //Чистим хэш endif set t = null set caster = null set target = null endfunction
function Trig_Burning_Actions takes nothing returns nothing local timer t = CreateTimer() local integer hid = GetHandleId(t) local unit target = GetSpellTargetUnit() //Сохраняем все переменные в хэш// call SaveAgentHandle(udg_hash, hid, 0, GetTriggerUnit()) //Кастер появился один раз, переменная не нужна call SaveAgentHandle(udg_hash, hid, 1, target) //А вот цель нужна будет для создания эффекта, поэтому она в переменной //Функция создания эффекта вернет нам тип effect, который мы спойкойно сохраняем в хэш call SaveAgentHandle(udg_hash, hid, 2, AddSpecialEffectTarget("Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", target, "origin")) //==============================================// call TimerStart(t, 1., true, function Trig_Burning_Timer) //Запускаем периодический таймер set t = null set target = null endfunction
function InitTrig_Burning takes nothing returns nothing set udg_hash = InitHashtable() set gg_trg_Burning = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(gg_trg_Burning, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gg_trg_Burning, Condition(function Trig_Burning_Condition)) call TriggerAddAction(gg_trg_Burning, function Trig_Burning_Actions) //Здесь я просто отключу туман войны call FogEnable(false) call FogMaskEnable(false) //================================== endfunction[/code]
На этом я заканчиваю и готов отвечать на любые вопросы в комментах к статье. Всего вам хорошего, до встречи!
by Max Karelov aka Ty3uK
Просмотров: 1946
Добавил: Ty3uK
Добавлено: 15 Марта 2013 в 20:45:04
Спасибо за статью:) А если сделать из нее "стрелы хускара" (ну накидывать постоянно чтобы эффект складывался), то не будет ошибок связанных с хэш? а то я не так силен в jass...
Ответ: Нет, при использовании хэша и таймера никаких ошибок в накладывании эффектов и урона не будет.
Оформление:5/5 Использованы цветовые выделения, ВВ-коды и спойлеры, код запихнулся туда, куда надо. Как и в Части 1, придраться не к чему. Шрифт Georgia очень здесь хорошо пошёл, получилось довольно недурно оформленная статья.
Содержание:5/5 Хотелось бы отметить, что тут рассказывается наиболее просто о сложном. Объём статьи, разумеется, соответствует норме.
Орфография:5/5 Всё написано строго согласно правилам русского языка.
Всего: 5/5 Определённо, объективно нравится то, что делает автор подобных статей. Статья прошла модерацию.
//=========== Один вопрос: почему весь код нельзя было запихнуть в блок Conditions и сделать его тем самым асинхронным?
Добавлять комментарии могут только зарегистрированные пользователи. [ Регистрация | Вход ]
Новая версия известнейшей карты в батл нете! Советую качать всем! Закупаешь магии(их около 100 и больше)потом прокачиваешь эти магии(максимальный уровень магии - 100). И мочишься с ордами монстров(правда не сразу), вобщем отличная карта!