28

» WarCraft 3 / Как узнать есть ли цель способности за кастующим юнитом

В дополнение к комменту выше, чтобы каст произошёл спиной, нужно настройку в ро (не помню название на русском, prop window), выставить на 180. Однако кастер всё равно повернётся. Этот параметр отвечает за разницу углов между целью/точкой каста и поворотом кастера. То есть если значение будет 1, юниту полностью нужно повернуться в точку каста, если 180, то кастовать начнет мгновенно со даже со спины. Кроме этого есть ещё 2 параметра, cast point и backswing point, первое отвечает за общее время каста спеллов у юнита, второе дополнительное время для каста во время поворота
28

» WarCraft 3 / Заклинания на заказ

ShadowNinja, чтоб ты не думал, что я ничем не занимался
Кроме этого в планах сделать кат со всеми выполненными заказами в самом ресурсе, чтобы люди могли искать интересующие спеллы не бегая по страницам комментариев
28

» WarCraft 3 / Заклинания на заказ

ShadowNinja, да не выгорел. Пытался заказ назарпанка сделать, а на южапи функции не работали должным образом, анрайз что-то поправил и продолжает исправлять, но я потратил на это выходной. Оставляй заказы, по мере заинтересованности буду делать. Вечером отталкиванием займусь, возьму анимку spell у чародея крови, выпущу двух фениксов по дуге в сторону цели и вместе со свистелками реализую отталкивание. Единственное что мне не очень нравится, это то, что цель может двигаться, из-за этого проблемы логические могут возникнуть. Впрочем, если делать мгновенное отталкивание без украшений, то всё будет оке, но у тебя технические задачи на полёт моей собственной фантазии. Буду делать на южапи
У меня в будние очень мало времени на хобби
28

» WarCraft 3 / Заклинания на заказ

Демонстрация работ
Shermanator00, вот тут ещё вариант прыжка, этот спеллпак делать начал где-то пол года назад, но мне не понравилось что он недостаточно интересный, поэтому не могу его выкатить, тут телепорт вверх с последующим приземлением, сразу после телепорта раскидывает сюрикены, которые застревают в земле, на них можно наступить и получить урон, через короткое время сюрикены, что остались в земле - возвращаются, попутно задевая всех противников
карту не скину, т.к. планирую переделать и выкатить, надеюсь это поможет немного интереснее фантазировать
Загруженные файлы
28

» WarCraft 3 / Заклинания на заказ

Shermanator00, демонстративный вариант, если что, написан на вджассе, а не гуи

Код

AllGlobals
library AllGlobalsLib
globals
    constant hashtable H = InitHashtable( )
    constant real UNIT_MAX_COLLISION = 200.00
    constant location LFZ = Location( 0.00, 0.00 )
    
    real MaxX
    real MinX
    real MaxY
    real MinY
endglobals

native UnitAlive takes unit id returns boolean

function GetLocZ takes real x, real y returns real
    call MoveLocation( LFZ, x, y )
    return GetLocationZ( LFZ )
endfunction

function GetCorY takes real y returns real
    if y > MaxY then
        return MaxY
    elseif y < MinY then
        return MinY
    endif
    
    return y
endfunction

function GetCorX takes real x returns real
    if x > MaxX then
        return MaxX
    elseif x < MinX then
        return MinX
    endif
    
    return x
endfunction

function ParabolaZ2 takes real y0, real y1, real h, real d, real x returns real
    return ((4 * h / d) * (d - x) + y1 - y0) * (x / d) + y0
endfunction

struct vector
    real x
    real y
    real z
    
    method length takes nothing returns real
        return SquareRoot( x * x + y * y + z * z )
    endmethod
    
    method normalize takes nothing returns nothing
        local real l = length( )
        
        if l == 0.00 then
            set l = 1.00
        endif
        
        set x = x / l
        set y = y / l
        set z = z / l
    endmethod
    
    static method create takes real x, real y, real z returns thistype
        local thistype this = thistype.allocate( )
        
        set this.x = x
        set this.y = y
        set this.z = z
        
        return this
    endmethod
endstruct

//===========================================================================
function InitTrig_AllGlobals takes nothing returns nothing
    //set gg_trg_AllGlobals = CreateTrigger(  )
    local rect r = GetWorldBounds( )
    
    set MaxX = GetRectMaxX( r )
    set MinX = GetRectMinX( r )
    set MaxY = GetRectMaxY( r )
    set MinY = GetRectMinY( r )
    
    call RemoveRect( r )
    set r = null
endfunction
endlibrary
Spell
library SpellLib
globals
    private constant group TempGroup = CreateGroup( )
endglobals

private struct ProjectileS
    timer t
    unit dummy
    unit dummy1
    
    vector array v[4]
    
    real time
    real timeMax
endstruct

private struct SpellS
    timer t
    unit caster
    player p
    vector v
    vector l
    
    real time
    real timeDamage
    
    real radius
    real damage
    real height
    
    real speed
    real distance
    real distanceMax
    
    attacktype attackType
    damagetype damageType
    weapontype weaponType
endstruct

private function MoveProjectile takes nothing returns nothing
    local ProjectileS A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local real array x
    local real array y
    local real array z
    local integer k = 0
    local integer i
    
    set A.time = A.time + 0.03125 / A.timeMax
    
    if A.time > 1.00 then
        set A.time = 1.00
    endif
    
    loop
        set x[k] = A.v[k].x
        set y[k] = A.v[k].y
        set z[k] = A.v[k].z
        
        set k = k + 1
        exitwhen k >= 4
    endloop
    
    set k = 0
    
    loop
        set i = 0
        
        loop
            set x[i] = ( 1.00 - A.time ) * x[i] + A.time * x[i + 1]
            set y[i] = ( 1.00 - A.time ) * y[i] + A.time * y[i + 1]
            set z[i] = ( 1.00 - A.time ) * z[i] + A.time * z[i + 1]
            
            set i = i + 1
            exitwhen i > 4 - k
        endloop
        
        set k = k + 1
        exitwhen k >= 4 - 1
    endloop
    
    set x[0] = GetCorX( x[0] )
    set y[0] = GetCorX( y[0] )
    
    call SetUnitX( A.dummy, x[0] )
    call SetUnitY( A.dummy, y[0] )
    call SetUnitFlyHeight( A.dummy, z[0] - GetLocZ( x[0], y[0] ), 0.00 )
    
    call SetUnitX( A.dummy1, x[0] )
    call SetUnitY( A.dummy1, y[0] )
    call SetUnitFlyHeight( A.dummy1, z[0] - GetLocZ( x[0], y[0] ), 0.00 )
    
    if A.time >= 1.00 then
        call PauseTimer( A.t )
        call FlushChildHashtable( H, GetHandleId( A.t ) )
        call DestroyTimer( A.t )
        
        call SetUnitAnimation( A.dummy, "death" )
        call UnitApplyTimedLife( A.dummy, 'BTLF', 1.50 )
        
        call SetUnitAnimation( A.dummy1, "death" )
        call UnitApplyTimedLife( A.dummy1, 'BTLF', 1.50 )
        
        set bj_lastCreatedUnit = CreateUnit( GetOwningPlayer( A.dummy ), 'u002', x[0], y[0], GetRandomReal( 0.00, 360.00 ) )
        call SetUnitX( bj_lastCreatedUnit, x[0] )
        call SetUnitY( bj_lastCreatedUnit, y[0] )
        call SetUnitAnimation( bj_lastCreatedUnit, "birth" )
        call QueueUnitAnimation( bj_lastCreatedUnit, "stand" )
        call UnitApplyTimedLife( bj_lastCreatedUnit, 'BTLF', 0.67 )
        call SetUnitScale( bj_lastCreatedUnit, 2.50, 2.50, 2.50 )
        
        call DestroyEffect( AddSpecialEffect( "Abilities\\Weapons\\LavaSpawnMissile\\LavaSpawnBirthMissile.mdl", x[0], y[0] ) )
        
        set i = 0
        
        loop
            call A.v[i].destroy( )
            
            set i = i + 1
            exitwhen i >= 4
        endloop
        
        set A.dummy = null
        set A.dummy1 = null
        call A.destroy( )
    endif
endfunction

private function Move takes nothing returns nothing
    local SpellS A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local ProjectileS B
    local real x
    local real y
    local real z
    local unit u
    local integer i
    
    if A.distance > 0.00 then
        set x = GetCorX( GetUnitX( A.caster ) + A.speed * A.v.x )
        set y = GetCorY( GetUnitY( A.caster ) + A.speed * A.v.y )
        
        if A.speed > A.distance then
            set A.speed = A.distance
        endif
        
        set A.distance = A.distance - A.speed
        
        call SetUnitX( A.caster, x )
        call SetUnitY( A.caster, y )
        call SetUnitFlyHeight( A.caster, ParabolaZ2( A.v.z, A.l.z, A.height, A.distanceMax, A.distance ) - GetLocZ( x, y ), 0.00 )
    endif
    
    if A.time > 0.00 then
        set A.time = A.time - 0.01
        
        if A.time <= 0.00 then
            set x = GetUnitX( A.caster )
            set y = GetUnitY( A.caster )
            set z = GetLocZ( x, y ) + GetUnitFlyHeight( A.caster ) + 85.00
            set i = 10
            
            loop
                set B = ProjectileS.create( )
                
                set B.t = CreateTimer( )
                set B.v[0] = vector.create( x, y, z )
                set B.v[1] = vector.create( x + 200.00 * GetRandomReal( -bj_PI, bj_PI ), y + 200.00 * GetRandomReal( -bj_PI, bj_PI ), GetRandomReal( z + 50.00, z + 200.00 ) )
                set B.v[2] = vector.create( x + 200.00 * GetRandomReal( -bj_PI, bj_PI ), y + 200.00 * GetRandomReal( -bj_PI, bj_PI ), GetRandomReal( z + 50.00, z + 100.00 ) )
                set B.v[3] = vector.create( A.l.x + GetRandomReal( -100.00, 100.00 ), A.l.y + GetRandomReal( -100.00, 100.00 ), A.l.z )
                
                set B.dummy = CreateUnit( A.p, 'u000', B.v[0].x, B.v[0].y, GetRandomReal( 0.00, 360.00 ) )
                call UnitAddAbility( B.dummy, 'Arav' )
                call SetUnitX( B.dummy, B.v[0].x )
                call SetUnitY( B.dummy, B.v[0].y )
                call SetUnitFlyHeight( B.dummy, B.v[0].z - GetLocZ( B.v[0].x, B.v[0].y ), 0.00 )
                call SetUnitAnimation( B.dummy, "birth" )
                call QueueUnitAnimation( B.dummy, "stand" )
                
                set B.dummy1 = CreateUnit( A.p, 'u001', B.v[0].x, B.v[0].y, GetRandomReal( 0.00, 360.00 ) )
                call UnitAddAbility( B.dummy1, 'Arav' )
                call SetUnitX( B.dummy1, B.v[0].x )
                call SetUnitY( B.dummy1, B.v[0].y )
                call SetUnitFlyHeight( B.dummy1, B.v[0].z - GetLocZ( B.v[0].x, B.v[0].y ), 0.00 )
                call SetUnitAnimation( B.dummy1, "birth" )
                call QueueUnitAnimation( B.dummy1, "stand" )
                
                set B.time = 0.00
                set B.timeMax = A.timeDamage
                
                call SaveInteger( H, GetHandleId( B.t ), 0, B )
                call TimerStart( B.t, 0.03125, true, function MoveProjectile )
                
                set i = i - 1
                exitwhen i <= 0
            endloop
        endif
    elseif A.timeDamage > 0.00 then
        set A.timeDamage = A.timeDamage - 0.01
        
        if A.timeDamage <= 0.00 then
            set bj_lastCreatedUnit = CreateUnit( A.p, 'u002', A.l.x, A.l.y, GetRandomReal( 0.00, 360.00 ) )
            call SetUnitX( bj_lastCreatedUnit, A.l.x )
            call SetUnitY( bj_lastCreatedUnit, A.l.y )
            call SetUnitAnimation( bj_lastCreatedUnit, "birth" )
            call QueueUnitAnimation( bj_lastCreatedUnit, "stand" )
            call UnitApplyTimedLife( bj_lastCreatedUnit, 'BTLF', 0.67 )
            call SetUnitScale( bj_lastCreatedUnit, 5.00, 5.00, 5.00 )
            
            set bj_lastCreatedUnit = CreateUnit( A.p, 'u002', A.l.x, A.l.y, GetRandomReal( 0.00, 360.00 ) )
            call SetUnitX( bj_lastCreatedUnit, A.l.x )
            call SetUnitY( bj_lastCreatedUnit, A.l.y )
            call SetUnitAnimation( bj_lastCreatedUnit, "birth" )
            call QueueUnitAnimation( bj_lastCreatedUnit, "stand" )
            call UnitApplyTimedLife( bj_lastCreatedUnit, 'BTLF', 0.67 )
            call SetUnitScale( bj_lastCreatedUnit, 8.00, 8.00, 8.00 )
            
            call DestroyEffect( AddSpecialEffect( "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl", A.l.x, A.l.y ) )
        
            call GroupEnumUnitsInRange( TempGroup, A.l.x, A.l.y, A.radius + UNIT_MAX_COLLISION, null )
        
            loop
                set u = FirstOfGroup( TempGroup )
                exitwhen u == null
                call GroupRemoveUnit( TempGroup, u )
                
                if IsUnitInRangeXY( u, A.l.x, A.l.y, A.radius ) then
                    if UnitAlive( u ) and IsUnitEnemy( u, A.p ) then
                        call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Weapons\\LordofFlameMissile\\LordofFlameMissile.mdl", u, "chest" ) )
                        call UnitDamageTarget( A.caster, u, A.damage, false, false, A.attackType, A.damageType, A.weaponType )
                    endif
                endif
            endloop
        endif
    endif
    
    if A.distance <= 0.00 and A.timeDamage <= 0.00 then
        call PauseTimer( A.t )
        call FlushChildHashtable( H, GetHandleId( A.t ) )
        call DestroyTimer( A.t )
        
        set A.caster = null
        call A.l.destroy( )
        call A.v.destroy( )
        call A.destroy( )
    endif
endfunction

private function Spell_Actions takes nothing returns nothing
    local SpellS A = SpellS.create( )
    
    set A.t = CreateTimer( )
    set A.caster = GetTriggerUnit( )
    set A.p = GetOwningPlayer( A.caster )
    
    set A.l = vector.create( GetUnitX( A.caster ), GetUnitY( A.caster ), ( GetUnitFacing( A.caster ) - 180.00 ) * bj_DEGTORAD )
    set A.v = vector.create( Cos( A.l.z ), Sin( A.l.z ), 0.00 )
    set A.l.z = GetLocZ( A.l.x, A.l.y )
    
    set A.damage = 100.00 * GetUnitAbilityLevel( A.caster, GetSpellAbilityId( ) )
    set A.radius = 185.00
    set A.distance = 400.00
    set A.distanceMax = A.distance
    set A.height = A.distance * 0.50
    set A.speed = A.distance * 0.01
    set A.time = 0.20
    set A.timeDamage = 1.20
    
    set A.v.z = GetLocZ( A.l.x + A.distance * A.v.x, A.l.y + A.distance * A.v.y )
    
    set A.attackType = ATTACK_TYPE_MAGIC
    set A.damageType = DAMAGE_TYPE_MAGIC
    set A.weaponType = null
    
    if UnitAddAbility( A.caster, 'Arav' ) then
        call UnitRemoveAbility( A.caster, 'Arav' )
    endif
    
    call SaveInteger( H, GetHandleId( A.t ), 0, A )
    call TimerStart( A.t, 0.01, true, function Move )
endfunction

//===========================================================================
private function Spell_Conditions takes nothing returns boolean
    return GetSpellAbilityId( ) == 'A000'
endfunction

function InitTrig_Spell takes nothing returns nothing
    set gg_trg_Spell = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Spell, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Spell, Condition( function Spell_Conditions ) )
    call TriggerAddAction( gg_trg_Spell, function Spell_Actions )
endfunction
endlibrary

28

» WarCraft 3 / Заклинания на заказ

Демонстрация работ
Shermanator00, блин, у тебя такой заказ простой, но даже тут не хватает банального разнообразия, придумывайте более интересные спеллы, например, в прыжке разбрасывает сюрикены в разные стороны, которые при непродолжительном полёте выбирают цели вокруг для преследования, при достижении цели - поджигают её временно, если целей не было обнаружено - падают в точку прыжка, создавая взрыв и поджигая почву на время

если кто-то захочет импортировать, то скопируйте папку Initialization в свою карту, объекты из ро и в триггере Spell настройте под себя 258-265 строки и поменяйте равкод абилки на 283 строке, ну и равкоды даммиков по коду, остальное требует минимальных знаний джасса
Загруженные файлы
28

» WarCraft 3 / Заклинания на заказ

Выполнение заказа

Заклинание готово!

Dodge (третий вариант )

инструкция по импорту
скопировать папку Initialization и вставить в свою карту, копировать элементы из редактора объектов и вставлять в свою карту
все настройки на гуи, думаю разберёшься
Загруженные файлы
28

» WarCraft 3 / Заклинания на заказ

SebastianCarrey, если не справляешься, то могу добавить вышеуказанное и скинуть код/карту
28

» WarCraft 3 / Эффект снижения лечения

В wc3 нельзя сделать 0.015 секунд,
не путай людей, в гуи* нельзя такое вписать
28

» WarCraft 3 / Заклинания на заказ

ME_RiDi, тз с результатом хз. Животным абилку дирижабля дать или это трансформация юнита как с лучница и и гиппогрифами? Или просто периодически героя перемещать в позицию животного? Как затрачивается шкала усталости? Что будет при полном нуле? Нужно ли отображать шкалу? Если да, то каким способом?
28

» WarCraft 3 / Заклинания на заказ

SebastianCarrey, я спрашивал у тебя за мин и макс атаку. Найди строку set A.damage = GetUniBaseDamageByIndex( ... ) и замени ее на
set A.damage = GetRandomInt( BlzGetUnitWeaponIntegerField( A.caster, UNIT_WEAPON_IF_ATTACK_DAMAGE_BASE_MINIMUM, 0 ), BlzGetUnitWeaponIntegerField( A.caster, UNIT_WEAPON_IF_ATTACK_DAMAGE_BASE_MAXIMUM, 0 ) ) + BlzGetUnitBonusDamageByIndex( A.caster, 0 )
Да, там суммарный урон выводится. Если нужно отдельный, то урон от атаки и абилки нужно записать в разные переменные, если нужно, могу объяснить, там буквально 2 строки написать в разных местах
28

» WarCraft 3 / Заклинания на заказ

SebastianCarrey, посмотрел, к сожалению южапи немного скривил, либо реф с новым патчем изменил аргументы и выглядит следующим образом
BlzGetAbilityCooldown takes integer abilId, integer level returns real
Первым аргументом принимает равкод, а вторым уровень абилки, так что замени все эти строки на
BlzGetAbilityCooldown( 'A000', GetUnitAbilityLevel( A.caster, 'A000' ) )
Где А000 - равкод основной абилки
28

» WarCraft 3 / Заклинания на заказ

SebastianCarrey, бле, забыл добавить учёт того, что кастер жив. но комп уже выключил. В общем найди следующую строку в триггере Spell в функции Move: local boolean stunned = GetUnitAbilityLevel( A.caster, 'BPSE' ) > 0 и добавь or not UnitAlive( A.caster )
Должно выйти так:
local boolean stunned = GetUnitAbilityLevel( A.caster, 'BPSE' ) > 0 or not UnitAlive( A.caster )

И чтобы вдруг он при смерти не проиграл анимку Stand, чуть ниже найди call SetUnitAnimation( A.caster, "stand" ), и замени на
if UnitAlive( A.caster ) then
    call SetUnitAnimation( A.caster, "stand" )
endif
28

» WarCraft 3 / Заклинания на заказ

Выполнение заказа

Заклинание готово!

Молот древних (Rolling Slam)

инструкция по импорту
удалить из папки Initialization триггеры, отделённые комментарием (нижние), скопировать папку и вставить в свою карту
поскольку у тебя уже есть триггер AllGlobals из прошлого заказа, тебе нужно просто скопировать строки 56-81 и вставить где-нибудь в своей библиотеке AllGlobalsLib
в триггере Spell всё, что тебе нужно/можно изменить, помечено комментарием
поскольку у тебя уже есть библиотека SpellLib из прошлого заказа, нужно переименовать библиотеку перед импортом, например, на SpellOneLib
на 291 строке укажи абилку ауры замедления у даммика землетрясения
строки 105-109 и 96-100 можно стереть, это дополнительный эффект при наличии усиления абилки, при усилении абилки так же могу порекомендовать изменять иконку способности на более усиленную версию
на строках 89-90 оффсет для удара (чтобы удар происходил на уровне меча, а не прямо под юнитом)
ну и перекопируй редактор объектов

Код
AllGlobals
library AllGlobalsLib
globals
    constant hashtable H = InitHashtable( )
    constant real UNIT_MAX_COLLISION = 200.00
endglobals

native UnitAlive takes unit id returns boolean

function UnitHasItemOfType takes unit u, integer id returns boolean
    local integer i = 0
    
    loop
        if GetItemTypeId( UnitItemInSlot( u, i ) ) == id then
            return true
        endif
        
        set i = i + 1
        exitwhen i >= bj_MAX_INVENTORY
    endloop
    
    return false
endfunction

struct vector
    real x
    real y
    real z
    
    method length takes nothing returns real
        return SquareRoot( x * x + y * y + z * z )
    endmethod
    
    method normalize takes nothing returns nothing
        local real l = length( )
        
        if l == 0.00 then
            set l = 1.00
        endif
        
        set x = x / l
        set y = y / l
        set z = z / l
    endmethod
    
    static method create takes real x, real y, real z returns thistype
        local thistype this = thistype.allocate( )
        
        set this.x = x
        set this.y = y
        set this.z = z
        
        return this
    endmethod
endstruct

function SetUnitAnimationByIndexTimer takes nothing returns nothing
    local timer t = GetExpiredTimer( )
    local integer i = GetHandleId( t )
    local unit u = LoadUnitHandle( H, i, 0 )
    
    call SetUnitAnimationByIndex( u, LoadInteger( H, i, 1 ) )
    call QueueUnitAnimation( u, "stand ready" )
    call QueueUnitAnimation( u, "stand" )
    
    call FlushChildHashtable( H, i )
    call DestroyTimer( t )
    
    set u = null
    set t = null
endfunction

function SetUnitAnimationByIndexEx takes unit u, integer i returns nothing
    local timer t = CreateTimer( )
    
    call SaveUnitHandle( H, GetHandleId( t ), 0, u )
    call SaveInteger( H, GetHandleId( t ), 1, i )
    
    call TimerStart( t, 0.00, false, function SetUnitAnimationByIndexTimer )
    
    set t = null
endfunction
endlibrary

//===========================================================================
//function InitTrig_AllGlobals takes nothing returns nothing
    //set gg_trg_AllGlobals = CreateTrigger(  )
//endfunction

Spell
library SpellLib
globals
    private constant group TempGroup = CreateGroup( )
    private item TempItem = null
    private integer UpgradedKill = 0
    
    constant key SpellUpgrade
endglobals

private struct SpellS
    timer t
    unit caster
    unit dummy
    player p
    effect e = null
    ability abil
    integer attack
    integer upgrade1
    integer upgrade2
    
    vector v
    
    real time
    real timePause
    
    real speed
    
    real radius
    real radiusMax
    
    real damage
    
    attacktype attackType
    damagetype damageType
    weapontype weaponType
endstruct

private function RemoveBuff takes nothing returns nothing
    local timer t = GetExpiredTimer( )
    local integer i = GetHandleId( t )
    
    call DestroyEffect( LoadEffectHandle( H, i, 0 ) )
    call RemoveSavedHandle( H, GetHandleId( LoadUnitHandle( H, i, 1 ) ), SpellUpgrade )
    
    call FlushChildHashtable( H, i )
    call DestroyTimer( t )
    
    set t = null
endfunction

private function Move takes nothing returns nothing
    local SpellS A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local real x = GetUnitX( A.caster ) + A.speed * A.v.x
    local real y = GetUnitY( A.caster ) + A.speed * A.v.y
    local real x1
    local real y1
    local unit u
    local boolean stunned = GetUnitAbilityLevel( A.caster, 'BPSE' ) > 0
    
    if not stunned then
        if A.timePause > 0.00 then
            set A.timePause = A.timePause - 0.01
            
            if A.timePause <= 0.00 then
                call SetUnitAnimationByIndex( A.caster, 3 ) // анимация второй атаки
            endif
        endif
        
        if A.timePause <= 0.00 then
            call SetItemPosition( TempItem, x, y )
            call SetItemVisible( TempItem, false )
            
            set x1 = GetItemX( TempItem )
            set y1 = GetItemY( TempItem )
            
            if not ( ( x - 1.00 > x1 or x + 1.00 < x1 ) or ( ( y - 1.00 > y1 ) or ( y + 1.00 < y1 ) ) ) then
                call SetUnitX( A.caster, x )
                call SetUnitY( A.caster, y )
            endif
            
            set A.time = A.time - 0.01
        endif
    else
        call SetUnitAnimation( A.caster, "stand" )
    endif
    
    if A.time <= 0.00 or stunned then
        if not stunned then
            set x = x + 100.00 * A.v.x
            set y = y + 100.00 * A.v.y
            
            if A.attack == 0 then
                call DestroyEffect( AddSpecialEffect( "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl", x, y ) ) // эффект первого удара
                
                // доп. эффект от усиления "Корона титана"
                if A.e != null then
                    set bj_lastCreatedEffect = AddSpecialEffect( "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl", x, y )
                    call BlzSetSpecialEffectScale( bj_lastCreatedEffect, 0.75 )
                    call DestroyEffect( bj_lastCreatedEffect )
                endif
            else
                call DestroyEffect( AddSpecialEffect( "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl", x, y ) ) // эффект второго удара
                
                // доп. эффект от усиления "Корона титана"
                if A.e != null then
                    set bj_lastCreatedEffect = AddSpecialEffect( "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl", x, y )
                    call BlzSetSpecialEffectScale( bj_lastCreatedEffect, 1.15 )
                    call DestroyEffect( bj_lastCreatedEffect )
                endif
                
                set A.radius = A.radiusMax
            endif
            
            call GroupEnumUnitsInRange( TempGroup, x, y, A.radius + UNIT_MAX_COLLISION, null )
            
            loop
                set u = FirstOfGroup( TempGroup )
                exitwhen u == null
                call GroupRemoveUnit( TempGroup, u )
                
                if IsUnitInRangeXY( u, x, y, A.radius ) then
                    if UnitAlive( u ) and IsUnitEnemy( u, A.p ) then
                        if A.upgrade1 == 1 and GetRandomInt( 1, 100 ) <= 15 then // шанс заспавнить землетрясение при убийстве (15%)
                            set UpgradedKill = 1
                        endif
                        
                        call UnitDamageTarget( A.caster, u, A.damage, false, false, A.attackType, A.damageType, A.weaponType )
                        
                        set UpgradedKill = 0
                    endif
                endif
            endloop
            
            // плавающий текст
            set bj_lastCreatedTextTag = CreateTextTag( )
            call SetTextTagText( bj_lastCreatedTextTag, I2S( R2I( A.damage ) ) + "!", 12.50 * 0.023 / 10.00 )
            call SetTextTagPosUnit( bj_lastCreatedTextTag, A.caster, 75.00 ) 
            call SetTextTagColor( bj_lastCreatedTextTag, 255, 75, 0, 255 )
            call SetTextTagVelocity( bj_lastCreatedTextTag, 0.00, 0.0355 )
            call SetTextTagPermanent( bj_lastCreatedTextTag, false )
            call SetTextTagLifespan( bj_lastCreatedTextTag, 1.50 )
            call SetTextTagFadepoint( bj_lastCreatedTextTag, 0.00 )
        endif
        
        if A.attack == 0 and not stunned then
            set A.attack = 1
            
            set A.speed = A.speed * 2.00 // ускорение второго удара
            set A.time = 0.70 // время для второго удара
            set A.timePause = 0.50 // пауза перед вторым ударом
        else
            call PauseTimer( A.t )
            call FlushChildHashtable( H, GetHandleId( A.t ) )
            call DestroyTimer( A.t )
            
            call IssueImmediateOrder( A.caster, "stop" )
            
            if A.upgrade2 == 1 and not stunned then
                if GetRandomInt( 1, 100 ) <= 30 then // шанс усилить следующий каст абилки и сбросить перезарядку
                    set A.t = CreateTimer( )
                    
                    call SaveTimerHandle( H, GetHandleId( A.caster ), SpellUpgrade, A.t )
                    
                    call SaveEffectHandle( H, GetHandleId( A.t ), 0, AddSpecialEffectTarget( "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl", A.caster, "weapon" ) ) // эффект усиления
                    call SaveUnitHandle( H, GetHandleId( A.t ), 1, A.caster )
                    
                    call TimerStart( A.t, 5.00, false, function RemoveBuff ) // время усиления
                    
                    if A.e != null then
                        call BlzStartAbilityCooldown( A.abil, BlzGetAbilityCooldown( A.abil ) )
                    endif
                else
                    call BlzStartAbilityCooldown( A.abil, BlzGetAbilityCooldown( A.abil ) )
                endif
            else
                call BlzStartAbilityCooldown( A.abil, BlzGetAbilityCooldown( A.abil ) )
            endif
            
            call PauseUnit( A.caster, false )
            
            if A.e != null then
                call DestroyEffect( A.e )
                set A.e = null
            endif
            
            set A.t = null
            set A.abil = null
            set A.caster = null
            call A.v.destroy( )
            call A.destroy( )
        endif
    endif
endfunction

private function Spell_Actions takes nothing returns nothing
    local SpellS A = SpellS.create( )
    local integer lvl
    local timer t
    
    set A.t = CreateTimer( )
    set A.caster = GetTriggerUnit( )
    set A.p = GetOwningPlayer( A.caster )
    set A.abil = GetSpellAbility( )
    
    set A.v = vector.create( GetSpellTargetX( ) - GetUnitX( A.caster ), GetSpellTargetY( ) - GetUnitY( A.caster ), 0.00 )
    call A.v.normalize( )
    
    set A.damage = BlzGetUnitBaseDamageByIndex( A.caster, 0 ) + BlzGetUnitBonusDamageByIndex( A.caster, 0 )
    
    set lvl = GetUnitAbilityLevel( A.caster, GetSpellAbilityId( ) )
    
    if lvl == 1 then // уровень
        set A.damage = A.damage + 60.00 // доп. урон
    elseif lvl == 2 then
        set A.damage = A.damage + 110.00
    elseif lvl == 3 then
        set A.damage = A.damage + 160.00
    else
        set A.damage = A.damage + 210.00
    endif
        
    set A.radius = 250.00 // радиус первого удара
    set A.radiusMax = 375.00 // радиус второго удара
    
    if UnitHasItemOfType( A.caster, 'afac' ) then // "Сотрясатель земли"
        set A.damage = A.damage * 2.00 // доп. урон (х2)
        set A.upgrade1 = 1
    else
        set A.upgrade1 = 0
    endif
    
    if UnitHasItemOfType( A.caster, 'spsh' ) then // "Корона титана", настройки в других частях кода
        set A.upgrade2 = 1
    else
        set A.upgrade2 = 0
    endif
    
    set t = LoadTimerHandle( H, GetHandleId( A.caster ), SpellUpgrade )
    
    if t != null then
        set A.e = LoadEffectHandle( H, GetHandleId( t ), 0 )
       
        call PauseTimer( t )
        call FlushChildHashtable( H, GetHandleId( t ) )
        call DestroyTimer( t )
        
        call RemoveSavedHandle( H, GetHandleId( A.caster ), SpellUpgrade )
        
        set A.damage = A.damage * 2.50 // дополнительный урон от усиления "Корона титана"
        
        set t = null
    endif
    
    set A.attackType = ATTACK_TYPE_MAGIC
    set A.damageType = DAMAGE_TYPE_DEMOLITION
    set A.weaponType = null
    
    set A.time = 0.40 // время для первого удара
    set A.timePause = 0.00
    set A.speed = 1.20 // скорость перемещения
    set A.attack = 0
    
    call PauseUnit( A.caster, true )
    call SetUnitAnimationByIndexEx( A.caster, 2 ) // анимация первой атаки
    
    call SaveInteger( H, GetHandleId( A.t ), 0, A )
    call TimerStart( A.t, 0.01, true, function Move )
endfunction

private function Damage takes nothing returns nothing
    local SpellS A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local unit u
    
    call GroupEnumUnitsInRange( TempGroup, A.v.x, A.v.y, A.radius + UNIT_MAX_COLLISION, null )
            
    loop
        set u = FirstOfGroup( TempGroup )
        exitwhen u == null
        call GroupRemoveUnit( TempGroup, u )
        
        if IsUnitInRangeXY( u, A.v.x, A.v.y, A.radius ) then
            if UnitAlive( u ) and IsUnitEnemy( u, A.p ) then
                call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Weapons\\LordofFlameMissile\\LordofFlameMissile.mdl", u, "chest" ) ) // доп. эффект при попадании
                call UnitDamageTarget( A.caster, u, A.damage, false, false, A.attackType, A.damageType, A.weaponType )
            endif
        endif
    endloop
    
    set A.time = A.time - 1.00 // периодичность урона землетрясения
    
    if A.time <= 0.00 then
        call PauseTimer( A.t )
        call FlushChildHashtable( H, GetHandleId( A.t ) )
        call DestroyTimer( A.t )
        
        call SetUnitAnimation( A.dummy, "death" )
        call UnitRemoveAbility( A.dummy, 'A001' )
        call UnitApplyTimedLife( A.dummy, 'BTLF', 3.00 ) // время анимации смерти
        
        set A.t = null
        set A.caster = null
        set A.dummy = null
        call A.v.destroy( )
        call A.destroy( )
    endif
endfunction

private function Spell_Actions_Death takes nothing returns nothing
    local SpellS A
    
    if UpgradedKill == 1 then
        set A = SpellS.create( )
        
        set A.t = CreateTimer( )
        set A.caster = GetKillingUnit( )
        set A.p = GetOwningPlayer( A.caster )
        set A.v = vector.create( GetUnitX( GetTriggerUnit( ) ), GetUnitY( GetTriggerUnit( ) ), 0.00 )
        
        set A.dummy = CreateUnit( A.p, 'u000', A.v.x, A.v.y, GetRandomReal( 0.00, 360.00 ) ) // даммик землетрясения
        
        call SetUnitX( A.dummy, A.v.x )
        call SetUnitY( A.dummy, A.v.y )
        call SetUnitAnimation( A.dummy, "birth" )
        call QueueUnitAnimation( A.dummy, "stand" )
        
        set A.radius = 300.00 // радиус землетрясения
        set A.damage = 100.00 // урон землетрясения
        set A.attackType = ATTACK_TYPE_MAGIC // тип атаки
        set A.damageType = DAMAGE_TYPE_FIRE // тип урона
        set A.weaponType = null // тип оружия (звук)
        set A.time = 6.00 // время землетрясения
        
        call SaveInteger( H, GetHandleId( A.t ), 0, A )
        call TimerStart( A.t, 1.00, true, function Damage ) // периодичность урона землетрясения
    endif
endfunction

//===========================================================================
private function Spell_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A000' // равкод абилки
endfunction

function InitTrig_Spell takes nothing returns nothing
    local trigger trg = CreateTrigger( )
    
    call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddAction( trg, function Spell_Actions_Death )
    
    set trg = null
    
    set gg_trg_Spell = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Spell, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Spell, Condition( function Spell_Conditions ) )
    call TriggerAddAction( gg_trg_Spell, function Spell_Actions )
    
    set TempItem = CreateItem( 'spsh', 0.00, 0.00 )
    call SetItemVisible( TempItem, false )
endfunction
endlibrary

Загруженные файлы
28

» WarCraft 3 / Способности и алгоритмы на заказ

Panda_95, исправлять я ничего не буду. По скринам увидел утеччный выбор юнитов и использование позиции юнита вместо переменной точки
28

» WarCraft 3 / Заклинания на заказ

SebastianCarrey, а что насчёт кубиков рандома у атаки? Там есть минимальное и максимальное значение
28

» WarCraft 3 / Заклинания на заказ

ShadowNinja, в принципе, отменить приказ движением всё же можно, но не через smart, а move
28

» WarCraft 3 / Заклинания на заказ

ShadowNinja, а я о чём говорю, когда предлагаю через пкм сделать луч. Кликнул во время каста и юнит повернулся в точку, куда кликнул, если кликнул на юнита, то кастер будет всё время поворачиваться на него, пока в другое место не кликнешь. Единственная проблема это невозможность отменить каст спелла движением. Но можно отменить, например, нажав на S
28

» WarCraft 3 / Способности и алгоритмы на заказ

Panda_95, если вы не понимаете что это значит, то после 100к кастов допустим (а может и раньше, если в карте и в другом коде присутствуют утечки), начнутся лаги, а потом и вовсе краш игры из-за нехватки памяти. Ну а поллед вейт может не соответствовать анимации удара по земле с разницей около 0.10 сек