Всем доброго времени суток! Пишу заклинание на jass на основе канала. Задумка следующая: повелитель ужаса кастует пустышку, воспроизводя необходимую анимацию, затем цель поднимается в воздух и там взрывается. Однако проблема с воспроизведением анимации кастера. Указанная анимация начинает воспроизводится и тут же прерывается, буквально спустя секунду. И как бы я не пытался и так и эдак настроить канал, ничего не помогает. Делаю время подготовки не 0, а N секунд, к примеру 3, он сначала ждет эти 3 секунды, потом происходит ровно то же самое, дерганье: анимация начинает проигрываться и сбивается, затем срабатывает код заклинания. Хорошо, я решил в коде задать нужную анимацию кастеру, выждать необходимое время, затем применить заклинание и тут начались удивительные вещи. Изначально хотел сделать без коллбэка, просто используя PolledWait:
function SetUnitZ takes unit target, real value returns nothing
    local location targetLocation = GetUnitLoc(target)

    call UnitAddAbility(target, 'Aave')
    call SetUnitFlyHeight(target, value - GetLocationZ(targetLocation), 100)
    call SetUnitAnimationByIndex(target, 5)
    call UnitRemoveAbility(target, 'Aave')
	
    call RemoveLocation(targetLocation)
    set targetLocation = null
endfunction

function AnnihilationActions takes nothing returns nothing
    local unit target = GetSpellTargetUnit()
    local unit caster = GetSpellAbilityUnit()

    call SetUnitAnimation(caster, "spell slam")
    call PolledWait(3)
    call SetUnitZ(target, 400)
    call SetUnitExploded(target, true)
    call KillUnit(target)
	
    set target = null
    set caster = null
endfunction

function InitTrig_Annihilation_Conditions takes nothing returns boolean
    if (GetUnitAbilityLevel(GetSpellAbilityUnit(), udg_Annihilation) > 0) then
        call AnnihilationActions()
    endif

    return false
endfunction

//===========================================================================
function InitTrig_Annihilation takes nothing returns nothing
    set gg_trg_Annihilation = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Annihilation, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
    call TriggerAddCondition(gg_trg_Annihilation, Condition(function InitTrig_Annihilation_Conditions))
endfunction
После применения способности, игра просто доходит до ожидания, вызывает его и все. Можно ждать бесконечно, остальной код не будет исполнен. Окей, я привык, что некоторые вещи в варкрафте не работают просто потому что, как например функция DisplayTextToForce в скриптах AI. Хорошо, я решил сделать свой таймер и свой цикл(далее буду приводить только необходимый код, без прочей мишуры триггера):
function AnnihilationActions takes nothing returns nothing
    local unit target = GetSpellTargetUnit()
    local unit caster = GetSpellAbilityUnit()
    local timer spellDurationTimer = CreateTimer()

    call SetUnitAnimation(caster, "spell slam")
    call TimerStart(spellDurationTimer, 3, false, null)
    loop
        exitwhen TimerGetRemaining(spellDurationTimer) <= 0
    endloop
	
    call DestroyTimer(spellDurationTimer)
    set spellDurationTimer = null
	
    call SetUnitZ(target, 400)
    call SetUnitExploded(target, true)
    call KillUnit(target)
	
    set target = null
    set caster = null
endfunction
Результат тот же, до кода, следующего после цикла, исполнение не доходит. Ладно, я сдался и написал коллбэк, хотя я в упор не понимаю в чем тут дело и хотел бы узнать, что происходит. Теперь это работает, но лишь отчасти:
function ExecuteCast takes nothing returns nothing
    local timer spellDurationTimer = GetExpiredTimer()
    local integer timerHandle = GetHandleId(spellDurationTimer)
    local unit target = LoadUnitHandle(udg_Hash, timerHandle, 0)
    local unit caster = LoadUnitHandle(udg_Hash, timerHandle, 1)

    call ResetUnitAnimation(caster)
    call SetUnitExploded(target, true)
    call KillUnit(target)
    call DestroyTimer(spellDurationTimer)
    set spellDurationTimer = null
endfunction

function AnnihilationActions takes nothing returns nothing
    local unit target = GetSpellTargetUnit()
    local unit caster = GetSpellAbilityUnit()
    local timer spellDurationTimer = CreateTimer()
    local integer timerHandle = GetHandleId(spellDurationTimer)

    call SaveUnitHandle(udg_Hash, timerHandle, 0, target)
    call SaveUnitHandle(udg_Hash, timerHandle, 1, caster)
    call SetUnitAnimationByIndex(caster, 11)
    call SetUnitZ(target, 400)
	
    call TimerStart(spellDurationTimer, 5, false, function ExecuteCast)
	
    set target = null
    set caster = null
endfunction
Дредлорда как колбасило при касте, так и колбасит, анимацию он отказывается адекватно воспроизводить, хотя я перепробовал SetUnitAnimation, QueueUnitAnimation и SetUnitAnimationByIndex. И еще есть одна проблемка, с которой я пока не могу справиться, почему то при взрыве юнит мгновенно оказывается на земле, а я добиваюсь, чтобы его разорвало именно в воздухе. Буду рад вашим советам.

После применения способности, игра просто доходит до ожидания, вызывает его и все. Можно ждать бесконечно, остальной код не будет исполнен.
TriggerSleepAction нельзя вызвать в кондишенах, только в действиях. Либо юзай экзекют, либо не юзай вейт

Результат тот же, до кода, следующего после цикла, исполнение не доходит.
Ты таким образом себе оп лимит сломал, ведь в первую же долю секунды цикл обрабатывает 25к+ действий

Дредлорда как колбасило при касте, так и колбасит, анимацию он отказывается адекватно
Таймером создаёшь нулевую задержку, устанавливаешь нужную анимацию, а дальше уже либо периодиком проверяешь приказ и отменяешь анимацию, либо через время просто отменяешь анимацию. Это основы гуи, не то что джасс)

вот так решается первый вариант
function InitTrig_Annihilation_Conditions takes nothing returns boolean
    if (GetUnitAbilityLevel(GetSpellAbilityUnit(), udg_Annihilation) > 0) then
        call ExecuteFunc( "AnnihilationActions" )
    endif

    return false
endfunction
и так второй
function ExecuteCast takes nothing returns nothing
    local timer spellDurationTimer = GetExpiredTimer()
    local integer timerHandle = GetHandleId(spellDurationTimer)
    local unit target = LoadUnitHandle(udg_Hash, timerHandle, 0)
    local unit caster = LoadUnitHandle(udg_Hash, timerHandle, 1)

    call ResetUnitAnimation(caster)
    call SetUnitExploded(target, true)
    call KillUnit(target)
    call DestroyTimer(spellDurationTimer)
    set spellDurationTimer = null
endfunction

function SetAnim takes nothing returns nothing
    local timer t = GetExpiredTimer( )
    
    call SetUnitAnimation( LoadUnitHandle( H, GetHandleId( t ), 1 ), "channel" )
    call TimerStart(t, 5, false, function ExecuteCast)
    
    set t = null
endfunction

function AnnihilationActions takes nothing returns nothing
    local unit target = GetSpellTargetUnit()
    local unit caster = GetSpellAbilityUnit()
    local timer spellDurationTimer = CreateTimer()
    local integer timerHandle = GetHandleId(spellDurationTimer)

    call SaveUnitHandle(udg_Hash, timerHandle, 0, target)
    call SaveUnitHandle(udg_Hash, timerHandle, 1, caster)
    call SetUnitAnimationByIndex(caster, 11)
    call SetUnitZ(target, 400)
    
	call TimerStart(spellDurationTimer, 0.00, false, function SetAnim)
	
    set target = null
    set caster = null
    set spellDurationTimer = null
endfunction
`
ОЖИДАНИЕ РЕКЛАМЫ...
28
После применения способности, игра просто доходит до ожидания, вызывает его и все. Можно ждать бесконечно, остальной код не будет исполнен.
TriggerSleepAction нельзя вызвать в кондишенах, только в действиях. Либо юзай экзекют, либо не юзай вейт

Результат тот же, до кода, следующего после цикла, исполнение не доходит.
Ты таким образом себе оп лимит сломал, ведь в первую же долю секунды цикл обрабатывает 25к+ действий

Дредлорда как колбасило при касте, так и колбасит, анимацию он отказывается адекватно
Таймером создаёшь нулевую задержку, устанавливаешь нужную анимацию, а дальше уже либо периодиком проверяешь приказ и отменяешь анимацию, либо через время просто отменяешь анимацию. Это основы гуи, не то что джасс)

вот так решается первый вариант
function InitTrig_Annihilation_Conditions takes nothing returns boolean
    if (GetUnitAbilityLevel(GetSpellAbilityUnit(), udg_Annihilation) > 0) then
        call ExecuteFunc( "AnnihilationActions" )
    endif

    return false
endfunction
и так второй
function ExecuteCast takes nothing returns nothing
    local timer spellDurationTimer = GetExpiredTimer()
    local integer timerHandle = GetHandleId(spellDurationTimer)
    local unit target = LoadUnitHandle(udg_Hash, timerHandle, 0)
    local unit caster = LoadUnitHandle(udg_Hash, timerHandle, 1)

    call ResetUnitAnimation(caster)
    call SetUnitExploded(target, true)
    call KillUnit(target)
    call DestroyTimer(spellDurationTimer)
    set spellDurationTimer = null
endfunction

function SetAnim takes nothing returns nothing
    local timer t = GetExpiredTimer( )
    
    call SetUnitAnimation( LoadUnitHandle( H, GetHandleId( t ), 1 ), "channel" )
    call TimerStart(t, 5, false, function ExecuteCast)
    
    set t = null
endfunction

function AnnihilationActions takes nothing returns nothing
    local unit target = GetSpellTargetUnit()
    local unit caster = GetSpellAbilityUnit()
    local timer spellDurationTimer = CreateTimer()
    local integer timerHandle = GetHandleId(spellDurationTimer)

    call SaveUnitHandle(udg_Hash, timerHandle, 0, target)
    call SaveUnitHandle(udg_Hash, timerHandle, 1, caster)
    call SetUnitAnimationByIndex(caster, 11)
    call SetUnitZ(target, 400)
    
	call TimerStart(spellDurationTimer, 0.00, false, function SetAnim)
	
    set target = null
    set caster = null
    set spellDurationTimer = null
endfunction
Принятый ответ
18
Делаю время подготовки не 0, а N секунд, к примеру 3, он сначала ждет эти 3 секунды, потом происходит ровно то же самое
Потому что "время подготовки" это время до старта заклинания и обычно везде 0. Если нужна кастующая способность, то это "следовать в течении времени". Далее отлавливаете либо отмену либо окончание заклинания. В случае отмены опускаете юнита, в случае окончания взрываете. Опускание скорее всего придется программировать вручную
15
rsfghd, ExecuteFunc еще не пробовал ни разу, попробую, благодарю. Как будет возможность вернуться к редактору, проверю ваш вариант.
Vlod, спасибо за ваше время. Я ориентировался на огненный столб, у которого время подготовки не 0 и чародей крови воспроизводит анимацию без проблем.
15
rsfghd, да, ваш рецепт с таймером на 0 помог. Так же я еще раз теперь перестартовываю таймер, чтобы добиться синхронизации между проигрыванием анимации и поднятием жертвы в воздух. Итого, 3 функции, которые запускают друг друга по истечению таймера, порой мгновенному, рестартуя его... Это работает, однако какое же все же это извращение) Не могу представить, чтобы в каком-нибудь другом яп нужно было стартовать таймер с временем 0, просто, чтобы добиться корректной работы. Остается лишь проблема финальной стадии заклинания, когда жертву разрывает на кусочки и она мгновенно оказывается на земле, есть идея попробовать удалять юнита, запомнив перед этим высоту и точку его нахождения и в этом месте создавать на высоте эффект этих самых разлетающихся кишков...
Ну и написать код на отмену заклинания и на случай оглушения, если только он не входит в случай отмены, это сейчас и пробую реализовать. Сдается мне, что я напрасно привязал заклинание к событию EVENT_PLAYER_UNIT_SPELL_CHANNEL, нужно было использовать все же EVENT_PLAYER_UNIT_SPELL_FINISH...

Vlod, в самом деле, сейчас вчитался еще раз в ваш комментарий и поставил в течение времени - секунду. Анимация стала воспроизводится адекватно, соответственно, код можно будет упростить, исключив функцию проигрывания анимации.

Да, теперь я исключил вовсе проигрывание анимации в коде, анимация воспроизводится юнитом самостоятельно по настройке канала, кода стало меньше, однако привязаться к EVENT_PLAYER_UNIT_SPELL_FINISH не выходит, потому как в таком случае не отлавливается GetSpellTargetUnit(), возвращается null, посему с отменой придется еще покумекать...

С последней фазой тоже пока не выходит, я так понял, что нельзя создать эффект на высоте в старом патче(1.26), пробую создать даммика и над ним создать эффект, работает только если даммик имеет модель, если модель убираешь, эффект не отображается...
28
Meddin, что тут сложного, просто в таймер сохрани кастера, таргета и время, периодическим проверяешь что приказ у кастера равен приказу абилки, отнимаешь от времени периодичность таймера, когда оно становится нулевым, взрываешь цель, если приказ сменился, а время нет - значит сбили, опускаешь таргета
15
rsfghd, я думал плясать от события EVENT_PLAYER_UNIT_SPELL_ENDCAST, но оказалось что оно срабатывает даже при успешном завершении спелла, а не только при отмене. В общем копал не в ту сторону.

Хотя мне казалось логичным, что раз есть 2 разных события EVENT_PLAYER_UNIT_SPELL_FINISH и EVENT_PLAYER_UNIT_SPELL_ENDCAST, то они должны отрабатывать при разных условиях. Но у близзов другая логика)
18
работает только если даммик имеет модель
Есть два основных способа создания дамми-эффектов. Первый способ это специальная прозрачная модель в центр которой крепится модель эффекта, в таком случае у вас будет 1 объект в РО на все ситуации, такого юнита можно будет вращать, двигать, но будут недоступны некоторые функции, например нельзя менять скорость анимации прикрепленного спецэффекта. Во втором способе для каждого эффекта создается объект-пустышка с конкретной моделью, из минусов то что при большом количестве объектов в РО становится невозможно работать из за лагов
15
Vlod, благодарю, да, нужно будет поизучать тему с даммиками. Я их еще ни разу не использовал. А тут использовать придется однозначно, потому как не дело это, что взрыв воспроизводится на земле почему то, хотя взрывается юнит с измененной высотой, но игру это абсолютно не смущает. К тому же, придется искать модель взрыва без брызг крови, которые ложатся на землю, потому как они тогда горизонтально висят в воздухе и выглядит это уродливо...
15
rsfghd, спасибо, добил реализацию вашего совета, пришлось помучаться с временем каста, сократив его и поиграться с величиной периодичности таймера, но вроде бы получилось более-менее сносно, отмена работает корректно. Так как вопрос многогранный, то трудно выбрать верный ответ, так как и ваши советы и советы Vlod сильно помогли довести до ума спелл. Выберу, пожалуй, ваш ответ, так как он был дан первым и охватил все темы вопроса, не в обиду, товарищу Vlod, его помощь точно также не может быть не оценена по достоинству, так как именно его совет помог сократить количество кода и убрать лишнюю логику в спелле. Теперь мне предстоит научиться работать с дамми-юнитами(снова привет близзам-наркоманам, что мешало просто сделать метод смены высоты созданного в точке эффекта и все¯\_(ツ)_/¯)
28
Meddin, юзай мемхак, там можно установить высоту эффекта
Чтобы оставить комментарий, пожалуйста, войдите на сайт.