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

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

(атакующая)

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

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

private function RegistPascalTriangle takes integer i returns nothing
    local integer n
    local integer k
    
    if i > PascalTriangleRows then
        set n = PascalTriangleRows
        
        loop
            set k = 0
            
            loop
                call SaveInteger( PascalTriangle, n, k, LoadInteger( PascalTriangle, n - 1, k - 1 ) + LoadInteger( PascalTriangle, n - 1, k ) )
                
                set k = k + 1
                exitwhen k > n
            endloop
            
            set n = n + 1
            exitwhen n > i
        endloop
    
        set PascalTriangleRows = i
    endif
endfunction

private struct Point
    real x
    real y
    real z
    
    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 Bezier
    unit dummy
    integer pCount
    
    real time
    real timeMax
    
    Point array p[6]
    Point last
endstruct

private struct OpalescenceS
    unit dummy
    unit caster
    player p
    boolexpr b
    
    Point pos
    Point trg
    real ax
    real ay
    real speed
    real damage
    real radius
    real time
    real timeThreshold
endstruct

private function OpalescenceMove takes nothing returns nothing
    local Bezier A = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local real x = 0.00
    local real y = 0.00
    local real z = 0.00
    local real r
    local integer i = 0
    
    set A.time = A.time + ( 0.03125 / A.timeMax )
    
    if A.time > 1.00 then
        set A.time = 1.00
    endif
    
    loop
        set r = LoadInteger( PascalTriangle, A.pCount, i ) * Pow( 1.00 - A.time, A.pCount - i ) * Pow( A.time, i )
        set x = x + r * A.p[i].x
        set y = y + r * A.p[i].y
        set z = z + r * A.p[i].z
        
        set i = i + 1
        exitwhen i > A.pCount
    endloop
    
    call SetUnitPositionEx( A.dummy, x, y )
    call SetUnitFlyHeight( A.dummy, z - GetLocZ( x, y ), 0.00 )
    call SetUnitFacing( A.dummy, Atan2( y - A.last.y, x - A.last.x ) * bj_RADTODEG )
    
    if A.time >= 1.00 then
        call PauseTimer( GetExpiredTimer( ) )
        call FlushChildHashtable( H, GetHandleId( GetExpiredTimer( ) ) )
        call DestroyTimer( GetExpiredTimer( ) )
        
        loop
            call A.p[A.pCount].destroy( )
            
            set A.pCount = A.pCount - 1
            exitwhen A.pCount < 0
        endloop
        
        call A.last.destroy( )
        
        call SetUnitAnimation( A.dummy, "death" )
        call UnitApplyTimedLife( A.dummy, 'BTLF', 2.00 )
        
        set A.dummy = null
        call A.destroy( )
    else
        set A.last.x = x
        set A.last.y = y
        set A.last.z = z
    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 B = LoadInteger( H, GetHandleId( GetExpiredTimer( ) ), 0 )
    local Bezier A
    local unit u
    local timer t
    local integer i
    local integer i1
    local real x
    local real y
    
    if B.timeThreshold > 0.00 then
        if B.timeThreshold == 0.20 then
            set TempUnit = CreateUnit( B.p, WispID, B.trg.x, B.trg.y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( TempUnit, 'Arav' )
            call SetUnitPositionEx( B.dummy, B.trg.x, B.trg.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, B.time - 2.00 ) )
        endif
        
        set B.timeThreshold = B.timeThreshold - 0.01
        set B.pos.x = B.pos.x + B.speed * B.ax
        set B.pos.y = B.pos.y + B.speed * B.ay
        set B.pos.z = B.pos.z + 2.00
        
        call SetUnitPositionEx( B.dummy, B.pos.x, B.pos.y )
        call SetUnitFlyHeight( B.dummy, B.pos.z - GetLocZ( B.pos.x, B.pos.y ), 0.00 )
        
        if B.timeThreshold <= 0.00 then
            call UnitApplyTimedLife( B.dummy, 'BTLF', 0.30 )
            set B.dummy = null
            
            call B.pos.destroy( )
            
            set TempUnit = CreateUnit( B.p, PhaseID, B.trg.x, B.trg.y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( TempUnit, 'Arav' )
            call SetUnitPositionEx( TempUnit, B.trg.x, B.trg.y )
            call SetUnitFlyHeight( TempUnit, 300.00, 0.00 )
            call SetUnitScale( TempUnit, 3.00, 3.00, 3.00 )
            call SetUnitAnimation( TempUnit, "birth" )
            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( B.p, HealingID, B.trg.x, B.trg.y, GetRandomReal( 0.00, 360.00 ) )
            call SetUnitPositionEx( TempUnit, B.trg.x, B.trg.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( B.p, PhaseID, B.trg.x, B.trg.y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( TempUnit, 'Arav' )
            call SetUnitPositionEx( TempUnit, B.trg.x, B.trg.y )
            call SetUnitFlyHeight( TempUnit, 300.00, 0.00 )
            call SetUnitScale( TempUnit, 2.00, 2.00, 2.00 )
            call SetUnitAnimation( TempUnit, "birth" )
            
            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 i = 10
            
            loop
                set t = CreateTimer( )
                set A = Bezier.create( )
                
                set A.pCount = 5
            
                set x = B.trg.x + GetRandomReal( B.radius * 0.25, B.radius * 1.25 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) )
                set y = B.trg.y + GetRandomReal( B.radius * 0.25, B.radius * 1.25 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) )
                
                set TempUnit = CreateUnit( B.p, FaerieID, x, y, GetRandomReal( 0.00, 360.00 ) )
                call UnitAddAbility( TempUnit, 'Arav' )
                call SetUnitPositionEx( TempUnit, x, y )
                call SetUnitFlyHeight( TempUnit, GetRandomReal( 0.00, 100.00 ), 0.00 )
                call SetUnitAnimation( TempUnit, "death" )
                call UnitApplyTimedLife( TempUnit, 'BTLF', 1.00 )
                
                set A.p[0] = Point.create( B.trg.x + GetRandomReal( 15.00, 50.00 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), B.trg.y + GetRandomReal( 15.00, 50.00 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), GetRandomReal( 400.00, 600.00 ) )
                set A.p[0].z = A.p[0].z + GetLocZ( A.p[0].x, A.p[0].y )
                
                set i1 = 1
                
                loop
                    set A.p[i1] = Point.create( B.trg.x + GetRandomReal( B.radius * 1.80, B.radius * 2.00 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), B.trg.y + GetRandomReal( B.radius * 1.80, B.radius * 2.00 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), GetRandomReal( -100.00, 650.00 ) )
                    set A.p[i1].z = A.p[i1].z + GetLocZ( A.p[i1].x, A.p[i1].y )
                    
                    set i1 = i1 + 1
                    exitwhen i1 >= A.pCount
                endloop
                
                set A.p[i1] = Point.create( B.trg.x + GetRandomReal( B.radius * 0.25, B.radius * 1.25 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), B.trg.y + GetRandomReal( B.radius * 0.25, B.radius * 1.25 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), GetRandomReal( -50.00, 100.00 ) )
                set A.p[i1].z = A.p[i1].z + GetLocZ( A.p[i1].x, A.p[i1].y )
                
                set A.last = Point.create( A.p[0].x, A.p[0].y, A.p[0].z )
                
                set A.time = 0.00
                set A.timeMax = 0.70
                
                set A.dummy = CreateUnit( B.p, FaerieID, A.p[0].x, A.p[0].y, GetRandomReal( 0.00, 360.00 ) )
                call UnitAddAbility( A.dummy, 'Arav' )
                call SetUnitPositionEx( A.dummy, A.p[0].x, A.p[0].y )
                call SetUnitFlyHeight( A.dummy, A.p[0].z - GetLocZ( A.p[0].x, A.p[0].y ), 0.00 )
                call SetUnitAnimation( A.dummy, "birth" )
                call QueueUnitAnimation( A.dummy, "stand" )
                call SetUnitVertexColor( A.dummy, 255, 255, 255, 0 )
                
                call SaveInteger( H, GetHandleId( t ), 0, A )
                call TimerStart( t, 0.03125, true, function OpalescenceMove )
                
                set i = i - 1
                exitwhen i < 0
            endloop
            
            call TimerStart( GetExpiredTimer( ), 0.05, true, function OpalescenceDamage )
            
            set t = null
        endif
    else
        set B.time = B.time - 0.05
        
        set udg_TempUnit = B.caster
        call GroupEnumUnitsInRange( TempGroup, B.trg.x, B.trg.y, B.radius + 200.00, B.b )
        
        loop
            set u = FirstOfGroup( TempGroup )
            exitwhen u == null
            call GroupRemoveUnit( TempGroup, u )
            
            if IsUnitInRangeXY( u, B.trg.x, B.trg.y, B.radius ) then
                call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Items\\WandOfNeutralization\\NeutralizationMissile.mdl", u, AttachPointName[GetRandomInt( 0, 5 )] ) )
                
                if B.damage >= 0.00 then
                    call UnitDamageTarget( B.caster, u, B.damage, false, false, null, null, null )
                else
                    call SetWidgetLife( u, GetWidgetLife( u ) - B.damage )
                endif
            endif
        endloop
        
        if B.time >= 0.70 then
            set t = CreateTimer( )
            set A = Bezier.create( )
            
            set A.pCount = 5
            
            set A.p[0] = Point.create( B.trg.x + GetRandomReal( 15.00, 50.00 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), B.trg.y + GetRandomReal( 15.00, 50.00 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), 300.00 )
            set A.p[0].z = A.p[0].z + GetLocZ( A.p[0].x, A.p[0].y )
                
            set i1 = 1
            
            loop
                set A.p[i1] = Point.create( B.trg.x + GetRandomReal( B.radius * 1.80, B.radius * 2.00 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), B.trg.y + GetRandomReal( B.radius * 1.80, B.radius * 2.00 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), GetRandomReal( -100.00, 650.00 ) )
                set A.p[i1].z = A.p[i1].z + GetLocZ( A.p[i1].x, A.p[i1].y )
                
                set i1 = i1 + 1
                exitwhen i1 >= A.pCount
            endloop
            
            set A.p[i1] = Point.create( B.trg.x + GetRandomReal( B.radius * 0.25, B.radius * 1.25 ) * Cos( GetRandomReal( -bj_PI, bj_PI ) ), B.trg.y + GetRandomReal( B.radius * 0.25, B.radius * 1.25 ) * Sin( GetRandomReal( -bj_PI, bj_PI ) ), GetRandomReal( -50.00, 100.00 ) )
            set A.p[i1].z = A.p[i1].z + GetLocZ( A.p[i1].x, A.p[i1].y )
            
            set A.last = Point.create( A.p[0].x, A.p[0].y, A.p[0].z )
            
            set A.time = 0.00
            set A.timeMax = 0.70
            
            set A.dummy = CreateUnit( B.p, FaerieID, A.p[0].x, A.p[0].y, GetRandomReal( 0.00, 360.00 ) )
            call UnitAddAbility( A.dummy, 'Arav' )
            call SetUnitPositionEx( A.dummy, A.p[0].x, A.p[0].y )
            call SetUnitFlyHeight( A.dummy, A.p[0].z - GetLocZ( A.p[0].x, A.p[0].y ), 0.00 )
            call SetUnitAnimation( A.dummy, "birth" )
            call QueueUnitAnimation( A.dummy, "stand" )
            call SetUnitVertexColor( A.dummy, 255, 255, 255, 0 )
            
            call SaveInteger( H, GetHandleId( t ), 0, A )
            call TimerStart( t, 0.03125, true, function OpalescenceMove )
            
            set t = null
        elseif B.time <= 0.00 then
            set t = GetExpiredTimer( )
            
            call PauseTimer( t )
            call FlushChildHashtable( H, GetHandleId( t ) )
            call DestroyTimer( t )
            
            set t = null
            set B.caster = null
            
            call B.trg.destroy( )
            call B.destroy( )
        endif
    endif
endfunction

function Opalescence_Actions takes unit u, real damage, real radius, real time, boolexpr b returns nothing
    local timer t = CreateTimer( )
    local OpalescenceS A = OpalescenceS.create( )
    
    set A.caster = u
    set A.p = GetOwningPlayer( u )
    set A.b = b
    set A.damage = damage
    set A.radius = radius
    set A.time = time
    set A.timeThreshold = 0.70
    
    set A.pos = Point.create( GetUnitX( u ), GetUnitY( u ), 0.00 )
    set A.pos.z = A.pos.z + GetLocZ( A.pos.x, A.pos.y )
    set A.trg = Point.create( GetSpellTargetX( ), GetSpellTargetY( ), 0.00 )
    set A.trg.z = A.trg.z + GetLocZ( A.trg.x, A.trg.y )
    
    set A.ax = Atan2( A.trg.y - A.pos.y, A.trg.x - A.pos.x )
    set A.ay = Sin( A.ax )
    set A.ax = Cos( A.ax )
    
    set A.speed = SquareRoot( ( A.trg.x - A.pos.x ) * ( A.trg.x - A.pos.x ) + ( A.trg.y - A.pos.y ) * ( A.trg.y - A.pos.y ) ) * 1.30 * 0.01
    
    set A.dummy = CreateUnit( A.p, AbolishID, A.pos.x, A.pos.y, Atan2( A.ay, A.ax ) * bj_RADTODEG )
    call UnitAddAbility( A.dummy, 'Arav' )
    call SetUnitX( A.dummy, A.pos.x )
    call SetUnitY( A.dummy, A.pos.y )
    call SetUnitScale( A.dummy, 2.00, 2.00, 2.00 )
    call UnitApplyTimedLife( A.dummy, 'BTLF', 1.00 )
    
    call SaveInteger( H, GetHandleId( t ), 0, A )
    call TimerStart( t, 0.01, true, function OpalescenceDamage )
    
    set t = null
endfunction

//===========================================================================
function InitTrig_Opalescence takes nothing returns nothing
    local rect r = GetWorldBounds( )
    //set gg_trg_Opalescence = CreateTrigger(  )
    
    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"
    
    call SaveInteger( PascalTriangle, 0, 0, 1 )
    call SaveInteger( PascalTriangle, 1, 0, 1 )
    call SaveInteger( PascalTriangle, 1, 1, 1 )
    
    call RegistPascalTriangle( 5 )
    
    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)
Чтобы оставить комментарий, пожалуйста, войдите на сайт.