Способ реализации:
Версия Warcraft:
Способность для обучения начинающих, но посложнее!
Возможны баги, но автор их не обнаружил :)
MUI - только для разных игроков.

Видео

Код

globals
    hashtable HT = InitHashtable()
    group Group = CreateGroup()
    group FastGroup
    player CasterOwner
    
    unit Caster
    unit Dummy
    unit EnumUnit
    
    timer Timer
    timer FastTimer
    integer TimerId
    integer FastTimerId
    integer CasterId
    integer Tick
    integer Alpha
    
    real Time
    real Angle
    real FastAngle
    real Distance
    real Range
    real Scale
    real Charge
    
    real CasterX
    real CasterY
    
    real CastX
    real CastY
    
    real StartX
    real StartY
    
    real NewX
    real NewY

    // Айди способностей и периодичность таймера
    constant integer WaterDragon_SpellPreparationId = 'A000'
    constant integer WaterDragon_SpellShotId = 'A001'
    
    constant real WaterDragon_TimerPeriod = 0.03
    constant real WaterDragon_PreparationTime = 3
    
    // Айди даммиков
    constant integer WaterDragon_DummyId = 'h000'
    constant integer WaterDragon_PreparationDummyId = 'h001'
    
    // Строки
    constant string WaterDragon_MoveEffectPath = "war3mapImported\\AquaSpike.mdx"
    constant string WaterDragon_OrderString = "wateryminion"
    constant string WaterDragon_ChargeText = "|cFFBBEEFFWater Dragon Charge: "
    constant string WaterDragon_TimeText = "|cFFBBEEFFTime Left For Cast: "
    
    // Длительности
    constant real WaterDragon_ChargeTextDuration = 5
    constant real WaterDragon_TimeTextDuration = 5
    
    // Ограничения и скорость убавления непрозрачности
    constant integer WaterDragon_TimeLeftForCast = 3
    constant integer WaterDragon_AlphaRemovingSpeed = 32
    constant integer WaterDragon_EffectLimit = 3
    
    // Стартовые значения и разброс угла в каждую сторону каждого даммика
    constant real WaterDragon_StartDistance = 1000
    constant real WaterDragon_StartSpeed = 900
    constant real WaterDragon_StartRange = 100
    constant real WaterDragon_StartDamage = 100
    constant real WaterDragon_StartOffset = 75
    constant real WaterDragon_StartScale = 1.5
    constant real WaterDragon_AngleScatter = 8
    
    // Множители и Делители
    constant real WaterDragon_DistanceMultiplier = 10
    constant real WaterDragon_SpeedMultiplier = 10
    constant real WaterDragon_RangeMultiplier = 1
    constant real WaterDragon_DamageMultiplier = 2 
    constant real WaterDragon_ScaleDivisor = 100
endglobals

native UnitAlive takes unit id returns boolean

function WaterDragonPreparation_EffectTimer takes nothing returns nothing
    set Timer = GetExpiredTimer()
    set TimerId = GetHandleId( Timer )
    
    set Dummy = LoadUnitHandle( HT, TimerId, 'efct' )
    set Alpha = LoadInteger( HT, TimerId, 'alph' ) - WaterDragon_AlphaRemovingSpeed
    
    call SaveInteger( HT, TimerId, 'alph', Alpha )
    call SetUnitVertexColor( Dummy, 255, 255, 255, Alpha )
    
    if Alpha <= 0 then
        call RemoveUnit( Dummy )
        call PauseTimer( Timer )
        call DestroyTimer( Timer )
        call FlushChildHashtable( HT, TimerId )
    endif
endfunction

function WaterDragonPreparation_Timer takes nothing returns nothing
    set Timer = GetExpiredTimer()
    set TimerId = GetHandleId( Timer )
    set Caster = LoadUnitHandle( HT, TimerId, 'cstr' )
    
    if GetUnitCurrentOrder( Caster ) == OrderId( WaterDragon_OrderString ) then
        set Tick = LoadInteger( HT, TimerId, 'tick' ) + 1
        call SaveInteger( HT, TimerId, 'tick', Tick )
        call SaveInteger( HT, GetHandleId( Caster ), 'chrg', Tick )
        call DisplayTimedTextToPlayer( GetOwningPlayer( Caster ), 0, 0, WaterDragon_ChargeTextDuration, WaterDragon_ChargeText + I2S( Tick ) + "%" )
    else
        set CasterId = GetHandleId( Caster )
        set Tick = LoadInteger( HT, CasterId, 'tick' )
        
        if not LoadBoolean( HT, TimerId, 'flag' ) then
            set FastTimer = CreateTimer()
            set FastTimerId = GetHandleId( FastTimer )
            
            call SaveUnitHandle( HT, FastTimerId, 'efct', LoadUnitHandle( HT, TimerId, 'efct' ) )
            call SaveInteger( HT, FastTimerId, 'alph', 255 )
            call TimerStart( FastTimer, 0.03, true, function WaterDragonPreparation_EffectTimer )
            
            set Tick = WaterDragon_TimeLeftForCast + 1
            call SaveInteger( HT, CasterId, 'tick', Tick )
            call SaveBoolean( HT, TimerId, 'flag', true )
            
            call PauseTimer( Timer )
            call TimerStart( Timer, 1, true, function WaterDragonPreparation_Timer )
        endif
        
        if not LoadBoolean( HT, CasterId, 'flag' ) then
            set Tick = Tick - 1
            call SaveInteger( HT, CasterId, 'tick', Tick )
            call DisplayTimedTextToPlayer( GetOwningPlayer( Caster ), 0, 0, WaterDragon_TimeTextDuration, WaterDragon_TimeText + I2S( Tick ) + " Seconds" )
        endif
        
        if Tick <= 0 then
            set CasterOwner = GetOwningPlayer( Caster )
            call SetPlayerAbilityAvailable( CasterOwner, WaterDragon_SpellShotId, false )
            call SetPlayerAbilityAvailable( CasterOwner, WaterDragon_SpellPreparationId, true )
            
            call PauseTimer( Timer )
            call DestroyTimer( Timer )
            call FlushChildHashtable( HT, TimerId )
            call FlushChildHashtable( HT, CasterId )
        endif
    endif
endfunction

function WaterDragonShot_Group takes nothing returns nothing
    set EnumUnit = GetEnumUnit()
    
    if IsUnitInRangeXY( EnumUnit, NewX, NewY, Range )  then
        if IsUnitInGroup( EnumUnit, FastGroup ) then
            return
        endif
        
        if not UnitAlive( EnumUnit ) then
            return
        endif
        
        if not IsUnitEnemy( EnumUnit, GetOwningPlayer( Caster ) ) then
            return
        endif
        
        if IsUnitType( EnumUnit, UNIT_TYPE_STRUCTURE ) then
            return
        endif
        
        call UnitDamageTarget( Caster, EnumUnit, LoadReal( HT, TimerId, 'damg' ), true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS )
        call GroupAddUnit( FastGroup, EnumUnit )
    endif
endfunction

function WaterDragonShot_Move takes unit dummy, real startX, real startY, real endX, real endY returns nothing
    set NewX = startX * ( 1 - Time ) + endX * Time
    set NewY = startY * ( 1 - Time ) + endY * Time
    
    if Tick == ( Tick / WaterDragon_EffectLimit ) * WaterDragon_EffectLimit then
        call DestroyEffect( AddSpecialEffect( WaterDragon_MoveEffectPath, NewX, NewY ) )
    endif
    
    call SetUnitX( dummy, NewX )
    call SetUnitY( dummy, NewY )
    
    call GroupEnumUnitsInRange( Group, NewX, NewY, Range + 200, null ) // Добавляем 200, чтобы выделить юнитов учитывая их физический размер через IsUnitInRangeXY, так как GroupEnum проверяет только центр юнита, а IsUnitInRangeXY — ещё и его физический размер.
    call ForGroup( Group, function WaterDragonShot_Group )
    call GroupClear( Group )
    
    if Time >= 1 then
        call RemoveUnit( dummy )
    endif
endfunction

function WaterDragonShot_Timer takes nothing returns nothing
    set Timer = GetExpiredTimer()
    set TimerId = GetHandleId( Timer )
    
    set Caster = LoadUnitHandle( HT, TimerId, 'cstr' )
    set FastGroup = LoadGroupHandle( HT, TimerId, 'grup' )
    
    set Range = LoadReal( HT, TimerId, 'rang' )
    set Tick = LoadInteger( HT, TimerId, 'tick' ) + 1
    call SaveInteger( HT, TimerId, 'tick', Tick )
    
    set Time = ( Tick * WaterDragon_TimerPeriod ) / ( LoadReal( HT, TimerId, 'dist' ) / LoadReal( HT, TimerId, 'sped' ) )
    call WaterDragonShot_Move( LoadUnitHandle( HT, TimerId, 'dmy1' ), LoadReal( HT, TimerId, 'RDSX' ), LoadReal( HT, TimerId, 'RDSY' ), LoadReal( HT, TimerId, 'RDEX' ), LoadReal( HT, TimerId, 'RDEY' ) )
    call WaterDragonShot_Move( LoadUnitHandle( HT, TimerId, 'dmy2' ), LoadReal( HT, TimerId, 'LDSX' ), LoadReal( HT, TimerId, 'LDSY' ), LoadReal( HT, TimerId, 'LDEX' ), LoadReal( HT, TimerId, 'LDEY' ) )
    
    if Time >= 1 then
        call GroupClear( FastGroup )
        call DestroyGroup( FastGroup )
        call PauseTimer( Timer )
        call DestroyTimer( Timer )
        call FlushChildHashtable( HT, TimerId )
        call SaveInteger( HT, GetHandleId( Caster ), 'tick', 0 )
    endif
endfunction

function WaterDragon_Handler takes nothing returns nothing
    if GetSpellAbilityId() == WaterDragon_SpellPreparationId then
        set Caster = GetTriggerUnit()
        set CasterOwner = GetOwningPlayer( Caster )
        
        set Timer = CreateTimer()
        set TimerId = GetHandleId( Timer )
        
        call SetPlayerAbilityAvailable( CasterOwner, WaterDragon_SpellPreparationId, false )
        call SetPlayerAbilityAvailable( CasterOwner, WaterDragon_SpellShotId, true )

        call SaveUnitHandle( HT, TimerId, 'cstr', Caster )
        call SaveUnitHandle( HT, TimerId, 'efct', CreateUnit( CasterOwner, WaterDragon_PreparationDummyId, GetUnitX( Caster ), GetUnitY( Caster ), GetRandomReal( 0, 360 ) ) )
        call TimerStart( Timer, WaterDragon_PreparationTime * 0.01, true, function WaterDragonPreparation_Timer )
    endif
    
    if GetSpellAbilityId() == WaterDragon_SpellShotId then
        set Caster = GetTriggerUnit()
        set CasterId = GetHandleId( Caster )
        set CasterOwner = GetOwningPlayer( Caster )
        set Charge = I2R( LoadInteger( HT, CasterId, 'chrg' ) )
        
        set CasterX = GetUnitX( Caster )
        set CasterY = GetUnitY( Caster )
        set Angle = Atan2( GetSpellTargetY() - CasterY, GetSpellTargetX() - CasterX ) * bj_RADTODEG // Конвертирую в градусы что-бы не было возни с радианами при создании даммиков 
        set Distance = WaterDragon_StartDistance + Charge * WaterDragon_DistanceMultiplier
        set Scale = WaterDragon_StartScale + Charge / WaterDragon_ScaleDivisor
        
        set Timer = CreateTimer()
        set TimerId = GetHandleId( Timer )
        
        call SetPlayerAbilityAvailable( CasterOwner, WaterDragon_SpellShotId, false )
        call SetPlayerAbilityAvailable( CasterOwner, WaterDragon_SpellPreparationId, true )
        
        call SaveUnitHandle( HT, TimerId, 'cstr', Caster )
        call SaveGroupHandle( HT, TimerId, 'grup', CreateGroup() )
        call SaveBoolean( HT, CasterId, 'flag', true )
        call SaveReal( HT, TimerId, 'dist', Distance )
        call SaveReal( HT, TimerId, 'sped', WaterDragon_StartSpeed + Charge * WaterDragon_SpeedMultiplier )
        call SaveReal( HT, TimerId, 'damg', WaterDragon_StartDamage + Charge * WaterDragon_DamageMultiplier )
        call SaveReal( HT, TimerId, 'rang', WaterDragon_StartRange + Charge * WaterDragon_RangeMultiplier )
        
        set FastAngle = ( Angle + WaterDragon_AngleScatter ) * bj_DEGTORAD
        set StartX = CasterX + WaterDragon_StartOffset * Cos( FastAngle )
        set StartY = CasterY + WaterDragon_StartOffset * Sin( FastAngle )
        set Dummy = CreateUnit( CasterOwner, WaterDragon_DummyId, StartX, StartY, FastAngle * bj_RADTODEG )
        
        call SetUnitScale( Dummy, Scale, Scale, Scale )
        call SaveUnitHandle( HT, TimerId, 'dmy1', Dummy )
        call SaveReal( HT, TimerId, 'RDSX', StartX ) // Right Dummy Start X
        call SaveReal( HT, TimerId, 'RDSY', StartY ) // Right Dummy Start Y
        call SaveReal( HT, TimerId, 'RDEX', StartX + Distance * Cos( FastAngle ) ) // Right Dummy End X
        call SaveReal( HT, TimerId, 'RDEY', StartY + Distance * Sin( FastAngle ) ) // Right Dummy End Y
        
        set FastAngle = ( Angle - WaterDragon_AngleScatter ) * bj_DEGTORAD
        set StartX = CasterX + WaterDragon_StartOffset * Cos( FastAngle )
        set StartY = CasterY + WaterDragon_StartOffset * Sin( FastAngle )
        set Dummy = CreateUnit( CasterOwner, WaterDragon_DummyId, StartX, StartY, FastAngle * bj_RADTODEG )
        
        call SetUnitScale( Dummy, Scale, Scale, Scale )
        call SaveUnitHandle( HT, TimerId, 'dmy2', Dummy )
        call SaveReal( HT, TimerId, 'LDSX', StartX ) // Left Dummy Start X
        call SaveReal( HT, TimerId, 'LDSY', StartY ) // Left Dummy Start Y
        call SaveReal( HT, TimerId, 'LDEX', StartX + Distance * Cos( FastAngle ) ) // Left Dummy EndX X
        call SaveReal( HT, TimerId, 'LDEY', StartY + Distance * Sin( FastAngle ) ) // Left Dummy EndY Y
        
        call TimerStart( Timer, WaterDragon_TimerPeriod, true, function WaterDragonShot_Timer )
    endif
endfunction

function InitTrig_WaterDragon takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer i = 0

    loop
        call TriggerRegisterPlayerUnitEvent( t, Player( i ), EVENT_PLAYER_UNIT_SPELL_EFFECT, null )
        set i = i + 1
        exitwhen i == bj_MAX_PLAYER_SLOTS
    endloop
    
    call TriggerAddAction( t, function WaterDragon_Handler )
    set t = null
endfunction

Требования

Скопировать код из шапки карты, скопировать триггер с инициализацией (обязательно должна блокироваться способность выстрела драконами для игроков при старте) и триггер со спеллом к себе на карту, если такие глобальные переменные уже имеются, то копировать не надо. Поменять равкоды способностей на свой.
`
ОЖИДАНИЕ РЕКЛАМЫ...
9
Я крайне сомневаюсь что это способность для "обучения", но мне понравился подход с записью дочернего индекса с помощью равкодов, возьму на заметку)
Ответы (2)
9
IzobretatelBoom, Не знаю, как по мне у автора оч классно всё пояснено. Пояснено достаточно моментов, для создания подобных спеллов описанных автором.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.