Добавлен , опубликован
Алгоритмы, Наработки и Способности
Способ реализации:
vJass
Тип:
Способность
Версия Warcraft:
1.26+

Опалесценция

(атакующая)

Герой посылает пучок света, который взрывается над выбранной областью вызывая флуктуацию из световых частиц, что наносит урон вражеской нежити и замедляет противников в области на время действия эффекта
код
library OpalescenceLib
globals
    constant hashtable H = InitHashtable( )
    constant location LFZ = Location( 0.00, 0.00 )
    
    private constant group TempGroup = CreateGroup( )
    
    private constant integer HealingID = 'u005'
    private constant integer AbolishID = 'u006'
    private constant integer FaerieID  = 'u000'
    private constant integer PhaseID   = 'u003'
    private constant integer WispID    = 'u002'
    
    private unit TempUnit = null
    
    private real MaxX
    private real MinX
    private real MaxY
    private real MinY
    
    private string array AttachPointName
endglobals

native UnitAlive takes unit id returns boolean

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

private function SetUnitPositionEx takes unit u, real x, real y returns nothing
    if x > MaxX then
        set x = MaxX
    elseif x < MinX then
        set x = MinX
    endif
    
    if y > MaxY then
        set y = MaxY
    elseif y < MinY then
        set y = MinY
    endif
    
    call SetUnitX( u, x )
    call SetUnitY( u, y )
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

private struct OpalescenceMoveS
    timer t
    unit dummy
    
    real time
    real timeMax
    
    vector array l[6]
    vector last
endstruct

private struct OpalescenceS
    timer t
    unit dummy
    unit caster
    player p
    
    vector v
    vector l
    vector endPos
    
    real speed
    real damage
    real radius
    real time
    real timeThreshold
endstruct

private function OpalescenceMove takes nothing returns nothing
    local OpalescenceMoveS A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local integer i = 0
    local integer k = 0
    local real array x
    local real array y
    local real array z
    local real r
    local unit u
    
    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.l[k].x
        set y[k] = A.l[k].y
        set z[k] = A.l[k].z
        
        set k = k + 1
        exitwhen k >= 6
    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 > 6 - k
        endloop
        
        set k = k + 1
        exitwhen k >= 6 - 1
    endloop
    
    call SetUnitPositionEx( A.dummy, x[0], y[0] )
    call SetUnitFlyHeight( A.dummy, z[0] - GetLocZ( x[0], y[0] ), 0.00 )
    call SetUnitFacing( A.dummy, Atan2( y[0] - A.last.y, x[0] - A.last.x ) * bj_RADTODEG )
    
    if A.time >= 1.00 then
        call PauseTimer( A.t )
        call FlushChildHashtable( H, GetHandleId( A.t ) )
        call DestroyTimer( A.t )
        
        call UnitApplyTimedLife( A.dummy, 'BTLF', 2.00 )
        call SetUnitAnimation( A.dummy, "death" )
        
        set i = 0
        
        loop
            call A.l[i].destroy( )
            
            set i = i + 1
            exitwhen i >= 6
        endloop
        
        call A.last.destroy( )
        
        set A.t = null
        set A.dummy = null
        call A.destroy( )
    else
        set A.last.x = x[0]
        set A.last.y = y[0]
        set A.last.z = z[0]
    endif
endfunction

private function SetScale_1 takes nothing returns nothing
    local timer t = GetExpiredTimer( )
    local integer i = GetHandleId( t )
    local real r = LoadReal( H, i, 1 ) + 0.35
    
    if r >= 15.00 then
        call KillUnit( LoadUnitHandle( H, i, 0 ) )
        
        call PauseTimer( t )
        call DestroyTimer( t )
        call FlushChildHashtable( H, i )
    else
        call SetUnitScale( LoadUnitHandle( H, i, 0 ), r, r, r )
        call SetUnitVertexColor( LoadUnitHandle( H, i, 0 ), 255, 255, 255, R2I( 255.00 * ( 1.00 - r / 15.00 ) ) )
        call SaveReal( H, i, 1, r )
    endif
    
    set t = null
endfunction

private function SetScale takes nothing returns nothing
    local timer t = GetExpiredTimer( )
    local integer i = GetHandleId( t )
    local real r = LoadReal( H, i, 1 ) + 0.05
    
    if r >= 30.00 then
        call KillUnit( LoadUnitHandle( H, i, 0 ) )
        
        call PauseTimer( t )
        call DestroyTimer( t )
        call FlushChildHashtable( H, i )
    else
        call SetUnitScale( LoadUnitHandle( H, i, 0 ), r, r, r )
        call SaveReal( H, i, 1, r )
    endif
    
    set t = null
endfunction

private function OpalescenceDamage takes nothing returns nothing
    local OpalescenceS A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local OpalescenceMoveS B
    local unit u
    local real x
    local real y
    local timer t
    local integer i
    local integer j
    
    if A.timeThreshold > 0.00 then
        set A.timeThreshold = A.timeThreshold - 0.01
        
        if A.timeThreshold == 0.20 then
            set TempUnit = CreateUnit( A.p, WispID, A.l.x + A.speed * 20.00 * A.v.x, A.l.y + A.speed * 20.00 * A.v.y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( TempUnit, 'Arav' )
            call SetUnitPositionEx( TempUnit, A.l.x + A.speed * 20.00 * A.v.x, A.l.y + A.speed * 20.00 * A.v.y )
            call SetUnitFlyHeight( TempUnit, 300.00, 0.00 )
            call SetUnitScale( TempUnit, 2.00, 2.00, 2.00 )
            call SetUnitTimeScale( TempUnit, 2.00 )
            call UnitApplyTimedLife( TempUnit, 'BTLF', RMaxBJ( 1.50, A.time - 2.00 ) )
        endif
        
        set A.l.x = A.l.x + A.speed * A.v.x
        set A.l.y = A.l.y + A.speed * A.v.y
        set A.l.z = A.l.z + A.speed * A.v.z
        
        call SetUnitPositionEx( A.dummy, A.l.x, A.l.y )
        call SetUnitFlyHeight( A.dummy, A.l.z - GetLocZ( A.l.x, A.l.y ), 0.00 )
        
        if A.timeThreshold <= 0.00 then
            call UnitApplyTimedLife( A.dummy, 'BTLF', 0.30 )
            call SetUnitAnimation( A.dummy, "death" )
            call TimerStart( A.t, 0.05, true, function OpalescenceDamage )
            
            set TempUnit = CreateUnit( A.p, PhaseID, A.l.x, A.l.y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( TempUnit, 'Arav' )
            call SetUnitPositionEx( TempUnit, A.l.x, A.l.y )
            call SetUnitFlyHeight( TempUnit, 300.00, 0.00 )
            call SetUnitScale( TempUnit, 3.00, 3.00, 3.00 )
            call SetUnitAnimation( A.dummy, "birth" )
            call QueueUnitAnimation( A.dummy, "stand" )
            call UnitApplyTimedLife( TempUnit, 'BTLF', 0.50 )
            
            set t = CreateTimer( )
            call SaveUnitHandle( H, GetHandleId( t ), 0, TempUnit )
            call SaveReal( H, GetHandleId( t ), 1, 2.00 )
            call TimerStart( t, 0.01, true, function SetScale )
            
            //===
            
            set TempUnit = CreateUnit( A.p, HealingID, A.l.x, A.l.y, GetRandomReal( 0.00, 360.00 ) )
            call SetUnitPositionEx( TempUnit, A.l.x, A.l.y )
            call SetUnitScale( TempUnit, 2.00, 2.00, 2.00 )
            call SetUnitAnimation( TempUnit, "death" )
            
            set t = CreateTimer( )
            call SaveUnitHandle( H, GetHandleId( t ), 0, TempUnit )
            call SaveReal( H, GetHandleId( t ), 1, 2.00 )
            call TimerStart( t, 0.01, true, function SetScale_1 )
            
            //===
            
            set TempUnit = CreateUnit( A.p, PhaseID, A.l.x, A.l.y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( TempUnit, 'Arav' )
            call SetUnitPositionEx( TempUnit, A.l.x, A.l.y )
            call SetUnitFlyHeight( TempUnit, 300.00, 0.00 )
            call SetUnitScale( TempUnit, 2.00, 2.00, 2.00 )
            call SetUnitAnimation( A.dummy, "birth" )
            call QueueUnitAnimation( A.dummy, "stand" )
            
            set t = CreateTimer( )
            call SaveUnitHandle( H, GetHandleId( t ), 0, TempUnit )
            call SaveReal( H, GetHandleId( t ), 1, 2.00 )
            call TimerStart( t, 0.01, true, function SetScale )
            
            set t = null
            
            set i = 10
            
            loop
                set B = OpalescenceMoveS.create( )
                
                set B.t = CreateTimer( )
                
                set B.l[0] = vector.create( A.l.x + GetRandomReal( A.radius * 0.25, A.radius * 1.25 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), A.l.y + GetRandomReal( A.radius * 0.25, A.radius * 1.25 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), 0.00 )
                
                set TempUnit = CreateUnit( A.p, FaerieID, B.l[0].x, B.l[0].y, GetRandomReal( 0.00, 360.00 ) )
                call UnitAddAbility( TempUnit, 'Arav' )
                call SetUnitPositionEx( TempUnit, B.l[0].x, B.l[0].y )
                call SetUnitFlyHeight( TempUnit, GetRandomReal( 0.00, 100.00 ), 0.00 )
                call SetUnitAnimation( TempUnit, "death" )
                call UnitApplyTimedLife( TempUnit, 'BTLF', 1.00 )
                
                set B.l[0].x = A.l.x + GetRandomReal( 15.00, 50.00 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) )
                set B.l[0].y = A.l.y + GetRandomReal( 15.00, 50.00 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) )
                set B.l[0].z = GetLocZ( B.l[0].x, B.l[0].y ) + GetRandomReal( 400.00, 600.00 )
                
                set j = 1
                
                loop
                    set B.l[j] = vector.create( A.l.x + GetRandomReal( A.radius * 1.80, A.radius * 2.00 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), A.l.y + GetRandomReal( A.radius * 1.80, A.radius * 2.00 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), 0.00 )
                    set B.l[j].z = GetLocZ( B.l[j].x, B.l[j].y ) + GetRandomReal( -100.00, 650.00 )
                    
                    set j = j + 1
                    exitwhen j >= 5
                endloop
                
                set B.l[j] = vector.create( A.l.x + GetRandomReal( A.radius * 0.80, A.radius ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), A.l.y + GetRandomReal( A.radius * 0.80, A.radius ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), 0.00 )
                set B.l[j].z = GetLocZ( B.l[j].x, B.l[j].y ) + GetRandomReal( 50.00, 100.00 )
                    
                set B.last = vector.create( B.l[0].x, B.l[0].y, B.l[0].z )
                
                set B.time = 0.00
                set B.timeMax = 0.70
                
                set B.dummy = CreateUnit( A.p, FaerieID, B.l[0].x, B.l[0].y, GetRandomReal( 0.00, 360.00 ) )
                call UnitAddAbility( B.dummy, 'Arav' )
                call SetUnitPositionEx( B.dummy, B.l[0].x, B.l[0].y )
                call SetUnitFlyHeight( B.dummy, B.l[0].z - GetLocZ( B.l[0].x, B.l[0].y ), 0.00 )
                call SetUnitAnimation( B.dummy, "birth" )
                call QueueUnitAnimation( B.dummy, "stand" )
                call SetUnitVertexColor( B.dummy, 255, 255, 255, 0 )
                
                call SaveInteger( H, GetHandleId( B.t ), 0, B )
                call TimerStart( B.t, 0.03125, true, function OpalescenceMove )
                
                set i = i - 1
                exitwhen i < 0
            endloop
        endif
    else
        set A.time = A.time - 0.05
        
        call GroupEnumUnitsInRange( TempGroup, A.l.x, A.l.y, A.radius + 200.00, 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\\Spells\\Items\\WandOfNeutralization\\NeutralizationMissile.mdl", u, AttachPointName[GetRandomInt( 0, 5 )] ) )
                    
                    if A.damage >= 0.00 then
                        call UnitDamageTarget( A.caster, u, A.damage, false, false, null, null, null )
                    endif
                endif
            endif
        endloop
        
        if A.time >= 0.70 then
            set B = OpalescenceMoveS.create( )
            
            set B.t = CreateTimer( )
            
            set B.l[0] = vector.create( A.l.x + GetRandomReal( A.radius * 0.25, A.radius * 1.25 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), A.l.y + GetRandomReal( A.radius * 0.25, A.radius * 1.25 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), 0.00 )
            
            set TempUnit = CreateUnit( A.p, FaerieID, B.l[0].x, B.l[0].y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( TempUnit, 'Arav' )
            call SetUnitPositionEx( TempUnit, B.l[0].x, B.l[0].y )
            call SetUnitFlyHeight( TempUnit, GetRandomReal( 0.00, 100.00 ), 0.00 )
            call SetUnitAnimation( TempUnit, "death" )
            call UnitApplyTimedLife( TempUnit, 'BTLF', 1.00 )
            
            set B.l[0].x = A.l.x + GetRandomReal( 15.00, 50.00 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) )
            set B.l[0].y = A.l.y + GetRandomReal( 15.00, 50.00 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) )
            set B.l[0].z = GetLocZ( B.l[0].x, B.l[0].y ) + GetRandomReal( 400.00, 600.00 )
            
            set j = 1
            
            loop
                set B.l[j] = vector.create( A.l.x + GetRandomReal( A.radius * 1.80, A.radius * 2.00 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), A.l.y + GetRandomReal( A.radius * 1.80, A.radius * 2.00 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), 0.00 )
                set B.l[j].z = GetLocZ( B.l[j].x, B.l[j].y ) + GetRandomReal( -100.00, 650.00 )
                
                set j = j + 1
                exitwhen j >= 5
            endloop
            
            set B.l[j] = vector.create( A.l.x + GetRandomReal( A.radius * 0.80, A.radius ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), A.l.y + GetRandomReal( A.radius * 0.80, A.radius ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), 0.00 )
            set B.l[j].z = GetLocZ( B.l[j].x, B.l[j].y ) + GetRandomReal( 50.00, 100.00 )
            
            set B.last = vector.create( B.l[0].x, B.l[0].y, B.l[0].z )
            
            set B.time = 0.00
            set B.timeMax = 0.70
            
            set B.dummy = CreateUnit( A.p, FaerieID, B.l[0].x, B.l[0].y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( B.dummy, 'Arav' )
            call SetUnitPositionEx( B.dummy, B.l[0].x, B.l[0].y )
            call SetUnitFlyHeight( B.dummy, B.l[0].z - GetLocZ( B.l[0].x, B.l[0].y ), 0.00 )
            call SetUnitAnimation( B.dummy, "birth" )
            call QueueUnitAnimation( B.dummy, "stand" )
            call SetUnitVertexColor( B.dummy, 255, 255, 255, 0 )
            
            call SaveInteger( H, GetHandleId( B.t ), 0, B )
            call TimerStart( B.t, 0.03125, true, function OpalescenceMove )
        elseif A.time <= 0.00 then
            call PauseTimer( A.t )
            call FlushChildHashtable( H, GetHandleId( A.t ) )
            call DestroyTimer( A.t )
            
            set A.t = null
            set A.dummy = null
            set A.caster = null
            call A.l.destroy( )
            call A.v.destroy( )
            call A.endPos.destroy( )
            call A.destroy( )
        endif
    endif
endfunction

function Opalescence_Actions takes nothing returns nothing
    local OpalescenceS A = OpalescenceS.create( )
    
    set A.t = CreateTimer( )
    set A.caster = GetTriggerUnit( )
    set A.p = GetOwningPlayer( A.caster )
    set A.damage = 5.00 // пошкодження
    set A.radius = 400.00 // радіус
    set A.time = 4.00 // час
    set A.timeThreshold = 0.70
    
    set A.l = vector.create( GetUnitX( A.caster ), GetUnitY( A.caster ), 0.00 )
    set A.l.z = GetLocZ( A.l.x, A.l.y )
    set A.endPos = vector.create( GetSpellTargetX( ), GetSpellTargetY( ), 0.00 )
    set A.endPos.z = GetLocZ( A.endPos.x, A.endPos.y ) + 300.00
    
    set A.v = vector.create( A.endPos.x - A.l.x, A.endPos.y - A.l.y, A.endPos.z - A.l.z )
    set A.speed = A.v.length( ) * 1.30 * 0.01
    call A.v.normalize( )
    
    set A.dummy = CreateUnit( A.p, AbolishID, A.l.x, A.l.y, Atan2( A.v.y, A.v.x ) * bj_RADTODEG )
    call UnitAddAbility( A.dummy, 'Arav' )
    call SetUnitX( A.dummy, A.l.x )
    call SetUnitY( A.dummy, A.l.y )
    call SetUnitScale( A.dummy, 2.00, 2.00, 2.00 )
    call UnitApplyTimedLife( A.dummy, 'BTLF', 1.00 )
    call SetUnitAnimation( A.dummy, "birth" )
    call QueueUnitAnimation( A.dummy, "stand" )
    
    call SaveInteger( H, GetHandleId( A.t ), 0, A )
    call TimerStart( A.t, 0.01, true, function OpalescenceDamage )
endfunction

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

function InitTrig_Opalescence takes nothing returns nothing
    local rect r = GetWorldBounds( )
    
    set gg_trg_Opalescence = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Opalescence, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Opalescence, Condition( function Opalescence_Conditions ) )
    call TriggerAddAction( gg_trg_Opalescence, function Opalescence_Actions )
    
    set AttachPointName[0] = "chest"
    set AttachPointName[1] = "head"
    set AttachPointName[2] = "left hand"
    set AttachPointName[3] = "right hand"
    set AttachPointName[4] = "left foot"
    set AttachPointName[5] = "right foot"
    
    set MaxX = GetRectMaxX( r ) - 32.00
    set MinX = GetRectMinX( r ) + 32.00
    set MaxY = GetRectMaxY( r ) - 32.00
    set MinY = GetRectMinY( r ) + 32.00
    
    call RemoveRect( r )
    
    set r = null
endfunction
endlibrary

инструкция по импорту
  • скопировать триггер Opalescence и вставить в карту
  • заполнить равкоды в 7-11 строках кода Opalescence в соответствии с объектами из ро
  • создать переменную с названием TempUnit для гуи пользования
отделённые комментарием триггеры не нужны, один для гуи примера, другой для показа урона
если нужна дополнительная помощь, а-ля изменить точки полёта, формулу, эффект при уроне, больше сгустков добавить, убрать какие-то лишние элементы и т.п., без проблем сделаю или поясню как сделать самому


я не думаю что кто-то всерьёз будет этот спелл где-то использовать, так что просто демонстрирую идею
`
ОЖИДАНИЕ РЕКЛАМЫ...
11
Неплохая способка, вот только я думаю если будет много таких кастов подобных fps много не будет, а в больших картах это может стать важно.
28
frozenfail, тут просто метод выбран не тот, можно взять оптимизированный вариант построения кривых, но впрочем, ты прав, от множества подобных кастов может провисать, в этом так же виноват перебор группы, я нинаю что с этим делать кроме как уменьшения периодичности таймеров и не использовать гуи кондишены
16
Сильно нагрузка падает если убрать все эти расчеты в циклах и юзать формулу для определенного кол-ва точек. Например для 4 точек:
P0 * (1-t)^3 + P1 * t * (1-t)^2 + P2 * t^2 * (1-t) + P3 * t^3

По крайней мере я заметил что у меня такое было когда я сразу ~100 снарядов двигал, разница была ощутима прям.
28
OVOgenez, а если тебе вдруг понадобится динамическое количество опорных точек? ты будешь через if then else проверять каждый вариант?
думаешь рассчёт в цикле может вызывать лаги?)
возможно, единственная функция которая вызывает просадку из предложенного тобой варианта это Pow, я не знаю насколько она ресурсоёмкая, да и с каждым увеличением опорной точки твоя полинома будет расти, из-за чего удобнее для читаемости перенести уже в цикл
у меня лично лагает от количества снарядов, периодичности и весомо перебор группы, тем более когда гуи юзается
и всё же лучшим вариантом будет воспользоваться оптимизированной версией, даже в проверке фпс разница была в 40% где-то (45 и 65)
28
Вышла новая версия! Прокрутить к ресурсу
обновил код, убрал всё, что было сделано для гуишников, дамажит теперь всех врагов, а не только нежить, оставил старую версию
Чтобы оставить комментарий, пожалуйста, войдите на сайт.