Добавлен , опубликован

Осваиваем World Editor

Содержание:

Переход на Jass

Автор: pLaY:) ( goldplay99)
04.02.2012
  1. Вступление.
  2. Первая часть: От глобальных до локальных.
  3. Вторая часть: Хэш и таймер.
  4. Третья часть : GetLocalPlayer().

Вступление

На вопрос: Почему вы не пишете на Jass? – отвечают, что это сильно тяжело и вообще не понятно, что там. Эта статья предназначена, что бы люди научились писать на Jass.
Я не буду показывать, как лучше делать. Просто покажу, как писать на Jass, а оптимизация, качественный текст и т.п. изучат потом. Статья рассчитана на людей, которые уже изучили GUI (триггеры) и знают, что такое глобальные переменные.
что такое локальные переменные?
Тоже самое, что и глобальные переменные, только используются в пределах одной функции.
Названия триггеров, глобальных или локальных переменных и функция – могут быть произвольными.
Строки которые начинаются на local пишутся в самом начале функции (кроме cJass).
А как узнать тип локальное переменной?
Создаёте глобальную переменную, нужного типа. В уже конвертированный текст добавляете любую английскую букву – вам выбит окно ошибки. Прокручиваете до самого верха, вы увидите Global Variables, а чуть ниже globals. По центру – название ваших переменных, а слева тип переменной.
Или экспортируете код и точно также ищите.

Первая часть: От глобальных до локальных.

Хотите создать способность, что бы после применения цель обжигало? Ну вот вы раскинули мозгами и сделали такой триггер:
Но вас не устраивает, что использовать способность можно раз в 5 секунд.
Что делать?
Правка>Конвертировать в текст.
Что это за функции?
function Trig_FIRE_Conditions takes nothing returns boolean
Это условие.
function Trig_GUI_FIRE_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A000' ) ) then
        return false
    endif
    return true
endfunction
//Спокойно можете всё стирать. Оставить только:
function Trig_LOCAL_FIRE_Conditions takes nothing returns boolean
return GetSpellAbilityId() == 'A000'
endfunction
function Trig_LOCAL_FIRE_Actions takes nothing returns nothing
Это действие.
function InitTrig_LOCAL_FIRE takes nothing returns nothing
И это событие.
Нам понадобится только функция действия. Не тяжело догадаться, что какая строка означает.
Заменяем начало первых трёх строк:
set udg_fire_cast на local unit cast
set udg_fire_target на local unit target
call AddSpecialEffectTargetUnitBJ… на local effect eff = AddSpecialEffectTargetUnitBJ…
Что мы сделали? Мы создали три локальных переменных: боевая единица (cast), боевая единица (target) и спецэффект(eff) и присвоили им значения:
cast - применяющий юнит
target - цель - применяемой способности
eff – спецэффект созданный над юнитом target
Дальше нам надо просто заменять:
udg_fire_cast на cast
udg_fire_target на target
udg_fire_effect на eff
Вот и всё. Теперь способность можно применять сколько хочешь раз.

Вторая часть: Хэш и таймер.

Если вы сделаете работу как указано в первой части, то вам скажут что лучше использовать таймеры. А как? Ведь локальные только в пределах одной функции пашут, но тут к нам на помощь прейдёт Хэш-таблица.
Сначала создадим новую функцию в которой будет наше действие. После функции(условия) пишем:
function Trig_LOCAL_FIRE_Timer takes nothing returns nothing
endfunction
Trig_LOCAL_FIRE_Timer Название функции. Оно может быть любым, главное что бы не повторялось.
В функции(действия) копируем строку
А потом стираем все, кроме первых 3 и последних 2 строк.
Должно остаться:
local unit cast = GetSpellAbilityUnit()
local unit target = GetSpellTargetUnit()
local effect eff = AddSpecialEffectTargetUnitBJ( "chest", target, "Abilities\\Spells\\Other\\Doom\\DoomTarget.mdl" )
set cast = null
set target = null
В функции Trig_LOCAL_FIRE_Timer вставляем строку(которую скопировали).
Создаём таймер.Для этого нужно в начале функции(действия) дописать:
local timer t = CreateTimer()
Таймер записывается в локальную t
Что бы запустить таймер используем:
call TimerStart(переменная таймера, время, тип таймера, функция)
время – как часто срабатывает.
Тип таймераfalse или true:
false – однократный
true – многократный
функция – та функция в которой будут нужные действия.
Значит пишем перед set cast = null такую строку:
call TimerStart(t,1.0,true,function Trig_LOCAL_FIRE_Timer)
Теперь время использования ХЕШ(прочтите).
Прочли? Продолжим. Создаём глобальную переменную Хэш-таблица с названием Hash. Создаём новый триггер с названием Hash, стираем всё и пишем:
function InitTrig_Hash takes nothing returns nothing
set udg_Hash = InitHashtableBJ( )
endfunction
Теперь можно забыть про этот триггер.
Вернёмся к нашим функциям.
В функции (действия) сохраним наши данные.
Перед call TimerStart… пишем
call SaveUnitHandle(udg_Hash,GetHandleId(t),1,cast)// Сохраняем применяющего юнита в 1 ячейку.
call SaveUnitHandle(udg_Hash,GetHandleId(t),2,target) )// Сохраняем цель-способности во 2 ячейку.
call SaveEffectHandle(udg_Hash,GetHandleId(t),3,eff) )// Сохраняем созданный спецэффект в 3 ячейку.
и в функции Trig_LOCAL_FIRE_Timer загрузим их:
local timer t = GetExpiredTimer()// Использованный таймер
local unit cast = LoadUnitHandle(udg_Hash,GetHandleId(t),1)// Загружаем применяющего юнита из 1 ячейки.
local unit target = LoadUnitHandle(udg_Hash,GetHandleId(t),2) )// Загружаем цель-способности из 2 ячейки.
local effect eff = LoadEffectHandle(udg_Hash,GetHandleId(t),3) )// Загружаем созданный спецэффект из 3 ячейки.
Мы сразу записываем их в переменные. Нечего страшного, что их названия идентичны - это же локальные. Если вы сейчас проверите карту, то урон будет наноситься постоянно. Что бы этого избежать добавим целое число(integer) в функцию(действия):
local integer i = 0
и после call SaveEffectHandle…. Сохраним в хэш
call SaveInteger(udg_Hash,GetHandleId(t),4,i)// Сохраняем i в 4 ячейку.
а в функции Trig_LOCAL_FIRE_Timer после local effect eff … загрузим:
local integer i = LoadInteger(udg_Hash,GetHandleId(t),4)//Загружаем i из 4 ячейки.
Теперь создадим условие, которое будет определять, когда надо закончить наносить урон.
if i == 5 then Если i равно 5
То
call FlushChildHashtable(udg_Hash,GetHandleId(t))// уничтожаем все ячейки
call DestroyEffect(eff)//уничтожить эффект
call DestroyTimer(t)//уничтожить таймер
set cast = null обнулить переменную
set target = null// обнулить переменную
set eff = null// обнулить переменную
set t = null// обнулить переменную
else//Иначе
call UnitDamageTargetBJ( cast, target, 20.00, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE ) Нанести урон
set i = i + 1// Увеличить значение
call SaveInteger(udg_Hash,GetHandleId(t),4,i) Сохранить в хэш
endif
Вот и всё).
Весь код:
function Trig_TIMER_FIRE_Conditions takes nothing returns boolean
return GetSpellAbilityId() == 'A000'
endfunction
function Trig_LOCAL_FIRE_Timer takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit cast = LoadUnitHandle(udg_Hash,GetHandleId(t),1)
local unit target = LoadUnitHandle(udg_Hash,GetHandleId(t),2)
local effect eff = LoadEffectHandle(udg_Hash,GetHandleId(t),3)
local integer i = LoadInteger(udg_Hash,GetHandleId(t),4)
if i == 5 then
call DestroyEffect(eff)
call DestroyTimer(t)
set cast = null
set target = null
set eff = null
set t = null
else
call UnitDamageTargetBJ( cast, target, 20.00, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE )
set i = i + 1
call SaveInteger(udg_Hash,GetHandleId(t),4,i)
endif
endfunction
function Trig_TIMER_FIRE_Actions takes nothing returns nothing
local timer t = CreateTimer()
local unit cast = GetSpellAbilityUnit()
local unit target = GetSpellTargetUnit()
local effect eff = AddSpecialEffectTargetUnitBJ( "chest", target, "Abilities\\Spells\\Other\\Doom\\DoomTarget.mdl" )
local integer i = 0
call SaveUnitHandle(udg_Hash,GetHandleId(t),1,cast)
call SaveUnitHandle(udg_Hash,GetHandleId(t),2,target)
call SaveEffectHandle(udg_Hash,GetHandleId(t),3,eff)
call SaveInteger(udg_Hash,GetHandleId(t),4,i)
call TimerStart(t,1.0,true,function Trig_LOCAL_FIRE_Timer)
set cast = null
set target = null
endfunction
===========================================================================
function InitTrig_TIMER_FIRE takes nothing returns nothing
set gg_trg_TIMER_FIRE = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_TIMER_FIRE, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_TIMER_FIRE, Condition( function Trig_TIMER_FIRE_Conditions ) )
call TriggerAddAction( gg_trg_TIMER_FIRE, function Trig_TIMER_FIRE_Actions )
endfunction

Третья часть: ​Get​Local​Player()

Возможно, вы часто задавались вопросом: почему я вижу этот объект так, а мой другой игрок по-другому? Приведу пример из карты “DOTA”, не потому что она нереальная, а потому что она популярная и каждые знает, про что идёт речь.
1)Герой Invoker > способность Sun Strike.
2)Герой Mortred > способность Blur.
3)Герой Admiral> способность Гейзер и Корабль - точнее, точка его появляния...
Подробней о GetLocalPlayer() можно узнать (тут). Но лучше пример - будем создавать вышеуказанную способность Sun Strike.
Создаём триггер:
Что за “Личный сценарий” и RemoveLocation()?
Личный сценарий - строка из Jass кода.
call RemoveLocation() – уничтожает точку.
Наш триггер конвертируем в текст и аналогично Первой части заменяем переменные.
В локальную eff – сразу не записываем созданный эффект,а делаем это на след строке:
Set eff = AddSpecial….
Второй спецэффект мы сразу удаляем. Мы используем для этого две строки, а можно одну:
    call AddSpecialEffectLocBJ(loc, "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl" )
    call DestroyEffectBJ( GetLastCreatedEffectBJ() ) на
    call DestroyEffectBJ( AddSpecialEffectLocBJ( loc, "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl" ) )
Заменили?
Должно получиться так:
function Trig_Jass_Sun_Strike_Actions takes nothing returns nothing
local unit cast = GetSpellAbilityUnit()
local location loc = GetSpellTargetLoc()
local effect eff
set eff = AddSpecialEffectLocBJ( loc, "Abilities\\Spells\\Items\\VampiricPotion\\VampPotionCaster.mdl" )
    call TriggerSleepAction( 2 )
    call DestroyEffectBJ( eff )
    call UnitDamagePointLoc( cast, 0.01, 250.00, loc, 200.00, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DEATH )
    call DestroyEffectBJ( AddSpecialEffectLocBJ( loc, "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl" ) )
    call RemoveLocation(loc)
    set cast = null
    set loc = null//Дописать эту строку
endfunction
Сейчас добавим строки, которые для союзников – создадут эффект, а для врагов - нечего. Для этого создадим группу игроков-союзников и запишем туда союзников:
Конвертируем в текст,допишем и изменим:
local force for = CreateForce()// Создаём пустую группу игроков и записываем её в for
local string str//Локальная строка
    set bj_forLoopAIndex = 1 // От 1
    set bj_forLoopAIndexEnd = 12 // До 12
    loop //Начало цикла действий, повторяем действия пока exitwhen не будет правдив.
        exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd // Если AIndex> AIndexEnd то exitwhen правдив
        if ( IsUnitAlly(cast, ConvertedPlayer(GetForLoopIndexA())) == true ) then// Если
// То
            call ForceAddPlayerSimple( ConvertedPlayer(GetForLoopIndexA()), for )
else//Иначе, можно стереть        
endif//Конец условия
        set bj_forLoopAIndex = bj_forLoopAIndex + 1//Если exitwhen не правдив, то прибавляет к AIndex единицу
    endloop// Конец границы цикла.
if (IsPlayerInForce(GetLocalPlayer(),for)) then //Если
set str = "Abilities\\Spells\\Items\\VampiricPotion\\VampPotionCaster.mdl"
// То
else//Иначе
set str = " "
endif//Конец условия
set eff = AddSpecialEffectLocBJ( loc, str )
Всё. Но, у нас опять присутствует
call TriggerSleepAction( 2 )
А это – не очень хорошо!
Мы уже умеем сохранять и загружать значения из Хеша, но сначала создадим функцию Trig_Jass_Sun_Strike_Boom.
local timer t = CreateTimer()//В начале 
После set eff = …. Пишем:
call SaveUnitHandle(udg_Hash,GetHandleId(t),1,cast)
call SaveLocationHandle(udg_Hash,GetHandleId(t),2,loc)
call SaveEffectHandle(udg_Hash,GetHandleId(t),3,eff)
call TimerStart(t,2.00,false,function Trig_Jass_Sun_Strike_Boom)
Остальное копируем, кроме:
    call DestroyForce(for)// дописываем
    set for = null // дописываем
    set cast = null
    set loc = null
И вставляем в функция Trig_Jass_Sun_Strike_Boom и загружаем наши значения.
local timer t = GetExpiredTimer()
local unit cast = LoadUnitHandle(udg_Hash,GetHandleId(t),1)
local location loc = LoadLocationHandle(udg_Hash,GetHandleId(t),2)
local effect eff = LoadEffectHandle(udg_Hash,GetHandleId(t),3)
    call DestroyEffectBJ( eff )
    call UnitDamagePointLoc( cast, 0.01, 250.00, loc, 200.00, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DEATH )
    call DestroyEffectBJ( AddSpecialEffectLocBJ( loc, "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl" ) )
    call FlushChildHashtable(udg_Hash,GetHandleId(t))
    call DestroyTimer(t)
    call RemoveLocation(loc)
    call DestroyForce(for)
    set for = null
    set cast = null
    set t = null
    set loc = null
Вот теперь – точно всё.
Весь код
function Trig_Jass_Sun_Strike_Conditions takes nothing returns boolean
return GetSpellAbilityId() == 'A001'
endfunction
function Trig_Jass_Sun_Strike_Boom takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit cast = LoadUnitHandle(udg_Hash,GetHandleId(t),1)
local location loc = LoadLocationHandle(udg_Hash,GetHandleId(t),2)
local effect eff = LoadEffectHandle(udg_Hash,GetHandleId(t),3)
    call DestroyEffectBJ( eff )
    call UnitDamagePointLoc( cast, 0.01, 250.00, loc, 200.00, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DEATH )
    call DestroyEffectBJ( AddSpecialEffectLocBJ( loc, "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl" ) )
    call FlushChildHashtable(udg_Hash,GetHandleId(t))
    call DestroyTimer(t)
    call RemoveLocation(loc)
    call DestroyForce(for)
    set for = null
    set cast = null
    set t = null
    set loc = null
endfunction
function Trig_Jass_Sun_Strike_Actions takes nothing returns nothing
local unit cast = GetSpellAbilityUnit()
local location loc = GetSpellTargetLoc()
local effect eff
local force for = CreateForce()
local string str
local timer t = CreateTimer()
    set bj_forLoopAIndex = 1
    set bj_forLoopAIndexEnd = 12
    loop
        exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
        if ( IsUnitAlly(cast, ConvertedPlayer(GetForLoopIndexA())) == true ) then
            call ForceAddPlayerSimple( ConvertedPlayer(GetForLoopIndexA()), for )
        endif
        set bj_forLoopAIndex = bj_forLoopAIndex + 1
    endloop
if (IsPlayerInForce(GetLocalPlayer(),for)) then

set str = "Abilities\\Spells\\Items\\VampiricPotion\\VampPotionCaster.mdl"
else
set str = " "
endif
set  eff = AddSpecialEffectLocBJ( loc, str )
call SaveUnitHandle(udg_Hash,GetHandleId(t),1,cast)
call SaveLocationHandle(udg_Hash,GetHandleId(t),2,loc)
call SaveEffectHandle(udg_Hash,GetHandleId(t),3,eff)
call TimerStart(t,2.00,false,function Trig_Jass_Sun_Strike_Boom)
set cast = null
set loc = null
endfunction

//===========================================================================
function InitTrig_Jass_Sun_Strike takes nothing returns nothing
    set gg_trg_Jass_Sun_Strike = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Jass_Sun_Strike, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Jass_Sun_Strike, Condition( function Trig_Jass_Sun_Strike_Conditions ) )
    call TriggerAddAction( gg_trg_Jass_Sun_Strike, function Trig_Jass_Sun_Strike_Actions )
endfunction
Спасибо Doc, ScorpioT1000
Карта пример - привязана )
Советую - тут
Дополнение - тут
Всё остальное - тут

`
ОЖИДАНИЕ РЕКЛАМЫ...
2
22
12 лет назад
2
цитирую батьку:
NCrashed:
Нужно добавить в разделе про getLocalPlayer() больше объяснений почему именно так делаем. Конкретно кратко почему call TriggerSleepAction( 2 ) это "не очень хорошо". Непонятно зачем создавать функцию Trig_Jass_Sun_Strike_Boom, хоть бы пару слов для описания что она должна делать. В коде (особенно для новичков) надо поправить отступы,
до тех пор пока не будет исправлено согласно рекомендациям не будет публикации
1
3
11 лет назад
1
Всем доброго времени суток.
У меня вопрос:
Вот о тригире “GUI FIRE” а точнее я понял, что надо создать переменную
1… Название: fire_cast тип переменной: "боевая единица" без массива и размера
2…Название: fire_target тип переменной: я уже не понял кокой именно тип без массива надо создать?
P.S: Я, конечно, извиняюсь, если такого рода вопрос уже был, но все, же прошу не откажите в вопросе, если конечно не это конечно не затруднит?
1
1
11 лет назад
1
Спасибо !
1
1
11 лет назад
1
спасибо, помог)
0
15
10 лет назад
0
как обнулить переменную типа Способность, если сделать так
set Spell=null
компилятор ругается на не совпадение типов. Работаю с ГУИ. Глобалка объявлена заранее.
0
28
10 лет назад
0
Ethernet, способность это инт
значит Spell=0
и вобще её не надо обнулять
0
15
10 лет назад
0
nvc123:
Ethernet, способность это инт
значит Spell=0
и вобще её не надо обнулять
Когда нужно чтобы переменная не имела значение, ее нужно обнулять:)
0
28
10 лет назад
0
Ethernet, обнулять инт?
не ну ты герой
что тут ещё скажешь
0
15
10 лет назад
0
nvc123:
Ethernet, обнулять инт?
не ну ты герой
что тут ещё скажешь
Ты же меня не понял.
0
28
10 лет назад
0
Ethernet, что тут можно не понять?
инт всегда имеет значение
0
4
7 лет назад
0
Спасибо, триггер на новую способность был очень полезен!) Давно не мог понять как триггерно делать заклинания =D!
Может быть, в дальнейшем, освою и jass...
Этот комментарий удален
0
9
4 года назад
0
А чем принципиально отличаются эти две команды в конце триггера:
• call DestroyGroup(upg_%);
• call DestroyForce(upg_%);
???
0
26
4 года назад
Отредактирован Extremator
0
Frostfall, форс - это группа игроков, в неё можно добавить конкретных игроков и провернуть какие либо действия именно с ними... Либо - использовать как маркер для игрока (хотя тут можно использовать и логическую с массивом).
А гроуп - это отряд юнитов, которые могут каким либо способом выбираться в неё... или удаляться из неё. Так же можно маркировать юнитов, и т.п.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.