Сейчас 14:11:56 Вторник, 26 ноября, 2024 год
[ x ] Главная ⇒ Форум ⇐ RSS Файлы Cтатьи Картинки В о й т и   или   з а р е г и с т р и р о в а т ь с я


[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Модератор форума: PUVer, SirNikolas, Ty3uK  
[Статья]Создание простого областного спелла на Jass
Ty3uKДата: Понедельник, 19 Сентября 2011, 16:16:51 | Сообщение # 1
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:

Вводное слово



Итак, сегодня я покажу людям, которых пугает слово Jass как с его помощью сделать безутечный спелл, в корне повторяющий стандартный Столб огня Каэля. Вы спросите, зачем это? Отвечаю - сделав этот спелл триггерно, вы можете прибавить к нему еще много всяких вкусных плюшек, как то отталкивание и т.д. Итак, начнем!


Первый шаг



Для начала давайте создадим новую способность на основе уже существующей. В качестве подопытной крыски мы возьмем способность Канал (Channel). Выставим все настройки согласно моему скриншоту (извините, но варик у меня англ, поэтому вы можете сидеть вкуривать сами, или просто скачать карту, которую я прикреплю и поглядеть сами)




Что мы должны выставить? Во-первых, уберем все "родные" эффекты способности - они нам ни к чему. Далее меняем иконку, описание, делаем спелл видимым и добавляем картинку области (строка Options), ставим все временные промежутки равными 0.01, устанавливаем тип приказа - точка, меняем области действия, перезарядку, ману и т.п. Первый шаг закончен!


Второй шаг



Заходим в редактор триггеров, делаем новую папку и создаем триггер. В событии ставим Юнит начинает применять способность, в условии - кастуемая абилка равна нашей.



Создаем в редакторе переменных переменную hash типа хэш-таблица. Создадим новый триггер с событием Инициализация карты и действием через личный сценарий:
Code
call InitHashtable(udg_hash)
Таким образом мы инициализировали нашу таблицу для использования! Вернемся к первому триггеру. Конвертируем триггер в текст (Edit - Conver To Custom Text)




Запомним рав-код способности и удаляем весь текст в промежутке между function Trig_Fire_Condition и endfunction. Вписываем следующее:
Code
return (GetSpellAbilityId() == 'A000') == true

Это оптимизирует громоздкую проверку от близзов.
Далее начинаются самые вкусности! Для начала возьмите на заметку - переменные из редактора переменных называются глобальными, у них есть свои плюсы, но, вот незадача, мы не сможем сделать с их помощью толковый MUI спелл. И тут к нам на помощь приходят локальные переменные - они действуют только в пределах одного триггера, следовательно, мы не сможем два раза использовать одну и ту же локалку. После строки function Trig_Fire_Actions takes nothing returns nothing начинаем их объявлять:


Code
function Trig_Fire_Actions takes nothing returns nothing
     local timer t = CreateTimer()
     local group g = CreateGroup()
     local filterfunc filter = Filter(function Group_Filter)


Что же мы тут имеем?
local unit caster - объявили юнита-кастера
local group g = CreateGroup() - объявили и создали группу
local filter func filter - объявили фильтр
local timer t - объявили и создали таймер


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

Code
function Trig_Fire_Conditions takes nothing returns boolean
  return (GetSpellAbilityId() == 'A000') == true
endfunction

и
Code
function Trig_Fire_Actions takes nothing returns nothing
мы вставим функцию фильтра. Вот что у нас получится:
Code
function Trig_Fire_Conditions takes nothing returns boolean
  return (GetSpellAbilityId() == 'A000') == true
endfunction

function Group_Filter takes nothing returns boolean     
     local unit u = GetTriggerUnit()
     local unit fu = GetFilterUnit()
      
     if (IsUnitEnemy(fu, GetOwningPlayer(u))) then
         call UnitDamageTarget(u, fu, 300., true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
     endif
      
     set u = null
     set fu = null
     return false
endfunction
          
function Trig_Fire_Actions takes nothing returns nothing


Отлично, теперь мы можем набирать юнитов в группу, а перед этим сохраним в хэш-таблицу эффект столба. Вот как преобразится наша функция действий:

Code
function Trig_Fire_Actions takes nothing returns nothing
     local timer t = CreateTimer()
     local group g = CreateGroup()
     local filterfunc filter = Filter(function Group_Filter)
     call SaveEffectHandle(udg_hash, GetHandleId(t), 2, AddSpecialEffect("Abilities\\Spells\\Human\\FlameStrike\\FlameStrike1.mdl", GetSpellTargetX(), GetSpellTargetY()))
     call GroupEnumUnitsInRange(g, GetSpellTargetX(), GetSpellTargetY(), 350., filter)


Итак, что же мы сделали? Только что мы добавили в группу g всех юнитов, которые находились в области 350 от точки заклинания и подходящие под критерии фильтра (юнит - противник нашего игрока), а так же сохранили эффект в хэш таблицу. Далее, после фильтра, добавляем функцию таймера с уничтожением нашего эффекта. Верхняя часть нашего кода примет такой вид:

Code
function Trig_Fire_Conditions takes nothing returns boolean
     return (GetSpellAbilityId() == 'A000') == true
endfunction

function Group_Filter takes nothing returns boolean     
     local unit u = GetTriggerUnit()
     local unit fu = GetFilterUnit()
      
     if (IsUnitEnemy(fu, GetOwningPlayer(u))) then
         call UnitDamageTarget(u, fu, 300., true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
     endif
      
     set u = null
     set fu = null
     return false
endfunction
      
function Timer takes nothing returns nothing
     local timer t = GetExpiredTimer()
     call DestroyEffect(LoadEffectHandle(udg_hash, GetHandleId(t), 2))
     call FlushChildHashtable(udg_hash, GetHandleId(t))
     call DestroyTimer(t)
endfunction     
      
function Trig_Fire_Actions takes nothing returns nothing


Теперь нам надо вызвать наш таймер! Вписываем функцию вызова, после чего наша часть с действиями примет такой вид:

Code
function Trig_Fire_Actions takes nothing returns nothing
     local timer t = CreateTimer()
     local group g = CreateGroup()
     local filterfunc filter = Filter(function Group_Filter)
     call SaveEffectHandle(udg_hash, GetHandleId(t), 2, AddSpecialEffect("Abilities\\Spells\\Human\\FlameStrike\\FlameStrike1.mdl", GetSpellTargetX(), GetSpellTargetY()))
     call GroupEnumUnitsInRange(g, GetSpellTargetX(), GetSpellTargetY(), 350., filter)
     call TimerStart(t, 3.5, false, function Timer)
     call DestroyFilter(filter)
     call DestroyGroup(g)
     call DestroyTimer(t)
     set t = null
     set filter = null
     set g = null
endfunction


Что же мы сделали? Вызвали таймер, который создаст наш эффект, а затем начинаем чистить наш триггер - уничтожаем фильтр, группу и таймер, а так же обнуляем их(!!!ЭТО ОБЯЗАТЕЛЬНО!!!)

В конечном итоге весь наш триггер примет такой вид:
Code
function Trig_Fire_Conditions takes nothing returns boolean
     return (GetSpellAbilityId() == 'A000') == true
endfunction

function Group_Filter takes nothing returns boolean     
     local unit u = GetTriggerUnit()
     local unit fu = GetFilterUnit()
      
     if (IsUnitEnemy(fu, GetOwningPlayer(u))) then
         call UnitDamageTarget(u, fu, 300., true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
     endif
      
     set u = null
     set fu = null
     return false
endfunction
      
function Timer takes nothing returns nothing
     local timer t = GetExpiredTimer()
     call DestroyEffect(LoadEffectHandle(udg_hash, GetHandleId(t), 2))
     call FlushChildHashtable(udg_hash, GetHandleId(t))
     call DestroyTimer(t)
endfunction
      
function Trig_Fire_Actions takes nothing returns nothing
     local timer t = CreateTimer()
     local group g = CreateGroup()
     local filterfunc filter = Filter(function Group_Filter)
     call SaveEffectHandle(udg_hash, GetHandleId(t), 2, AddSpecialEffect("Abilities\\Spells\\Human\\FlameStrike\\FlameStrike1.mdl", GetSpellTargetX(), GetSpellTargetY()))
     call GroupEnumUnitsInRange(g, GetSpellTargetX(), GetSpellTargetY(), 350., filter)
     call TimerStart(t, 3.5, false, function Timer)
     call DestroyFilter(filter)
     call DestroyGroup(g)
     call DestroyTimer(t)
     set t = null
     set filter = null
     set g = null
endfunction

//===========================================================================
function InitTrig_Fire takes nothing returns nothing
     set gg_trg_Fire = CreateTrigger(  )
     call TriggerRegisterAnyUnitEventBJ( gg_trg_Fire, EVENT_PLAYER_UNIT_SPELL_EFFECT )
     call TriggerAddCondition( gg_trg_Fire, Condition( function Trig_Fire_Conditions ) )
     call TriggerAddAction( gg_trg_Fire, function Trig_Fire_Actions )
endfunction


Спасибо за внимание, надеюсь эта статья вам поможет! Не стесняйтесь задавать вопросы!

Отдельное спасибо Дуосу за критику и правку дурацких ошибок!

Скачать пример


╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек


Сообщение отредактировал Ty3uK - Вторник, 20 Сентября 2011, 16:26:51
 

Evil_GoogleДата: Понедельник, 19 Сентября 2011, 21:50:54 | Сообщение # 2
7 уровень
Группа: Проверенные
Сообщений: 272
Награды: 0
Репутация: 229
Блокировки:
Ох. Хорошая статья. Дома попробую сделать.

Я один из старых пользователей этого сайта - ветеран
 

[DUОS]Дата: Понедельник, 19 Сентября 2011, 22:57:32 | Сообщение # 3
Группа: Заблокированные
Сообщений: 6279
Награды: 9
Репутация: 1708
Блокировки:
Ty3uK,
Code
function Trig_Fire_Conditions takes nothing returns boolean
     return GetSpellAbilityId() == 'A000'
endfunction

function Group_Filter takes nothing returns boolean
     local timer t = GetExpiredTimer()
     local unit u = GetFilterUnit()
     local unit tu = LoadUnitHandle(udg_H,GetHandleId(t),0)
      
     if IsUnitEnemy(u,GetOwningPlayer(tu)) then
         call UnitDamageTarget(tu,u,200.,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
     endif
      
     set u = null
     set tu = null
     set t = null
     return false
endfunction

function Group_Timer takes nothing returns nothing
     local timer t = GetExpiredTimer()
     local group g = CreateGroup()  
     local integer tid = GetHandleId(t)  
     local unit tu = LoadUnitHandle(udg_H,tid,0)
     local real stx = LoadReal(udg_H,tid,1)
     local real sty = LoadReal(udg_H,tid,2)
     local boolean b = LoadBoolean(udg_H,tid,3)
     local filterfunc filter = Filter(function Group_Filter)
      
     if b then
         call DestroyEffect(LoadEffectHandle(udg_H,tid,4))
         call FlushChildHashtable(udg_H,tid)
         call DestroyTimer(t)
     else
         call GroupEnumUnitsInRange(g,stx,sty,350.,filter)
         call SaveBoolean(udg_H,tid,3,true)  
         call TimerStart(t,3.5,false,function Group_Timer)
     endif
      
     call DestroyGroup(g)
     call DestroyFilter(filter)
      
     set filter = null
     set g = null
     set tu = null
     set t = null      
endfunction
          
function Trig_Fire_Actions takes nothing returns nothing
     local unit caster = GetTriggerUnit()
     local timer t = GetExpiredTimer()
      
     call SaveUnitHandle(udg_H,GetHandleId(t),0,caster)
     call SaveReal(udg_H,GetHandleId(t),1,GetSpellTargetX())
     call SaveReal(udg_H,GetHandleId(t),2,GetSpellTargetY())
     call SaveBoolean(udg_H,GetHandleId(t),3,false)
     call SaveEffectHandle(udg_H,GetHandleId(t),4,AddSpecialEffect("Abilities\\Spells\\Human\\FlameStrike\\FlameStrike1.mdl",GetSpellTargetX(),GetSpel lTargetY()))
      
     call TimerStart(t,.3,false,function Group_Timer)

     set caster = null
     set t = null
endfunction

//===========================================================================
function InitTrig_Fire takes nothing returns nothing
     set gg_trg_Fire = CreateTrigger()
     call TriggerRegisterAnyUnitEventBJ(gg_trg_Fire,EVENT_PLAYER_UNIT_SPELL_EFFECT)
     call TriggerAddCondition(gg_trg_Fire,Condition(function Trig_Fire_Conditions))
     call TriggerAddAction(gg_trg_Fire,function Trig_Fire_Actions)
endfunction
Почему всё так, объяснил тебе в ICQ.


НУ И ЧТО ТЕПЕРЬ?


Кликайте на дракошку ;)
 

lawsonДата: Понедельник, 19 Сентября 2011, 23:16:51 | Сообщение # 4
Группа: Проверенные
Сообщений: 3482
Награды: 0
Репутация: 974
Блокировки:
Я не понял это муи или что? почему здесь есть это "Polled Wait". Я понимаю что хочешь как проще но надо как лучше. Проще и делать все на гуи(так как все понятно)
Для первой темы хорошое оформление, повествование, развивай дальше а на счет статьи как какого то урока то пфф мое мнение: ерунда, если хочешь обучать новичков то и начинай с НУЛЯ, так как ты пишешь про вейты так как будто новички сами должны догадаться а чем же они заменяються. Но за статью ставлю +!


Nic nie wiem bo mam chuj.
редактирую посты! ВСЕ!


Сообщение отредактировал lawson - Вторник, 20 Сентября 2011, 00:29:44
 

Ty3uKДата: Понедельник, 19 Сентября 2011, 23:29:37 | Сообщение # 5
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
Спасибо, я поправлю все, что надо, спасибо дуосу :)

╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

SkoricДата: Вторник, 20 Сентября 2011, 12:26:16 | Сообщение # 6
Peace, people!
Группа: Проверенные
Сообщений: 633
Награды: 0
Репутация: 425
Блокировки:
Quote (|DUОS|)
Почему всё так, объяснил тебе в ICQ.

А народу объяснить?


лимончики
 

Ty3uKДата: Вторник, 20 Сентября 2011, 16:27:19 | Сообщение # 7
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
Skoric, я объясню, погодите

Добавлено (20 Сентябрь 2011, 16:27:19)
---------------------------------------------
Исправил статью и пример, довал новые комментарии и удалил ненужные


╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

lawsonДата: Вторник, 20 Сентября 2011, 17:10:15 | Сообщение # 8
Группа: Проверенные
Сообщений: 3482
Награды: 0
Репутация: 974
Блокировки:
Quote (Ty3uK)
 local filterfunc filter = Filter(function Group_Filter)

А почему не boolexpr в нем нет утечки поэтому можно при пике юнитов в группу сразу писать Condition(####)?
Помоему статья просто показывыет уровень знаний автора в жассе, а не обучение новичков жассу.


Nic nie wiem bo mam chuj.
редактирую посты! ВСЕ!


Сообщение отредактировал lawson - Вторник, 20 Сентября 2011, 17:19:51
 

Ty3uKДата: Вторник, 20 Сентября 2011, 18:04:44 | Сообщение # 9
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
Это к Дусе- он сказал, что кондишн юзает 97% нубов, плюс логично использовать на функцию-фильтр локалку, изначально сделанную под это, не?

Добавлено (20 Сентябрь 2011, 18:04:44)
---------------------------------------------
Плюс статья-то расчитана на людей, далеких до твоего уровня :)


╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

lawsonДата: Среда, 21 Сентября 2011, 12:09:26 | Сообщение # 10
Группа: Проверенные
Сообщений: 3482
Награды: 0
Репутация: 974
Блокировки:
Quote (Ty3uK)
плюс логично использовать на функцию-фильтр локалку

Нет тогда лечге использовать forGroup().
Этой локалкой ты создаешь еще и функцию. Хотя делай как хочешь, мне удобней через loop делать действия если условий не так много, сразу в одной функции.
Мда я бы на тебя посмотрел как ты сделаешь ИИ с таймер и пиком юнитов каждую секунду + анализирование этих юнитов. Как ты бы смог использовать фильтр!?
Добавлено (21 Сентябрь 2011, 12:09:26)
---------------------------------------------
Мне только интересно как ты в фильтре смог взять GetTriggerUnit()?


Nic nie wiem bo mam chuj.
редактирую посты! ВСЕ!


Сообщение отредактировал lawson - Среда, 21 Сентября 2011, 12:11:54
 

Ty3uKДата: Среда, 21 Сентября 2011, 12:21:49 | Сообщение # 11
Группа: Ветераны
Сообщений: 6125
Награды: 2
Репутация: 1617
Блокировки:
Хм... Так же как и всегда- руками. О.о

╭∩╮(︶︿︶)╭∩╮
"Ульта Тайда мне в жопу!" © k0fe1n
Статьи: MUI-1|MUI-2|Шрифт
Полезности: JASP|JNGP|Уголок библиотек
 

[DUОS]Дата: Среда, 21 Сентября 2011, 12:55:42 | Сообщение # 12
Группа: Заблокированные
Сообщений: 6279
Награды: 9
Репутация: 1708
Блокировки:
Quote (Skoric)
А народу объяснить?

Народ же знает, как юзаются таймеры? :>
Quote (lawson)
лечге использовать forGroup().

Легче не значит лучше. Форгрупом ты создаёшь отдельную ф-ию, когда эти референции можно легко вынести и дать оперативке вздохнуть полной грудью.
Quote (lawson)
как ты в фильтре смог взять GetTriggerUnit()?

Он там ловится, мы ведь в экшнах его использовали, а не при истечении таймера)
Quote (lawson)
удобней

Quote (lawson)
легче

Quote (lawson)
мне

Надо делать не так, как удобно и легче ТЕБЕ, а так, как удобно и легче будет КОМПЬЮТЕРУ прочитать.


НУ И ЧТО ТЕПЕРЬ?


Кликайте на дракошку ;)
 

lawsonДата: Среда, 21 Сентября 2011, 13:52:29 | Сообщение # 13
Группа: Проверенные
Сообщений: 3482
Награды: 0
Репутация: 974
Блокировки:
Quote (|DUОS|)
Он там ловится, мы ведь в экшнах его использовали, а не при истечении таймера)

При событии я знаю что можно отловить в любой функции. Я просто думал что filter который вызывает эту функцию уже не относиться к событии и поэтому не может отловить тригг юнита.

Quote (|DUОS|)
Надо делать не так, как удобно и легче ТЕБЕ, а так, как удобно и легче будет КОМПЬЮТЕРУ прочитать.

Тогда вопрос что же лучше? Делать циклом или фильтром хотя они работают одинаково так как фильтр тоже использует цикл.


Nic nie wiem bo mam chuj.
редактирую посты! ВСЕ!


Сообщение отредактировал lawson - Среда, 21 Сентября 2011, 13:52:43
 

DaroДата: Воскресенье, 26 Мая 2013, 14:48:01 | Сообщение # 14
6 уровень
Группа: Проверенные
Сообщений: 217
Награды: 0
Репутация: 105
Блокировки:
обновите карту пример

 

  • Страница 1 из 1
  • 1
Поиск:

Copyright © 2006 - 2024 Warcraft3FT.info При копировании материалов c сайта ставьте, пожалуйста, активную обратную ссылку на нас • Design by gReeB04ki ©
Хостинг от uCoz